[JSC] Object.create should have intrinsic
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Jun 2018 21:08:10 +0000 (21:08 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Jun 2018 21:08:10 +0000 (21:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186200

Reviewed by Filip Pizlo.

JSTests:

* microbenchmarks/object-create-null.js: Added.
(test):
* microbenchmarks/object-create-unknown-object-prototype.js: Added.
(test):
* microbenchmarks/object-create-untyped-prototype.js: Added.
(test):
* stress/get-by-pname-only-prototype-properties.js: Added.
(foo):
* stress/object-create-define.js: Added.
(shouldBe):
(test):
* stress/object-create-null-external.js: Added.
(shouldBe):
(test):
* stress/object-create-null.js: Added.
(shouldBe):
(test):
* stress/object-create-prototype.js: Added.
(shouldBe):
(test):
* stress/object-create-undefined.js: Added.
(shouldThrow):
(i.shouldThrow):

Source/JavaScriptCore:

Object.create is used in various JS code. `Object.create(null)` is particularly used
to create empty plain object with null [[Prototype]]. We can find `Object.create(null)`
call in ARES-6/Babylon code.

This patch adds ObjectCreateIntrinsic to JSC. DFG recognizes it and produces ObjectCreate
DFG node. DFG AI and constant folding attempt to convert it to NewObject when prototype
object is null. It offers significant performance boost for `Object.create(null)`.

                                                 baseline                  patched

object-create-null                           53.7940+-1.5297     ^     19.8846+-0.6584        ^ definitely 2.7053x faster
object-create-unknown-object-prototype       38.9977+-1.1364     ^     37.2207+-0.6143        ^ definitely 1.0477x faster
object-create-untyped-prototype              22.5632+-0.6917           22.2539+-0.6876          might be 1.0139x faster

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToNewObject):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectCreate):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileObjectCreate):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::nullPrototypeObjectStructure const):
* runtime/ObjectConstructor.cpp:

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

34 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/object-create-null.js [new file with mode: 0644]
JSTests/microbenchmarks/object-create-unknown-object-prototype.js [new file with mode: 0644]
JSTests/microbenchmarks/object-create-untyped-prototype.js [new file with mode: 0644]
JSTests/stress/get-by-pname-only-prototype-properties.js [new file with mode: 0644]
JSTests/stress/object-create-define.js [new file with mode: 0644]
JSTests/stress/object-create-null-external.js [new file with mode: 0644]
JSTests/stress/object-create-null.js [new file with mode: 0644]
JSTests/stress/object-create-prototype.js [new file with mode: 0644]
JSTests/stress/object-create-undefined.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/ObjectConstructor.cpp

index fed1a4d..a994fe0 100644 (file)
@@ -1,3 +1,34 @@
+2018-06-02  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Object.create should have intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=186200
+
+        Reviewed by Filip Pizlo.
+
+        * microbenchmarks/object-create-null.js: Added.
+        (test):
+        * microbenchmarks/object-create-unknown-object-prototype.js: Added.
+        (test):
+        * microbenchmarks/object-create-untyped-prototype.js: Added.
+        (test):
+        * stress/get-by-pname-only-prototype-properties.js: Added.
+        (foo):
+        * stress/object-create-define.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-create-null-external.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-create-null.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-create-prototype.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-create-undefined.js: Added.
+        (shouldThrow):
+        (i.shouldThrow):
+
 2018-06-02  Caio Lima  <ticaiolima@gmail.com>
 
         [ESNext][BigInt] Implement support for addition operations
