We should support the ability to do a non-effectful getById
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Apr 2016 19:38:00 +0000 (19:38 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Apr 2016 19:38:00 +0000 (19:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156116

Reviewed by Benjamin Poulain.

Currently, there is no way in JS to do a non-effectful getById. A non-effectful getById is
useful because it enables us to take different code paths based on values that we would
otherwise not be able to have knowledge of. This patch adds this new feature called
try_get_by_id that will attempt to do as much of a get_by_id as possible without performing
an effectful behavior. Thus, try_get_by_id will return the value if the slot is a value, the
GetterSetter object if the slot is a normal accessor (not a CustomGetterSetter) and
undefined if the slot is unset.  If the slot is proxied or any other cases then the result
is null. In theory, if we ever wanted to check for null we could add a sentinal object to
the global object that indicates we could not get the result.

In order to implement this feature we add a new enum GetByIdKind that indicates what to do
for accessor properties in PolymorphicAccess. If the GetByIdKind is pure then we treat the
get_by_id the same way we would for load and return the value at the appropriate offset.
Additionally, in order to make sure the we can properly compare the GetterSetter object
with === GetterSetters are now JSObjects. This comes at the cost of eight extra bytes on the
GetterSetter object but it vastly simplifies the patch. Additionally, the extra bytes are
likely to have little to no impact on memory usage as normal accessors are generally rare.

* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/BuiltinExecutableCreator.cpp: Added.
(JSC::createBuiltinExecutable):
* builtins/BuiltinExecutableCreator.h: Copied from Source/JavaScriptCore/builtins/BuiltinExecutables.h.
* builtins/BuiltinExecutables.cpp:
(JSC::BuiltinExecutables::createDefaultConstructor):
(JSC::BuiltinExecutables::createBuiltinExecutable):
(JSC::createBuiltinExecutable):
(JSC::BuiltinExecutables::createExecutable):
(JSC::createExecutableInternal): Deleted.
* builtins/BuiltinExecutables.h:
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/PolymorphicAccess.cpp:
(JSC::AccessCase::tryGet):
(JSC::AccessCase::generate):
(WTF::printInternal):
* bytecode/PolymorphicAccess.h:
(JSC::AccessCase::isGet): Deleted.
(JSC::AccessCase::isPut): Deleted.
(JSC::AccessCase::isIn): Deleted.
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::reset):
* bytecode/StructureStubInfo.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitTryGetById):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_tryGetById):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::getById):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITInlineCacheGenerator.cpp:
(JSC::JITGetByIdGenerator::JITGetByIdGenerator):
* jit/JITInlineCacheGenerator.h:
* jit/JITInlines.h:
(JSC::JIT::callOperation):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitGetByValWithCachedId):
(JSC::JIT::emit_op_try_get_by_id):
(JSC::JIT::emitSlow_op_try_get_by_id):
(JSC::JIT::emit_op_get_by_id):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitGetByValWithCachedId):
(JSC::JIT::emit_op_try_get_by_id):
(JSC::JIT::emitSlow_op_try_get_by_id):
(JSC::JIT::emit_op_get_by_id):
* jit/Repatch.cpp:
(JSC::repatchByIdSelfAccess):
(JSC::appropriateOptimizingGetByIdFunction):
(JSC::appropriateGenericGetByIdFunction):
(JSC::tryCacheGetByID):
(JSC::repatchGetByID):
(JSC::resetGetByID):
* jit/Repatch.h:
* jsc.cpp:
(GlobalObject::finishCreation):
(functionGetGetterSetter):
(functionCreateBuiltin):
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
* runtime/GetterSetter.cpp:
* runtime/GetterSetter.h:
* runtime/JSType.h:
* runtime/PropertySlot.cpp:
(JSC::PropertySlot::getPureResult):
* runtime/PropertySlot.h:
* runtime/ProxyObject.cpp:
(JSC::ProxyObject::getOwnPropertySlotCommon):
* tests/stress/try-get-by-id.js: Added.
(tryGetByIdText):
(getCaller.obj.1.throw.new.Error.let.func):
(getCaller.obj.1.throw.new.Error):
(throw.new.Error.get let):
(throw.new.Error.):
(throw.new.Error.let.get createBuiltin):
(get let):
(let.get createBuiltin):
(let.func):
(get let.func):
(get throw):

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

43 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp [new file with mode: 0644]
Source/JavaScriptCore/builtins/BuiltinExecutableCreator.h [new file with mode: 0644]
Source/JavaScriptCore/builtins/BuiltinExecutables.cpp
Source/JavaScriptCore/builtins/BuiltinExecutables.h
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
Source/JavaScriptCore/bytecode/PolymorphicAccess.h
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlineCacheGenerator.cpp
Source/JavaScriptCore/jit/JITInlineCacheGenerator.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/jit/Repatch.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/llint/LLIntData.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/runtime/GetterSetter.cpp
Source/JavaScriptCore/runtime/GetterSetter.h
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/runtime/PropertySlot.cpp
Source/JavaScriptCore/runtime/PropertySlot.h
Source/JavaScriptCore/runtime/ProxyObject.cpp
Source/JavaScriptCore/tests/stress/try-get-by-id.js [new file with mode: 0644]

