Array.concat() should work on runtime arrays too.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Jul 2014 04:18:35 +0000 (04:18 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Jul 2014 04:18:35 +0000 (04:18 +0000)
<https://webkit.org/b/135179>

Reviewed by Geoffrey Garen.

* jsc.cpp:
(WTF::RuntimeArray::create):
(WTF::RuntimeArray::~RuntimeArray):
(WTF::RuntimeArray::destroy):
(WTF::RuntimeArray::getOwnPropertySlot):
(WTF::RuntimeArray::getOwnPropertySlotByIndex):
(WTF::RuntimeArray::put):
(WTF::RuntimeArray::deleteProperty):
(WTF::RuntimeArray::getLength):
(WTF::RuntimeArray::createPrototype):
(WTF::RuntimeArray::createStructure):
(WTF::RuntimeArray::finishCreation):
(WTF::RuntimeArray::RuntimeArray):
(WTF::RuntimeArray::lengthGetter):
(GlobalObject::finishCreation):
(functionCreateRuntimeArray):
- Added support to create a runtime array for testing purpose.
* runtime/ArrayPrototype.cpp:
(JSC::getLength):
- Added fast case for when the array object is a JSArray.
(JSC::arrayProtoFuncJoin):
- Added a needed but missing exception check.
(JSC::arrayProtoFuncConcat):
- Use getLength() to compute the array length instead of assuming that
  the array is a JSArray instance.
* tests/stress/regexp-matches-array.js: Added.
(testArrayConcat):
* tests/stress/runtime-array.js: Added.
(testArrayConcat):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@171390 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/ArrayPrototype.cpp
Source/JavaScriptCore/tests/stress/regexp-matches-array.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/runtime-array.js [new file with mode: 0644]

index 41a09e6..0d7b504 100644 (file)
@@ -1,3 +1,40 @@
+2014-07-22  Mark Lam  <mark.lam@apple.com>
+
+        Array.concat() should work on runtime arrays too.
+        <https://webkit.org/b/135179>
+
+        Reviewed by Geoffrey Garen.
+
+        * jsc.cpp:
+        (WTF::RuntimeArray::create):
+        (WTF::RuntimeArray::~RuntimeArray):
+        (WTF::RuntimeArray::destroy):
+        (WTF::RuntimeArray::getOwnPropertySlot):
+        (WTF::RuntimeArray::getOwnPropertySlotByIndex):
+        (WTF::RuntimeArray::put):
+        (WTF::RuntimeArray::deleteProperty):
+        (WTF::RuntimeArray::getLength):
+        (WTF::RuntimeArray::createPrototype):
+        (WTF::RuntimeArray::createStructure):
+        (WTF::RuntimeArray::finishCreation):
+        (WTF::RuntimeArray::RuntimeArray):
+        (WTF::RuntimeArray::lengthGetter):
+        (GlobalObject::finishCreation):
+        (functionCreateRuntimeArray):
+        - Added support to create a runtime array for testing purpose.
+        * runtime/ArrayPrototype.cpp:
+        (JSC::getLength):
+        - Added fast case for when the array object is a JSArray.
+        (JSC::arrayProtoFuncJoin):
+        - Added a needed but missing exception check.
+        (JSC::arrayProtoFuncConcat):
+        - Use getLength() to compute the array length instead of assuming that
+          the array is a JSArray instance.
+        * tests/stress/regexp-matches-array.js: Added.
+        (testArrayConcat):
+        * tests/stress/runtime-array.js: Added.
+        (testArrayConcat):
+
 2014-07-22  Brent Fulgham  <bfulgham@apple.com>
 
         Fix Windows (return a value!)
index 363fb44..13f65e3 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include "ArrayPrototype.h"
 #include "ButterflyInlines.h"
 #include "BytecodeGenerator.h"
 #include "Completion.h"
@@ -114,6 +115,7 @@ class Element;
 class ElementHandleOwner;
 class Masuqerader;
 class Root;
+class RuntimeArray;
 
 class Element : public JSNonFinalObject {
 public:
@@ -293,10 +295,117 @@ private:
     WriteBarrier<JSObject> m_delegate;
 };
 
+class RuntimeArray : public JSArray {
+public:
+    typedef JSArray Base;
+
+    static RuntimeArray* create(ExecState* exec)
+    {
+        VM& vm = exec->vm();
+        JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+        Structure* structure = createStructure(vm, globalObject, createPrototype(vm, globalObject));
+        RuntimeArray* runtimeArray = new (NotNull, allocateCell<RuntimeArray>(*exec->heap())) RuntimeArray(exec, structure);
+        runtimeArray->finishCreation(exec);
+        vm.heap.addFinalizer(runtimeArray, destroy);
+        return runtimeArray;
+    }
+
+    ~RuntimeArray() { }
+
+    static void destroy(JSCell* cell)
+    {
+        static_cast<RuntimeArray*>(cell)->RuntimeArray::~RuntimeArray();
+    }
+
+    static const bool needsDestruction = false;
+
+    static bool getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+    {
+        RuntimeArray* thisObject = jsCast<RuntimeArray*>(object);
+        if (propertyName == exec->propertyNames().length) {
+            slot.setCacheableCustom(thisObject, DontDelete | ReadOnly | DontEnum, thisObject->lengthGetter);
+            return true;
+        }
+
+        unsigned index = propertyName.asIndex();
+        if (index < thisObject->getLength()) {
+            ASSERT(index != PropertyName::NotAnIndex);
+            slot.setValue(thisObject, DontDelete | DontEnum, jsNumber(thisObject->m_vector[index]));
+            return true;
+        }
+
+        return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+    }
+
+    static bool getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot)
+    {
+        RuntimeArray* thisObject = jsCast<RuntimeArray*>(object);
+        if (index < thisObject->getLength()) {
+            slot.setValue(thisObject, DontDelete | DontEnum, jsNumber(thisObject->m_vector[index]));
+            return true;
+        }
+
+        return JSObject::getOwnPropertySlotByIndex(thisObject, exec, index, slot);
+    }
+
+    static NO_RETURN_DUE_TO_CRASH void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&)
+    {
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    static NO_RETURN_DUE_TO_CRASH bool deleteProperty(JSCell*, ExecState*, PropertyName)
+    {
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    unsigned getLength() const { return m_vector.size(); }
+
+    DECLARE_INFO;
+
+    static ArrayPrototype* createPrototype(VM&, JSGlobalObject* globalObject)
+    {
+        return globalObject->arrayPrototype();
+    }
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
+    }
+
+protected:
+    void finishCreation(ExecState* exec)
+    {
+        Base::finishCreation(exec->vm());
+        ASSERT(inherits(info()));
+
+        for (size_t i = 0; i < exec->argumentCount(); i++)
+            m_vector.append(exec->argument(i).toInt32(exec));
+    }
+
+    static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | JSArray::StructureFlags;
+
+private:
+    RuntimeArray(ExecState* exec, Structure* structure)
+        : JSArray(exec->vm(), structure, 0)
+    {
+    }
+
+    static EncodedJSValue lengthGetter(ExecState* exec, JSObject*, EncodedJSValue thisValue, PropertyName)
+    {
+        RuntimeArray* thisObject = jsDynamicCast<RuntimeArray*>(JSValue::decode(thisValue));
+        if (!thisObject)
+            return throwVMTypeError(exec);
+        return JSValue::encode(jsNumber(thisObject->getLength()));
+    }
+
+    Vector<int> m_vector;
+};
+
 const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Element) };
 const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Masquerader) };
 const ClassInfo Root::s_info = { "Root", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Root) };
 const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ImpureGetter) };
