Title: [202067] trunk
Revision
202067
Author
[email protected]
Date
2016-06-14 15:34:09 -0700 (Tue, 14 Jun 2016)

Log Message

The Array species constructor watchpoints should be created the first time they are needed rather than on creation
https://bugs.webkit.org/show_bug.cgi?id=158754

Reviewed by Benjamin Poulain.

Source/_javascript_Core:

We use adaptive watchpoints for some Array prototype functions to
ensure that the user has not overridden the value of the
Array.prototype.constructor or Array[Symbol.species]. This patch
changes when the Array species constructor watchpoints are
initialized. Before, those watchpoints would be created when the
global object is initialized. This had the advantage that it did
not require validating the constructor and Symbol.species
properties. On the other hand, it also meant that if the user were
to reconfigure properties Array.prototype, which would cause the
structure of the property to become an uncachable dictionary,
prior to running code that the watchpoints would be
invalidated. It turns out that JSBench amazon, for instance, does
reconfigure some of Array.prototype's properties. This patch
initializes the watchpoints the first time they are needed. Since
we only initialize once we also flatten the structure of Array and
Array.prototype.

* runtime/ArrayPrototype.cpp:
(JSC::speciesConstructArray):
(JSC::ArrayPrototype::attemptToInitializeSpeciesWatchpoint):
(JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
(JSC::ArrayPrototype::setConstructor): Deleted.
* runtime/ArrayPrototype.h:
(JSC::ArrayPrototype::speciesWatchpointStatus):
(JSC::ArrayPrototype::didChangeConstructorOrSpeciesProperties): Deleted.
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::speciesGetterSetter):
(JSC::JSGlobalObject::arrayConstructor):
* tests/stress/array-symbol-species-lazy-watchpoints.js: Added.
(test):
(arrayEq):
(A):

LayoutTests:

Add new micro-benchmark that tests the impact of lazily
initializing the array species watchpoints.

* js/regress/lazy-array-species-watchpoints-expected.txt: Added.
* js/regress/lazy-array-species-watchpoints.html: Added.
* js/regress/script-tests/lazy-array-species-watchpoints.js: Added.
(test):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (202066 => 202067)


--- trunk/LayoutTests/ChangeLog	2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/LayoutTests/ChangeLog	2016-06-14 22:34:09 UTC (rev 202067)
@@ -1,3 +1,18 @@
+2016-06-14  Keith Miller  <[email protected]>
+
+        The Array species constructor watchpoints should be created the first time they are needed rather than on creation
+        https://bugs.webkit.org/show_bug.cgi?id=158754
+
+        Reviewed by Benjamin Poulain.
+
+        Add new micro-benchmark that tests the impact of lazily
+        initializing the array species watchpoints.
+
+        * js/regress/lazy-array-species-watchpoints-expected.txt: Added.
+        * js/regress/lazy-array-species-watchpoints.html: Added.
+        * js/regress/script-tests/lazy-array-species-watchpoints.js: Added.
+        (test):
+
 2016-06-14  Benjamin Poulain  <[email protected]>
 
         Add the unprefixed version of the pseudo element ::placeholder

Added: trunk/LayoutTests/js/regress/lazy-array-species-watchpoints-expected.txt (0 => 202067)


--- trunk/LayoutTests/js/regress/lazy-array-species-watchpoints-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/regress/lazy-array-species-watchpoints-expected.txt	2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,10 @@
+JSRegress/lazy-array-species-watchpoints
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/regress/lazy-array-species-watchpoints.html (0 => 202067)


--- trunk/LayoutTests/js/regress/lazy-array-species-watchpoints.html	                        (rev 0)
+++ trunk/LayoutTests/js/regress/lazy-array-species-watchpoints.html	2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/regress/script-tests/lazy-array-species-watchpoints.js (0 => 202067)