index 23b03bc..9558b4b 100644 (file)
@@ -1,3 +1,128 @@
+2016-04-07  Keith Miller  <keith_miller@apple.com>
+
+        We should support the ability to do a non-effectful getById
+        https://bugs.webkit.org/show_bug.cgi?id=156116
+
+        Reviewed by Benjamin Poulain.
+
+        Currently, there is no way in JS to do a non-effectful getById. A non-effectful getById is
+        useful because it enables us to take different code paths based on values that we would
+        otherwise not be able to have knowledge of. This patch adds this new feature called
+        try_get_by_id that will attempt to do as much of a get_by_id as possible without performing
+        an effectful behavior. Thus, try_get_by_id will return the value if the slot is a value, the
+        GetterSetter object if the slot is a normal accessor (not a CustomGetterSetter) and
+        undefined if the slot is unset.  If the slot is proxied or any other cases then the result
+        is null. In theory, if we ever wanted to check for null we could add a sentinal object to
+        the global object that indicates we could not get the result.
+
+        In order to implement this feature we add a new enum GetByIdKind that indicates what to do
+        for accessor properties in PolymorphicAccess. If the GetByIdKind is pure then we treat the
+        get_by_id the same way we would for load and return the value at the appropriate offset.
+        Additionally, in order to make sure the we can properly compare the GetterSetter object
+        with === GetterSetters are now JSObjects. This comes at the cost of eight extra bytes on the
+        GetterSetter object but it vastly simplifies the patch. Additionally, the extra bytes are
+        likely to have little to no impact on memory usage as normal accessors are generally rare.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/BuiltinExecutableCreator.cpp: Added.
+        (JSC::createBuiltinExecutable):
+        * builtins/BuiltinExecutableCreator.h: Copied from Source/JavaScriptCore/builtins/BuiltinExecutables.h.
+        * builtins/BuiltinExecutables.cpp:
+        (JSC::BuiltinExecutables::createDefaultConstructor):
+        (JSC::BuiltinExecutables::createBuiltinExecutable):
+        (JSC::createBuiltinExecutable):
+        (JSC::BuiltinExecutables::createExecutable):
+        (JSC::createExecutableInternal): Deleted.
+        * builtins/BuiltinExecutables.h:
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        * bytecode/PolymorphicAccess.cpp:
+        (JSC::AccessCase::tryGet):
+        (JSC::AccessCase::generate):
+        (WTF::printInternal):
+        * bytecode/PolymorphicAccess.h:
+        (JSC::AccessCase::isGet): Deleted.
+        (JSC::AccessCase::isPut): Deleted.
+        (JSC::AccessCase::isIn): Deleted.
+        * bytecode/StructureStubInfo.cpp:
+        (JSC::StructureStubInfo::reset):
+        * bytecode/StructureStubInfo.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitTryGetById):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_tryGetById):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::getById):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * jit/JIT.h:
+        * jit/JITInlineCacheGenerator.cpp:
+        (JSC::JITGetByIdGenerator::JITGetByIdGenerator):
+        * jit/JITInlineCacheGenerator.h:
+        * jit/JITInlines.h:
+        (JSC::JIT::callOperation):
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emitGetByValWithCachedId):
+        (JSC::JIT::emit_op_try_get_by_id):
+        (JSC::JIT::emitSlow_op_try_get_by_id):
+        (JSC::JIT::emit_op_get_by_id):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emitGetByValWithCachedId):
+        (JSC::JIT::emit_op_try_get_by_id):
+        (JSC::JIT::emitSlow_op_try_get_by_id):
+        (JSC::JIT::emit_op_get_by_id):
+        * jit/Repatch.cpp:
+        (JSC::repatchByIdSelfAccess):
+        (JSC::appropriateOptimizingGetByIdFunction):
+        (JSC::appropriateGenericGetByIdFunction):
+        (JSC::tryCacheGetByID):
+        (JSC::repatchGetByID):
+        (JSC::resetGetByID):
+        * jit/Repatch.h:
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionGetGetterSetter):
+        (functionCreateBuiltin):
+        * llint/LLIntData.cpp:
+        (JSC::LLInt::Data::performAssertions):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter.asm:
+        * runtime/GetterSetter.cpp:
+        * runtime/GetterSetter.h:
+        * runtime/JSType.h:
+        * runtime/PropertySlot.cpp:
+        (JSC::PropertySlot::getPureResult):
+        * runtime/PropertySlot.h:
+        * runtime/ProxyObject.cpp:
+        (JSC::ProxyObject::getOwnPropertySlotCommon):
+        * tests/stress/try-get-by-id.js: Added.
+        (tryGetByIdText):
+        (getCaller.obj.1.throw.new.Error.let.func):
+        (getCaller.obj.1.throw.new.Error):
+        (throw.new.Error.get let):
+        (throw.new.Error.):
+        (throw.new.Error.let.get createBuiltin):
+        (get let):
+        (let.get createBuiltin):
+        (let.func):
+        (get let.func):
+        (get throw):
+
 2016-04-07  Filip Pizlo  <fpizlo@apple.com>
 
         Rationalize the makeSpaceForCCall stuff
index 3e5ff31..ff97a01 100644 (file)
                DC17E8181C9C91D9008A6AB3 /* ShadowChicken.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */; };
                DC17E8191C9C91DB008A6AB3 /* ShadowChickenInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */; };
                DC17E81A1C9C91E9008A6AB3 /* CCallHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */; };
+               DE26E9031CB5DD0500D2BE82 /* BuiltinExecutableCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = DE26E9021CB5DD0500D2BE82 /* BuiltinExecutableCreator.h */; };
+               DE26E9071CB5DEFB00D2BE82 /* BuiltinExecutableCreator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DE26E9061CB5DD9600D2BE82 /* BuiltinExecutableCreator.cpp */; };
                DE5A0A001BA3AC3E003D4424 /* IntrinsicEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */; };
                DEA7E2441BBC677200D78440 /* JSTypedArrayViewPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */; };
                DEA7E2451BBC677F00D78440 /* JSTypedArrayViewPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7C1B791106000EBD33 /* JSTypedArrayViewPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DC17E8141C9C7FD4008A6AB3 /* ShadowChicken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowChicken.h; sourceTree = "<group>"; };
                DC17E8151C9C7FD4008A6AB3 /* ShadowChickenInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowChickenInlines.h; sourceTree = "<group>"; };
                DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CCallHelpers.cpp; sourceTree = "<group>"; };
+               DE26E9021CB5DD0500D2BE82 /* BuiltinExecutableCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuiltinExecutableCreator.h; sourceTree = "<group>"; };
+               DE26E9061CB5DD9600D2BE82 /* BuiltinExecutableCreator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BuiltinExecutableCreator.cpp; sourceTree = "<group>"; };
                DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntrinsicEmitter.cpp; sourceTree = "<group>"; };
                E124A8F50E555775003091F1 /* OpaqueJSString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueJSString.h; sourceTree = "<group>"; };
                E124A8F60E555775003091F1 /* OpaqueJSString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpaqueJSString.cpp; sourceTree = "<group>"; };
                                A7D801A01880D66E0026C39B /* ArrayPrototype.js */,
                                7CF9BC581B65D9A3009DB1EF /* ArrayConstructor.js */,
                                7CF9BC591B65D9A3009DB1EF /* ArrayIteratorPrototype.js */,
+                               DE26E9061CB5DD9600D2BE82 /* BuiltinExecutableCreator.cpp */,
+                               DE26E9021CB5DD0500D2BE82 /* BuiltinExecutableCreator.h */,
                                A7D801A11880D66E0026C39B /* BuiltinExecutables.cpp */,
                                A7D801A21880D66E0026C39B /* BuiltinExecutables.h */,
                                A75EE9B018AAB7E200AAD043 /* BuiltinNames.h */,
                                14142E531B796EDD00F4BF4B /* ExecutableInfo.h in Headers */,
                                0F56A1D315000F35002992B1 /* ExecutionCounter.h in Headers */,
                                0F3AC754188E5EC80032029F /* ExitingJITType.h in Headers */,
+                               DE26E9031CB5DD0500D2BE82 /* BuiltinExecutableCreator.h in Headers */,
                                0FB105861675481200F8AB6E /* ExitKind.h in Headers */,
                                0F0B83AB14BCF5BB00885B4F /* ExpressionRangeInfo.h in Headers */,
                                0F4DE1CF1C4C1B54004D6C11 /* AirFixObviousSpills.h in Headers */,
                                0FEA0A31170D40BF00BB722C /* DFGCommonData.cpp in Sources */,
                                0F38B01717CFE75500B144D3 /* DFGCompilationKey.cpp in Sources */,
                                0F38B01917CFE75500B144D3 /* DFGCompilationMode.cpp in Sources */,
+                               DE26E9071CB5DEFB00D2BE82 /* BuiltinExecutableCreator.cpp in Sources */,
                                0F3B3A1A153E68F2003ED0FF /* DFGConstantFoldingPhase.cpp in Sources */,
                                0FED67B91B26256D0066CE15 /* DFGConstantHoistingPhase.cpp in Sources */,
                                0FBE0F7216C1DB030082C5E8 /* DFGCPSRethreadingPhase.cpp in Sources */,
diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp
new file mode 100644 (file)
index 0000000..2b79e7e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "BuiltinExecutableCreator.h"
+
+#include "BuiltinExecutables.h"
+
+namespace JSC {
+
+UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& source, const Identifier& ident, ConstructorKind kind, ConstructAbility ability)
+{
+    return BuiltinExecutables::createExecutable(vm, source, ident, kind, ability);
+}
+    
+} // namespace JSC
diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.h b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.h
new file mode 100644 (file)
index 0000000..720a480
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef BuiltinExecutableCreator_h
+#define BuiltinExecutableCreator_h
+
+#include "ConstructAbility.h"
+#include "ParserModes.h"
+#include "SourceCode.h"
+
+namespace JSC {
+
+JS_EXPORT_PRIVATE UnlinkedFunctionExecutable* createBuiltinExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility);
+
+} // namespace JSC
+
+#endif /* BuiltinExecutableCreator_h */
index 8ee54ee..4c43b04 100644 (file)
@@ -35,8 +35,6 @@
 
 namespace JSC {
 
-static UnlinkedFunctionExecutable* createExecutableInternal(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility);
-
 BuiltinExecutables::BuiltinExecutables(VM& vm)
     : m_vm(vm)
 #define INITIALIZE_BUILTIN_SOURCE_MEMBERS(name, functionName, length) , m_##name##Source(makeSource(StringImpl::createFromLiteral(s_##name, length)))
@@ -54,9 +52,9 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(Constru
     case ConstructorKind::None:
         break;
     case ConstructorKind::Base:
-        return createExecutableInternal(m_vm, makeSource(baseConstructorCode), name, constructorKind, ConstructAbility::CanConstruct);
+        return createExecutable(m_vm, makeSource(baseConstructorCode), name, constructorKind, ConstructAbility::CanConstruct);
     case ConstructorKind::Derived:
-        return createExecutableInternal(m_vm, makeSource(derivedConstructorCode), name, constructorKind, ConstructAbility::CanConstruct);
+        return createExecutable(m_vm, makeSource(derivedConstructorCode), name, constructorKind, ConstructAbility::CanConstruct);
     }
     ASSERT_NOT_REACHED();
     return nullptr;
@@ -64,15 +62,15 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(Constru
 
 UnlinkedFunctionExecutable* BuiltinExecutables::createBuiltinExecutable(const SourceCode& code, const Identifier& name, ConstructAbility constructAbility)
 {
-    return createExecutableInternal(m_vm, code, name, ConstructorKind::None, constructAbility);
+    return createExecutable(m_vm, code, name, ConstructorKind::None, constructAbility);
 }
 
 UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& code, const Identifier& name, ConstructAbility constructAbility)
 {
-    return createExecutableInternal(vm, code, name, ConstructorKind::None, constructAbility);
+    return BuiltinExecutables::createExecutable(vm, code, name, ConstructorKind::None, constructAbility);
 }
 
-UnlinkedFunctionExecutable* createExecutableInternal(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility)
+UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility)
 {
     JSTextPosition positionBeforeLastNewline;
     ParserError error;
index ec8d577..f1431b7 100644 (file)
@@ -52,6 +52,7 @@ const SourceCode& name##Source() { return m_##name##Source; }
 
     UnlinkedFunctionExecutable* createDefaultConstructor(ConstructorKind, const Identifier& name);
 
+    static UnlinkedFunctionExecutable* createExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility);
 private:
     void finalize(Handle<Unknown>, void* context) override;
 
index bf42a31..40352a1 100644 (file)
@@ -41,6 +41,7 @@ class Identifier;
 #define JSC_COMMON_BYTECODE_INTRINSIC_FUNCTIONS_EACH_NAME(macro) \
     macro(assert) \
     macro(isObject) \
+    macro(tryGetById) \
     macro(putByValDirect) \
     macro(toString)
 
index 8293f76..e8eb9b6 100644 (file)
@@ -57,6 +57,7 @@
             { "name" : "op_is_object_or_null", "length" : 3 },
             { "name" : "op_is_function", "length" : 3 },
             { "name" : "op_in", "length" : 4 },
+            { "name" : "op_try_get_by_id", "length" : 4 },
             { "name" : "op_get_by_id", "length" : 9  },
             { "name" : "op_get_array_length", "length" : 9 },
             { "name" : "op_put_by_id", "length" : 9 },
index 7236b58..33c04a8 100644 (file)
@@ -143,6 +143,7 @@ void computeUsesForBytecodeOffset(
     case op_resolve_scope:
     case op_get_from_scope:
     case op_to_primitive:
+    case op_try_get_by_id:
     case op_get_by_id:
     case op_get_array_length:
     case op_typeof:
@@ -367,6 +368,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
     case op_tail_call:
     case op_call_eval:
     case op_construct:
+    case op_try_get_by_id:
     case op_get_by_id:
     case op_get_array_length:
     case op_overrides_has_instance:
index ac402e8..73b87bd 100644 (file)
@@ -1095,6 +1095,14 @@ void CodeBlock::dumpBytecode(
             printBinaryOp(out, exec, location, it, "in");
             break;
         }
+        case op_try_get_by_id: {
+            int r0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            int id0 = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "try_get_by_id");
+            out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
+            break;
+        }
         case op_get_by_id:
         case op_get_array_length: {
             printGetByIdOp(out, exec, location, it);
index 6076baf..344246e 100644 (file)
@@ -159,6 +159,26 @@ AccessCase::AccessCase()
 {
 }
 
+std::unique_ptr<AccessCase> AccessCase::tryGet(
+    VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure,
+    const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet)
+{
+    std::unique_ptr<AccessCase> result(new AccessCase());
+
+    result->m_type = type;
+    result->m_offset = offset;
+    result->m_structure.set(vm, owner, structure);
+    result->m_conditionSet = conditionSet;
+
+    if (viaProxy || additionalSet) {
+        result->m_rareData = std::make_unique<RareData>();
+        result->m_rareData->viaProxy = viaProxy;
+        result->m_rareData->additionalSet = additionalSet;
+    }
+
+    return result;
+}
+
 std::unique_ptr<AccessCase> AccessCase::get(
     VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure,
     const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet,
@@ -685,6 +705,7 @@ void AccessCase::generate(AccessGenerationState& state)
         return;
 
     case Load:
+    case GetGetter:
     case Getter:
     case Setter:
     case CustomValueGetter:
@@ -720,7 +741,7 @@ void AccessCase::generate(AccessGenerationState& state)
 
         GPRReg loadedValueGPR = InvalidGPRReg;
         if (m_type != CustomValueGetter && m_type != CustomAccessorGetter && m_type != CustomValueSetter && m_type != CustomAccessorSetter) {
-            if (m_type == Load)
+            if (m_type == Load || m_type == GetGetter)
                 loadedValueGPR = valueRegs.payloadGPR();
             else
                 loadedValueGPR = scratchGPR;
@@ -739,7 +760,7 @@ void AccessCase::generate(AccessGenerationState& state)
             jit.load64(
                 CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset)), loadedValueGPR);
 #else
-            if (m_type == Load) {
+            if (m_type == Load || m_type == GetGetter) {
                 jit.load32(
                     CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset) + TagOffset),
                     valueRegs.tagGPR());
@@ -750,7 +771,7 @@ void AccessCase::generate(AccessGenerationState& state)
 #endif
         }
 
-        if (m_type == Load) {
+        if (m_type == Load || m_type == GetGetter) {
             state.succeed();
             return;
         }
@@ -1567,6 +1588,9 @@ void printInternal(PrintStream& out, AccessCase::AccessType type)
     case AccessCase::Miss:
         out.print("Miss");
         return;
+    case AccessCase::GetGetter:
+        out.print("GetGetter");
+        return;
     case AccessCase::Getter:
         out.print("Getter");
         return;
index 87f4dc0..526ddfd 100644 (file)
@@ -57,6 +57,7 @@ public:
         Transition,
         Replace,
         Miss,
+        GetGetter,
         Getter,
         Setter,
         CustomValueGetter,
@@ -70,77 +71,11 @@ public:
         StringLength
     };
 