+const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(RuntimeArray) };
 
 ElementHandleOwner* Element::handleOwner()
 {
@@ -317,6 +426,7 @@ void Element::finishCreation(VM& vm)
 static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer);
 
 static EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionCreateRuntimeArray(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState*);
 
@@ -502,6 +612,7 @@ protected:
         addFunction(vm, "makeMasquerader", functionMakeMasquerader, 0);
 
         addFunction(vm, "createProxy", functionCreateProxy, 1);
+        addFunction(vm, "createRuntimeArray", functionCreateRuntimeArray, 0);
 
         addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
         addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);
@@ -675,6 +786,13 @@ EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState* exec)
     return JSValue::encode(proxy);
 }
 
+EncodedJSValue JSC_HOST_CALL functionCreateRuntimeArray(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    RuntimeArray* array = RuntimeArray::create(exec);
+    return JSValue::encode(array);
+}
+
 EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState* exec)
 {
     JSLockHolder lock(exec);
index f602c1a..d8e48ba 100644 (file)
@@ -159,6 +159,8 @@ static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* obj, unsigne
 
 static ALWAYS_INLINE unsigned getLength(ExecState* exec, JSObject* obj)
 {
+    if (isJSArray(obj))
+        return jsCast<JSArray*>(obj)->length();
     return obj->get(exec, exec->propertyNames().length).toUInt32(exec);
 }
 
@@ -412,6 +414,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
 
     for (; k < length; k++) {
         JSValue element = thisObj->get(exec, k);
+        if (exec->hadException())
+            return JSValue::encode(jsUndefined());
         if (!element.isUndefinedOrNull())
             stringJoiner.append(element.toWTFStringInline(exec));
         else
@@ -429,9 +433,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
     Checked<unsigned, RecordOverflow> finalArraySize = 0;
 
     for (size_t i = 0;;) {
-        if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg))
-            finalArraySize += currentArray->length();
-        else
+        if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
+            finalArraySize += getLength(exec, currentArray);
+            if (exec->hadException())
+                return JSValue::encode(jsUndefined());
+        } else
             finalArraySize++;
         if (i == argCount)
             break;
