Diff
Modified: trunk/JSTests/ChangeLog (206135 => 206136)
--- trunk/JSTests/ChangeLog 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/JSTests/ChangeLog 2016-09-20 01:05:50 UTC (rev 206136)
@@ -1,3 +1,25 @@
+2016-09-19 Saam Barati <sbar...@apple.com>
+
+ Make HasOwnProperty faster
+ https://bugs.webkit.org/show_bug.cgi?id=161708
+
+ Reviewed by Geoffrey Garen.
+
+ * microbenchmarks/has-own-property-name-cache.js: Added.
+ (foo):
+ * stress/has-own-property-cache-basics.js: Added.
+ (assert):
+ (foo):
+ * stress/has-own-property-name-cache-string-keys.js: Added.
+ (assert):
+ (foo):
+ * stress/has-own-property-name-cache-symbol-keys.js: Added.
+ (assert):
+ (foo):
+ * stress/has-own-property-name-cache-symbols-and-strings.js: Added.
+ (assert):
+ (foo):
+
2016-09-19 Benjamin Poulain <bpoul...@apple.com>
[JSC] Make the rounding-related nodes support any type
Added: trunk/JSTests/microbenchmarks/has-own-property-name-cache.js (0 => 206136)
--- trunk/JSTests/microbenchmarks/has-own-property-name-cache.js (rev 0)
+++ trunk/JSTests/microbenchmarks/has-own-property-name-cache.js 2016-09-20 01:05:50 UTC (rev 206136)
@@ -0,0 +1,45 @@
+let objs = [
+ {
+ __proto__: {
+ foo: 25
+ },
+ bar: 50,
+ baz: 75,
+ jaz: 80,
+ },
+ {
+ __proto__: {
+ bar: 25
+ },
+ baz: 75,
+ kaz: 80,
+ bar: 50,
+ jaz: 80,
+ },
+ {
+ __proto__: {
+ bar: 25,
+ jaz: 50
+ },
+ bar: 50,
+ baz: 75,
+ kaz: 80,
+ jaz: 80,
+ foo: 55
+ }
+];
+
+function foo(o) {
+ for (let p in o)
+ o.hasOwnProperty(p);
+
+}
+noInline(foo);
+
+let start = Date.now();
+for (let i = 0; i < 1000000; ++i) {
+ foo(objs[i % objs.length]);
+}
+const verbose = false;
+if (verbose)
+ print(Date.now() - start);
Added: trunk/JSTests/stress/has-own-property-cache-basics.js (0 => 206136)
--- trunk/JSTests/stress/has-own-property-cache-basics.js (rev 0)
+++ trunk/JSTests/stress/has-own-property-cache-basics.js 2016-09-20 01:05:50 UTC (rev 206136)
@@ -0,0 +1,31 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion.")
+}
+noInline(assert);
+
+let objs = [
+ {f: 50},
+ {f: 50, g: 70},
+ {g: 50, f: 70},
+ {h: 50, f: 70},
+ {z: 50, f: 70},
+ {k: 50, f: 70},
+];
+
+let s1 = Symbol();
+let s2 = Symbol();
+for (let o of objs)
+ o[s1] = "foo";
+
+function foo(o) {
+ assert(o.hasOwnProperty("f"));
+ assert(!o.hasOwnProperty("ff"));
+ assert(o.hasOwnProperty(s1));
+ assert(!o.hasOwnProperty(s2));
+}
+noInline(foo);
+
+for (let i = 0; i < 40000; ++i) {
+ foo(objs[i % objs.length]);
+}
Added: trunk/JSTests/stress/has-own-property-name-cache-string-keys.js (0 => 206136)
--- trunk/JSTests/stress/has-own-property-name-cache-string-keys.js (rev 0)
+++ trunk/JSTests/stress/has-own-property-name-cache-string-keys.js 2016-09-20 01:05:50 UTC (rev 206136)
@@ -0,0 +1,37 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion.");
+}
+noInline(assert);
+
+let objs = [];
+let keyPool = [];
+const numKeys = 800;
+for (let i = 0; i < numKeys; ++i)
+ keyPool.push(i + "foo");
+
+for (let i = 0; i < 10000; i++) {
+ let num = (Math.random() * numKeys) | 0;
+ let o = {};
+ for (let i = 0; i < num; ++i) {
+ o[keyPool[i]] = 25;
+ }
+ objs.push(o);
+}
+
+function foo(o) {
+ let props = Object.getOwnPropertyNames(o);
+ for (let i = 0; i < props.length; ++i) {
+ let s = props[i];
+ assert(o.hasOwnProperty(s));
+ }
+}
+noInline(foo);
+
+let start = Date.now();
+for (let o of objs) {
+ foo(o);
+}
+const verbose = false;
+if (verbose)
+ print(Date.now() - start);
Added: trunk/JSTests/stress/has-own-property-name-cache-symbol-keys.js (0 => 206136)
--- trunk/JSTests/stress/has-own-property-name-cache-symbol-keys.js (rev 0)
+++ trunk/JSTests/stress/has-own-property-name-cache-symbol-keys.js 2016-09-20 01:05:50 UTC (rev 206136)
@@ -0,0 +1,34 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion.");
+}
+noInline(assert);
+
+let objs = [];
+let symbolPool = [];
+const numSymbols = 800;
+for (let i = 0; i < numSymbols; ++i)
+ symbolPool.push(Symbol());
+
+for (let i = 0; i < 10000; i++) {
+ let num = (Math.random() * numSymbols) | 0;
+ let o = {};
+ for (let i = 0; i < num; ++i) {
+ o[symbolPool[i]] = 25;
+ }
+ objs.push(o);
+}
+
+function foo(o) {
+ let props = Object.getOwnPropertySymbols(o);
+ for (let i = 0; i < props.length; ++i) {
+ let s = props[i];
+ assert(o.hasOwnProperty(s));
+ }
+}
+noInline(foo);
+
+let start = Date.now();
+for (let o of objs) {
+ foo(o);
+}
Added: trunk/JSTests/stress/has-own-property-name-cache-symbols-and-strings.js (0 => 206136)
--- trunk/JSTests/stress/has-own-property-name-cache-symbols-and-strings.js (rev 0)
+++ trunk/JSTests/stress/has-own-property-name-cache-symbols-and-strings.js 2016-09-20 01:05:50 UTC (rev 206136)
@@ -0,0 +1,45 @@
+
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion.");
+}
+noInline(assert);
+
+let objs = [];
+let keyPool = [];
+let symbolPool = [];
+const numKeys = 500;
+for (let i = 0; i < numKeys; ++i) {
+ keyPool.push(i + "foo");
+ symbolPool.push(Symbol("Foo"));
+}
+
+for (let i = 0; i < 10000; i++) {
+ let num = (Math.random() * numKeys) | 0;
+ let o = {};
+ for (let i = 0; i < num; ++i) {
+ o[keyPool[i]] = 25;
+ o[symbolPool[i]] = 40;
+ }
+ objs.push(o);
+}
+
+let time = 0;
+function foo(o) {
+ let props = Object.getOwnPropertyNames(o);
+ props.push(...Object.getOwnPropertySymbols(o));
+ let start = Date.now();
+ for (let i = 0; i < props.length; ++i) {
+ let s = props[i];
+ assert(o.hasOwnProperty(s));
+ }
+ time += Date.now() - start;
+}
+noInline(foo);
+
+for (let o of objs) {
+ foo(o);
+}
+const verbose = false;
+if (verbose)
+ print(time);
Modified: trunk/Source/_javascript_Core/ChangeLog (206135 => 206136)
--- trunk/Source/_javascript_Core/ChangeLog 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-09-20 01:05:50 UTC (rev 206136)
@@ -1,3 +1,90 @@
+2016-09-19 Saam Barati <sbar...@apple.com>
+
+ Make HasOwnProperty faster
+ https://bugs.webkit.org/show_bug.cgi?id=161708
+
+ Reviewed by Geoffrey Garen.
+
+ This patch adds a cache for HasOwnProperty. The cache holds tuples
+ of {StructureID, UniquedStringImpl*, boolean} where the boolean indicates
+ the result of performing hasOwnProperty on an object with StructureID and
+ UniquedStringImpl*. If the cache contains an item, we can be guaranteed
+ that it contains the same result as performing hasOwnProperty on an
+ object O with a given structure and key. To guarantee this, we only add
+ items into the cache when the Structure of the given item is cacheable.
+
+ The caching strategy is simple: when adding new items into the cache,
+ we will evict any item that was in the location that the new item
+ is hashed into. We also clear the cache on every GC. This strategy
+ proves to be successful on speedometer, which sees a cache hit rate
+ over 90%. This caching strategy is now inlined into the DFG/FTL JITs
+ by now recognizing hasOwnProperty as an intrinsic with the corresponding
+ HasOwnProperty node. The goal of the node is to emit inlined code for
+ the cache lookup to prevent the overhead of the call for the common
+ case where we get a cache hit.
+
+ I'm seeing around a 1% to 1.5% percent improvement on Speedometer on
+ my machine. Hopefully the perf bots agree with my machine.
+
+ This patch also speeds up the microbenchmark I added by 2.5x.
+
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ (JSC::DFG::SpeculateCellOperand::SpeculateCellOperand):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGValidate.cpp:
+ * ftl/FTLAbstractHeapRepository.h:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileHasOwnProperty):
+ * heap/Heap.cpp:
+ (JSC::Heap::collectImpl):
+ * jit/JITOperations.h:
+ * runtime/HasOwnPropertyCache.h: Added.
+ (JSC::HasOwnPropertyCache::Entry::offsetOfStructureID):
+ (JSC::HasOwnPropertyCache::Entry::offsetOfImpl):
+ (JSC::HasOwnPropertyCache::Entry::offsetOfResult):
+ (JSC::HasOwnPropertyCache::operator delete):
+ (JSC::HasOwnPropertyCache::create):
+ (JSC::HasOwnPropertyCache::hash):
+ (JSC::HasOwnPropertyCache::get):
+ (JSC::HasOwnPropertyCache::tryAdd):
+ (JSC::HasOwnPropertyCache::clear):
+ (JSC::VM::ensureHasOwnPropertyCache):
+ * runtime/Intrinsic.h:
+ * runtime/JSObject.h:
+ * runtime/JSObjectInlines.h:
+ (JSC::JSObject::hasOwnProperty):
+ * runtime/ObjectPrototype.cpp:
+ (JSC::ObjectPrototype::finishCreation):
+ (JSC::objectProtoFuncHasOwnProperty):
+ * runtime/Symbol.h:
+ * runtime/VM.cpp:
+ * runtime/VM.h:
+ (JSC::VM::hasOwnPropertyCache):
+
2016-09-19 Benjamin Poulain <bpoul...@apple.com>
[JSC] Make the rounding-related nodes support any type
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (206135 => 206136)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2016-09-20 01:05:50 UTC (rev 206136)
@@ -1334,6 +1334,7 @@
79CFC6F01C33B10000C768EA /* LLIntPCRanges.h in Headers */ = {isa = PBXBuildFile; fileRef = 79CFC6EF1C33B10000C768EA /* LLIntPCRanges.h */; settings = {ATTRIBUTES = (Private, ); }; };
79D5CD5A1C1106A900CECA07 /* SamplingProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79D5CD581C1106A900CECA07 /* SamplingProfiler.cpp */; };
79D5CD5B1C1106A900CECA07 /* SamplingProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 79D5CD591C1106A900CECA07 /* SamplingProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 79DFCBDB1D88C59600527D03 /* HasOwnPropertyCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 79DFCBDA1D88C59600527D03 /* HasOwnPropertyCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
79EE0BFF1B4AFB85000385C9 /* VariableEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */; };
79EE0C001B4AFB85000385C9 /* VariableEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
79F8FC1E1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */; };
@@ -3578,6 +3579,7 @@
79CFC6EF1C33B10000C768EA /* LLIntPCRanges.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntPCRanges.h; path = llint/LLIntPCRanges.h; sourceTree = "<group>"; };
79D5CD581C1106A900CECA07 /* SamplingProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplingProfiler.cpp; sourceTree = "<group>"; };
79D5CD591C1106A900CECA07 /* SamplingProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SamplingProfiler.h; sourceTree = "<group>"; };
+ 79DFCBDA1D88C59600527D03 /* HasOwnPropertyCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HasOwnPropertyCache.h; sourceTree = "<group>"; };
79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VariableEnvironment.cpp; sourceTree = "<group>"; };
79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableEnvironment.h; sourceTree = "<group>"; };
79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGMaximalFlushInsertionPhase.cpp; path = dfg/DFGMaximalFlushInsertionPhase.cpp; sourceTree = "<group>"; };
@@ -5805,6 +5807,7 @@
BC337BDE0E1AF0B80076918A /* GetterSetter.h */,
79A0907D1D768465008B889B /* HashMapImpl.cpp */,
79A0907E1D768465008B889B /* HashMapImpl.h */,
+ 79DFCBDA1D88C59600527D03 /* HasOwnPropertyCache.h */,
933A349D038AE80F008635CE /* Identifier.cpp */,
933A349A038AE7C6008635CE /* Identifier.h */,
8606DDE918DA44AB00A383D0 /* IdentifierInlines.h */,
@@ -8288,6 +8291,7 @@
0FF42748158EBE91004CB9FF /* udis86_syn.h in Headers */,
0FF42749158EBE91004CB9FF /* udis86_types.h in Headers */,
A7E5AB391799E4B200D2833D /* UDis86Disassembler.h in Headers */,
+ 79DFCBDB1D88C59600527D03 /* HasOwnPropertyCache.h in Headers */,
A7A8AF4117ADB5F3005AB174 /* Uint16Array.h in Headers */,
866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */,
A7A8AF4217ADB5F3005AB174 /* Uint32Array.h in Headers */,
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -2665,6 +2665,12 @@
forNode(node).setType(SpecBoolean);
break;
}
+
+ case HasOwnProperty: {
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(SpecBoolean);
+ break;
+ }
case GetEnumerableLength: {
forNode(node).setType(SpecInt32Only);
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -2561,6 +2561,26 @@
return true;
}
+ case HasOwnPropertyIntrinsic: {
+ if (argumentCountIncludingThis != 2)
+ return false;
+
+ // This can be racy, that's fine. We know that once we observe that this is created,
+ // that it will never be destroyed until the VM is destroyed. It's unlikely that
+ // we'd ever get to the point where we inline this as an intrinsic without the
+ // cache being created, however, it's possible if we always throw exceptions inside
+ // hasOwnProperty.
+ if (!m_vm->hasOwnPropertyCache())
+ return false;
+
+ insertChecks();
+ Node* object = get(virtualRegisterForArgument(0, registerOffset));
+ Node* key = get(virtualRegisterForArgument(1, registerOffset));
+ Node* result = addToGraph(HasOwnProperty, object, key);
+ set(VirtualRegister(resultOperand), result);
+ return true;
+ }
+
default:
return false;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -501,6 +501,7 @@
case ConstructForwardVarargs:
case ToPrimitive:
case In:
+ case HasOwnProperty:
case ValueAdd:
case SetFunctionName:
case GetDynamicVar:
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -173,6 +173,7 @@
case ToString:
case CallStringConstructor:
case In:
+ case HasOwnProperty:
case Jump:
case Branch:
case Switch:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -1311,6 +1311,22 @@
break;
}
+ case HasOwnProperty: {
+ fixEdge<ObjectUse>(node->child1());
+#if CPU(X86) && USE(JSVALUE32_64)
+ // We don't have enough registers to do anything interesting on x86.
+ fixEdge<UntypedUse>(node->child2());
+#else
+ if (node->child2()->shouldSpeculateString())
+ fixEdge<StringUse>(node->child2());
+ else if (node->child2()->shouldSpeculateSymbol())
+ fixEdge<SymbolUse>(node->child2());
+ else
+ fixEdge<UntypedUse>(node->child2());
+#endif
+ break;
+ }
+
case Check: {
m_graph.doToChildren(
node,
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -331,6 +331,7 @@
macro(ProfileType, NodeMustGenerate) \
macro(ProfileControlFlow, NodeMustGenerate) \
macro(SetFunctionName, NodeMustGenerate) \
+ macro(HasOwnProperty, NodeResultBoolean) \
\
macro(CreateActivation, NodeResultJS) \
\
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -42,6 +42,7 @@
#include "FTLForOSREntryJITCode.h"
#include "FTLOSREntry.h"
#include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
#include "HostCallReturnValue.h"
#include "Interpreter.h"
#include "JIT.h"
@@ -1673,6 +1674,28 @@
return sizeOfVarargs(exec, arguments, firstVarArgOffset);
}
+int32_t JIT_OPERATION operationHasOwnProperty(ExecState* exec, JSObject* thisObject, EncodedJSValue encodedKey)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSValue key = JSValue::decode(encodedKey);
+ Identifier propertyName = key.toPropertyKey(exec);
+ if (UNLIKELY(scope.exception()))
+ return false;
+
+ PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty);
+ bool result = thisObject->hasOwnProperty(exec, propertyName.impl(), slot);
+ if (UNLIKELY(scope.exception()))
+ return false;
+
+ HasOwnPropertyCache* hasOwnPropertyCache = vm.hasOwnPropertyCache();
+ ASSERT(hasOwnPropertyCache);
+ hasOwnPropertyCache->tryAdd(vm, slot, thisObject, propertyName.impl(), result);
+ return result;
+}
+
void JIT_OPERATION operationLoadVarargs(ExecState* exec, int32_t firstElementDest, EncodedJSValue encodedArguments, int32_t offset, int32_t length, int32_t mandatoryMinimum)
{
VM& vm = exec->vm();
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -177,6 +177,8 @@
int32_t JIT_OPERATION operationSizeOfVarargs(ExecState*, EncodedJSValue arguments, int32_t firstVarArgOffset);
void JIT_OPERATION operationLoadVarargs(ExecState*, int32_t firstElementDest, EncodedJSValue arguments, int32_t offset, int32_t length, int32_t mandatoryMinimum);
+int32_t JIT_OPERATION operationHasOwnProperty(ExecState*, JSObject*, EncodedJSValue);
+
JSCell* JIT_OPERATION operationResolveScope(ExecState*, JSScope*, UniquedStringImpl*);
EncodedJSValue JIT_OPERATION operationGetDynamicVar(ExecState*, JSObject* scope, UniquedStringImpl*, unsigned);
void JIT_OPERATION operationPutDynamicVar(ExecState*, JSObject* scope, EncodedJSValue, UniquedStringImpl*, unsigned);
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -910,6 +910,10 @@
setPrediction(SpecBoolean);
break;
+ case HasOwnProperty:
+ setPrediction(SpecBoolean);
+ break;
+
case GetEnumerableLength: {
setPrediction(SpecInt32Only);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -284,6 +284,7 @@
case NewStringObject:
case MakeRope:
case In:
+ case HasOwnProperty:
case CreateActivation:
case CreateDirectArguments:
case CreateScopedArguments:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -982,6 +982,11 @@
m_jit.setupArgumentsWithExecState(TrustedImmPtr(index), arg1);
return appendCallSetResult(operation, result);
}
+ JITCompiler::Call callOperation(Z_JITOperation_EOI operation, GPRReg result, GPRReg obj, GPRReg impl)
+ {
+ m_jit.setupArgumentsWithExecState(obj, impl);
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(P_JITOperation_ESt operation, GPRReg result, Structure* structure)
{
m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
@@ -1309,19 +1314,21 @@
#if USE(JSVALUE64)
-
+ JITCompiler::Call callOperation(Z_JITOperation_EOJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(C_JITOperation_ECJZ operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
return appendCallSetResult(operation, result);
}
-
JITCompiler::Call callOperation(J_JITOperation_EJJMic operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr mathIC)
{
m_jit.setupArgumentsWithExecState(arg1.gpr(), arg2.gpr(), mathIC);
return appendCallSetResult(operation, result.gpr());
}
-
JITCompiler::Call callOperation(J_JITOperation_EJJI operation, GPRReg result, GPRReg arg1, GPRReg arg2, UniquedStringImpl* uid)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(uid));
@@ -1749,7 +1756,11 @@
return appendCallSetResult(operation, result);
}
#else // USE(JSVALUE32_64)
-
+ JITCompiler::Call callOperation(Z_JITOperation_EOJ operation, GPRReg result, GPRReg arg1, JSValueRegs arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR());
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(C_JITOperation_ECJZ operation, GPRReg result, GPRReg arg1, JSValueRegs arg2, GPRReg arg3)
{
m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR(), arg3);
@@ -3522,6 +3533,8 @@
};
class SpeculateCellOperand {
+ WTF_MAKE_NONCOPYABLE(SpeculateCellOperand);
+
public:
explicit SpeculateCellOperand(SpeculativeJIT* jit, Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
: m_jit(jit)
@@ -3536,6 +3549,16 @@
gpr();
}
+ explicit SpeculateCellOperand(SpeculateCellOperand&& other)
+ {
+ m_jit = other.m_jit;
+ m_edge = other.m_edge;
+ m_gprOrInvalid = other.m_gprOrInvalid;
+
+ other.m_gprOrInvalid = InvalidGPRReg;
+ other.m_edge = Edge();
+ }
+
~SpeculateCellOperand()
{
if (!m_edge)
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -37,6 +37,7 @@
#include "DFGSlowPathGenerator.h"
#include "DirectArguments.h"
#include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
#include "HashMapImpl.h"
#include "JSEnvironmentRecord.h"
#include "JSLexicalEnvironment.h"
@@ -4888,6 +4889,122 @@
compileIn(node);
break;
+ case HasOwnProperty: {
+#if CPU(X86)
+ ASSERT(node->child2().useKind() == UntypedUse);
+ SpeculateCellOperand object(this, node->child1());
+ JSValueOperand key(this, node->child2());
+ GPRTemporary result(this, Reuse, object);
+
+ JSValueRegs keyRegs = key.jsValueRegs();
+ GPRReg objectGPR = object.gpr();
+ GPRReg resultGPR = result.gpr();
+ flushRegisters();
+ callOperation(operationHasOwnProperty, resultGPR, objectGPR, keyRegs);
+ booleanResult(resultGPR, node);
+#else
+ SpeculateCellOperand object(this, node->child1());
+ GPRTemporary uniquedStringImpl(this);
+ GPRTemporary temp(this);
+ GPRTemporary hash(this);
+ GPRTemporary structureID(this);
+ GPRTemporary result(this);
+
+ Optional<SpeculateCellOperand> keyAsCell;
+ Optional<JSValueOperand> keyAsValue;
+ JSValueRegs keyRegs;
+ if (node->child2().useKind() == UntypedUse) {
+ keyAsValue = JSValueOperand(this, node->child2());
+ keyRegs = keyAsValue->jsValueRegs();
+ } else {
+ ASSERT(node->child2().useKind() == StringUse || node->child2().useKind() == SymbolUse);
+ keyAsCell = SpeculateCellOperand(this, node->child2());
+ keyRegs = JSValueRegs::payloadOnly(keyAsCell->gpr());
+ }
+
+ GPRReg objectGPR = object.gpr();
+ GPRReg implGPR = uniquedStringImpl.gpr();
+ GPRReg tempGPR = temp.gpr();
+ GPRReg hashGPR = hash.gpr();
+ GPRReg structureIDGPR = structureID.gpr();
+ GPRReg resultGPR = result.gpr();
+
+ speculateObject(node->child1());
+
+ MacroAssembler::JumpList slowPath;
+ switch (node->child2().useKind()) {
+ case SymbolUse: {
+ speculateSymbol(node->child2(), keyRegs.payloadGPR());
+ m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), Symbol::offsetOfSymbolImpl()), implGPR);
+ break;
+ }
+ case StringUse: {
+ speculateString(node->child2(), keyRegs.payloadGPR());
+ m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), JSString::offsetOfValue()), implGPR);
+ slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+ slowPath.append(m_jit.branchTest32(
+ MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+ MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+ break;
+ }
+ case UntypedUse: {
+ slowPath.append(m_jit.branchIfNotCell(keyRegs));
+ auto isNotString = m_jit.branchIfNotString(keyRegs.payloadGPR());
+ m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), JSString::offsetOfValue()), implGPR);
+ slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+ slowPath.append(m_jit.branchTest32(
+ MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+ MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+ auto hasUniquedImpl = m_jit.jump();
+
+ isNotString.link(&m_jit);
+ slowPath.append(m_jit.branchIfNotSymbol(keyRegs.payloadGPR()));
+ m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), Symbol::offsetOfSymbolImpl()), implGPR);
+
+ hasUniquedImpl.link(&m_jit);
+ break;
+ }
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ // Note that we don't test if the hash is zero here. AtomicStringImpl's can't have a zero
+ // hash, however, a SymbolImpl may. But, because this is a cache, we don't care. We only
+ // ever load the result from the cache if the cache entry matches what we are querying for.
+ // So we either get super lucky and use zero for the hash and somehow collide with the entity
+ // we're looking for, or we realize we're comparing against another entity, and go to the
+ // slow path anyways.
+ m_jit.load32(MacroAssembler::Address(implGPR, UniquedStringImpl::flagsOffset()), hashGPR);
+ m_jit.urshift32(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), hashGPR);
+ m_jit.load32(MacroAssembler::Address(objectGPR, JSCell::structureIDOffset()), structureIDGPR);
+ m_jit.add32(structureIDGPR, hashGPR);
+ m_jit.and32(TrustedImm32(HasOwnPropertyCache::mask), hashGPR);
+ m_jit.mul32(TrustedImm32(sizeof(HasOwnPropertyCache::Entry)), hashGPR, hashGPR);
+ ASSERT(m_jit.vm()->hasOwnPropertyCache());
+ m_jit.move(TrustedImmPtr(m_jit.vm()->hasOwnPropertyCache()), tempGPR);
+ slowPath.append(m_jit.branchPtr(MacroAssembler::NotEqual,
+ MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfImpl()), implGPR));
+ m_jit.load8(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfResult()), resultGPR);
+ m_jit.load32(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfStructureID()), tempGPR);
+ slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, structureIDGPR));
+ auto done = m_jit.jump();
+
+ slowPath.link(&m_jit);
+ silentSpillAllRegisters(resultGPR);
+ if (node->child2().useKind() != UntypedUse) {
+ m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag), tempGPR);
+ keyRegs = JSValueRegs(tempGPR, keyRegs.payloadGPR());
+ }
+ callOperation(operationHasOwnProperty, resultGPR, objectGPR, keyRegs);
+ silentFillAllRegisters(resultGPR);
+ m_jit.exceptionCheck();
+
+ done.link(&m_jit);
+ booleanResult(resultGPR, node);
+#endif // CPU(X86)
+ break;
+ }
+
case StoreBarrier: {
compileStoreBarrier(node);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -36,6 +36,7 @@
#include "DFGSlowPathGenerator.h"
#include "DirectArguments.h"
#include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
#include "JSCInlines.h"
#include "JSEnvironmentRecord.h"
#include "JSLexicalEnvironment.h"
@@ -4628,7 +4629,7 @@
m_jit.loadPtr(MacroAssembler::Address(inputGPR, JSString::offsetOfValue()), resultGPR);
slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, resultGPR));
m_jit.load32(MacroAssembler::Address(resultGPR, StringImpl::flagsOffset()), resultGPR);
- m_jit.urshift64(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), resultGPR);
+ m_jit.urshift32(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), resultGPR);
slowPath.append(m_jit.branchTest32(MacroAssembler::Zero, resultGPR));
done.append(m_jit.jump());
@@ -4935,6 +4936,106 @@
case In:
compileIn(node);
break;
+
+ case HasOwnProperty: {
+ SpeculateCellOperand object(this, node->child1());
+ GPRTemporary uniquedStringImpl(this);
+ GPRTemporary temp(this);
+ GPRTemporary hash(this);
+ GPRTemporary structureID(this);
+ GPRTemporary result(this);
+
+ Optional<SpeculateCellOperand> keyAsCell;
+ Optional<JSValueOperand> keyAsValue;
+ GPRReg keyGPR;
+ if (node->child2().useKind() == UntypedUse) {
+ keyAsValue = JSValueOperand(this, node->child2());
+ keyGPR = keyAsValue->gpr();
+ } else {
+ ASSERT(node->child2().useKind() == StringUse || node->child2().useKind() == SymbolUse);
+ keyAsCell = SpeculateCellOperand(this, node->child2());
+ keyGPR = keyAsCell->gpr();
+ }
+
+ GPRReg objectGPR = object.gpr();
+ GPRReg implGPR = uniquedStringImpl.gpr();
+ GPRReg tempGPR = temp.gpr();
+ GPRReg hashGPR = hash.gpr();
+ GPRReg structureIDGPR = structureID.gpr();
+ GPRReg resultGPR = result.gpr();
+
+ speculateObject(node->child1());
+
+ MacroAssembler::JumpList slowPath;
+ switch (node->child2().useKind()) {
+ case SymbolUse: {
+ speculateSymbol(node->child2(), keyGPR);
+ m_jit.loadPtr(MacroAssembler::Address(keyGPR, Symbol::offsetOfSymbolImpl()), implGPR);
+ break;
+ }
+ case StringUse: {
+ speculateString(node->child2(), keyGPR);
+ m_jit.loadPtr(MacroAssembler::Address(keyGPR, JSString::offsetOfValue()), implGPR);
+ slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+ slowPath.append(m_jit.branchTest32(
+ MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+ MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+ break;
+ }
+ case UntypedUse: {
+ slowPath.append(m_jit.branchIfNotCell(JSValueRegs(keyGPR)));
+ auto isNotString = m_jit.branchIfNotString(keyGPR);
+ m_jit.loadPtr(MacroAssembler::Address(keyGPR, JSString::offsetOfValue()), implGPR);
+ slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+ slowPath.append(m_jit.branchTest32(
+ MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+ MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+ auto hasUniquedImpl = m_jit.jump();
+
+ isNotString.link(&m_jit);
+ slowPath.append(m_jit.branchIfNotSymbol(keyGPR));
+ m_jit.loadPtr(MacroAssembler::Address(keyGPR, Symbol::offsetOfSymbolImpl()), implGPR);
+
+ hasUniquedImpl.link(&m_jit);
+ break;
+ }
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ // Note that we don't test if the hash is zero here. AtomicStringImpl's can't have a zero
+ // hash, however, a SymbolImpl may. But, because this is a cache, we don't care. We only
+ // ever load the result from the cache if the cache entry matches what we are querying for.
+ // So we either get super lucky and use zero for the hash and somehow collide with the entity
+ // we're looking for, or we realize we're comparing against another entity, and go to the
+ // slow path anyways.
+ m_jit.load32(MacroAssembler::Address(implGPR, UniquedStringImpl::flagsOffset()), hashGPR);
+ m_jit.urshift32(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), hashGPR);
+ m_jit.load32(MacroAssembler::Address(objectGPR, JSCell::structureIDOffset()), structureIDGPR);
+ m_jit.add32(structureIDGPR, hashGPR);
+ m_jit.and32(TrustedImm32(HasOwnPropertyCache::mask), hashGPR);
+ static_assert(sizeof(HasOwnPropertyCache::Entry) == 16, "Strong assumption of that here.");
+ m_jit.lshift32(TrustedImm32(4), hashGPR);
+ ASSERT(m_jit.vm()->hasOwnPropertyCache());
+ m_jit.move(TrustedImmPtr(m_jit.vm()->hasOwnPropertyCache()), tempGPR);
+ slowPath.append(m_jit.branchPtr(MacroAssembler::NotEqual,
+ MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfImpl()), implGPR));
+ m_jit.load8(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfResult()), resultGPR);
+ m_jit.load32(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfStructureID()), tempGPR);
+ slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, structureIDGPR));
+ auto done = m_jit.jump();
+
+ slowPath.link(&m_jit);
+ silentSpillAllRegisters(resultGPR);
+ callOperation(operationHasOwnProperty, resultGPR, objectGPR, keyGPR);
+ silentFillAllRegisters(resultGPR);
+ m_jit.exceptionCheck();
+
+ done.link(&m_jit);
+ m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
+ jsValueResult(resultGPR, node, DataFormatJSBoolean);
+ break;
+ }
case CountExecution:
m_jit.add64(TrustedImm32(1), MacroAssembler::AbsoluteAddress(node->executionCounter()->address()));
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -309,6 +309,10 @@
// while the inline one will not take a storage child at all.
// https://bugs.webkit.org/show_bug.cgi?id=159602
break;
+ case HasOwnProperty: {
+ VALIDATE((node), !!m_graph.m_vm.hasOwnPropertyCache());
+ break;
+ }
default:
break;
}
Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (206135 => 206136)
--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -31,9 +31,11 @@
#include "B3Value.h"
#include "DFGArrayMode.h"
#include "FTLAbstractHeap.h"
+#include "HasOwnPropertyCache.h"
#include "IndexingType.h"
#include "JSMap.h"
#include "JSSet.h"
+#include "Symbol.h"
namespace JSC { namespace FTL {
@@ -110,6 +112,7 @@
macro(HashMapImpl_buffer, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
macro(HashMapBucket_value, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()) \
macro(HashMapBucket_key, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey()) \
+ macro(Symbol_symbolImpl, Symbol::offsetOfSymbolImpl()) \
#define FOR_EACH_INDEXED_ABSTRACT_HEAP(macro) \
macro(DirectArguments_storage, DirectArguments::storageOffset(), sizeof(EncodedJSValue)) \
@@ -128,7 +131,8 @@
macro(scopedArgumentsTableArguments, 0, sizeof(int32_t)) \
macro(singleCharacterStrings, 0, sizeof(JSString*)) \
macro(structureTable, 0, sizeof(Structure*)) \
- macro(variables, 0, sizeof(Register))
+ macro(variables, 0, sizeof(Register)) \
+ macro(HasOwnPropertyCache, 0, sizeof(HasOwnPropertyCache::Entry)) \
#define FOR_EACH_NUMBERED_ABSTRACT_HEAP(macro) \
macro(properties)
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -181,6 +181,7 @@
case ThrowReferenceError:
case Unreachable:
case In:
+ case HasOwnProperty:
case IsCellWithType:
case MapHash:
case GetMapBucket:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -632,6 +632,9 @@
case In:
compileIn();
break;
+ case HasOwnProperty:
+ compileHasOwnProperty();
+ break;
case PutById:
case PutByIdDirect:
case PutByIdFlush:
@@ -6772,6 +6775,108 @@
setJSValue(vmCall(Int64, m_out.operation(operationGenericIn), m_callFrame, cell, lowJSValue(m_node->child1())));
}
+ void compileHasOwnProperty()
+ {
+ LBasicBlock slowCase = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+ LBasicBlock lastNext = nullptr;
+
+ LValue object = lowObject(m_node->child1());
+ LValue uniquedStringImpl;
+ LValue keyAsValue = nullptr;
+ switch (m_node->child2().useKind()) {
+ case StringUse: {
+ LBasicBlock isNonEmptyString = m_out.newBlock();
+ LBasicBlock isAtomicString = m_out.newBlock();
+
+ keyAsValue = lowString(m_node->child2());
+ uniquedStringImpl = m_out.loadPtr(keyAsValue, m_heaps.JSString_value);
+ m_out.branch(m_out.notNull(uniquedStringImpl), usually(isNonEmptyString), rarely(slowCase));
+
+ lastNext = m_out.appendTo(isNonEmptyString, isAtomicString);
+ LValue isNotAtomic = m_out.testIsZero32(m_out.load32(uniquedStringImpl, m_heaps.StringImpl_hashAndFlags), m_out.constInt32(StringImpl::flagIsAtomic()));
+ m_out.branch(isNotAtomic, rarely(slowCase), usually(isAtomicString));
+
+ m_out.appendTo(isAtomicString, slowCase);
+ break;
+ }
+ case SymbolUse: {
+ keyAsValue = lowSymbol(m_node->child2());
+ uniquedStringImpl = m_out.loadPtr(keyAsValue, m_heaps.Symbol_symbolImpl);
+ lastNext = m_out.insertNewBlocksBefore(slowCase);
+ break;
+ }
+ case UntypedUse: {
+ LBasicBlock isCellCase = m_out.newBlock();
+ LBasicBlock isStringCase = m_out.newBlock();
+ LBasicBlock notStringCase = m_out.newBlock();
+ LBasicBlock isNonEmptyString = m_out.newBlock();
+ LBasicBlock isSymbolCase = m_out.newBlock();
+ LBasicBlock hasUniquedStringImpl = m_out.newBlock();
+
+ keyAsValue = lowJSValue(m_node->child2());
+ m_out.branch(isCell(keyAsValue), usually(isCellCase), rarely(slowCase));
+
+ lastNext = m_out.appendTo(isCellCase, isStringCase);
+ m_out.branch(isString(keyAsValue), unsure(isStringCase), unsure(notStringCase));
+
+ m_out.appendTo(isStringCase, isNonEmptyString);
+ LValue implFromString = m_out.loadPtr(keyAsValue, m_heaps.JSString_value);
+ ValueFromBlock stringResult = m_out.anchor(implFromString);
+ m_out.branch(m_out.notNull(implFromString), usually(isNonEmptyString), rarely(slowCase));
+
+ m_out.appendTo(isNonEmptyString, notStringCase);
+ LValue isNotAtomic = m_out.testIsZero32(m_out.load32(implFromString, m_heaps.StringImpl_hashAndFlags), m_out.constInt32(StringImpl::flagIsAtomic()));
+ m_out.branch(isNotAtomic, rarely(slowCase), usually(hasUniquedStringImpl));
+
+ m_out.appendTo(notStringCase, isSymbolCase);
+ m_out.branch(isSymbol(keyAsValue), unsure(isSymbolCase), unsure(slowCase));
+
+ m_out.appendTo(isSymbolCase, hasUniquedStringImpl);
+ ValueFromBlock symbolResult = m_out.anchor(m_out.loadPtr(keyAsValue, m_heaps.Symbol_symbolImpl));
+ m_out.jump(hasUniquedStringImpl);
+
+ m_out.appendTo(hasUniquedStringImpl, slowCase);
+ uniquedStringImpl = m_out.phi(pointerType(), stringResult, symbolResult);
+ break;
+ }
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ ASSERT(keyAsValue);
+
+ // Note that we don't test if the hash is zero here. AtomicStringImpl's can't have a zero
+ // hash, however, a SymbolImpl may. But, because this is a cache, we don't care. We only
+ // ever load the result from the cache if the cache entry matches what we are querying for.
+ // So we either get super lucky and use zero for the hash and somehow collide with the entity
+ // we're looking for, or we realize we're comparing against another entity, and go to the
+ // slow path anyways.
+ LValue hash = m_out.lShr(m_out.load32(uniquedStringImpl, m_heaps.StringImpl_hashAndFlags), m_out.constInt32(StringImpl::s_flagCount));
+
+ LValue structureID = m_out.load32(object, m_heaps.JSCell_structureID);
+ LValue index = m_out.add(hash, structureID);
+ index = m_out.zeroExtPtr(m_out.bitAnd(index, m_out.constInt32(HasOwnPropertyCache::mask)));
+ ASSERT(vm().hasOwnPropertyCache());
+ LValue cache = m_out.constIntPtr(vm().hasOwnPropertyCache());
+
+ IndexedAbstractHeap& heap = m_heaps.HasOwnPropertyCache;
+ LValue sameStructureID = m_out.equal(structureID, m_out.load32(m_out.baseIndex(heap, cache, index, JSValue(), HasOwnPropertyCache::Entry::offsetOfStructureID())));
+ LValue sameImpl = m_out.equal(uniquedStringImpl, m_out.loadPtr(m_out.baseIndex(heap, cache, index, JSValue(), HasOwnPropertyCache::Entry::offsetOfImpl())));
+ ValueFromBlock fastResult = m_out.anchor(m_out.load8ZeroExt32(m_out.baseIndex(heap, cache, index, JSValue(), HasOwnPropertyCache::Entry::offsetOfResult())));
+ LValue cacheHit = m_out.bitAnd(sameStructureID, sameImpl);
+
+ m_out.branch(m_out.notZero32(cacheHit), usually(continuation), rarely(slowCase));
+
+ m_out.appendTo(slowCase, continuation);
+ ValueFromBlock slowResult;
+ slowResult = m_out.anchor(vmCall(Int32, m_out.operation(operationHasOwnProperty), m_callFrame, object, keyAsValue));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setBoolean(m_out.phi(Int32, fastResult, slowResult));
+ }
+
void compileOverridesHasInstance()
{
JSFunction* defaultHasInstanceFunction = jsCast<JSFunction*>(m_node->cellOperand()->value());
Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/heap/Heap.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -29,6 +29,7 @@
#include "GCActivityCallback.h"
#include "GCIncomingRefCountedSetInlines.h"
#include "GCTypeMap.h"
+#include "HasOwnPropertyCache.h"
#include "HeapHelperPool.h"
#include "HeapIterationScope.h"
#include "HeapProfiler.h"
@@ -1022,6 +1023,9 @@
stopAllocation();
prepareForMarking();
flushWriteBarrierBuffer();
+
+ if (HasOwnPropertyCache* cache = vm()->hasOwnPropertyCache())
+ cache->clear();
}
markRoots(gcStartTime, stackOrigin, stackTop, calleeSavedRegisters);
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (206135 => 206136)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -221,6 +221,8 @@
typedef int32_t (JIT_OPERATION *Z_JITOperation_EJOJ)(ExecState*, EncodedJSValue, JSObject*, EncodedJSValue);
typedef int32_t (JIT_OPERATION *Z_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
typedef int32_t (JIT_OPERATION *Z_JITOperation_EJZZ)(ExecState*, EncodedJSValue, int32_t, int32_t);
+typedef int32_t (JIT_OPERATION *Z_JITOperation_EOI)(ExecState*, JSObject*, UniquedStringImpl*);
+typedef int32_t (JIT_OPERATION *Z_JITOperation_EOJ)(ExecState*, JSObject*, EncodedJSValue);
typedef size_t (JIT_OPERATION *S_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
typedef size_t (JIT_OPERATION *S_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
typedef size_t (JIT_OPERATION *S_JITOperation_EGJJ)(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue);
Added: trunk/Source/_javascript_Core/runtime/HasOwnPropertyCache.h (0 => 206136)
--- trunk/Source/_javascript_Core/runtime/HasOwnPropertyCache.h (rev 0)
+++ trunk/Source/_javascript_Core/runtime/HasOwnPropertyCache.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "JSProxy.h"
+#include "PropertySlot.h"
+#include "Structure.h"
+
+namespace JSC {
+
+class HasOwnPropertyCache {
+ static const uint32_t size = 2 * 1024;
+ static_assert(!(size & (size - 1)), "size should be a power of two.");
+public:
+ static const uint32_t mask = size - 1;
+
+ struct Entry {
+ static ptrdiff_t offsetOfStructureID() { return OBJECT_OFFSETOF(Entry, structureID); }
+ static ptrdiff_t offsetOfImpl() { return OBJECT_OFFSETOF(Entry, impl); }
+ static ptrdiff_t offsetOfResult() { return OBJECT_OFFSETOF(Entry, result); }
+
+ UniquedStringImpl* impl;
+ StructureID structureID;
+ bool result;
+ };
+
+ HasOwnPropertyCache() = delete;
+
+ void operator delete(void* cache)
+ {
+ fastFree(cache);
+ }
+
+ static HasOwnPropertyCache* create()
+ {
+ size_t allocationSize = sizeof(Entry) * size;
+ HasOwnPropertyCache* result = static_cast<HasOwnPropertyCache*>(fastMalloc(allocationSize));
+ result->clear();
+ return result;
+ }
+
+ ALWAYS_INLINE static uint32_t hash(StructureID structureID, UniquedStringImpl* impl)
+ {
+ return bitwise_cast<uint32_t>(structureID) + impl->hash();
+ }
+
+ ALWAYS_INLINE Optional<bool> get(Structure* structure, PropertyName propName)
+ {
+ UniquedStringImpl* impl = propName.uid();
+ StructureID id = structure->id();
+ uint32_t index = HasOwnPropertyCache::hash(id, impl) & mask;
+ Entry& entry = bitwise_cast<Entry*>(this)[index];
+ if (entry.structureID == id && entry.impl == impl)
+ return entry.result;
+ return Nullopt;
+ }
+
+ ALWAYS_INLINE void tryAdd(VM& vm, PropertySlot& slot, JSObject* object, PropertyName propName, bool result)
+ {
+ if (parseIndex(propName))
+ return;
+
+ if (!slot.isCacheable() && !slot.isUnset())
+ return;
+
+ if (object->type() == PureForwardingProxyType || object->type() == ImpureProxyType)
+ return;
+
+ Structure* structure = object->structure(vm);
+ if (!structure->typeInfo().prohibitsPropertyCaching()
+ && structure->propertyAccessesAreCacheable()
+ && (!slot.isUnset() || structure->propertyAccessesAreCacheableForAbsence())) {
+ if (structure->isDictionary()) {
+ if (structure->hasBeenFlattenedBefore())
+ return;
+ object->flattenDictionaryObject(vm);
+ }
+
+ ASSERT(!result == slot.isUnset());
+
+ UniquedStringImpl* impl = propName.uid();
+ StructureID id = structure->id();
+ uint32_t index = HasOwnPropertyCache::hash(id, impl) & mask;
+ bitwise_cast<Entry*>(this)[index] = Entry{ impl, id, result };
+ }
+ }
+
+ void clear()
+ {
+ memset(this, 0, sizeof(Entry) * size);
+ }
+};
+
+ALWAYS_INLINE HasOwnPropertyCache* VM::ensureHasOwnPropertyCache()
+{
+ if (UNLIKELY(!m_hasOwnPropertyCache))
+ m_hasOwnPropertyCache = std::unique_ptr<HasOwnPropertyCache>(HasOwnPropertyCache::create());
+ return m_hasOwnPropertyCache.get();
+}
+
+} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/Intrinsic.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -65,6 +65,7 @@
JSMapGetIntrinsic,
JSMapHasIntrinsic,
JSSetHasIntrinsic,
+ HasOwnPropertyIntrinsic,
// Getter intrinsics.
TypedArrayLengthIntrinsic,
Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/JSObject.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -523,6 +523,7 @@
JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
bool hasPropertyGeneric(ExecState*, PropertyName, PropertySlot::InternalMethodType) const;
bool hasPropertyGeneric(ExecState*, unsigned propertyName, PropertySlot::InternalMethodType) const;
+ bool hasOwnProperty(ExecState*, PropertyName, PropertySlot&) const;
bool hasOwnProperty(ExecState*, PropertyName) const;
bool hasOwnProperty(ExecState*, unsigned) const;
Modified: trunk/Source/_javascript_Core/runtime/JSObjectInlines.h (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/JSObjectInlines.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/JSObjectInlines.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -199,14 +199,20 @@
// HasOwnProperty(O, P) from section 7.3.11 in the spec.
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasownproperty
-ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
{
- PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+ ASSERT(slot.internalMethodType() == PropertySlot::InternalMethodType::GetOwnProperty);
if (LIKELY(const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot == JSObject::getOwnPropertySlot))
return JSObject::getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
}
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+{
+ PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+ return hasOwnProperty(exec, propertyName, slot);
+}
+
ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const
{
PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
Modified: trunk/Source/_javascript_Core/runtime/ObjectPrototype.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/ObjectPrototype.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/ObjectPrototype.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -23,6 +23,7 @@
#include "Error.h"
#include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
#include "JSFunction.h"
#include "JSString.h"
#include "JSCInlines.h"
@@ -60,7 +61,7 @@
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, objectProtoFuncToString, DontEnum, 0);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, objectProtoFuncToLocaleString, DontEnum, 0);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0);
- JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1);
+ JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1, HasOwnPropertyIntrinsic);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable, DontEnum, 1);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isPrototypeOf, objectProtoFuncIsPrototypeOf, DontEnum, 1);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__defineGetter__, objectProtoFuncDefineGetter, DontEnum, 2);
@@ -97,9 +98,24 @@
if (UNLIKELY(scope.exception()))
return JSValue::encode(jsUndefined());
JSObject* thisObject = thisValue.toObject(exec);
- if (!thisObject)
+ if (UNLIKELY(!thisObject))
return JSValue::encode(JSValue());
- return JSValue::encode(jsBoolean(thisObject->hasOwnProperty(exec, propertyName)));
+
+ Structure* structure = thisObject->structure(vm);
+ HasOwnPropertyCache* hasOwnPropertyCache = vm.ensureHasOwnPropertyCache();
+ if (Optional<bool> result = hasOwnPropertyCache->get(structure, propertyName)) {
+ ASSERT(*result == thisObject->hasOwnProperty(exec, propertyName));
+ ASSERT(!scope.exception());
+ return JSValue::encode(jsBoolean(*result));
+ }
+
+ PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty);
+ bool result = thisObject->hasOwnProperty(exec, propertyName, slot);
+ if (UNLIKELY(scope.exception()))
+ return JSValue::encode(jsUndefined());
+
+ hasOwnPropertyCache->tryAdd(vm, slot, thisObject, propertyName, result);
+ return JSValue::encode(jsBoolean(result));
}
EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec)
Modified: trunk/Source/_javascript_Core/runtime/Symbol.h (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/Symbol.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/Symbol.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -59,6 +59,12 @@
JSObject* toObject(ExecState*, JSGlobalObject*) const;
double toNumber(ExecState*) const;
+ static ptrdiff_t offsetOfSymbolImpl()
+ {
+ // PrivateName is just a Ref<SymbolImpl> which can just be used as a SymbolImpl*.
+ return OBJECT_OFFSETOF(Symbol, m_privateName);
+ }
+
protected:
static void destroy(JSCell*);
Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/VM.cpp 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp 2016-09-20 01:05:50 UTC (rev 206136)
@@ -47,6 +47,7 @@
#include "FunctionConstructor.h"
#include "GCActivityCallback.h"
#include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
#include "Heap.h"
#include "HeapIterationScope.h"
#include "HeapProfiler.h"
Modified: trunk/Source/_javascript_Core/runtime/VM.h (206135 => 206136)
--- trunk/Source/_javascript_Core/runtime/VM.h 2016-09-20 01:02:40 UTC (rev 206135)
+++ trunk/Source/_javascript_Core/runtime/VM.h 2016-09-20 01:05:50 UTC (rev 206136)
@@ -89,6 +89,7 @@
class HandleStack;
class TypeProfiler;
class TypeProfilerLog;
+class HasOwnPropertyCache;
class HeapProfiler;
class Identifier;
class Interpreter;
@@ -554,6 +555,10 @@
BumpPointerAllocator m_regExpAllocator;
ConcurrentJITLock m_regExpAllocatorLock;
+ std::unique_ptr<HasOwnPropertyCache> m_hasOwnPropertyCache;
+ ALWAYS_INLINE HasOwnPropertyCache* hasOwnPropertyCache() { return m_hasOwnPropertyCache.get(); }
+ HasOwnPropertyCache* ensureHasOwnPropertyCache();
+
#if ENABLE(REGEXP_TRACING)
typedef ListHashSet<RegExp*> RTTraceList;
RTTraceList* m_rtTraceList;