--- trunk/LayoutTests/js/regress/script-tests/lazy-array-species-watchpoints.js	                        (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/lazy-array-species-watchpoints.js	2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,12 @@
+Object.defineProperty(Array.prototype, "sort", { writable: false, value: Array.prototype.sort });
+
+function test(array) {
+    array = array.splice(2, 2);
+    array = array.slice(0, 5);
+    array = array.concat([1,2,3]);
+    return array;
+}
+noInline(test);
+
+for (let i = 0; i < 100000; i++)
+    test([1,2,3,4,5,6,7,8,9]);

Modified: trunk/Source/_javascript_Core/ChangeLog (202066 => 202067)


--- trunk/Source/_javascript_Core/ChangeLog	2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-06-14 22:34:09 UTC (rev 202067)
@@ -1,5 +1,47 @@
 2016-06-14  Keith Miller  <[email protected]>
 
+        The Array species constructor watchpoints should be created the first time they are needed rather than on creation
+        https://bugs.webkit.org/show_bug.cgi?id=158754
+
+        Reviewed by Benjamin Poulain.
+
+        We use adaptive watchpoints for some Array prototype functions to
+        ensure that the user has not overridden the value of the
+        Array.prototype.constructor or Array[Symbol.species]. This patch
+        changes when the Array species constructor watchpoints are
+        initialized. Before, those watchpoints would be created when the
+        global object is initialized. This had the advantage that it did
+        not require validating the constructor and Symbol.species
+        properties. On the other hand, it also meant that if the user were
+        to reconfigure properties Array.prototype, which would cause the
+        structure of the property to become an uncachable dictionary,
+        prior to running code that the watchpoints would be
+        invalidated. It turns out that JSBench amazon, for instance, does
+        reconfigure some of Array.prototype's properties. This patch
+        initializes the watchpoints the first time they are needed. Since
+        we only initialize once we also flatten the structure of Array and
+        Array.prototype.
+
+        * runtime/ArrayPrototype.cpp:
+        (JSC::speciesConstructArray):
+        (JSC::ArrayPrototype::attemptToInitializeSpeciesWatchpoint):
+        (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
+        (JSC::ArrayPrototype::setConstructor): Deleted.
+        * runtime/ArrayPrototype.h:
+        (JSC::ArrayPrototype::speciesWatchpointStatus):
+        (JSC::ArrayPrototype::didChangeConstructorOrSpeciesProperties): Deleted.
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::speciesGetterSetter):
+        (JSC::JSGlobalObject::arrayConstructor):
+        * tests/stress/array-symbol-species-lazy-watchpoints.js: Added.
+        (test):
+        (arrayEq):
+        (A):
+
+2016-06-14  Keith Miller  <[email protected]>
+
         REGRESSION(202002-202014): 845 32-bit JSC Stress Test failures
         https://bugs.webkit.org/show_bug.cgi?id=158737
 

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (202066 => 202067)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2016-06-14 22:34:09 UTC (rev 202067)
@@ -32,6 +32,7 @@
 #include "CodeBlock.h"
 #include "CopiedSpaceInlines.h"
 #include "Error.h"
+#include "GetterSetter.h"
 #include "Interpreter.h"
 #include "JIT.h"
 #include "JSArrayIterator.h"
@@ -183,11 +184,16 @@
     // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
     JSValue constructor = jsUndefined();
     if (LIKELY(isArray(exec, thisObject))) {
+        ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
+        ArrayPrototype::SpeciesWatchpointStatus status = arrayPrototype->speciesWatchpointStatus();
+        if (UNLIKELY(status == ArrayPrototype::SpeciesWatchpointStatus::Uninitialized))
+            status = arrayPrototype->attemptToInitializeSpeciesWatchpoint(exec);
+        ASSERT(status != ArrayPrototype::SpeciesWatchpointStatus::Uninitialized);
         // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
         // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
         if (LIKELY(!thisObject->hasCustomProperties()
-            && thisObject->globalObject()->arrayPrototype() == thisObject->getPrototypeDirect()
-            && !thisObject->globalObject()->arrayPrototype()->didChangeConstructorOrSpeciesProperties()))
+            && arrayPrototype == thisObject->getPrototypeDirect()
+            && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized))
             return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 
         constructor = thisObject->get(exec, exec->propertyNames().constructor);
@@ -1079,6 +1085,8 @@
 
 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
 
+static bool verbose = false;
+
 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
 public:
     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
@@ -1090,32 +1098,61 @@
     ArrayPrototype* m_arrayPrototype;
 };
 
-void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes)
+ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeciesWatchpoint(ExecState* exec)
 {
-    putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes);
+    ASSERT(m_speciesWatchpointStatus == SpeciesWatchpointStatus::Uninitialized);
 
-    // Do the watchpoint on our constructor property
-    PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor);
-    ASSERT(isValidOffset(offset));
-    this->structure()->startWatchingPropertyForReplacements(vm, offset);
+    VM& vm = exec->vm();
 