diff --git a/JSTests/microbenchmarks/object-create-null.js b/JSTests/microbenchmarks/object-create-null.js
new file mode 100644 (file)
index 0000000..0517d24
--- /dev/null
@@ -0,0 +1,8 @@
+function test()
+{
+    return Object.create(null);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test();
diff --git a/JSTests/microbenchmarks/object-create-unknown-object-prototype.js b/JSTests/microbenchmarks/object-create-unknown-object-prototype.js
new file mode 100644 (file)
index 0000000..28c468e
--- /dev/null
@@ -0,0 +1,18 @@
+function test(prototype)
+{
+    return Object.create(prototype);
+}
+noInline(test);
+
+var prototype1 = {};
+var prototype2 = [];
+var prototype3 = new Date();
+var prototype4 = { hello: 42 };
+var prototype5 = new Map();
+for (var i = 0; i < 1e5; ++i) {
+    test(prototype1);
+    test(prototype2);
+    test(prototype3);
+    test(prototype4);
+    test(prototype5);
+}
diff --git a/JSTests/microbenchmarks/object-create-untyped-prototype.js b/JSTests/microbenchmarks/object-create-untyped-prototype.js
new file mode 100644 (file)
index 0000000..78ca472
--- /dev/null
@@ -0,0 +1,13 @@
+function test(prototype)
+{
+    return Object.create(prototype);
+}
+noInline(test);
+
+var prototype1 = {};
+var prototype2 = [];
+for (var i = 0; i < 1e5; ++i) {
+    test(prototype1);
+    test(prototype2);
+    test(null);
+}
diff --git a/JSTests/stress/get-by-pname-only-prototype-properties.js b/JSTests/stress/get-by-pname-only-prototype-properties.js
new file mode 100644 (file)
index 0000000..e1d6ce8
--- /dev/null
@@ -0,0 +1,50 @@
+var foo = function (C, A) {
+    for(var B in (A||{})) {
+        C[B]=A[B];
+    }
+    return C;
+}
+
+var protos = [];
+for (var i = 0; i < 256; i++) {
+    var proto = Object.create(null);
+    protos.push(proto);
+    proto.aa = 1;
+    proto.ab = 1;
+    proto.ac = 1;
+    proto.ad = 1;
+    proto.ae = 1;
+    proto.af = 1;
+    proto.ag = 1;
+    proto.ah = 1;
+    proto.ai = 1;
+    proto.aj = 1;
+    proto.ak = 1;
+    proto.al = 1;
+    proto.am = 1;
+    proto.an = 1;
+    proto.ao = 1;
+    proto.ap = 1;
+    proto.aq = 1;
+    proto.ar = 1;
+    proto.as = 1;
+    proto.at = 1;
+    proto.au = 1;
+    proto.av = 1;
+    proto.aw = 1;
+    proto.ax = 1;
+    proto.ay = 1;
+    proto.az = 1;
+    proto.ba = 1;
+    proto.bb = 1;
+    proto.bc = 1;
+    proto.bd = 1;
+    proto.be = 1;
+    proto.bf = 1;
+    var weirdObject = Object.create(proto);
+    var result = foo({}, weirdObject);
+    for (var p in result) {
+        if (result[p] !== result["" + p])
+            throw new Error("OUT");
+    }
+}
diff --git a/JSTests/stress/object-create-define.js b/JSTests/stress/object-create-define.js
new file mode 100644 (file)
index 0000000..a3ed990
--- /dev/null
@@ -0,0 +1,31 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(prototype, data)
+{
+    return Object.create(prototype, data);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    var prototype = { Cocoa: false };
+    var object = test(prototype, {
+        Cappuccino: {
+            value: 42,
+            enumerable: true,
+            configurable: true,
+            writable: true
+        },
+        Matcha: {
+            value: 40,
+            enumerable: false,
+            configurable: true,
+            writable: true
+        }
+    });
+    shouldBe(Object.getPrototypeOf(object), prototype);
+    shouldBe(JSON.stringify(Object.getOwnPropertyNames(object).sort()), `["Cappuccino","Matcha"]`);
+    shouldBe(JSON.stringify(Object.keys(object).sort()), `["Cappuccino"]`);
+}
diff --git a/JSTests/stress/object-create-null-external.js b/JSTests/stress/object-create-null-external.js
new file mode 100644 (file)
index 0000000..96fd04d
--- /dev/null
@@ -0,0 +1,16 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(prototype)
+{
+    return Object.create(prototype);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    var object = test(null);
+    shouldBe(Object.getPrototypeOf(object), null);
+    shouldBe(JSON.stringify(Object.getOwnPropertyNames(object)), `[]`);
+}
diff --git a/JSTests/stress/object-create-null.js b/JSTests/stress/object-create-null.js
new file mode 100644 (file)
index 0000000..0bc968e
--- /dev/null
@@ -0,0 +1,16 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+    return Object.create(null);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    var object = test();
+    shouldBe(Object.getPrototypeOf(object), null);
+    shouldBe(JSON.stringify(Object.getOwnPropertyNames(object)), `[]`);
+}
diff --git a/JSTests/stress/object-create-prototype.js b/JSTests/stress/object-create-prototype.js
new file mode 100644 (file)
index 0000000..0d33fb4
--- /dev/null
@@ -0,0 +1,17 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(prototype)
+{
+    return Object.create(prototype);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    var prototype = { Cocoa: false };
+    var object = test(prototype);
+    shouldBe(Object.getPrototypeOf(object), prototype);
+    shouldBe(JSON.stringify(Object.getOwnPropertyNames(object)), `[]`);
+}
diff --git a/JSTests/stress/object-create-undefined.js b/JSTests/stress/object-create-undefined.js
new file mode 100644 (file)
index 0000000..9aa3c21
--- /dev/null
@@ -0,0 +1,26 @@
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function test(value)
+{
+    return Object.create(value);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    shouldThrow(() => {
+        test(undefined);
+    }, `TypeError: Object prototype may only be an Object or null.`);
+}
index eae9dc2..5d2df98 100644 (file)
@@ -1,3 +1,66 @@
+2018-06-02  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Object.create should have intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=186200
+
+        Reviewed by Filip Pizlo.
+
+        Object.create is used in various JS code. `Object.create(null)` is particularly used
+        to create empty plain object with null [[Prototype]]. We can find `Object.create(null)`
+        call in ARES-6/Babylon code.
+
+        This patch adds ObjectCreateIntrinsic to JSC. DFG recognizes it and produces ObjectCreate
+        DFG node. DFG AI and constant folding attempt to convert it to NewObject when prototype
+        object is null. It offers significant performance boost for `Object.create(null)`.
+
+                                                         baseline                  patched
+
+        object-create-null                           53.7940+-1.5297     ^     19.8846+-0.6584        ^ definitely 2.7053x faster
+        object-create-unknown-object-prototype       38.9977+-1.1364     ^     37.2207+-0.6143        ^ definitely 1.0477x faster
+        object-create-untyped-prototype              22.5632+-0.6917           22.2539+-0.6876          might be 1.0139x faster
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToNewObject):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileObjectCreate):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileObjectCreate):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::nullPrototypeObjectStructure const):
+        * runtime/ObjectConstructor.cpp:
+
 2018-06-02  Caio Lima  <ticaiolima@gmail.com>
 
         [ESNext][BigInt] Implement support for addition operations
