[JSC] Object constructor need to be aware of new.target
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 May 2016 17:40:46 +0000 (17:40 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 May 2016 17:40:46 +0000 (17:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157196

Reviewed by Darin Adler.

Object constructor should be aware of new.target.
When the new.target is specified, we should store it.prototype to the newly created
object's [[Prototype]].

* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
Take the design that caches the structure used for empty object.
This structure is also used in constructEmptyObject frequently.

* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::objectStructureForObjectConstructor):
* runtime/ObjectConstructor.cpp:
(JSC::constructObject):
(JSC::constructWithObjectConstructor):
(JSC::callObjectConstructor):
* runtime/ObjectConstructor.h:
(JSC::constructEmptyObject):
Construct the object by using the plain structure that is also used in the ObjectConstructor.

* tests/stress/object-constructor-should-be-new-target-aware.js: Added.
(shouldBe):
(Hello):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/ObjectConstructor.h
Source/JavaScriptCore/tests/stress/object-constructor-should-be-new-target-aware.js [new file with mode: 0644]

index 606290a..87da5ed 100644 (file)
@@ -1,3 +1,34 @@
+2016-05-04  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Object constructor need to be aware of new.target
+        https://bugs.webkit.org/show_bug.cgi?id=157196
+
+        Reviewed by Darin Adler.
+
+        Object constructor should be aware of new.target.
+        When the new.target is specified, we should store it.prototype to the newly created
+        object's [[Prototype]].
+
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        Take the design that caches the structure used for empty object.
+        This structure is also used in constructEmptyObject frequently.
+
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::objectStructureForObjectConstructor):
+        * runtime/ObjectConstructor.cpp:
+        (JSC::constructObject):
+        (JSC::constructWithObjectConstructor):
+        (JSC::callObjectConstructor):
+        * runtime/ObjectConstructor.h:
+        (JSC::constructEmptyObject):
+        Construct the object by using the plain structure that is also used in the ObjectConstructor.
+
+        * tests/stress/object-constructor-should-be-new-target-aware.js: Added.
+        (shouldBe):
+        (Hello):
+
 2016-05-04  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, rolling out r200383 and r200406.
index 3448f11..bdb558a 100644 (file)
@@ -315,6 +315,7 @@ void JSGlobalObject::init(VM& vm)
     protoAccessor->setSetter(vm, this, JSFunction::create(vm, this, 0, makeString("set ", vm.propertyNames->underscoreProto.string()), globalFuncProtoSetter));
     m_objectPrototype->putDirectNonIndexAccessor(vm, vm.propertyNames->underscoreProto, protoAccessor, Accessor | DontEnum);
     m_functionPrototype->structure()->setPrototypeWithoutTransition(vm, m_objectPrototype.get());
+    m_objectStructureForObjectConstructor.set(vm, this, vm.prototypeMap.emptyObjectStructureForPrototype(m_objectPrototype.get(), JSFinalObject::defaultInlineCapacity()));
 
     JSTypedArrayViewPrototype* typedArrayProto = JSTypedArrayViewPrototype::create(vm, this, JSTypedArrayViewPrototype::createStructure(vm, this, m_objectPrototype.get()));
 
@@ -966,6 +967,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_directArgumentsStructure);
     visitor.append(&thisObject->m_scopedArgumentsStructure);
     visitor.append(&thisObject->m_clonedArgumentsStructure);
+    visitor.append(&thisObject->m_objectStructureForObjectConstructor);
     for (unsigned i = 0; i < NumberOfIndexingShapes; ++i)
         visitor.append(&thisObject->m_originalArrayStructureForIndexingShape[i]);
     for (unsigned i = 0; i < NumberOfIndexingShapes; ++i)
index 8bb14c3..8863a1c 100644 (file)
@@ -254,6 +254,8 @@ protected:
     WriteBarrier<Structure> m_directArgumentsStructure;
     WriteBarrier<Structure> m_scopedArgumentsStructure;
     WriteBarrier<Structure> m_clonedArgumentsStructure;
+
+    WriteBarrier<Structure> m_objectStructureForObjectConstructor;
         
     // Lists the actual structures used for having these particular indexing shapes.
     WriteBarrier<Structure> m_originalArrayStructureForIndexingShape[NumberOfIndexingShapes];