-    ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty);
-    ASSERT(condition.isWatchable());
+    if (verbose)
+        dataLog("Attempting to initialize Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
+    // First we need to make sure that the Array.prototype.constructor property points to Array
+    // and that Array[Symbol.species] is the primordial GetterSetter.
 
-    m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
+    // We only initialize once so flattening the structures does not have any real cost.
+    Structure* prototypeStructure = this->structure(vm);
+    if (prototypeStructure->isDictionary())
+        prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
+    RELEASE_ASSERT(!prototypeStructure->isDictionary());
+
+    JSGlobalObject* globalObject = this->globalObject();
+    ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
+
+    PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
+    JSValue(this).get(exec, vm.propertyNames->constructor, constructorSlot);
+    if (constructorSlot.slotBase() != this
+        || !constructorSlot.isCacheableValue()
+        || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor)
+        return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+
+    Structure* constructorStructure = arrayConstructor->structure(vm);
+    if (constructorStructure->isDictionary())
+        constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
+
+    PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
+    JSValue(arrayConstructor).get(exec, vm.propertyNames->speciesSymbol, speciesSlot);
+    if (speciesSlot.slotBase() != arrayConstructor
+        || !speciesSlot.isCacheableGetter()
+        || speciesSlot.getterSetter() != globalObject->speciesGetterSetter())
+        return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+
+    // Now we need to setup the watchpoints to make sure these conditions remain valid.
+    prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
+    constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
+
+    ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
+    ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
+
+    if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable())
+        return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+
+    m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
     m_constructorWatchpoint->install();
-    
-    // Do the watchpoint on the constructor's Symbol.species property
-    offset = constructorProperty->structure()->get(vm, vm.propertyNames->speciesSymbol);
-    ASSERT(isValidOffset(offset));
-    constructorProperty->structure()->startWatchingPropertyForReplacements(vm, offset);
 
-    ASSERT(constructorProperty->getDirect(offset).isGetterSetter());
-    condition = ObjectPropertyCondition::equivalence(vm, this, constructorProperty, vm.propertyNames->speciesSymbol.impl(), constructorProperty->getDirect(offset));
-    ASSERT(condition.isWatchable());
+    m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
+    m_constructorSpeciesWatchpoint->install();
 
-    m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
-    m_constructorSpeciesWatchpoint->install();
+    return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Initialized;
 }
 
 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
@@ -1131,7 +1168,10 @@
 
     StringFireDetail stringDetail(out.toCString().data());
 
-    m_arrayPrototype->m_didChangeConstructorOrSpeciesProperties = true;
+    if (verbose)
+        WTF::dataLog(stringDetail, "\n");
+
+    m_arrayPrototype->m_speciesWatchpointStatus = ArrayPrototype::SpeciesWatchpointStatus::Fired;
 }
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.h (202066 => 202067)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.h	2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.h	2016-06-14 22:34:09 UTC (rev 202067)
@@ -35,6 +35,12 @@
 public:
     typedef JSArray Base;
 
+    enum class SpeciesWatchpointStatus {
+        Uninitialized,
+        Initialized,
+        Fired
+    };
+
     static ArrayPrototype* create(VM&, JSGlobalObject*, Structure*);
         
     DECLARE_INFO;
@@ -44,10 +50,9 @@
         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
     }
 
-    void setConstructor(VM&, JSObject* constructorProperty, unsigned attributes);
+    SpeciesWatchpointStatus speciesWatchpointStatus() const { return m_speciesWatchpointStatus; }
+    SpeciesWatchpointStatus attemptToInitializeSpeciesWatchpoint(ExecState*);
 
-    bool didChangeConstructorOrSpeciesProperties() const { return m_didChangeConstructorOrSpeciesProperties; }
-
     static const bool needsDestruction = false;
     // We don't need destruction since we use a finalizer.
     static void destroy(JSC::JSCell*);
@@ -60,7 +65,7 @@
     friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
     std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
     std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorSpeciesWatchpoint;
-    bool m_didChangeConstructorOrSpeciesProperties = false;
+    SpeciesWatchpointStatus m_speciesWatchpointStatus { SpeciesWatchpointStatus::Uninitialized };
 };
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (202066 => 202067)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-06-14 22:34:09 UTC (rev 202067)
@@ -561,7 +561,8 @@
     m_definePropertyFunction.set(vm, this, definePropertyFunction);
 
     JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get());
