Title: [222901] trunk
Revision
222901
Author
sbar...@apple.com
Date
2017-10-05 00:38:00 -0700 (Thu, 05 Oct 2017)

Log Message

Make sure all prototypes under poly proto get added into the VM's prototype map
https://bugs.webkit.org/show_bug.cgi?id=177909

Reviewed by Keith Miller.

JSTests:

* stress/poly-proto-prototype-map-having-a-bad-time.js: Added.
(assert):
(foo.C):
(foo):
(set x):

Source/_javascript_Core:

This is an invariant of prototypes that I broke when doing poly proto. This patch fixes it.

* _javascript_Core.xcodeproj/project.pbxproj:
* bytecode/BytecodeList.json:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGOperations.cpp:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSCInlines.h:
* runtime/PrototypeMap.cpp:
(JSC::PrototypeMap::addPrototype): Deleted.
* runtime/PrototypeMap.h:
* runtime/PrototypeMapInlines.h:
(JSC::PrototypeMap::isPrototype const):
(JSC::PrototypeMap::addPrototype):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (222900 => 222901)


--- trunk/JSTests/ChangeLog	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/JSTests/ChangeLog	2017-10-05 07:38:00 UTC (rev 222901)
@@ -1,3 +1,16 @@
+2017-10-05  Saam Barati  <sbar...@apple.com>
+
+        Make sure all prototypes under poly proto get added into the VM's prototype map
+        https://bugs.webkit.org/show_bug.cgi?id=177909
+
+        Reviewed by Keith Miller.
+
+        * stress/poly-proto-prototype-map-having-a-bad-time.js: Added.
+        (assert):
+        (foo.C):
+        (foo):
+        (set x):
+
 2017-09-30  Yusuke Suzuki  <utatane....@gmail.com>
 
         [JSC] Introduce import.meta

Added: trunk/JSTests/stress/poly-proto-prototype-map-having-a-bad-time.js (0 => 222901)


--- trunk/JSTests/stress/poly-proto-prototype-map-having-a-bad-time.js	                        (rev 0)
+++ trunk/JSTests/stress/poly-proto-prototype-map-having-a-bad-time.js	2017-10-05 07:38:00 UTC (rev 222901)
@@ -0,0 +1,30 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion");
+}
+
+function foo() {
+    class C {
+        constructor() {
+            this.x = 20;
+        }
+    };
+    let item = new C;
+    item[0] = 42;
+    return [item, C.prototype];
+}
+
+for (let i = 0; i < 50; ++i)
+    foo();
+
+let [item, proto] = foo();
+let called = false;
+Object.defineProperty(proto, "1", {
+    set(x) {
+        called = true;
+    }
+});
+
+assert(!called);
+item[1] = 42;
+assert(called);

Modified: trunk/Source/_javascript_Core/ChangeLog (222900 => 222901)


--- trunk/Source/_javascript_Core/ChangeLog	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-10-05 07:38:00 UTC (rev 222901)
@@ -1,3 +1,27 @@
+2017-10-05  Saam Barati  <sbar...@apple.com>
+
+        Make sure all prototypes under poly proto get added into the VM's prototype map
+        https://bugs.webkit.org/show_bug.cgi?id=177909
+
+        Reviewed by Keith Miller.
+
+        This is an invariant of prototypes that I broke when doing poly proto. This patch fixes it.
+
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * bytecode/BytecodeList.json:
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGOperations.cpp:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSCInlines.h:
+        * runtime/PrototypeMap.cpp:
+        (JSC::PrototypeMap::addPrototype): Deleted.
+        * runtime/PrototypeMap.h:
+        * runtime/PrototypeMapInlines.h:
+        (JSC::PrototypeMap::isPrototype const):
+        (JSC::PrototypeMap::addPrototype):
+
 2017-09-30  Yusuke Suzuki  <utatane....@gmail.com>
 
         [JSC] Introduce import.meta

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (222900 => 222901)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2017-10-05 07:38:00 UTC (rev 222901)
@@ -447,7 +447,7 @@
 		0F9E32641B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9E32621B05AB0400801ED5 /* DFGStoreBarrierInsertionPhase.h */; };
 		0F9FB4F517FCB91700CB67F8 /* DFGStackLayoutPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9FB4F317FCB91700CB67F8 /* DFGStackLayoutPhase.h */; };
 		0F9FC8C514E1B60400D52AE0 /* PutKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		0FA131711D8DD72B00EC130A /* PrototypeMapInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA131701D8DD72900EC130A /* PrototypeMapInlines.h */; };