index d490d42..dae05fd 100644 (file)
@@ -2368,6 +2368,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         setForNode(node, node->structure());
         break;
 
+    case ObjectCreate: {
+        if (JSValue base = forNode(node->child1()).m_value) {
+            if (base.isNull()) {
+                JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+                m_state.setFoundConstants(true);
+                if (node->child1().useKind() == UntypedUse)
+                    didFoldClobberWorld();
+                setForNode(node, globalObject->nullPrototypeObjectStructure());
+                break;
+            }
+            // FIXME: We should get a structure for a constant prototype. We need to allow concurrent
+            // access to StructureCache from compiler threads.
+            // https://bugs.webkit.org/show_bug.cgi?id=186199
+        }
+        if (node->child1().useKind() == UntypedUse)
+            clobberWorld();
+        setTypeForNode(node, SpecFinalObject);
+        break;
+    }
+
     case ToObject:
     case CallObjectConstructor: {
         AbstractValue& source = forNode(node->child1());
index d9bd850..caf9f05 100644 (file)
@@ -2642,6 +2642,15 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case ObjectCreateIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+
+        insertChecks();
+        set(VirtualRegister(resultOperand), addToGraph(ObjectCreate, get(virtualRegisterForArgument(1, registerOffset))));
+        return true;
+    }
+
     case ObjectGetPrototypeOfIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;
index f781b15..e19c749 100644 (file)
@@ -1489,6 +1489,23 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
     }
 