-    JSObject* arrayConstructor = ArrayConstructor::create(vm, this, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), m_speciesGetterSetter.get());
+    ArrayConstructor* arrayConstructor = ArrayConstructor::create(vm, this, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), m_speciesGetterSetter.get());
+    m_arrayConstructor.set(vm, this, arrayConstructor);
     
     m_regExpConstructor.set(vm, this, RegExpConstructor::create(vm, RegExpConstructor::createStructure(vm, this, m_functionPrototype.get()), m_regExpPrototype.get(), m_speciesGetterSetter.get()));
     
@@ -608,7 +609,7 @@
     
     m_objectPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, objectConstructor, DontEnum);
     m_functionPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, functionConstructor, DontEnum);
-    m_arrayPrototype->setConstructor(vm, arrayConstructor, DontEnum);
+    m_arrayPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, arrayConstructor, DontEnum);
     m_regExpPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, m_regExpConstructor.get(), DontEnum);
     
     putDirectWithoutTransition(vm, vm.propertyNames->Object, objectConstructor, DontEnum);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (202066 => 202067)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-06-14 22:09:33 UTC (rev 202066)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-06-14 22:34:09 UTC (rev 202067)
@@ -55,6 +55,7 @@
 
 namespace JSC {
 
+class ArrayConstructor;
 class ArrayPrototype;
 class BooleanPrototype;
 class ConsoleClient;
@@ -226,6 +227,7 @@
     WriteBarrier<NativeErrorConstructor> m_typeErrorConstructor;
     LazyProperty<JSGlobalObject, NativeErrorConstructor> m_URIErrorConstructor;
     WriteBarrier<ObjectConstructor> m_objectConstructor;
+    WriteBarrier<ArrayConstructor> m_arrayConstructor;
     WriteBarrier<JSPromiseConstructor> m_promiseConstructor;
     WriteBarrier<JSInternalPromiseConstructor> m_internalPromiseConstructor;
 
@@ -458,9 +460,12 @@
     // The following accessors return pristine values, even if a script 
     // replaces the global object's associated property.
 
+    GetterSetter* speciesGetterSetter() const { return m_speciesGetterSetter.get(); }
+
     RegExpConstructor* regExpConstructor() const { return m_regExpConstructor.get(); }
 
     ErrorConstructor* errorConstructor() const { return m_errorConstructor.get(); }
+    ArrayConstructor* arrayConstructor() const { return m_arrayConstructor.get(); }
     ObjectConstructor* objectConstructor() const { return m_objectConstructor.get(); }
     JSPromiseConstructor* promiseConstructor() const { return m_promiseConstructor.get(); }
     JSInternalPromiseConstructor* internalPromiseConstructor() const { return m_internalPromiseConstructor.get(); }

Added: trunk/Source/_javascript_Core/tests/stress/array-symbol-species-lazy-watchpoints.js (0 => 202067)


--- trunk/Source/_javascript_Core/tests/stress/array-symbol-species-lazy-watchpoints.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/array-symbol-species-lazy-watchpoints.js	2016-06-14 22:34:09 UTC (rev 202067)
@@ -0,0 +1,47 @@
+// This tests that the lazy watchpoints we set for Symbol.species in our Builtin arrayPrototype functions work.
+
+
+function test(array) {
+    array = array.splice(2, 2);
+    array = array.slice(0, 5);
+    array = array.concat([1,2,3]);
+    return array;
+}
+noInline(test);
+
+function arrayEq(a, b) {
+    if (a.length !== b.length)
+        throw new Error();
+
+    for (let i = 0; i < a.length; i++) {
+        if (a[i] !== b[i])
+            throw new Error();
+    }
+}
+
+for (let i = 0; i < 100; i++)
+    arrayEq(test([1,2,3,4,5,6,7,8,9]), [3,4,1,2,3]);
+
+class A extends Array { }
+
+for (let i = 0; i < 100; i++) {
+    let result = test(new A(1,2,3,4,5,6,7,8,9));
+    arrayEq(result, [3,4,1,2,3]);
+    if (!(result instanceof A))
+        throw new Error();
+}
+
+for (let i = 0; i < 100; i++)
+    arrayEq(test([1,2,3,4,5,6,7,8,9]), [3,4,1,2,3]);
+
+delete Array.prototype.sort;
+
+for (let i = 0; i < 100; i++)
+    arrayEq(test([1,2,3,4,5,6,7,8,9]), [3,4,1,2,3]);
+
+for (let i = 0; i < 100; i++) {
+    let result = test(new A(1,2,3,4,5,6,7,8,9));
+    arrayEq(result, [3,4,1,2,3]);
+    if (!(result instanceof A))
+        throw new Error();
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to