-    static bool isGet(AccessType type)
-    {
-        switch (type) {
-        case Transition:
-        case Replace:
-        case Setter:
-        case CustomValueSetter:
-        case CustomAccessorSetter:
-        case InHit:
-        case InMiss:
-            return false;
-        case Load:
-        case MegamorphicLoad:
-        case Miss:
-        case Getter:
-        case CustomValueGetter:
-        case CustomAccessorGetter:
-        case IntrinsicGetter:
-        case ArrayLength:
-        case StringLength:
-            return true;
-        }
-    }
-
-    static bool isPut(AccessType type)
-    {
-        switch (type) {
-        case Load:
-        case MegamorphicLoad:
-        case Miss:
-        case Getter:
-        case CustomValueGetter:
-        case CustomAccessorGetter:
-        case IntrinsicGetter:
-        case InHit:
-        case InMiss:
-        case ArrayLength:
-        case StringLength:
-            return false;
-        case Transition:
-        case Replace:
-        case Setter:
-        case CustomValueSetter:
-        case CustomAccessorSetter:
-            return true;
-        }
-    }
-
-    static bool isIn(AccessType type)
-    {
-        switch (type) {
-        case Load:
-        case MegamorphicLoad:
-        case Miss:
-        case Getter:
-        case CustomValueGetter:
-        case CustomAccessorGetter:
-        case IntrinsicGetter:
-        case Transition:
-        case Replace:
-        case Setter:
-        case CustomValueSetter:
-        case CustomAccessorSetter:
-        case ArrayLength:
-        case StringLength:
-            return false;
-        case InHit:
-        case InMiss:
-            return true;
-        }
-    }
+    static std::unique_ptr<AccessCase> tryGet(
+        VM&, JSCell* owner, AccessType, PropertyOffset, Structure*,
+        const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
+        bool viaProxy = false,
+        WatchpointSet* additionalSet = nullptr);
 
     static std::unique_ptr<AccessCase> get(
         VM&, JSCell* owner, AccessType, PropertyOffset, Structure*,
index ace61f4..ff9fed2 100644 (file)
@@ -148,8 +148,11 @@ void StructureStubInfo::reset(CodeBlock* codeBlock)
     }
 
     switch (accessType) {
+    case AccessType::GetPure:
+        resetGetByID(codeBlock, *this, GetByIDKind::Pure);
+        break;
     case AccessType::Get:
-        resetGetByID(codeBlock, *this);
+        resetGetByID(codeBlock, *this, GetByIDKind::Normal);
         break;
     case AccessType::Put:
         resetPutByID(codeBlock, *this);
index ca8fdf4..f39d614 100644 (file)
@@ -47,6 +47,7 @@ class PolymorphicAccess;
 
 enum class AccessType : int8_t {
     Get,
+    GetPure,
     Put,
     In
 };
index 5a6d99e..b46be58 100644 (file)
@@ -2360,6 +2360,17 @@ RegisterID* BytecodeGenerator::emitInstanceOfCustom(RegisterID* dst, RegisterID*
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitTryGetById(RegisterID* dst, RegisterID* base, const Identifier& property)
+{
+    ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties are not supported with tryGetById.");
+
+    emitOpcode(op_try_get_by_id);
+    instructions().append(kill(dst));
+    instructions().append(base->index());
+    instructions().append(addConstant(property));
+    return dst;
+}
+
 RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property)
 {
     ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with get_by_val.");
index 3925444..ef88e30 100644 (file)
@@ -540,6 +540,7 @@ namespace JSC {
         RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); }
         RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); }
 
+        RegisterID* emitTryGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
         RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
         RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value);
         RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType);
index 713c50b..89c8a57 100644 (file)
@@ -842,6 +842,21 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_putByValDirect(BytecodeGenerat
     return generator.moveToDestinationIfNeeded(dst, generator.emitDirectPutByVal(base.get(), index.get(), value.get()));
 }
 
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_tryGetById(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ArgumentListNode* node = m_args->m_listNode;
+    RefPtr<RegisterID> base = generator.emitNode(node);
+    node = node->m_next;
+
+    // Since this is a builtin we expect the creator to use a string literal as the second argument.
+    ASSERT(node->m_expr->isString());
+    const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
+    ASSERT(!node->m_next);
+
+    RegisterID* finalDest = generator.finalDestination(dst);
+    return generator.emitTryGetById(finalDest, base.get(), ident);
+}
+
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toString(BytecodeGenerator& generator, RegisterID* dst)
 {
     ArgumentListNode* node = m_args->m_listNode;
index 295c6ad..f9debb7 100644 (file)
@@ -194,7 +194,7 @@ void SpeculativeJIT::cachedGetById(
     JITGetByIdGenerator gen(
         m_jit.codeBlock(), codeOrigin, callSite, usedRegisters,
         JSValueRegs(baseTagGPROrNone, basePayloadGPR),
-        JSValueRegs(resultTagGPR, resultPayloadGPR));
+        JSValueRegs(resultTagGPR, resultPayloadGPR), AccessType::Get);
     
     gen.generateFastPath(m_jit);
     
index a62768d..0df4a30 100644 (file)
@@ -164,7 +164,7 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg
     }
     JITGetByIdGenerator gen(
         m_jit.codeBlock(), codeOrigin, callSite, usedRegisters, JSValueRegs(baseGPR),
-        JSValueRegs(resultGPR));
+        JSValueRegs(resultGPR), AccessType::Get);
     gen.generateFastPath(m_jit);
     
     JITCompiler::JumpList slowCases;
index 584020b..60daa7b 100644 (file)
@@ -7331,7 +7331,7 @@ private:
                 auto generator = Box<JITGetByIdGenerator>::create(
                     jit.codeBlock(), node->origin.semantic, callSiteIndex,
                     params.unavailableRegisters(), JSValueRegs(params[1].gpr()),
-                    JSValueRegs(params[0].gpr()));
+                    JSValueRegs(params[0].gpr()), AccessType::Get);
 
                 generator->generateFastPath(jit);
                 CCallHelpers::Label done = jit.label();
index 4c2f478..2d74cf6 100644 (file)
@@ -228,6 +228,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_get_scope)
         DEFINE_OP(op_eq)
         DEFINE_OP(op_eq_null)
+        DEFINE_OP(op_try_get_by_id)
         case op_get_array_length:
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_by_val)
@@ -406,6 +407,7 @@ void JIT::privateCompileSlowCases()
         DEFINE_SLOWCASE_OP(op_create_this)
         DEFINE_SLOWCASE_OP(op_div)
         DEFINE_SLOWCASE_OP(op_eq)
+        DEFINE_SLOWCASE_OP(op_try_get_by_id)
         case op_get_array_length:
         DEFINE_SLOWCASE_OP(op_get_by_id)
         DEFINE_SLOWCASE_OP(op_get_by_val)