+    case ObjectCreate: {
+        switch (node->child1().useKind()) {
+        case ObjectUse:
+            read(HeapObjectCount);
+            write(HeapObjectCount);
+            return;
+        case UntypedUse:
+            read(World);
+            write(Heap);
+            return;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            return;
+        }
+    }
+
+
     case NewObject:
     case NewRegexp:
     case NewStringObject:
index 99976e0..74cf85d 100644 (file)
@@ -737,6 +737,21 @@ private:
                 break;
             }
 
+            case ObjectCreate: {
+                if (JSValue base = m_state.forNode(node->child1()).m_value) {
+                    if (base.isNull()) {
+                        JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+                        node->convertToNewObject(m_graph.registerStructure(globalObject->nullPrototypeObjectStructure()));
+                        changed = true;
+                        break;
+                    }
+                    // FIXME: We should get a structure for a constant prototype. We need to allow concurrent
+                    // access to StructureCache from compiler threads.
+                    // https://bugs.webkit.org/show_bug.cgi?id=186199
+                }
+                break;
+            }
+
             case ToNumber: {
                 if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
                     break;
index e08d21a..20bffec 100644 (file)
@@ -324,6 +324,7 @@ bool doesGC(Graph& graph, Node* node)
     case ToObject:
     case ToThis:
     case CreateThis:
+    case ObjectCreate:
     case AllocatePropertyStorage:
     case ReallocatePropertyStorage:
     case Arrayify:
index cd73ce2..2a7acc4 100644 (file)
@@ -1446,6 +1446,15 @@ private:
             break;
         }
 