@@ -450,7 +456,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
     unsigned n = 0;
     for (size_t i = 0;;) {
         if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
-            unsigned length = currentArray->length();
+            unsigned length = getLength(exec, currentArray);
+            if (exec->hadException())
+                return JSValue::encode(jsUndefined());
             for (unsigned k = 0; k < length; ++k) {
                 JSValue v = getProperty(exec, currentArray, k);
                 if (exec->hadException())
diff --git a/Source/JavaScriptCore/tests/stress/regexp-matches-array.js b/Source/JavaScriptCore/tests/stress/regexp-matches-array.js
new file mode 100644 (file)
index 0000000..6ccb27a
--- /dev/null
@@ -0,0 +1,14 @@
+function testArrayConcat() {
+    var array = 'abc'.match(/(a)(b)(c)/);
+    var result = array.concat();
+    var expectedResult = ["abc", "a", "b", "c"];
+
+    if (result.length != 4)
+        throw new Error("Runtime array length is incorrect");
+    for (var i = 0; i < result.length; i++) {
+        if (result[i] != expectedResult[i])
+            throw new Error("Runtime array concat result is incorrect");
+    }
+};
+
+testArrayConcat();
diff --git a/Source/JavaScriptCore/tests/stress/runtime-array.js b/Source/JavaScriptCore/tests/stress/runtime-array.js
new file mode 100644 (file)
index 0000000..aee6fd7
--- /dev/null
@@ -0,0 +1,13 @@
+function testArrayConcat() {
+    var array = createRuntimeArray(1, 2, 3);
+    var result = array.concat();
+
+    if (result.length != 3)
+        throw new Error("Runtime array length is incorrect");
+    for (var i = 0; i < result.length; i++) {
+        if (result[i] != i + 1)
+            throw new Error("Runtime array concat result is incorrect");
+    }
+};
+
+testArrayConcat();