index 717dba4..d355a4c 100644 (file)
@@ -502,6 +502,7 @@ namespace JSC {
         void emit_op_get_scope(Instruction*);
         void emit_op_eq(Instruction*);
         void emit_op_eq_null(Instruction*);
+        void emit_op_try_get_by_id(Instruction*);
         void emit_op_get_by_id(Instruction*);
         void emit_op_get_arguments_length(Instruction*);
         void emit_op_get_by_val(Instruction*);
@@ -614,6 +615,7 @@ namespace JSC {
         void emitSlow_op_div(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_eq(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_callee(Instruction*, Vector<SlowCaseEntry>::iterator&);
+        void emitSlow_op_try_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_arguments_length(Instruction*, Vector<SlowCaseEntry>::iterator&);
         void emitSlow_op_get_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&);
@@ -738,8 +740,10 @@ namespace JSC {
         MacroAssembler::Call callOperation(V_JITOperation_EC, JSCell*);
         MacroAssembler::Call callOperation(J_JITOperation_EJ, int, GPRReg);
 #if USE(JSVALUE64)
+        MacroAssembler::Call callOperation(J_JITOperation_ESsiJI, int, StructureStubInfo*, GPRReg, UniquedStringImpl*);
         MacroAssembler::Call callOperation(WithProfileTag, J_JITOperation_ESsiJI, int, StructureStubInfo*, GPRReg, UniquedStringImpl*);
 #else
+        MacroAssembler::Call callOperation(J_JITOperation_ESsiJI, int, StructureStubInfo*, GPRReg, GPRReg, UniquedStringImpl*);
         MacroAssembler::Call callOperation(WithProfileTag, J_JITOperation_ESsiJI, int, StructureStubInfo*, GPRReg, GPRReg, UniquedStringImpl*);
 #endif
         MacroAssembler::Call callOperation(J_JITOperation_EJIdc, int, GPRReg, const Identifier*);
index 3ca56f6..24fe9d3 100644 (file)
@@ -104,9 +104,9 @@ void JITByIdGenerator::generateFastPathChecks(MacroAssembler& jit)
 
 JITGetByIdGenerator::JITGetByIdGenerator(
     CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, const RegisterSet& usedRegisters,
-    JSValueRegs base, JSValueRegs value)
+    JSValueRegs base, JSValueRegs value, AccessType accessType)
     : JITByIdGenerator(
-        codeBlock, codeOrigin, callSite, AccessType::Get, usedRegisters, base, value)
+        codeBlock, codeOrigin, callSite, accessType, usedRegisters, base, value)
 {
     RELEASE_ASSERT(base.payloadGPR() != value.tagGPR());
 }
index 0ada0f8..25cda15 100644 (file)
@@ -96,7 +96,7 @@ public:
 
     JITGetByIdGenerator(
         CodeBlock*, CodeOrigin, CallSiteIndex, const RegisterSet& usedRegisters, JSValueRegs base,
-        JSValueRegs value);
+        JSValueRegs value, AccessType);
     
     void generateFastPath(MacroAssembler&);
 };
index 8d815a9..e1eeec3 100644 (file)
@@ -459,6 +459,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EZJ operati
     return appendCallWithExceptionCheck(operation);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_ESsiJI operation, int dst, StructureStubInfo* stubInfo, GPRReg arg1, UniquedStringImpl* uid)
+{
+    setupArgumentsWithExecState(TrustedImmPtr(stubInfo), arg1, TrustedImmPtr(uid));
+    return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(JIT::WithProfileTag, J_JITOperation_ESsiJI operation, int dst, StructureStubInfo* stubInfo, GPRReg arg1, UniquedStringImpl* uid)
 {
     setupArgumentsWithExecState(TrustedImmPtr(stubInfo), arg1, TrustedImmPtr(uid));
@@ -642,6 +648,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EJ operatio
     return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_ESsiJI operation, int dst, StructureStubInfo* stubInfo, GPRReg arg1Tag, GPRReg arg1Payload, UniquedStringImpl* uid)
+{
+    setupArgumentsWithExecState(TrustedImmPtr(stubInfo), arg1Payload, arg1Tag, TrustedImmPtr(uid));
+    return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(JIT::WithProfileTag, J_JITOperation_ESsiJI operation, int dst, StructureStubInfo* stubInfo, GPRReg arg1Tag, GPRReg arg1Payload, UniquedStringImpl* uid)
 {
     setupArgumentsWithExecState(TrustedImmPtr(stubInfo), arg1Payload, arg1Tag, TrustedImmPtr(uid));
index 21b9ad4..ec73c1c 100644 (file)
@@ -156,6 +156,36 @@ int32_t JIT_OPERATION operationConstructArityCheck(ExecState* exec)
     return missingArgCount;
 }
 
+EncodedJSValue JIT_OPERATION operationTryGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    Identifier ident = Identifier::fromUid(vm, uid);
+    stubInfo->tookSlowPath = true;
+
+    JSValue baseValue = JSValue::decode(base);
+    PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
+    baseValue.getPropertySlot(exec, ident, slot);
+
+    return JSValue::encode(slot.getPureResult());
+}
+
+EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    Identifier ident = Identifier::fromUid(vm, uid);
+
+    JSValue baseValue = JSValue::decode(base);
+    PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
+
+    baseValue.getPropertySlot(exec, ident, slot);
+    if (stubInfo->considerCaching() && !slot.isTaintedByProxy() && (slot.isCacheableValue() || slot.isCacheableGetter() || slot.isUnset()))
+        repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Pure);
+
+    return JSValue::encode(slot.getPureResult());
+}
+
 EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
 {
     VM* vm = &exec->vm();
@@ -195,7 +225,7 @@ EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, Structure
     
     bool hasResult = baseValue.getPropertySlot(exec, ident, slot);
     if (stubInfo->considerCaching())
-        repatchGetByID(exec, baseValue, ident, slot, *stubInfo);
+        repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Normal);
     
     return JSValue::encode(hasResult? slot.getValue(exec, ident) : jsUndefined());
 }
index 941a993..8e82b3b 100644 (file)
@@ -287,9 +287,10 @@ void JIT_OPERATION operationThrowOutOfBoundsAccessError(ExecState*) WTF_INTERNAL
 #endif
 int32_t JIT_OPERATION operationCallArityCheck(ExecState*) WTF_INTERNAL;
 int32_t JIT_OPERATION operationConstructArityCheck(ExecState*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationTryGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationGetByIdBuildList(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationInOptimize(ExecState*, StructureStubInfo*, JSCell*, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationIn(ExecState*, StructureStubInfo*, JSCell*, UniquedStringImpl*) WTF_INTERNAL;
index ddf8e1e..95b17a6 100644 (file)
@@ -213,7 +213,7 @@ JITGetByIdGenerator JIT::emitGetByValWithCachedId(Instruction* currentInstructio
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), RegisterSet::stubUnavailableRegisters(),
-        JSValueRegs(regT0), JSValueRegs(regT0));
+        JSValueRegs(regT0), JSValueRegs(regT0), AccessType::Get);
     gen.generateFastPath(*this);
 
     fastDoneCase = jump();
@@ -531,6 +531,43 @@ void JIT::emit_op_del_by_id(Instruction* currentInstruction)
     callOperation(operationDeleteById, dst, regT0, &m_codeBlock->identifier(property));
 }
 