+        case ObjectCreate: {
+            if (node->child1()->shouldSpeculateObject()) {
+                fixEdge<ObjectUse>(node->child1());
+                node->clearFlags(NodeMustGenerate);
+                break;
+            }
+            break;
+        }
+
         case CheckStringIdent: {
             fixEdge<StringIdentUse>(node->child1());
             break;
index 10b51fe..eee5e77 100644 (file)
@@ -733,7 +733,7 @@ public:
 
     void convertToNewObject(RegisteredStructure structure)
     {
-        ASSERT(m_op == CallObjectConstructor || m_op == CreateThis);
+        ASSERT(m_op == CallObjectConstructor || m_op == CreateThis || m_op == ObjectCreate);
         setOpAndDefaultFlags(NewObject);
         children.reset();
         m_opInfo = structure;
index 52b7bb9..6164c7f 100644 (file)
@@ -255,6 +255,7 @@ namespace JSC { namespace DFG {
     macro(CheckSubClass, NodeMustGenerate) \
     macro(ParseInt, NodeMustGenerate | NodeResultJS) \
     macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \
+    macro(ObjectCreate, NodeMustGenerate | NodeResultJS) \
     \
     /* Atomics object functions. */\
     macro(AtomicsAdd, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
index 7777368..26de111 100644 (file)
@@ -248,6 +248,34 @@ EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState* exec, EncodedJSVal
     return JSValue::encode(JSValue::decode(encodedOp).toThis(exec, StrictMode));
 }
 
+JSCell* JIT_OPERATION operationObjectCreate(ExecState* exec, EncodedJSValue encodedPrototype)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue prototype = JSValue::decode(encodedPrototype);
+
+    if (!prototype.isObject() && !prototype.isNull()) {
+        throwVMTypeError(exec, scope, ASCIILiteral("Object prototype may only be an Object or null."));
+        return nullptr;
+    }
+
+    if (prototype.isObject()) {
+        scope.release();
+        return constructEmptyObject(exec, asObject(prototype));
+    }
+    scope.release();
+    return constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure());
+}
+
+JSCell* JIT_OPERATION operationObjectCreateObject(ExecState* exec, JSObject* prototype)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    return constructEmptyObject(exec, prototype);
+}
+
 JSCell* JIT_OPERATION operationCreateThis(ExecState* exec, JSObject* constructor, uint32_t inlineCapacity)
 {
     VM& vm = exec->vm();
index 6309ec3..35bf6bc 100644 (file)
@@ -43,6 +43,8 @@ EncodedJSValue JIT_OPERATION operationStringFromCharCodeUntyped(ExecState*, Enco
 // These routines provide callbacks out to C++ implementations of operations too complex to JIT.
 JSCell* JIT_OPERATION operationCallObjectConstructor(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationToObject(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget, UniquedStringImpl*) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationObjectCreate(ExecState*, EncodedJSValue) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, uint32_t inlineCapacity) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
index 5a6addb..0119a25 100644 (file)
@@ -906,6 +906,7 @@ private:
             break;
         }
             
+        case ObjectCreate:
         case CreateThis:
         case NewObject: {
             setPrediction(SpecFinalObject);
index b35078f..93916d8 100644 (file)
@@ -171,6 +171,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
     case IdentityWithProfile:
     case ToThis:
     case CreateThis:
+    case ObjectCreate:
     case GetCallee:
     case SetCallee:
     case GetArgumentCountIncludingThis:
index 73cee6a..6996d42 100644 (file)
@@ -12172,6 +12172,47 @@ void SpeculativeJIT::compileToThis(Node* node)
     jsValueResult(tempRegs, node);
 }
 
+void SpeculativeJIT::compileObjectCreate(Node* node)
+{
+    switch (node->child1().useKind()) {
+    case ObjectUse: {
+        SpeculateCellOperand prototype(this, node->child1());
+
+        GPRReg prototypeGPR = prototype.gpr();
+
+        speculateObject(node->child1(), prototypeGPR);
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationObjectCreateObject, resultGPR, prototypeGPR);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        break;
+    }
+
+    case UntypedUse: {
+        JSValueOperand prototype(this, node->child1());
+
+        JSValueRegs prototypeRegs = prototype.jsValueRegs();
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationObjectCreate, resultGPR, prototypeRegs);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        break;
+    }
+
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
 void SpeculativeJIT::compileCreateThis(Node* node)
 {
     // Note that there is not so much profit to speculate here. The only things we
index 8b04194..848fd3b 100644 (file)
@@ -1472,6 +1472,7 @@ public:
     void compileNewArrayWithSize(Node*);
     void compileNewTypedArray(Node*);
     void compileToThis(Node*);
+    void compileObjectCreate(Node*);
     void compileCreateThis(Node*);
     void compileNewObject(Node*);
     void compileToPrimitive(Node*);
index ac73b7b..a8967d2 100644 (file)
@@ -3132,6 +3132,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ObjectCreate: {
+        compileObjectCreate(node);
+        break;
+    }
+
     case CreateThis: {
         compileCreateThis(node);
         break;
index db19347..b70c211 100644 (file)
@@ -3382,6 +3382,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ObjectCreate: {
+        compileObjectCreate(node);
+        break;
+    }
+
     case CreateThis: {
         compileCreateThis(node);
         break;
index 7b44b66..dc6b686 100644 (file)
@@ -191,6 +191,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ToObject:
     case CallObjectConstructor:
     case CallStringConstructor:
+    case ObjectCreate:
     case MakeRope:
     case NewArrayWithSize:
     case TryGetById:
index 71354b3..56bfad5 100644 (file)
@@ -838,6 +838,9 @@ private:
         case CreateClonedArguments:
             compileCreateClonedArguments();
             break;
+        case ObjectCreate:
+            compileObjectCreate();
+            break;
         case NewObject:
             compileNewObject();
             break;
@@ -5353,6 +5356,21 @@ private:
         m_out.appendTo(continuation, lastNext);
         setInt32(m_out.phi(Int32, zeroLengthResult, nonZeroLengthResult));
     }
+
+    void compileObjectCreate()
+    {
+        switch (m_node->child1().useKind()) {
+        case ObjectUse:
+            setJSValue(vmCall(Int64, m_out.operation(operationObjectCreateObject), m_callFrame, lowObject(m_node->child1())));
+            break;
+        case UntypedUse:
+            setJSValue(vmCall(Int64, m_out.operation(operationObjectCreate), m_callFrame, lowJSValue(m_node->child1())));
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            break;
+        }
+    }
     
     void compileNewObject()
     {
index 6652de0..801ed54 100644 (file)
@@ -113,6 +113,8 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "RegExpTestFastIntrinsic";
     case RegExpMatchFastIntrinsic:
         return "RegExpMatchFastIntrinsic";
+    case ObjectCreateIntrinsic:
+        return "ObjectCreateIntrinsic";
     case ObjectGetPrototypeOfIntrinsic:
         return "ObjectGetPrototypeOfIntrinsic";
     case ObjectIsIntrinsic:
index 61fe0bd..757df05 100644 (file)
@@ -69,6 +69,7 @@ enum Intrinsic {
     RegExpTestIntrinsic,
     RegExpTestFastIntrinsic,
     RegExpMatchFastIntrinsic,
+    ObjectCreateIntrinsic,
     ObjectGetPrototypeOfIntrinsic,
     ObjectIsIntrinsic,
     ReflectGetPrototypeOfIntrinsic,
index 14ee147..b4045e2 100644 (file)
@@ -537,10 +537,7 @@ void JSGlobalObject::init(VM& vm)
             init.set(JSWithScope::createStructure(init.vm, init.owner, jsNull()));
         });
     
-    m_nullPrototypeObjectStructure.initLater(
-        [] (const Initializer<Structure>& init) {
-            init.set(JSFinalObject::createStructure(init.vm, init.owner, jsNull(), JSFinalObject::defaultInlineCapacity()));
-        });
+    m_nullPrototypeObjectStructure.set(vm, this, JSFinalObject::createStructure(vm, this, jsNull(), JSFinalObject::defaultInlineCapacity()));
     
     m_callbackFunctionStructure.initLater(
         [] (const Initializer<Structure>& init) {
@@ -1394,7 +1391,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     thisObject->m_glibCallbackFunctionStructure.visit(visitor);
     thisObject->m_glibWrapperObjectStructure.visit(visitor);
 #endif
-    thisObject->m_nullPrototypeObjectStructure.visit(visitor);
+    visitor.append(thisObject->m_nullPrototypeObjectStructure);
     visitor.append(thisObject->m_errorStructure);
     visitor.append(thisObject->m_calleeStructure);
     visitor.append(thisObject->m_strictFunctionStructure);
index d42caab..fe330de 100644 (file)
@@ -339,7 +339,7 @@ public:
     LazyProperty<JSGlobalObject, Structure> m_glibCallbackFunctionStructure;
     LazyProperty<JSGlobalObject, Structure> m_glibWrapperObjectStructure;
 #endif
-    LazyProperty<JSGlobalObject, Structure> m_nullPrototypeObjectStructure;
+    WriteBarrier<Structure> m_nullPrototypeObjectStructure;
     WriteBarrier<Structure> m_calleeStructure;
     WriteBarrier<Structure> m_strictFunctionStructure;
     WriteBarrier<Structure> m_arrowFunctionStructure;
@@ -654,7 +654,7 @@ public:
     Structure* glibWrapperObjectStructure() const { return m_glibWrapperObjectStructure.get(this); }
 #endif
     Structure* dateStructure() const { return m_dateStructure.get(this); }
-    Structure* nullPrototypeObjectStructure() const { return m_nullPrototypeObjectStructure.get(this); }
+    Structure* nullPrototypeObjectStructure() const { return m_nullPrototypeObjectStructure.get(); }
     Structure* errorStructure() const { return m_errorStructure.get(); }
     Structure* calleeStructure() const { return m_calleeStructure.get(); }
     Structure* strictFunctionStructure() const { return m_strictFunctionStructure.get(); }
index 8152be7..f55dc29 100644 (file)
@@ -76,7 +76,7 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i
   keys                      objectConstructorKeys                       DontEnum|Function 1
   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
   defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
-  create                    objectConstructorCreate                     DontEnum|Function 2
+  create                    objectConstructorCreate                     DontEnum|Function 2 ObjectCreateIntrinsic
   seal                      objectConstructorSeal                       DontEnum|Function 1
   freeze                    objectConstructorFreeze                     DontEnum|Function 1
   preventExtensions         objectConstructorPreventExtensions          DontEnum|Function 1