+		0FA131711D8DD72B00EC130A /* PrototypeMapInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA131701D8DD72900EC130A /* PrototypeMapInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0FA2C17C17D7CF84009D015F /* TestRunnerUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA2C17A17D7CF84009D015F /* TestRunnerUtils.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0FA581BB150E953000B9A2D9 /* DFGNodeFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */; };
 		0FA581BC150E953000B9A2D9 /* DFGNodeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */; };

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.json (222900 => 222901)


--- trunk/Source/_javascript_Core/bytecode/BytecodeList.json	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.json	2017-10-05 07:38:00 UTC (rev 222901)
@@ -8,7 +8,11 @@
             { "name" : "op_create_direct_arguments", "length" : 2 },
             { "name" : "op_create_scoped_arguments", "length" : 3 },
             { "name" : "op_create_cloned_arguments", "length" : 2 },
-            { "name" : "op_create_this", "length" : 5 },
+            { "name" : "op_create_this", "offsets" :
+                       [{"dst" : "int"},
+                        {"callee" : "int"},
+                        {"inlineCapacity" : "int"},
+                        {"cachedCallee" : "WriteBarrier<JSCell>"}]},
             { "name" : "op_get_argument", "length" : 4 },
             { "name" : "op_argument_count", "length" : 2 },
             { "name" : "op_to_this", "length" : 4 },

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (222900 => 222901)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-10-05 07:38:00 UTC (rev 222901)
@@ -4324,12 +4324,12 @@
         }
 
         case op_create_this: {
-            int calleeOperand = currentInstruction[2].u.operand;
-            Node* callee = get(VirtualRegister(calleeOperand));
+            auto& bytecode = *reinterpret_cast<OpCreateThis*>(currentInstruction);
+            Node* callee = get(VirtualRegister(bytecode.callee()));
 
             JSFunction* function = callee->dynamicCastConstant<JSFunction*>(*m_vm);
             if (!function) {
-                JSCell* cachedFunction = currentInstruction[4].u.jsCell.unvalidatedGet();
+                JSCell* cachedFunction = bytecode.cachedCallee().unvalidatedGet();
                 if (cachedFunction
                     && cachedFunction != JSCell::seenMultipleCalleeObjects()
                     && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell)) {
@@ -4353,7 +4353,7 @@
                             m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
                             // The callee is still live up to this point.
                             addToGraph(Phantom, callee);
-                            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(NewObject, OpInfo(m_graph.registerStructure(structure))));
+                            set(VirtualRegister(bytecode.dst()), addToGraph(NewObject, OpInfo(m_graph.registerStructure(structure))));
                             alreadyEmitted = true;
                         }
                     }
@@ -4360,8 +4360,8 @@
                 }
             }
             if (!alreadyEmitted) {
-                set(VirtualRegister(currentInstruction[1].u.operand),
-                    addToGraph(CreateThis, OpInfo(currentInstruction[3].u.operand), callee));
+                set(VirtualRegister(bytecode.dst()),
+                    addToGraph(CreateThis, OpInfo(bytecode.inlineCapacity()), callee));
             }
             NEXT_OPCODE(op_create_this);
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (222900 => 222901)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2017-10-05 07:38:00 UTC (rev 222901)
@@ -246,8 +246,11 @@
         RETURN_IF_EXCEPTION(scope, nullptr);
         Structure* structure = rareData->objectAllocationProfile()->structure();
         JSObject* result = constructEmptyObject(exec, structure);
-        if (structure->hasPolyProto())
-            result->putDirect(vm, structure->polyProtoOffset(), jsCast<JSFunction*>(constructor)->prototypeForConstruction(vm, exec));
+        if (structure->hasPolyProto()) {
+            JSObject* prototype = jsCast<JSFunction*>(constructor)->prototypeForConstruction(vm, exec);
+            result->putDirect(vm, structure->polyProtoOffset(), prototype);
+            vm.prototypeMap.addPrototype(prototype);
+        }
         return result;
     }
 

Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp (222900 => 222901)