@@ -499,6 +501,7 @@ public:
     Structure* directArgumentsStructure() const { return m_directArgumentsStructure.get(); }
     Structure* scopedArgumentsStructure() const { return m_scopedArgumentsStructure.get(); }
     Structure* clonedArgumentsStructure() const { return m_clonedArgumentsStructure.get(); }
+    Structure* objectStructureForObjectConstructor() const { return m_objectStructureForObjectConstructor.get(); }
     Structure* originalArrayStructureForIndexingType(IndexingType indexingType) const
     {
         ASSERT(indexingType & IsArray);
index 15eeba5..3fcf9ec 100644 (file)
@@ -118,19 +118,37 @@ bool ObjectConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, Pr
     return getStaticFunctionSlot<JSObject>(exec, objectConstructorTable, jsCast<ObjectConstructor*>(object), propertyName, slot);
 }
 
-static ALWAYS_INLINE JSObject* constructObject(ExecState* exec)
+// ES 19.1.1.1 Object([value])
+static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, JSValue newTarget)
 {
-    JSGlobalObject* globalObject = exec->callee()->globalObject();
+    ObjectConstructor* objectConstructor = jsCast<ObjectConstructor*>(exec->callee());
+    JSGlobalObject* globalObject = objectConstructor->globalObject();
+
+    // We need to check newTarget condition in this caller side instead of InternalFunction::createSubclassStructure side.
+    // Since if we found this condition is met, we should not fall into the type conversion in the step 3.
+
+    // 1. If NewTarget is neither undefined nor the active function, then
+    if (newTarget && newTarget != objectConstructor) {
+        // a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
+        Structure* objectStructure = InternalFunction::createSubclassStructure(exec, newTarget, globalObject->objectStructureForObjectConstructor());
+        if (exec->hadException())
+            return nullptr;
+        return constructEmptyObject(exec, objectStructure);
+    }
+
+    // 2. If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
     ArgList args(exec);
     JSValue arg = args.at(0);
     if (arg.isUndefinedOrNull())
-        return constructEmptyObject(exec, globalObject->objectPrototype());
+        return constructEmptyObject(exec, globalObject->objectStructureForObjectConstructor());
+
+    // 3. Return ToObject(value).
     return arg.toObject(exec, globalObject);
 }
 
 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec)
 {
-    return JSValue::encode(constructObject(exec));
+    return JSValue::encode(constructObject(exec, exec->newTarget()));
 }
 
 ConstructType ObjectConstructor::getConstructData(JSCell*, ConstructData& constructData)
@@ -141,7 +159,7 @@ ConstructType ObjectConstructor::getConstructData(JSCell*, ConstructData& constr
 
 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec)
 {
-    return JSValue::encode(constructObject(exec));
+    return JSValue::encode(constructObject(exec, JSValue()));
 }
 
 CallType ObjectConstructor::getCallData(JSCell*, CallData& callData)
index 1b22ab6..d148ab0 100644 (file)
@@ -88,7 +88,7 @@ inline JSObject* constructEmptyObject(ExecState* exec, JSObject* prototype)
 
 inline JSObject* constructEmptyObject(ExecState* exec)
 {
-    return constructEmptyObject(exec, exec->lexicalGlobalObject()->objectPrototype());
+    return constructEmptyObject(exec, exec->lexicalGlobalObject()->objectStructureForObjectConstructor());
 }
 
 // Section 6.2.4.4 of the ES6 specification.
diff --git a/Source/JavaScriptCore/tests/stress/object-constructor-should-be-new-target-aware.js b/Source/JavaScriptCore/tests/stress/object-constructor-should-be-new-target-aware.js
new file mode 100644 (file)
index 0000000..aa392bd
--- /dev/null
@@ -0,0 +1,16 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+class Hello extends Object {
+    constructor()
+    {
+        super();
+    }
+}
+
+var hello = new Hello();
+shouldBe(hello.__proto__, Hello.prototype);
+
+shouldBe(Reflect.construct(Object, [], Hello).__proto__, Hello.prototype);