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