--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2017-10-05 07:38:00 UTC (rev 222901)
@@ -29,6 +29,7 @@
 #include "ArithProfile.h"
 #include "ArrayConstructor.h"
 #include "BuiltinNames.h"
+#include "BytecodeStructs.h"
 #include "CallFrame.h"
 #include "ClonedArguments.h"
 #include "CodeProfiling.h"
@@ -93,6 +94,8 @@
 #define OP(index) (exec->uncheckedR(pc[index].u.operand))
 #define OP_C(index) (exec->r(pc[index].u.operand))
 
+#define GET(operand) (exec->uncheckedR(operand))
+
 #define RETURN_TWO(first, second) do {       \
         return encodeResult(first, second);        \
     } while (false)
@@ -229,21 +232,25 @@
 SLOW_PATH_DECL(slow_path_create_this)
 {
     BEGIN();
+    auto& bytecode = *reinterpret_cast<OpCreateThis*>(pc);
     JSObject* result;
-    JSObject* constructorAsObject = asObject(OP(2).jsValue());
+    JSObject* constructorAsObject = asObject(GET(bytecode.callee()).jsValue());
     if (constructorAsObject->type() == JSFunctionType) {
         JSFunction* constructor = jsCast<JSFunction*>(constructorAsObject);
-        auto& cacheWriteBarrier = pc[4].u.jsCell;
-        if (!cacheWriteBarrier)
-            cacheWriteBarrier.set(vm, exec->codeBlock(), constructor);
-        else if (cacheWriteBarrier.unvalidatedGet() != JSCell::seenMultipleCalleeObjects() && cacheWriteBarrier.get() != constructor)
-            cacheWriteBarrier.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects());
+        WriteBarrier<JSCell>& cachedCallee = bytecode.cachedCallee();
+        if (!cachedCallee)
+            cachedCallee.set(vm, exec->codeBlock(), constructor);
+        else if (cachedCallee.unvalidatedGet() != JSCell::seenMultipleCalleeObjects() && cachedCallee.get() != constructor)
+            cachedCallee.setWithoutWriteBarrier(JSCell::seenMultipleCalleeObjects());
 
-        size_t inlineCapacity = pc[3].u.operand;
+        size_t inlineCapacity = bytecode.inlineCapacity();
         Structure* structure = constructor->rareData(exec, inlineCapacity)->objectAllocationProfile()->structure();
         result = constructEmptyObject(exec, structure);
-        if (structure->hasPolyProto())
-            result->putDirect(vm, structure->polyProtoOffset(), constructor->prototypeForConstruction(vm, exec));
+        if (structure->hasPolyProto()) {
+            JSObject* prototype = constructor->prototypeForConstruction(vm, exec);
+            result->putDirect(vm, structure->polyProtoOffset(), prototype);
+            vm.prototypeMap.addPrototype(prototype);
+        }
     } else {
         // http://ecma-international.org/ecma-262/6.0/#sec-ordinarycreatefromconstructor
         JSValue proto = constructorAsObject->get(exec, vm.propertyNames->prototype);

Modified: trunk/Source/_javascript_Core/runtime/JSCInlines.h (222900 => 222901)


--- trunk/Source/_javascript_Core/runtime/JSCInlines.h	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/runtime/JSCInlines.h	2017-10-05 07:38:00 UTC (rev 222901)
@@ -49,6 +49,7 @@
 #include "JSProxy.h"
 #include "JSString.h"
 #include "Operations.h"
+#include "PrototypeMapInlines.h"
 #include "SlotVisitorInlines.h"
 #include "StructureInlines.h"
 #include "ThrowScope.h"

Modified: trunk/Source/_javascript_Core/runtime/PrototypeMap.cpp (222900 => 222901)


--- trunk/Source/_javascript_Core/runtime/PrototypeMap.cpp	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/runtime/PrototypeMap.cpp	2017-10-05 07:38:00 UTC (rev 222901)
@@ -33,27 +33,6 @@
 
 namespace JSC {
 
-void PrototypeMap::addPrototype(JSObject* object)
-{
-    m_prototypes.set(object, object);
-
-    // Note that this method makes the somewhat odd decision to not check if this
-    // object currently has indexed accessors. We could do that check here, and if
-    // indexed accessors were found, we could tell the global object to have a bad
-    // time. But we avoid this, to allow the following to be always fast:
-    //
-    // 1) Create an object.
-    // 2) Give it a setter or read-only property that happens to have a numeric name.
-    // 3) Allocate objects that use this object as a prototype.
-    //
-    // This avoids anyone having a bad time. Even if the instance objects end up
-    // having indexed storage, the creation of indexed storage leads to a prototype
-    // chain walk that detects the presence of indexed setters and then does the
-    // right thing. As a result, having a bad time only happens if you add an
-    // indexed setter (or getter, or read-only field) to an object that is already
-    // used as a prototype.
-}
-
 inline Structure* PrototypeMap::createEmptyStructure(JSGlobalObject* globalObject, JSObject* prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity, bool makePolyProtoStructure)
 {
     RELEASE_ASSERT(!!prototype); // We use nullptr inside the HashMap for prototype to mean poly proto, so user's of this API must provide non-null prototypes.

Modified: trunk/Source/_javascript_Core/runtime/PrototypeMap.h (222900 => 222901)


--- trunk/Source/_javascript_Core/runtime/PrototypeMap.h	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/runtime/PrototypeMap.h	2017-10-05 07:38:00 UTC (rev 222901)
@@ -49,8 +49,8 @@
     JS_EXPORT_PRIVATE Structure* emptyObjectStructureForPrototype(JSGlobalObject*, JSObject*, unsigned inlineCapacity, bool makePolyProtoStructure = false);
     JS_EXPORT_PRIVATE Structure* emptyStructureForPrototypeFromBaseStructure(JSGlobalObject*, JSObject*, Structure*);
     void clearEmptyObjectStructureForPrototype(JSGlobalObject*, JSObject*, unsigned inlineCapacity);
-    JS_EXPORT_PRIVATE void addPrototype(JSObject*);
-    inline TriState isPrototype(JSObject*) const; // Returns a conservative estimate.
+    ALWAYS_INLINE void addPrototype(JSObject*);
+    ALWAYS_INLINE TriState isPrototype(JSObject*) const; // Returns a conservative estimate.
 
 private:
     Structure* createEmptyStructure(JSGlobalObject*, JSObject* prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity, bool makePolyProtoStructure);

Modified: trunk/Source/_javascript_Core/runtime/PrototypeMapInlines.h (222900 => 222901)


--- trunk/Source/_javascript_Core/runtime/PrototypeMapInlines.h	2017-10-05 07:05:44 UTC (rev 222900)
+++ trunk/Source/_javascript_Core/runtime/PrototypeMapInlines.h	2017-10-05 07:38:00 UTC (rev 222901)
@@ -30,7 +30,7 @@
 
 namespace JSC {
 
-inline TriState PrototypeMap::isPrototype(JSObject* object) const
+ALWAYS_INLINE TriState PrototypeMap::isPrototype(JSObject* object) const
 {
     if (!m_prototypes.contains(object))
         return FalseTriState;
@@ -42,5 +42,26 @@
     return MixedTriState;
 }
 
+ALWAYS_INLINE void PrototypeMap::addPrototype(JSObject* object)
+{
+    m_prototypes.set(object, object);
+
+    // Note that this method makes the somewhat odd decision to not check if this
+    // object currently has indexed accessors. We could do that check here, and if
+    // indexed accessors were found, we could tell the global object to have a bad
+    // time. But we avoid this, to allow the following to be always fast:
+    //
+    // 1) Create an object.
+    // 2) Give it a setter or read-only property that happens to have a numeric name.
+    // 3) Allocate objects that use this object as a prototype.
+    //
+    // This avoids anyone having a bad time. Even if the instance objects end up
+    // having indexed storage, the creation of indexed storage leads to a prototype
+    // chain walk that detects the presence of indexed setters and then does the
+    // right thing. As a result, having a bad time only happens if you add an
+    // indexed setter (or getter, or read-only field) to an object that is already
+    // used as a prototype.
+}
+
 } // namespace JSC
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to