+void JIT::emit_op_try_get_by_id(Instruction* currentInstruction)
+{
+    int resultVReg = currentInstruction[1].u.operand;
+    int baseVReg = currentInstruction[2].u.operand;
+
+    emitGetVirtualRegister(baseVReg, regT0);
+
+    emitJumpSlowCaseIfNotJSCell(regT0, baseVReg);
+
+    JITGetByIdGenerator gen(
+        m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), RegisterSet::stubUnavailableRegisters(),
+        JSValueRegs(regT0), JSValueRegs(regT0), AccessType::GetPure);
+    gen.generateFastPath(*this);
+    addSlowCase(gen.slowPathJump());
+    m_getByIds.append(gen);
+    
+    emitPutVirtualRegister(resultVReg);
+}
+
+void JIT::emitSlow_op_try_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    int resultVReg = currentInstruction[1].u.operand;
+    int baseVReg = currentInstruction[2].u.operand;
+    const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+    linkSlowCaseIfNotJSCell(iter, baseVReg);
+    linkSlowCase(iter);
+
+    JITGetByIdGenerator& gen = m_getByIds[m_getByIdIndex++];
+
+    Label coldPathBegin = label();
+
+    Call call = callOperation(operationTryGetByIdOptimize, resultVReg, gen.stubInfo(), regT0, ident->impl());
+    
+    gen.reportSlowPathCall(coldPathBegin, call);
+}
+
 void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 {
     int resultVReg = currentInstruction[1].u.operand;
@@ -546,7 +583,7 @@ void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), RegisterSet::stubUnavailableRegisters(),
-        JSValueRegs(regT0), JSValueRegs(regT0));
+        JSValueRegs(regT0), JSValueRegs(regT0), AccessType::Get);
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
     m_getByIds.append(gen);
index 27f9404..51f96b9 100644 (file)
@@ -282,7 +282,7 @@ JITGetByIdGenerator JIT::emitGetByValWithCachedId(Instruction* currentInstructio
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
-        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0));
+        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::Get);
     gen.generateFastPath(*this);
 
     fastDoneCase = jump();
@@ -573,6 +573,43 @@ void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCas
     m_byValInstructionIndex++;
 }
 
+void JIT::emit_op_try_get_by_id(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int base = currentInstruction[2].u.operand;
+
+    emitLoad(base, regT1, regT0);
+    emitJumpSlowCaseIfNotJSCell(base, regT1);
+
+    JITGetByIdGenerator gen(
+        m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::GetPure);
+    gen.generateFastPath(*this);
+    addSlowCase(gen.slowPathJump());
+    m_getByIds.append(gen);
+    
+    emitStore(dst, regT1, regT0);
+}
+
+void JIT::emitSlow_op_try_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+    int resultVReg = currentInstruction[1].u.operand;
+    int baseVReg = currentInstruction[2].u.operand;
+    const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+    linkSlowCaseIfNotJSCell(iter, baseVReg);
+    linkSlowCase(iter);
+
+    JITGetByIdGenerator& gen = m_getByIds[m_getByIdIndex++];
+
+    Label coldPathBegin = label();
+
+    Call call = callOperation(operationTryGetByIdOptimize, resultVReg, gen.stubInfo(), regT1, regT0, ident->impl());
+    
+    gen.reportSlowPathCall(coldPathBegin, call);
+}
+
+
 void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
@@ -587,7 +624,7 @@ void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
-        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0));
+        JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::Get);
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
     m_getByIds.append(gen);
index e4ca30d..81fe3b0 100644 (file)
@@ -93,7 +93,7 @@ static void repatchCall(CodeBlock* codeBlock, CodeLocationCall call, FunctionPtr
 
 static void repatchByIdSelfAccess(
     CodeBlock* codeBlock, StructureStubInfo& stubInfo, Structure* structure,
-    PropertyOffset offset, const FunctionPtr &slowPathFunction,
+    PropertyOffset offset, const FunctionPtrslowPathFunction,
     bool compact)
 {
     // Only optimize once!
@@ -213,7 +213,21 @@ static bool forceICFailure(ExecState*)
     return Options::forceICFailure();
 }
 
-static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
+inline J_JITOperation_ESsiJI appropriateOptimizingGetByIdFunction(GetByIDKind kind)
+{
+    if (kind == GetByIDKind::Normal)
+        return operationGetByIdOptimize;
+    return operationTryGetByIdOptimize;
+}
+
+inline J_JITOperation_ESsiJI appropriateGenericGetByIdFunction(GetByIDKind kind)
+{
+    if (kind == GetByIDKind::Normal)
+        return operationGetById;
+    return operationTryGetById;
+}
+
+static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
 {
     if (forceICFailure(exec))
         return GiveUpOnCache;
@@ -262,7 +276,7 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
             && !structure->needImpurePropertyWatchpoint()
             && !loadTargetFromProxy) {
             structure->startWatchingPropertyForReplacements(vm, slot.cachedOffset());
-            repatchByIdSelfAccess(codeBlock, stubInfo, structure, slot.cachedOffset(), operationGetByIdOptimize, true);
+            repatchByIdSelfAccess(codeBlock, stubInfo, structure, slot.cachedOffset(), appropriateOptimizingGetByIdFunction(kind), true);
             stubInfo.initGetByIdSelf(codeBlock, structure, slot.cachedOffset());
             return RetryCacheLater;
         }
@@ -295,7 +309,19 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
         if (slot.isCacheableGetter())
             getter = jsDynamicCast<JSFunction*>(slot.getterSetter()->getter());
 
-        if (!loadTargetFromProxy && getter && AccessCase::canEmitIntrinsicGetter(getter, structure))
+        if (kind == GetByIDKind::Pure) {
+            AccessCase::AccessType type;
+            if (slot.isCacheableValue())
+                type = AccessCase::Load;
+            else if (slot.isUnset())
+                type = AccessCase::Miss;
+            else if (slot.isCacheableGetter())
+                type = AccessCase::GetGetter;
+            else
+                RELEASE_ASSERT_NOT_REACHED();
+
+            newCase = AccessCase::tryGet(vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet());
+        } else if (!loadTargetFromProxy && getter && AccessCase::canEmitIntrinsicGetter(getter, structure))
             newCase = AccessCase::getIntrinsic(vm, codeBlock, getter, slot.cachedOffset(), structure, conditionSet);
         else {
             AccessCase::AccessType type;
@@ -330,12 +356,12 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
     return RetryCacheLater;
 }
 
-void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
+void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
 {
     GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
     
-    if (tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo) == GiveUpOnCache)
-        repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
+    if (tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo, kind) == GiveUpOnCache)
+        repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericGetByIdFunction(kind));
 }
 
 static V_JITOperation_ESsiJJI appropriateGenericPutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
@@ -910,9 +936,9 @@ void linkPolymorphicCall(
         callLinkInfo.remove();
 }
 
-void resetGetByID(CodeBlock* codeBlock, StructureStubInfo& stubInfo)
+void resetGetByID(CodeBlock* codeBlock, StructureStubInfo& stubInfo, GetByIDKind kind)
 {
-    repatchCall(codeBlock, stubInfo.callReturnLocation, operationGetByIdOptimize);
+    repatchCall(codeBlock, stubInfo.callReturnLocation, appropriateOptimizingGetByIdFunction(kind));
     resetGetByIDCheckAndLoad(stubInfo);
     MacroAssembler::repatchJump(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
 }
index 443e944..204762d 100644 (file)
 
 namespace JSC {
 
-void repatchGetByID(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&);
+enum class GetByIDKind {
+    Normal,
+    Pure
+};
+
+void repatchGetByID(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&, GetByIDKind);
 void buildGetByIDList(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&);
 void buildGetByIDProtoList(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&);
 void repatchPutByID(ExecState*, JSValue, Structure*, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind);
@@ -46,7 +51,7 @@ void linkSlowFor(ExecState*, CallLinkInfo&);
 void unlinkFor(VM&, CallLinkInfo&);
 void linkVirtualFor(ExecState*, CallLinkInfo&);
 void linkPolymorphicCall(ExecState*, CallLinkInfo&, CallVariant);
-void resetGetByID(CodeBlock*, StructureStubInfo&);
+void resetGetByID(CodeBlock*, StructureStubInfo&, GetByIDKind);
 void resetPutByID(CodeBlock*, StructureStubInfo&);
 void resetIn(CodeBlock*, StructureStubInfo&);
 
index 0f46e2d..e0a79f0 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 
 #include "ArrayPrototype.h"
+#include "BuiltinExecutableCreator.h"
 #include "ButterflyInlines.h"
 #include "BytecodeGenerator.h"
 #include "CodeBlock.h"
@@ -32,6 +33,7 @@
 #include "Disassembler.h"
 #include "Exception.h"
 #include "ExceptionHelpers.h"
+#include "GetterSetter.h"
 #include "HeapProfiler.h"
 #include "HeapSnapshotBuilder.h"
 #include "HeapStatistics.h"
@@ -552,6 +554,7 @@ static EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateRuntimeArray(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateCustomGetterObject(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionCreateBuiltin(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState*);
 
 static EncodedJSValue JSC_HOST_CALL functionSetElementRoot(ExecState*);
@@ -572,6 +575,7 @@ static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionForceGCSlowPaths(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionHeapSize(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionAddressOf(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionGetGetterSetter(ExecState*);
 #ifndef NDEBUG
 static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
 #endif
@@ -743,6 +747,7 @@ protected:
         addFunction(vm, "forceGCSlowPaths", functionForceGCSlowPaths, 0);
         addFunction(vm, "gcHeapSize", functionHeapSize, 0);
         addFunction(vm, "addressOf", functionAddressOf, 1);
+        addFunction(vm, "getGetterSetter", functionGetGetterSetter, 2);
 #ifndef NDEBUG
         addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
 #endif
@@ -792,6 +797,7 @@ protected:
 
         addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
         addFunction(vm, "createCustomGetterObject", functionCreateCustomGetterObject, 0);
+        addFunction(vm, "createBuiltin", functionCreateBuiltin, 2);
         addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);
 
         addFunction(vm, "dumpTypesForAllVariables", functionDumpTypesForAllVariables , 0);
@@ -1332,6 +1338,30 @@ EncodedJSValue JSC_HOST_CALL functionAddressOf(ExecState* exec)
     return returnValue;
 }
 
+static EncodedJSValue JSC_HOST_CALL functionGetGetterSetter(ExecState* exec)
+{
+    JSValue value = exec->argument(0);
+    if (!value.isObject())
+        return JSValue::encode(jsUndefined());
+
+    JSValue property = exec->argument(1);
+    if (!property.isString())
+        return JSValue::encode(jsUndefined());
+
+    Identifier ident = Identifier::fromString(&exec->vm(), property.toString(exec)->value(exec));
+
+    PropertySlot slot(value, PropertySlot::InternalMethodType::VMInquiry);
+    value.getPropertySlot(exec, ident, slot);
+
+    JSValue result;
+    if (slot.isCacheableGetter())
+        result = slot.getterSetter();
+    else
+        result = jsNull();
+
+    return JSValue::encode(result);
+}
+
 EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
 {
     // We need this function for compatibility with the Mozilla JS tests but for now
@@ -1721,6 +1751,22 @@ EncodedJSValue JSC_HOST_CALL functionLoadModule(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL functionCreateBuiltin(ExecState* exec)
+{
+    if (exec->argumentCount() < 1 || !exec->argument(0).isString())
+        return JSValue::encode(jsUndefined());
+
+    String functionText = exec->argument(0).toString(exec)->value(exec);
+    if (exec->hadException())
+        return JSValue::encode(JSValue());
+
+    VM& vm = exec->vm();
+    const SourceCode& source = makeSource(functionText);
+    JSFunction* func = JSFunction::createBuiltinFunction(vm, createBuiltinExecutable(vm, source, Identifier::fromString(&vm, "foo"), ConstructorKind::None, ConstructAbility::CannotConstruct)->link(vm, source), exec->lexicalGlobalObject());
+
+    return JSValue::encode(func);
+}
+
 EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState* exec)
 {
     String source = exec->argument(0).toString(exec)->value(exec);
index 21488a9..f140ca3 100644 (file)
@@ -144,9 +144,9 @@ void Data::performAssertions(VM& vm)
     
     STATIC_ASSERT(StringType == 6);
     STATIC_ASSERT(SymbolType == 7);
-    STATIC_ASSERT(ObjectType == 21);
-    STATIC_ASSERT(FinalObjectType == 22);
-    STATIC_ASSERT(JSFunctionType == 24);
+    STATIC_ASSERT(ObjectType == 20);
+    STATIC_ASSERT(FinalObjectType == 21);
+    STATIC_ASSERT(JSFunctionType == 23);
     STATIC_ASSERT(MasqueradesAsUndefined == 1);
     STATIC_ASSERT(ImplementsDefaultHasInstance == 2);
     STATIC_ASSERT(FirstConstantRegisterIndex == 0x40000000);
index 7e35175..42407bc 100644 (file)
@@ -545,6 +545,19 @@ LLINT_SLOW_PATH_DECL(slow_path_instanceof_custom)
     LLINT_RETURN(result);
 }
 
+LLINT_SLOW_PATH_DECL(slow_path_try_get_by_id)
+{
+    LLINT_BEGIN();
+    CodeBlock* codeBlock = exec->codeBlock();
+    const Identifier& ident = codeBlock->identifier(pc[3].u.operand);
+    JSValue baseValue = LLINT_OP_C(2).jsValue();
+    PropertySlot slot(baseValue, PropertySlot::PropertySlot::InternalMethodType::VMInquiry);
+
+    baseValue.getPropertySlot(exec, ident, slot);
+
+    LLINT_RETURN(slot.getPureResult());
+}
+
 LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
 {
     LLINT_BEGIN();
index ec56bae..e3d41e4 100644 (file)
@@ -69,6 +69,7 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array_buffer);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_regexp);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof_custom);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_try_get_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_arguments_length);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_id);
index e565d67..70907a4 100644 (file)
@@ -326,9 +326,9 @@ const SlowPutArrayStorageShape = 30
 # Type constants.
 const StringType = 6
 const SymbolType = 7
-const ObjectType = 21
-const FinalObjectType = 22
-const JSFunctionType = 24
+const ObjectType = 20
+const FinalObjectType = 21
+const JSFunctionType = 23
 
 # Type flags constants.
 const MasqueradesAsUndefined = 1
@@ -1301,6 +1301,12 @@ _llint_op_in:
     dispatch(4)
 
 
+_llint_op_try_get_by_id:
+    traceExecution()
+    callSlowPath(_llint_slow_path_try_get_by_id)
+    dispatch(4)
+
+
 _llint_op_del_by_id:
     traceExecution()
     callSlowPath(_llint_slow_path_del_by_id)
index 8730794..a8fda49 100644 (file)
@@ -33,7 +33,7 @@ namespace JSC {
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(GetterSetter);
 
-const ClassInfo GetterSetter::s_info = { "GetterSetter", 0, 0, CREATE_METHOD_TABLE(GetterSetter) };
+const ClassInfo GetterSetter::s_info = { "GetterSetter", &Base::s_info, 0, CREATE_METHOD_TABLE(GetterSetter) };
 
 void GetterSetter::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
index 2b22b3e..1b47e68 100644 (file)
@@ -41,20 +41,20 @@ class JSObject;
 // that if a property holding a GetterSetter reference is constant-inferred and
 // that constant is observed to have a non-null setter (or getter) then we can
 // constant fold that setter (or getter).
-class GetterSetter final : public JSCell {
+class GetterSetter final : public JSNonFinalObject {
     friend class JIT;
-
+    typedef JSNonFinalObject Base;
 private:
     GetterSetter(VM& vm, JSGlobalObject* globalObject)
-        : JSCell(vm, vm.getterSetterStructure.get())
+        : Base(vm, vm.getterSetterStructure.get())
     {
         m_getter.set(vm, this, globalObject->nullGetterFunction());
         m_setter.set(vm, this, globalObject->nullSetterFunction());
     }
 
 public:
-    typedef JSCell Base;
-    static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+
+    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | StructureIsImmortal;
 
     static GetterSetter* create(VM& vm, JSGlobalObject* globalObject)
     {
@@ -128,7 +128,12 @@ public:
         return OBJECT_OFFSETOF(GetterSetter, m_setter);
     }
 
-    DECLARE_INFO;
+    DECLARE_EXPORT_INFO;
+
+    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&) { RELEASE_ASSERT_NOT_REACHED(); return false; }
+    static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&) { RELEASE_ASSERT_NOT_REACHED(); return false; }
+    static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool) { RELEASE_ASSERT_NOT_REACHED(); return false; }
+    static bool deleteProperty(JSCell*, ExecState*, PropertyName) { RELEASE_ASSERT_NOT_REACHED(); return false; }
 
 private:
     WriteBarrier<JSObject> m_getter;
index fff4042..21934bc 100644 (file)
@@ -35,7 +35,6 @@ enum JSType : uint8_t {
     StringType,
     SymbolType,
 
-    GetterSetterType,
     CustomGetterSetterType,
     APIValueWrapperType,
 
@@ -75,6 +74,7 @@ enum JSType : uint8_t {
     Float64ArrayType,
     DataViewType,
 
+    GetterSetterType,
     GlobalObjectType,
     LexicalEnvironmentType,
     GlobalLexicalEnvironmentType,
index ad555c7..0410139 100644 (file)
@@ -39,4 +39,21 @@ JSValue PropertySlot::customGetter(ExecState* exec, PropertyName propertyName) c
     return JSValue::decode(m_data.custom.getValue(exec, JSValue::encode(thisValue), propertyName));
 }
 
+JSValue PropertySlot::getPureResult() const
+{
+    JSValue result;
+    if (isTaintedByProxy())
+        result = jsNull();
+    else if (isCacheableValue())
+        result = JSValue::decode(m_data.value);
+    else if (isCacheableGetter())
+        result = getterSetter();
+    else if (isUnset())
+        result = jsUndefined();
+    else
+        result = jsNull();
+    
+    return result;
+}
+
 } // namespace JSC
index d05f729..d163a24 100644 (file)
@@ -94,6 +94,7 @@ public:
 
     JSValue getValue(ExecState*, PropertyName) const;
     JSValue getValue(ExecState*, unsigned propertyName) const;
+    JSValue getPureResult() const;
 
     bool isCacheable() const { return m_cacheability == CachingAllowed && m_offset != invalidOffset; }
     bool isUnset() const { return m_propertyType == TypeUnset; }
index 9f124d3..539185f 100644 (file)
@@ -332,8 +332,7 @@ bool ProxyObject::performHasProperty(ExecState* exec, PropertyName propertyName,
 bool ProxyObject::getOwnPropertySlotCommon(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     slot.disableCaching();
-    if (slot.internalMethodType() != PropertySlot::InternalMethodType::VMInquiry)
-        slot.setIsTaintedByProxy();
+    slot.setIsTaintedByProxy();
     switch (slot.internalMethodType()) {
     case PropertySlot::InternalMethodType::Get:
         slot.setCustom(this, CustomAccessor, performProxyGet);
diff --git a/Source/JavaScriptCore/tests/stress/try-get-by-id.js b/Source/JavaScriptCore/tests/stress/try-get-by-id.js
new file mode 100644 (file)
index 0000000..0d9b67c
--- /dev/null
@@ -0,0 +1,117 @@
+function tryGetByIdText(propertyName) { return `(function (base) { return @tryGetById(base, '${propertyName}'); })`; }
+
+
+// Test get value off object.
+{
+    let getCaller = createBuiltin(tryGetByIdText("caller"));
+    noInline(getCaller);
+    let obj = {caller: 1};
+
+    for (let i = 0; i < 100000; i++) {
+        if (getCaller(obj) !== 1)
+            throw new Error("wrong on iteration: " + i);
+    }
+}
+
+// Test slot is custom function trap for a value.
+{
+    let getCaller = createBuiltin(tryGetByIdText("caller"));
+    noInline(getCaller);
+    let func = function () {};
+
+    for (let i = 0; i < 100000; i++) {
+        if (getCaller(func) !== null)
+            throw new Error("wrong on iteration: " + i);
+    }
+}
+
+// Test slot is a GetterSetter.
+{
+    let get = createBuiltin(tryGetByIdText("getterSetter"));
+    noInline(get);
+    let obj = {};
+    Object.defineProperty(obj, "getterSetter", { get: function () { throw new Error("should not be called"); } });
+
+    for (let i = 0; i < 100000; i++) {
+        if (get(obj) !== getGetterSetter(obj, "getterSetter"))
+            throw new Error("wrong on iteration: " + i);
+    }
+}
+
+// Test slot is unset.
+{
+    let get = createBuiltin(tryGetByIdText("getterSetter"));
+    noInline(get);
+    let obj = {};
+
+    for (let i = 0; i < 100000; i++) {
+        if (get(obj) !== undefined)
+            throw new Error("wrong on iteration: " + i);
+    }
+}
+
+// Test slot is on a proxy with value.
+{
+    let get = createBuiltin(tryGetByIdText("value"));
+    noInline(get);
+
+    let obj = {value: 1};
+    let p = new Proxy(obj, { get: function() { throw new Error("should not be called"); } });
+
+    for (let i = 0; i < 100000; i++) {
+        if (get(p) !== null)
+            throw new Error("wrong on iteration: " + i);
+    }
+}
+
+// Test mutating inline cache.
+{
+    let get = createBuiltin(tryGetByIdText("caller"));
+    noInline(get);
+
+    let obj = {caller : 1};
+    let func = function() {};
+
+    for (let i = 0; i < 100000; i++) {
+        if (i % 100 === 0) {
+            if (get(func) !== null)
+                throw new Error("wrong on iteration: " + i);
+        } else {
+            if (get(obj) !== 1)
+                throw new Error("wrong on iteration: " + i);
+        }
+    }
+}
+
+// Test new type on each iteration.
+{
+    let get = createBuiltin(tryGetByIdText("caller"));
+    noInline(get);
+
+    let func = function() {};
+
+    for (let i = 0; i < 100000; i++) {
+        if (i % 100 === 0) {
+            if (get(func) !== null)
+                throw new Error("wrong on iteration: " + i);
+        } else {
+            let obj = {caller : 1};
+            if (get(obj) !== 1)
+                throw new Error("wrong on iteration: " + i);
+        }
+    }
+}
+
+// Test with array length. This is null because the value is not cacheable.
+{
+    let get = createBuiltin(tryGetByIdText("length"));
+    noInline(get);
+
+    let arr = [];
+
+    for (let i = 0; i < 100000; i++) {
+        if (get(arr) !== null)
+            throw new Error("wrong on iteration: " + i);
+
+    }
+}