diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt index 08badc32775c2..56ccae768a15e 100644 --- a/Source/JavaScriptCore/CMakeLists.txt +++ b/Source/JavaScriptCore/CMakeLists.txt @@ -1093,6 +1093,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS runtime/JSPromiseConstructor.h runtime/JSPropertyNameEnumerator.h runtime/JSProxy.h + runtime/JSRemoteFunction.h runtime/JSRunLoopTimer.h runtime/JSScope.h runtime/JSScriptFetchParameters.h diff --git a/Source/JavaScriptCore/Sources.txt b/Source/JavaScriptCore/Sources.txt index e0de6b72f8c08..d374dac183b98 100644 --- a/Source/JavaScriptCore/Sources.txt +++ b/Source/JavaScriptCore/Sources.txt @@ -907,6 +907,7 @@ runtime/JSPromiseConstructor.cpp runtime/JSPromisePrototype.cpp runtime/JSPropertyNameEnumerator.cpp runtime/JSProxy.cpp +runtime/JSRemoteFunction.cpp runtime/JSRunLoopTimer.cpp runtime/JSScope.cpp runtime/JSScriptFetcher.cpp diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h index 568da19c86573..2682c72e33bdf 100644 --- a/Source/JavaScriptCore/builtins/BuiltinNames.h +++ b/Source/JavaScriptCore/builtins/BuiltinNames.h @@ -188,6 +188,8 @@ namespace JSC { macro(outOfLineReactionCounts) \ macro(emptyPropertyNameEnumerator) \ macro(sentinelString) \ + macro(createRemoteFunction) \ + macro(isRemoteFunction) \ namespace Symbols { diff --git a/Source/JavaScriptCore/builtins/ShadowRealmPrototype.js b/Source/JavaScriptCore/builtins/ShadowRealmPrototype.js index 29ba83067e65b..1877ca9644368 100644 --- a/Source/JavaScriptCore/builtins/ShadowRealmPrototype.js +++ b/Source/JavaScriptCore/builtins/ShadowRealmPrototype.js @@ -32,28 +32,7 @@ function wrap(fromShadowRealm, shadowRealm, target) "use strict"; if (@isCallable(target)) { - var wrapped = function(/* args... */) { - var length = arguments.length; - var wrappedArgs = @newArrayWithSize(length); - for (var index = 0; index < length; ++index) - // Note that for arguments, we flip `fromShadowRealm` since to - // wrap a function from realm A to work in realm B, we need to - // wrap the arguments (from realm B) to work in realm A before - // calling the wrapped function - @putByValDirect(wrappedArgs, index, @wrap(!fromShadowRealm, shadowRealm, arguments[index])); - - var result = target.@apply(@undefined, wrappedArgs); - return @wrap(fromShadowRealm, shadowRealm, result); - }; - delete wrapped['name']; - delete wrapped['length']; - - // Because this function (wrap) will run with the incubating realm - // active, we only need to fix the prototype on `wrapped` if we are - // moving the function from the incubating realm to the shadow realm - if (!fromShadowRealm) - @moveFunctionToRealm(wrapped, shadowRealm); - return wrapped; + target = @createRemoteFunction(target, fromShadowRealm ? null : shadowRealm); } else if (@isObject(target)) { @throwTypeError("value passing between realms must be callable or primitive"); } diff --git a/Source/JavaScriptCore/bytecode/LinkTimeConstant.h b/Source/JavaScriptCore/bytecode/LinkTimeConstant.h index 57a90ec0facc1..478455fdd13df 100644 --- a/Source/JavaScriptCore/bytecode/LinkTimeConstant.h +++ b/Source/JavaScriptCore/bytecode/LinkTimeConstant.h @@ -116,6 +116,8 @@ class JSGlobalObject; v(createPrivateSymbol, nullptr) \ v(emptyPropertyNameEnumerator, nullptr) \ v(sentinelString, nullptr) \ + v(createRemoteFunction, nullptr) \ + v(isRemoteFunction, nullptr) \ #define DECLARE_LINK_TIME_CONSTANT(name, code) name, diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp index 6bce74ab82c55..60dbca1b815bb 100644 --- a/Source/JavaScriptCore/jit/JITOperations.cpp +++ b/Source/JavaScriptCore/jit/JITOperations.cpp @@ -55,6 +55,7 @@ #include "JSGlobalObjectFunctions.h" #include "JSInternalPromise.h" #include "JSLexicalEnvironment.h" +#include "JSRemoteFunction.h" #include "JSWithScope.h" #include "LLIntEntrypoint.h" #include "ObjectConstructor.h" @@ -115,6 +116,32 @@ JSC_DEFINE_JIT_OPERATION(operationThrowStackOverflowErrorFromThunk, void, (JSGlo ASSERT(vm.targetMachinePCForThrow); } +JSC_DEFINE_JIT_OPERATION(operationGetWrappedValue, EncodedJSValue, (JSFunction* callee, EncodedJSValue encodedValue)) +{ + ASSERT(isRemoteFunction(callee)); + JSGlobalObject* globalObject = callee->globalObject(); + VM& vm = globalObject->vm(); + + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSGlobalObject* targetGlobalObject = jsCast(callee)->targetFunction()->globalObject(); + + JSValue value = JSValue::decode(encodedValue); + if (!value.isObject()) { + return encodedValue; + } + + if (value.isCallable(vm)) { + JSFunction* targetCallee = static_cast(value.asCell()); + return JSValue::encode(JSRemoteFunction::create(vm, targetGlobalObject, targetCallee)); + } + + auto scope = DECLARE_THROW_SCOPE(vm); + throwTypeError(targetGlobalObject, scope, "value passing between realms must be callable or primitive"); + return encodedJSValue(); +} + JSC_DEFINE_JIT_OPERATION(operationThrowIteratorResultIsNotObject, void, (JSGlobalObject* globalObject)) { VM& vm = globalObject->vm(); diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp.autosave b/Source/JavaScriptCore/jit/JITOperations.cpp.autosave new file mode 100644 index 0000000000000..697ce6fe6dfe5 --- /dev/null +++ b/Source/JavaScriptCore/jit/JITOperations.cpp.autosave @@ -0,0 +1,3503 @@ +/* + * Copyright (C) 2013-2021 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 "JITOperations.h" + +#if ENABLE(JIT) + +#include "ArithProfile.h" +#include "ArrayConstructor.h" +#include "CacheableIdentifierInlines.h" +#include "CodeBlockInlines.h" +#include "CommonSlowPathsInlines.h" +#include "DFGDriver.h" +#include "DFGOSREntry.h" +#include "DFGThunks.h" +#include "Debugger.h" +#include "ExceptionFuzz.h" +#include "FrameTracers.h" +#include "GetterSetter.h" +#include "ICStats.h" +#include "Interpreter.h" +#include "JIT.h" +#include "JITExceptions.h" +#include "JITToDFGDeferredCompilationCallback.h" +#include "JITWorklist.h" +#include "JSAsyncFunction.h" +#include "JSAsyncGenerator.h" +#include "JSAsyncGeneratorFunction.h" +#include "JSCInlines.h" +#include "JSCPtrTag.h" +#include "JSGeneratorFunction.h" +#include "JSGlobalObjectFunctions.h" +#include "JSInternalPromise.h" +#include "JSLexicalEnvironment.h" +#include "JSRemoteFunction.h" +#include "JSWithScope.h" +#include "LLIntEntrypoint.h" +#include "ObjectConstructor.h" +#include "PropertyName.h" +#include "RegExpObject.h" +#include "RepatchInlines.h" +#include "ShadowChicken.h" +#include "StructureStubInfo.h" +#include "SuperSampler.h" +#include "ThunkGenerators.h" +#include "TypeProfilerLog.h" +#include "VMInlines.h" +#include "VMTrapsInlines.h" + +IGNORE_WARNINGS_BEGIN("frame-address") + +namespace JSC { + +ALWAYS_INLINE JSValue profiledAdd(JSGlobalObject* globalObject, JSValue op1, JSValue op2, BinaryArithProfile& arithProfile) +{ + arithProfile.observeLHSAndRHS(op1, op2); + JSValue result = jsAdd(globalObject, op1, op2); + arithProfile.observeResult(result); + return result; +} + +#if COMPILER(MSVC) +extern "C" void * _ReturnAddress(void); +#pragma intrinsic(_ReturnAddress) + +#define OUR_RETURN_ADDRESS _ReturnAddress() +#else +// FIXME (see rdar://72897291): Work around a Clang bug where __builtin_return_address() +// sometimes gives us a signed pointer, and sometimes does not. +#define OUR_RETURN_ADDRESS removeCodePtrTag(__builtin_return_address(0)) +#endif + + +JSC_DEFINE_JIT_OPERATION(operationThrowStackOverflowError, void, (CodeBlock* codeBlock)) +{ + // We pass in our own code block, because the callframe hasn't been populated. + VM& vm = codeBlock->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + callFrame->convertToStackOverflowFrame(vm, codeBlock); + throwStackOverflowError(codeBlock->globalObject(), scope); +} + +JSC_DEFINE_JIT_OPERATION(operationThrowStackOverflowErrorFromThunk, void, (JSGlobalObject* globalObject)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + throwStackOverflowError(globalObject, scope); + genericUnwind(vm, callFrame); + ASSERT(vm.targetMachinePCForThrow); +} + +JSC_DEFINE_JIT_OPERATION(operationGetWrappedValue, EncodedJSValue, (JSGlobalObject* targetGlobalObject, EncodedJSValue encodedValue)) +{ + VM& vm = targetGlobalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue value = JSValue::decode(encodedValue); + if (value.isPrimitive()) + RELEASE_AND_RETURN(scope, encodedValue); + + if (value.isCallable(vm)) { + JSCallee* targetCallee = jsCast(value.asCell()); + ASSERT(targetCallee->structure()->globalObject() != targetGlobalObject); + + return JSValue::encode(JSRemoteFunction::create(vm, targetGlobalObject, targetCallee)); + } + throwTypeError(targetGlobalObject, scope, "value passing between realms must be callable or primitive"); + return encodedJSValue(); +} + +JSC_DEFINE_JIT_OPERATION(operationThrowIteratorResultIsNotObject, void, (JSGlobalObject* globalObject)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + throwTypeError(globalObject, scope, "Iterator result interface is not an object."_s); +} + +JSC_DEFINE_JIT_OPERATION(operationCallArityCheck, int32_t, (JSGlobalObject* globalObject)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t missingArgCount = CommonSlowPaths::arityCheckFor(vm, callFrame, CodeForCall); + if (UNLIKELY(missingArgCount < 0)) { + CodeBlock* codeBlock = CommonSlowPaths::codeBlockFromCallFrameCallee(callFrame, CodeForCall); + callFrame->convertToStackOverflowFrame(vm, codeBlock); + throwStackOverflowError(globalObject, scope); + } + + return missingArgCount; +} + +JSC_DEFINE_JIT_OPERATION(operationConstructArityCheck, int32_t, (JSGlobalObject* globalObject)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t missingArgCount = CommonSlowPaths::arityCheckFor(vm, callFrame, CodeForConstruct); + if (UNLIKELY(missingArgCount < 0)) { + CodeBlock* codeBlock = CommonSlowPaths::codeBlockFromCallFrameCallee(callFrame, CodeForConstruct); + callFrame->convertToStackOverflowFrame(vm, codeBlock); + throwStackOverflowError(globalObject, scope); + } + + return missingArgCount; +} + +JSC_DEFINE_JIT_OPERATION(operationTryGetById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry, &vm); + baseValue.getPropertySlot(globalObject, ident, slot); + + return JSValue::encode(slot.getPureResult()); +} + + +JSC_DEFINE_JIT_OPERATION(operationTryGetByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry, &vm); + baseValue.getPropertySlot(globalObject, ident, slot); + + return JSValue::encode(slot.getPureResult()); +} + +JSC_DEFINE_JIT_OPERATION(operationTryGetByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry, &vm); + + baseValue.getPropertySlot(globalObject, ident, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier) && !slot.isTaintedByOpaqueObject() && (slot.isCacheableValue() || slot.isCacheableGetter() || slot.isUnset())) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::TryById); + + return JSValue::encode(slot.getPureResult()); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdDirect, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty); + + bool found = baseValue.getOwnPropertySlot(globalObject, ident, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(globalObject, ident) : jsUndefined())); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdDirectGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty); + + bool found = baseValue.getOwnPropertySlot(globalObject, ident, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(globalObject, ident) : jsUndefined())); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdDirectOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty); + + bool found = baseValue.getOwnPropertySlot(globalObject, ident, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByIdDirect); + + RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(globalObject, ident) : jsUndefined())); +} + +JSC_DEFINE_JIT_OPERATION(operationGetById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + JSValue result = baseValue.get(globalObject, ident, slot); + + LOG_IC((ICEvent::OperationGetById, baseValue.classInfoOrNull(vm), ident, baseValue == slot.slotBase())); + + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(base); + PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + JSValue result = baseValue.get(globalObject, ident, slot); + + LOG_IC((ICEvent::OperationGetByIdGeneric, baseValue.classInfoOrNull(vm), ident, baseValue == slot.slotBase())); + + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + + return JSValue::encode(baseValue.getPropertySlot(globalObject, ident, [&] (bool found, PropertySlot& slot) -> JSValue { + + LOG_IC((ICEvent::OperationGetByIdOptimize, baseValue.classInfoOrNull(vm), ident, baseValue == slot.slotBase())); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ById); + return found ? slot.getValue(globalObject, ident) : jsUndefined(); + })); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdWithThis, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(base); + JSValue thisValue = JSValue::decode(thisEncoded); + PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); + + return JSValue::encode(baseValue.get(globalObject, ident, slot)); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdWithThisGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue thisEncoded, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + JSValue thisValue = JSValue::decode(thisEncoded); + PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); + + return JSValue::encode(baseValue.get(globalObject, ident, slot)); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByIdWithThisOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + JSValue thisValue = JSValue::decode(thisEncoded); + + PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); + return JSValue::encode(baseValue.getPropertySlot(globalObject, ident, slot, [&] (bool found, PropertySlot& slot) -> JSValue { + LOG_IC((ICEvent::OperationGetByIdWithThisOptimize, baseValue.classInfoOrNull(vm), ident, baseValue == slot.slotBase())); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByIdWithThis); + return found ? slot.getValue(globalObject, ident) : jsUndefined(); + })); +} + +JSC_DEFINE_JIT_OPERATION(operationInByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + stubInfo->tookSlowPath = true; + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return JSValue::encode(jsUndefined()); + } + JSObject* baseObject = asObject(baseValue); + + LOG_IC((ICEvent::OperationInByIdGeneric, baseObject->classInfo(vm), ident)); + + scope.release(); + PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); + return JSValue::encode(jsBoolean(baseObject->getPropertySlot(globalObject, ident, slot))); +} + +JSC_DEFINE_JIT_OPERATION(operationInByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + JSValue baseValue = JSValue::decode(base); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return JSValue::encode(jsUndefined()); + } + JSObject* baseObject = asObject(baseValue); + + LOG_IC((ICEvent::OperationInByIdOptimize, baseObject->classInfo(vm), ident)); + + scope.release(); + PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); + bool found = baseObject->getPropertySlot(globalObject, ident, slot); + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(vm), identifier)) + repatchInBy(globalObject, codeBlock, baseObject, identifier, found, slot, *stubInfo, InByKind::ById); + return JSValue::encode(jsBoolean(found)); +} + +JSC_DEFINE_JIT_OPERATION(operationInByValOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* arrayProfile, EncodedJSValue encodedBase, EncodedJSValue encodedKey)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBase); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return encodedJSValue(); + } + JSObject* baseObject = asObject(baseValue); + if (arrayProfile) + arrayProfile->observeStructure(baseObject->structure(vm)); + + JSValue key = JSValue::decode(encodedKey); + uint32_t i; + if (key.getUInt32(i)) { + // FIXME: InByVal should have inline caching for integer indices too, as GetByVal does. + // https://bugs.webkit.org/show_bug.cgi?id=226619 + if (arrayProfile) + arrayProfile->observeIndexedRead(vm, baseObject, i); + RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(baseObject->hasProperty(globalObject, i)))); + } + + const Identifier propertyName = key.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); + bool found = baseObject->getPropertySlot(globalObject, propertyName, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + if (CacheableIdentifier::isCacheableIdentifierCell(key) && (key.isSymbol() || !parseIndex(propertyName))) { + CodeBlock* codeBlock = callFrame->codeBlock(); + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(key.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(vm), identifier)) + repatchInBy(globalObject, codeBlock, baseObject, identifier, found, slot, *stubInfo, InByKind::ByVal); + } + + return JSValue::encode(jsBoolean(found)); +} + +JSC_DEFINE_JIT_OPERATION(operationInByValGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* arrayProfile, EncodedJSValue base, EncodedJSValue key)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + return JSValue::encode(jsBoolean(CommonSlowPaths::opInByVal(globalObject, JSValue::decode(base), JSValue::decode(key), arrayProfile))); +} + +JSC_DEFINE_JIT_OPERATION(operationHasPrivateNameOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBase); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return encodedJSValue(); + } + JSObject* baseObject = asObject(baseValue); + + JSValue propertyValue = JSValue::decode(encodedProperty); + ASSERT(propertyValue.isSymbol()); + auto property = propertyValue.toPropertyKey(globalObject); + EXCEPTION_ASSERT(!scope.exception()); + + PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); + bool found = JSObject::getPrivateFieldSlot(baseObject, globalObject, property, slot); + + ASSERT(CacheableIdentifier::isCacheableIdentifierCell(propertyValue)); + CodeBlock* codeBlock = callFrame->codeBlock(); + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(propertyValue.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(vm), identifier)) + repatchInBy(globalObject, codeBlock, baseObject, identifier, found, slot, *stubInfo, InByKind::PrivateName); + + return JSValue::encode(jsBoolean(found)); +} + +JSC_DEFINE_JIT_OPERATION(operationHasPrivateNameGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(encodedBase); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return encodedJSValue(); + } + + JSValue propertyValue = JSValue::decode(encodedProperty); + ASSERT(propertyValue.isSymbol()); + auto property = propertyValue.toPropertyKey(globalObject); + EXCEPTION_ASSERT(!scope.exception()); + + return JSValue::encode(jsBoolean(asObject(baseValue)->hasPrivateField(globalObject, property))); +} + +JSC_DEFINE_JIT_OPERATION(operationHasPrivateBrandOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedBrand)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBase); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return encodedJSValue(); + } + JSObject* baseObject = asObject(baseValue); + + JSValue brand = JSValue::decode(encodedBrand); + bool found = asObject(baseValue)->hasPrivateBrand(globalObject, brand); + + ASSERT(CacheableIdentifier::isCacheableIdentifierCell(brand)); + CodeBlock* codeBlock = callFrame->codeBlock(); + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(brand.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(vm), identifier)) + repatchHasPrivateBrand(globalObject, codeBlock, baseObject, identifier, found, *stubInfo); + + return JSValue::encode(jsBoolean(found)); +} + +JSC_DEFINE_JIT_OPERATION(operationHasPrivateBrandGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedBrand)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(encodedBase); + if (!baseValue.isObject()) { + throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); + return encodedJSValue(); + } + + return JSValue::encode(jsBoolean(asObject(baseValue)->hasPrivateBrand(globalObject, JSValue::decode(encodedBrand)))); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(encodedBase); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + PutPropertySlot slot(baseValue, true, callFrame->codeBlock()->putByIdContext()); + baseValue.putInline(globalObject, ident, JSValue::decode(encodedValue), slot); + + LOG_IC((ICEvent::OperationPutByIdStrict, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue)); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdNonStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(encodedBase); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + PutPropertySlot slot(baseValue, false, callFrame->codeBlock()->putByIdContext()); + baseValue.putInline(globalObject, ident, JSValue::decode(encodedValue), slot); + + LOG_IC((ICEvent::OperationPutByIdNonStrict, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue)); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(encodedBase); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + PutPropertySlot slot(baseValue, true, callFrame->codeBlock()->putByIdContext()); + CommonSlowPaths::putDirectWithReify(vm, globalObject, asObject(baseValue), ident, JSValue::decode(encodedValue), slot); + + LOG_IC((ICEvent::OperationPutByIdDirectStrict, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue)); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectNonStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(encodedBase); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + PutPropertySlot slot(baseValue, false, callFrame->codeBlock()->putByIdContext()); + CommonSlowPaths::putDirectWithReify(vm, globalObject, asObject(baseValue), ident, JSValue::decode(encodedValue), slot); + + LOG_IC((ICEvent::OperationPutByIdDirectNonStrict, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue)); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + AccessType accessType = static_cast(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + CodeBlock* codeBlock = callFrame->codeBlock(); + PutPropertySlot slot(baseValue, true, codeBlock->putByIdContext()); + + Structure* structure = CommonSlowPaths::originalStructureBeforePut(vm, baseValue); + baseValue.putInline(globalObject, ident, value, slot); + + LOG_IC((ICEvent::OperationPutByIdStrictOptimize, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue)); + + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::NotDirect); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdNonStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + AccessType accessType = static_cast(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + CodeBlock* codeBlock = callFrame->codeBlock(); + PutPropertySlot slot(baseValue, false, codeBlock->putByIdContext()); + + Structure* structure = CommonSlowPaths::originalStructureBeforePut(vm, baseValue); + baseValue.putInline(globalObject, ident, value, slot); + + LOG_IC((ICEvent::OperationPutByIdNonStrictOptimize, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue)); + + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::NotDirect); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + AccessType accessType = static_cast(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSObject* baseObject = asObject(JSValue::decode(encodedBase)); + CodeBlock* codeBlock = callFrame->codeBlock(); + PutPropertySlot slot(baseObject, true, codeBlock->putByIdContext()); + Structure* structure = nullptr; + CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, ident, value, slot, &structure); + + LOG_IC((ICEvent::OperationPutByIdDirectStrictOptimize, baseObject->classInfo(vm), ident, slot.base() == baseObject)); + + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseObject, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::Direct); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectNonStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + AccessType accessType = static_cast(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSObject* baseObject = asObject(JSValue::decode(encodedBase)); + CodeBlock* codeBlock = callFrame->codeBlock(); + PutPropertySlot slot(baseObject, false, codeBlock->putByIdContext()); + Structure* structure = nullptr; + CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, ident, value, slot, &structure); + + LOG_IC((ICEvent::OperationPutByIdDirectNonStrictOptimize, baseObject->classInfo(vm), ident, slot.base() == baseObject)); + + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseObject, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::Direct); +} + +template +ALWAYS_INLINE static void setPrivateField(VM& vm, JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, CacheableIdentifier identifier, JSValue value, PutPrivateFieldCallback callback) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + ASSERT(ident.isPrivateName()); + + JSObject* baseObject = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + CodeBlock* codeBlock = callFrame->codeBlock(); + Structure* oldStructure = baseObject->structure(vm); + + PutPropertySlot putSlot(baseObject, true, codeBlock->putByIdContext()); + baseObject->setPrivateField(globalObject, ident, value, putSlot); + RETURN_IF_EXCEPTION(scope, void()); + + callback(vm, codeBlock, oldStructure, putSlot, ident); +} + +template +ALWAYS_INLINE static void definePrivateField(VM& vm, JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, CacheableIdentifier identifier, JSValue value, PutPrivateFieldCallback callback) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + ASSERT(ident.isPrivateName()); + + JSObject* baseObject = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + CodeBlock* codeBlock = callFrame->codeBlock(); + Structure* oldStructure = baseObject->structure(vm); + + PutPropertySlot putSlot(baseObject, true, codeBlock->putByIdContext()); + baseObject->definePrivateField(globalObject, ident, value, putSlot); + RETURN_IF_EXCEPTION(scope, void()); + + callback(vm, codeBlock, oldStructure, putSlot, ident); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdDefinePrivateFieldStrict, void, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + + definePrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [](VM&, CodeBlock*, Structure*, PutPropertySlot&, const Identifier&) { }); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdDefinePrivateFieldStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + AccessType accessType = static_cast(stubInfo->accessType); + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + + definePrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [=](VM& vm, CodeBlock* codeBlock, Structure* oldStructure, PutPropertySlot& putSlot, const Identifier& ident) { + JSObject* baseObject = asObject(baseValue); + LOG_IC((ICEvent::OperationPutByIdDefinePrivateFieldFieldStrictOptimize, baseObject->classInfo(vm), ident, putSlot.base() == baseObject)); + + ASSERT_UNUSED(accessType, accessType == static_cast(stubInfo->accessType)); + + if (stubInfo->considerCachingBy(vm, codeBlock, oldStructure, identifier)) + repatchPutBy(globalObject, codeBlock, baseObject, oldStructure, identifier, putSlot, *stubInfo, PutByKind::ById, PutKind::DirectPrivateFieldDefine); + }); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdSetPrivateFieldStrict, void, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + + setPrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [](VM&, CodeBlock*, Structure*, PutPropertySlot&, const Identifier&) { }); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByIdSetPrivateFieldStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + AccessType accessType = static_cast(stubInfo->accessType); + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + + setPrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [&](VM& vm, CodeBlock* codeBlock, Structure* oldStructure, PutPropertySlot& putSlot, const Identifier& ident) { + JSObject* baseObject = asObject(baseValue); + LOG_IC((ICEvent::OperationPutByIdPutPrivateFieldFieldStrictOptimize, baseObject->classInfo(vm), ident, putSlot.base() == baseObject)); + + ASSERT_UNUSED(accessType, accessType == static_cast(stubInfo->accessType)); + + if (stubInfo->considerCachingBy(vm, codeBlock, oldStructure, identifier)) + repatchPutBy(globalObject, codeBlock, baseObject, oldStructure, identifier, putSlot, *stubInfo, PutByKind::ById, PutKind::DirectPrivateFieldSet); + }); +} + +static void putByVal(JSGlobalObject* globalObject, JSValue baseValue, JSValue subscript, JSValue value, ArrayProfile* arrayProfile, ECMAMode ecmaMode) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (std::optional index = subscript.tryGetAsUint32Index()) { + uint32_t i = *index; + if (baseValue.isObject()) { + JSObject* object = asObject(baseValue); + if (object->trySetIndexQuickly(vm, i, value, arrayProfile)) + return; + + if (arrayProfile) + arrayProfile->setOutOfBounds(); + scope.release(); + object->methodTable(vm)->putByIndex(object, globalObject, i, value, ecmaMode.isStrict()); + return; + } + + scope.release(); + baseValue.putByIndex(globalObject, i, value, ecmaMode.isStrict()); + return; + } + + if (subscript.isNumber()) { + if (baseValue.isObject()) { + if (arrayProfile) + arrayProfile->setOutOfBounds(); + } + } + + auto property = subscript.toPropertyKey(globalObject); + // Don't put to an object if toString threw an exception. + RETURN_IF_EXCEPTION(scope, void()); + + scope.release(); + PutPropertySlot slot(baseValue, ecmaMode.isStrict()); + baseValue.putInline(globalObject, property, value, slot); +} + +static void directPutByVal(JSGlobalObject* globalObject, JSObject* baseObject, JSValue subscript, JSValue value, ArrayProfile* arrayProfile, ECMAMode ecmaMode) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (std::optional maybeIndex = subscript.tryGetAsUint32Index()) { + uint32_t index = *maybeIndex; + + switch (baseObject->indexingType()) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (index < baseObject->butterfly()->vectorLength()) + break; + FALLTHROUGH; + default: + if (arrayProfile) + arrayProfile->setOutOfBounds(); + break; + } + + scope.release(); + baseObject->putDirectIndex(globalObject, index, value, 0, ecmaMode.isStrict() ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); + return; + } + + // Don't put to an object if toString threw an exception. + auto property = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + if (std::optional index = parseIndex(property)) { + scope.release(); + baseObject->putDirectIndex(globalObject, index.value(), value, 0, ecmaMode.isStrict() ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); + return; + } + + scope.release(); + PutPropertySlot slot(baseObject, ecmaMode.isStrict()); + CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, property, value, slot); +} + +enum class OptimizationResult { + NotOptimized, + SeenOnce, + Optimized, + GiveUp, +}; + +static ALWAYS_INLINE void putByValOptimize(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue subscript, JSValue value, StructureStubInfo* stubInfo, ArrayProfile* profile, ECMAMode ecmaMode) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (baseValue.isObject()) { + JSObject* baseObject = asObject(baseValue); + if (!isCopyOnWrite(baseObject->indexingMode()) && subscript.isInt32()) { + Structure* structure = baseObject->structure(vm); + if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { + if (profile) { + ConcurrentJSLocker locker(codeBlock->m_lock); + profile->computeUpdatedPrediction(locker, codeBlock, structure); + } + repatchArrayPutByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo, PutKind::NotDirect, ecmaMode); + } + } + + if (CacheableIdentifier::isCacheableIdentifierCell(subscript)) { + const Identifier propertyName = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + if (subscript.isSymbol() || !parseIndex(propertyName)) { + AccessType accessType = static_cast(stubInfo->accessType); + PutPropertySlot slot(baseValue, ecmaMode.isStrict(), codeBlock->putByIdContext()); + + Structure* structure = CommonSlowPaths::originalStructureBeforePut(vm, baseValue); + baseObject->putInline(globalObject, propertyName, value, slot); + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ByVal, PutKind::NotDirect); + return; + } + } + } + + RELEASE_AND_RETURN(scope, putByVal(globalObject, baseValue, subscript, value, profile, ecmaMode)); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + putByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::strict()); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValNonStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + putByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::sloppy()); +} + +static ALWAYS_INLINE void directPutByValOptimize(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue subscript, JSValue value, StructureStubInfo* stubInfo, ArrayProfile* profile, ECMAMode ecmaMode) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + RELEASE_ASSERT(baseValue.isObject()); + JSObject* baseObject = asObject(baseValue); + + if (!isCopyOnWrite(baseObject->indexingMode()) && subscript.isInt32()) { + Structure* structure = baseObject->structure(vm); + if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { + if (profile) { + ConcurrentJSLocker locker(codeBlock->m_lock); + profile->computeUpdatedPrediction(locker, codeBlock, structure); + } + repatchArrayPutByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo, PutKind::Direct, ecmaMode); + } + } + + if (CacheableIdentifier::isCacheableIdentifierCell(subscript)) { + const Identifier propertyName = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + if (subscript.isSymbol() || !parseIndex(propertyName)) { + AccessType accessType = static_cast(stubInfo->accessType); + PutPropertySlot slot(baseValue, ecmaMode.isStrict(), codeBlock->putByIdContext()); + + Structure* structure = CommonSlowPaths::originalStructureBeforePut(vm, baseValue); + CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, propertyName, value, slot); + + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ByVal, PutKind::Direct); + return; + } + } + + RELEASE_AND_RETURN(scope, directPutByVal(globalObject, baseObject, subscript, value, profile, ecmaMode)); + +} + +JSC_DEFINE_JIT_OPERATION(operationDirectPutByValStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + directPutByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::strict()); +} + +JSC_DEFINE_JIT_OPERATION(operationDirectPutByValNonStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + directPutByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::sloppy()); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + stubInfo->tookSlowPath = true; + + putByVal(globalObject, baseValue, subscript, value, profile, ECMAMode::strict()); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValNonStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + stubInfo->tookSlowPath = true; + + putByVal(globalObject, baseValue, subscript, value, profile, ECMAMode::sloppy()); +} + +JSC_DEFINE_JIT_OPERATION(operationDirectPutByValStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + RELEASE_ASSERT(baseValue.isObject()); + + stubInfo->tookSlowPath = true; + + directPutByVal(globalObject, asObject(baseValue), subscript, value, profile, ECMAMode::strict()); +} + +JSC_DEFINE_JIT_OPERATION(operationDirectPutByValNonStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + RELEASE_ASSERT(baseValue.isObject()); + + stubInfo->tookSlowPath = true; + + directPutByVal(globalObject, asObject(baseValue), subscript, value, profile, ECMAMode::sloppy()); +} + +JSC_DEFINE_JIT_OPERATION(operationSetPrivateBrandOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue brand = JSValue::decode(encodedBrand); + + ASSERT(baseValue.isObject()); + ASSERT(brand.isSymbol()); + + JSObject* baseObject = asObject(baseValue); + Structure* oldStructure = baseObject->structure(vm); + baseObject->setPrivateBrand(globalObject, brand); + RETURN_IF_EXCEPTION(scope, void()); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (CacheableIdentifier::isCacheableIdentifierCell(brand)) { + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(brand.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(vm), identifier)) + repatchSetPrivateBrand(globalObject, codeBlock, baseObject, oldStructure, identifier, *stubInfo); + } + +} + +JSC_DEFINE_JIT_OPERATION(operationSetPrivateBrandGeneric, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue brand = JSValue::decode(encodedBrand); + + if (stubInfo) + stubInfo->tookSlowPath = true; + + ASSERT(baseValue.isObject()); + ASSERT(brand.isSymbol()); + + JSObject* baseObject = asObject(baseValue); + baseObject->setPrivateBrand(globalObject, brand); + RETURN_IF_EXCEPTION(scope, void()); +} + +JSC_DEFINE_JIT_OPERATION(operationCheckPrivateBrandOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue brand = JSValue::decode(encodedBrand); + + JSObject* baseObject = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + ASSERT(brand.isSymbol()); + + baseObject->checkPrivateBrand(globalObject, brand); + RETURN_IF_EXCEPTION(scope, void()); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (CacheableIdentifier::isCacheableIdentifierCell(brand)) { + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(brand.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(vm), identifier)) + repatchCheckPrivateBrand(globalObject, codeBlock, baseObject, identifier, *stubInfo); + } +} + +JSC_DEFINE_JIT_OPERATION(operationCheckPrivateBrandGeneric, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue brand = JSValue::decode(encodedBrand); + + stubInfo->tookSlowPath = true; + + JSObject* baseObject = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + ASSERT(brand.isSymbol()); + + baseObject->checkPrivateBrand(globalObject, brand); + RETURN_IF_EXCEPTION(scope, void()); +} + +template +static ALWAYS_INLINE void putPrivateNameOptimize(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue subscript, JSValue value, StructureStubInfo* stubInfo) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto baseObject = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + auto propertyName = subscript.toPropertyKey(globalObject); + EXCEPTION_ASSERT(!scope.exception()); + + // Private fields can only be accessed within class lexical scope + // and class methods are always in strict mode + AccessType accessType = static_cast(stubInfo->accessType); + Structure* structure = CommonSlowPaths::originalStructureBeforePut(vm, baseValue); + constexpr bool isStrictMode = true; + PutPropertySlot slot(baseObject, isStrictMode); + if constexpr (define) + baseObject->definePrivateField(globalObject, propertyName, value, slot); + else + baseObject->setPrivateField(globalObject, propertyName, value, slot); + RETURN_IF_EXCEPTION(scope, void()); + + if (accessType != static_cast(stubInfo->accessType)) + return; + + if (baseValue.isObject() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) + repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ByVal, define ? PutKind::DirectPrivateFieldDefine : PutKind::DirectPrivateFieldSet); + } +} + +template +static ALWAYS_INLINE void putPrivateName(JSGlobalObject* globalObject, JSValue baseValue, JSValue subscript, JSValue value) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto baseObject = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + auto propertyName = subscript.toPropertyKey(globalObject); + EXCEPTION_ASSERT(!scope.exception()); + + scope.release(); + + // Private fields can only be accessed within class lexical scope + // and class methods are always in strict mode + constexpr bool isStrictMode = true; + PutPropertySlot slot(baseObject, isStrictMode); + if constexpr (define) + baseObject->definePrivateField(globalObject, propertyName, value, slot); + else + baseObject->setPrivateField(globalObject, propertyName, value, slot); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValDefinePrivateFieldOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + CodeBlock* codeBlock = callFrame->codeBlock(); + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + putPrivateNameOptimize(globalObject, codeBlock, baseValue, subscript, value, stubInfo); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValSetPrivateFieldOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + CodeBlock* codeBlock = callFrame->codeBlock(); + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + putPrivateNameOptimize(globalObject, codeBlock, baseValue, subscript, value, stubInfo); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValDefinePrivateFieldGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + if (stubInfo) + stubInfo->tookSlowPath = true; + + putPrivateName(globalObject, baseValue, subscript, value); +} + +JSC_DEFINE_JIT_OPERATION(operationPutByValSetPrivateFieldGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); + + if (stubInfo) + stubInfo->tookSlowPath = true; + + putPrivateName(globalObject, baseValue, subscript, value); +} + +JSC_DEFINE_JIT_OPERATION(operationCallEval, EncodedJSValue, (JSGlobalObject* globalObject, CallFrame* calleeFrame, ECMAMode ecmaMode)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + calleeFrame->setCodeBlock(nullptr); + + if (!isHostFunction(calleeFrame->guaranteedJSValueCallee(), globalFuncEval)) + return JSValue::encode(JSValue()); + + JSValue result = eval(globalObject, calleeFrame, ecmaMode); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationLinkCall, SlowPathReturnType, (CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)) +{ + return linkFor(calleeFrame, globalObject, callLinkInfo); +} + +JSC_DEFINE_JIT_OPERATION(operationLinkPolymorphicCall, SlowPathReturnType, (CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)) +{ + ASSERT(callLinkInfo->specializationKind() == CodeForCall); + JSCell* calleeAsFunctionCell; + SlowPathReturnType result = virtualForWithFunction(globalObject, calleeFrame, callLinkInfo, calleeAsFunctionCell); + + linkPolymorphicCall(globalObject, calleeFrame, *callLinkInfo, CallVariant(calleeAsFunctionCell)); + + return result; +} + +JSC_DEFINE_JIT_OPERATION(operationVirtualCall, SlowPathReturnType, (CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)) +{ + JSCell* calleeAsFunctionCellIgnored; + return virtualForWithFunction(globalObject, calleeFrame, callLinkInfo, calleeAsFunctionCellIgnored); +} + +JSC_DEFINE_JIT_OPERATION(operationCompareLess, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return jsLess(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); +} + +JSC_DEFINE_JIT_OPERATION(operationCompareLessEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return jsLessEq(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); +} + +JSC_DEFINE_JIT_OPERATION(operationCompareGreater, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return jsLess(globalObject, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1)); +} + +JSC_DEFINE_JIT_OPERATION(operationCompareGreaterEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return jsLessEq(globalObject, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1)); +} + +JSC_DEFINE_JIT_OPERATION(operationCompareEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return JSValue::equalSlowCaseInline(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); +} + +#if USE(JSVALUE64) +JSC_DEFINE_JIT_OPERATION(operationCompareStringEq, EncodedJSValue, (JSGlobalObject* globalObject, JSCell* left, JSCell* right)) +#else +JSC_DEFINE_JIT_OPERATION(operationCompareStringEq, size_t, (JSGlobalObject* globalObject, JSCell* left, JSCell* right)) +#endif +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + bool result = asString(left)->equal(globalObject, asString(right)); +#if USE(JSVALUE64) + return JSValue::encode(jsBoolean(result)); +#else + return result; +#endif +} + +JSC_DEFINE_JIT_OPERATION(operationCompareStrictEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue src1 = JSValue::decode(encodedOp1); + JSValue src2 = JSValue::decode(encodedOp2); + + return JSValue::strictEqual(globalObject, src1, src2); +} + +#if USE(BIGINT32) +JSC_DEFINE_JIT_OPERATION(operationCompareEqHeapBigIntToInt32, size_t, (JSGlobalObject* globalObject, JSCell* heapBigInt, int32_t smallInt)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(heapBigInt->isHeapBigInt()); + + return static_cast(heapBigInt)->equalsToInt32(smallInt); +} +#endif + +JSC_DEFINE_JIT_OPERATION(operationNewArrayWithProfile, EncodedJSValue, (JSGlobalObject* globalObject, ArrayAllocationProfile* profile, const JSValue* values, int size)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return JSValue::encode(constructArrayNegativeIndexed(globalObject, profile, values, size)); +} + +JSC_DEFINE_JIT_OPERATION(operationNewArrayWithSizeAndProfile, EncodedJSValue, (JSGlobalObject* globalObject, ArrayAllocationProfile* profile, EncodedJSValue size)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue sizeValue = JSValue::decode(size); + return JSValue::encode(constructArrayWithSizeQuirk(globalObject, profile, sizeValue)); +} + +template +static EncodedJSValue newFunctionCommon(VM& vm, JSScope* scope, JSCell* functionExecutable, bool isInvalidated) +{ + ASSERT(functionExecutable->inherits(vm)); + if (isInvalidated) + return JSValue::encode(FunctionType::createWithInvalidatedReallocationWatchpoint(vm, static_cast(functionExecutable), scope)); + return JSValue::encode(FunctionType::create(vm, static_cast(functionExecutable), scope)); +} + +JSC_DEFINE_JIT_OPERATION(operationNewFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, false); +} + +JSC_DEFINE_JIT_OPERATION(operationNewFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, true); +} + +JSC_DEFINE_JIT_OPERATION(operationNewGeneratorFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, false); +} + +JSC_DEFINE_JIT_OPERATION(operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, true); +} + +JSC_DEFINE_JIT_OPERATION(operationNewAsyncFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, false); +} + +JSC_DEFINE_JIT_OPERATION(operationNewAsyncFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, true); +} + +JSC_DEFINE_JIT_OPERATION(operationNewAsyncGeneratorFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, false); +} + +JSC_DEFINE_JIT_OPERATION(operationNewAsyncGeneratorFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return newFunctionCommon(vm, scope, functionExecutable, true); +} + +JSC_DEFINE_JIT_OPERATION(operationSetFunctionName, void, (JSGlobalObject* globalObject, JSCell* funcCell, EncodedJSValue encodedName)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSFunction* func = jsCast(funcCell); + JSValue name = JSValue::decode(encodedName); + func->setFunctionName(globalObject, name); +} + +JSC_DEFINE_JIT_OPERATION(operationNewObject, JSCell*, (VM* vmPointer, Structure* structure)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return constructEmptyObject(vm, structure); +} + +JSC_DEFINE_JIT_OPERATION(operationNewPromise, JSCell*, (VM* vmPointer, Structure* structure)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return JSPromise::create(vm, structure); +} + +JSC_DEFINE_JIT_OPERATION(operationNewInternalPromise, JSCell*, (VM* vmPointer, Structure* structure)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return JSInternalPromise::create(vm, structure); +} + +JSC_DEFINE_JIT_OPERATION(operationNewGenerator, JSCell*, (VM* vmPointer, Structure* structure)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return JSGenerator::create(vm, structure); +} + +JSC_DEFINE_JIT_OPERATION(operationNewAsyncGenerator, JSCell*, (VM* vmPointer, Structure* structure)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return JSAsyncGenerator::create(vm, structure); +} + +JSC_DEFINE_JIT_OPERATION(operationNewRegexp, JSCell*, (JSGlobalObject* globalObject, JSCell* regexpPtr)) +{ + SuperSamplerScope superSamplerScope(false); + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + RegExp* regexp = static_cast(regexpPtr); + ASSERT(regexp->isValid()); + static constexpr bool areLegacyFeaturesEnabled = true; + return RegExpObject::create(vm, globalObject->regExpStructure(), regexp, areLegacyFeaturesEnabled); +} + +// The only reason for returning an UnusedPtr (instead of void) is so that we can reuse the +// existing DFG slow path generator machinery when creating the slow path for CheckTraps +// in the DFG. If a DFG slow path generator that supports a void return type is added in the +// future, we can switch to using that then. +JSC_DEFINE_JIT_OPERATION(operationHandleTraps, UnusedPtr, (JSGlobalObject* globalObject)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + ASSERT(vm.traps().needHandling(VMTraps::AsyncEvents)); + vm.traps().handleTraps(VMTraps::AsyncEvents); + return nullptr; +} + +JSC_DEFINE_JIT_OPERATION(operationDebug, void, (VM* vmPointer, int32_t debugHookType)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + vm.interpreter->debug(callFrame, static_cast(debugHookType)); +} + +#if ENABLE(DFG_JIT) +static void updateAllPredictionsAndOptimizeAfterWarmUp(CodeBlock* codeBlock) +{ + codeBlock->updateAllPredictions(); + codeBlock->optimizeAfterWarmUp(); +} + +JSC_DEFINE_JIT_OPERATION(operationOptimize, SlowPathReturnType, (VM* vmPointer, uint32_t bytecodeIndexBits)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + BytecodeIndex bytecodeIndex = BytecodeIndex::fromBits(bytecodeIndexBits); + + // Defer GC for a while so that it doesn't run between when we enter into this + // slow path and when we figure out the state of our code block. This prevents + // a number of awkward reentrancy scenarios, including: + // + // - The optimized version of our code block being jettisoned by GC right after + // we concluded that we wanted to use it, but have not planted it into the JS + // stack yet. + // + // - An optimized version of our code block being installed just as we decided + // that it wasn't ready yet. + // + // Note that jettisoning won't happen if we already initiated OSR, because in + // that case we would have already planted the optimized code block into the JS + // stack. + DeferGCForAWhile deferGC(vm); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (UNLIKELY(codeBlock->jitType() != JITType::BaselineJIT)) { + dataLog("Unexpected code block in Baseline->DFG tier-up: ", *codeBlock, "\n"); + RELEASE_ASSERT_NOT_REACHED(); + } + + if (bytecodeIndex) { + // If we're attempting to OSR from a loop, assume that this should be + // separately optimized. + codeBlock->m_shouldAlwaysBeInlined = false; + } + + if (UNLIKELY(Options::verboseOSR())) { + dataLog( + *codeBlock, ": Entered optimize with bytecodeIndex = ", bytecodeIndex, + ", executeCounter = ", codeBlock->jitExecuteCounter(), + ", optimizationDelayCounter = ", codeBlock->reoptimizationRetryCounter(), + ", exitCounter = "); + if (codeBlock->hasOptimizedReplacement()) + dataLog(codeBlock->replacement()->osrExitCounter()); + else + dataLog("N/A"); + dataLog("\n"); + } + + if (!codeBlock->checkIfOptimizationThresholdReached()) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("counter = ", codeBlock->jitExecuteCounter())); + codeBlock->updateAllPredictions(); + dataLogLnIf(Options::verboseOSR(), "Choosing not to optimize ", *codeBlock, " yet, because the threshold hasn't been reached."); + return encodeResult(nullptr, nullptr); + } + + if (UNLIKELY(vm.terminationInProgress())) { + // If termination of the current stack of execution is in progress, + // then we need to hold off on optimized compiles so that termination + // checks will be called, and we can unwind out of the current stack. + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("Terminating current execution")); + updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); + return encodeResult(nullptr, nullptr); + } + + Debugger* debugger = codeBlock->globalObject()->debugger(); + if (UNLIKELY(debugger && (debugger->isStepping() || codeBlock->baselineAlternative()->hasDebuggerRequests()))) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("debugger is stepping or has requests")); + updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); + return encodeResult(nullptr, nullptr); + } + + if (codeBlock->m_shouldAlwaysBeInlined) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should always be inlined")); + updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); + dataLogLnIf(Options::verboseOSR(), "Choosing not to optimize ", *codeBlock, " yet, because m_shouldAlwaysBeInlined == true."); + return encodeResult(nullptr, nullptr); + } + + // The call to JITWorklist::completeAllReadyPlansForVM() will complete all ready + // (i.e. compiled) code blocks. But if it completes ours, we also need to know + // what the result was so that we don't plow ahead and attempt OSR or immediate + // reoptimization. This will have already also set the appropriate JIT execution + // count threshold depending on what happened, so if the compilation was anything + // but successful we just want to return early. See the case for worklistState == + // JITWorklist::Compiled, below. + + // Note that we could have alternatively just called Worklist::compilationState() + // here, and if it returned Compiled, we could have then called + // completeAndScheduleOSR() below. But that would have meant that it could take + // longer for code blocks to be completed: they would only complete when *their* + // execution count trigger fired; but that could take a while since the firing is + // racy. It could also mean that code blocks that never run again after being + // compiled would sit on the worklist until next GC. That's fine, but it's + // probably a waste of memory. Our goal here is to complete code blocks as soon as + // possible in order to minimize the chances of us executing baseline code after + // optimized code is already available. + JITWorklist::State worklistState = JITWorklist::ensureGlobalWorklist().completeAllReadyPlansForVM( + vm, JITCompilationKey(codeBlock, JITCompilationMode::DFG)); + + if (worklistState == JITWorklist::Compiling) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiling")); + // We cannot be in the process of asynchronous compilation and also have an optimized + // replacement. + RELEASE_ASSERT(!codeBlock->hasOptimizedReplacement()); + codeBlock->setOptimizationThresholdBasedOnCompilationResult(CompilationDeferred); + return encodeResult(nullptr, nullptr); + } + + if (worklistState == JITWorklist::Compiled) { + // If we don't have an optimized replacement but we did just get compiled, then + // the compilation failed or was invalidated, in which case the execution count + // thresholds have already been set appropriately by + // CodeBlock::setOptimizationThresholdBasedOnCompilationResult() and we have + // nothing left to do. + if (!codeBlock->hasOptimizedReplacement()) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiled and failed")); + codeBlock->updateAllPredictions(); + dataLogLnIf(Options::verboseOSR(), "Code block ", *codeBlock, " was compiled but it doesn't have an optimized replacement."); + return encodeResult(nullptr, nullptr); + } + } else if (codeBlock->hasOptimizedReplacement()) { + CodeBlock* replacement = codeBlock->replacement(); + dataLogLnIf(Options::verboseOSR(), "Considering OSR ", codeBlock, " -> ", replacement, "."); + // If we have an optimized replacement, then it must be the case that we entered + // cti_optimize from a loop. That's because if there's an optimized replacement, + // then all calls to this function will be relinked to the replacement and so + // the prologue OSR will never fire. + + // This is an interesting threshold check. Consider that a function OSR exits + // in the middle of a loop, while having a relatively low exit count. The exit + // will reset the execution counter to some target threshold, meaning that this + // code won't be reached until that loop heats up for >=1000 executions. But then + // we do a second check here, to see if we should either reoptimize, or just + // attempt OSR entry. Hence it might even be correct for + // shouldReoptimizeFromLoopNow() to always return true. But we make it do some + // additional checking anyway, to reduce the amount of recompilation thrashing. + if (replacement->shouldReoptimizeFromLoopNow()) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize from loop now")); + dataLogLnIf(Options::verboseOSR(), + "Triggering reoptimization of ", codeBlock, + "(", replacement, ") (in loop)."); + replacement->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTrigger, CountReoptimization); + return encodeResult(nullptr, nullptr); + } + } else { + if (!codeBlock->shouldOptimizeNow()) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("insufficient profiling")); + dataLogLnIf(Options::verboseOSR(), + "Delaying optimization for ", *codeBlock, + " because of insufficient profiling."); + return encodeResult(nullptr, nullptr); + } + + dataLogLnIf(Options::verboseOSR(), "Triggering optimized compilation of ", *codeBlock); + + unsigned numVarsWithValues = 0; + if (bytecodeIndex) + numVarsWithValues = codeBlock->numCalleeLocals(); + + Operands> mustHandleValues(codeBlock->numParameters(), numVarsWithValues, 0); + int localsUsedForCalleeSaves = static_cast(CodeBlock::llintBaselineCalleeSaveSpaceAsVirtualRegisters()); + for (size_t i = 0; i < mustHandleValues.size(); ++i) { + Operand operand = mustHandleValues.operandForIndex(i); + + if (operand.isLocal() && operand.toLocal() < localsUsedForCalleeSaves) + continue; + mustHandleValues[i] = callFrame->uncheckedR(operand.virtualRegister()).jsValue(); + } + + CodeBlock* replacementCodeBlock = codeBlock->newReplacement(); + CompilationResult result = DFG::compile( + vm, replacementCodeBlock, nullptr, JITCompilationMode::DFG, bytecodeIndex, + mustHandleValues, JITToDFGDeferredCompilationCallback::create()); + + if (result != CompilationSuccessful) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compilation failed")); + return encodeResult(nullptr, nullptr); + } + } + + CodeBlock* optimizedCodeBlock = codeBlock->replacement(); + ASSERT(optimizedCodeBlock && JITCode::isOptimizingJIT(optimizedCodeBlock->jitType())); + + if (void* dataBuffer = DFG::prepareOSREntry(vm, callFrame, optimizedCodeBlock, bytecodeIndex)) { + CODEBLOCK_LOG_EVENT(optimizedCodeBlock, "osrEntry", ("at bc#", bytecodeIndex)); + dataLogLnIf(Options::verboseOSR(), "Performing OSR ", codeBlock, " -> ", optimizedCodeBlock); + + codeBlock->optimizeSoon(); + codeBlock->unlinkedCodeBlock()->setDidOptimize(TriState::True); + void* targetPC = untagCodePtr(vm.getCTIStub(DFG::osrEntryThunkGenerator).code().executableAddress()); + targetPC = tagCodePtrWithStackPointerForJITCall(targetPC, callFrame); + return encodeResult(targetPC, dataBuffer); + } + + dataLogLnIf(Options::verboseOSR(), + "Optimizing ", codeBlock, " -> ", codeBlock->replacement(), + " succeeded, OSR failed, after a delay of ", + codeBlock->optimizationDelayCounter()); + + // Count the OSR failure as a speculation failure. If this happens a lot, then + // reoptimize. + optimizedCodeBlock->countOSRExit(); + + // We are a lot more conservative about triggering reoptimization after OSR failure than + // before it. If we enter the optimize_from_loop trigger with a bucket full of fail + // already, then we really would like to reoptimize immediately. But this case covers + // something else: there weren't many (or any) speculation failures before, but we just + // failed to enter the speculative code because some variable had the wrong value or + // because the OSR code decided for any spurious reason that it did not want to OSR + // right now. So, we only trigger reoptimization only upon the more conservative (non-loop) + // reoptimization trigger. + if (optimizedCodeBlock->shouldReoptimizeNow()) { + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize now")); + dataLogLnIf(Options::verboseOSR(), + "Triggering reoptimization of ", codeBlock, " -> ", + codeBlock->replacement(), " (after OSR fail)."); + optimizedCodeBlock->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail, CountReoptimization); + return encodeResult(nullptr, nullptr); + } + + // OSR failed this time, but it might succeed next time! Let the code run a bit + // longer and then try again. + codeBlock->optimizeAfterWarmUp(); + + CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("OSR failed")); + return encodeResult(nullptr, nullptr); +} + +JSC_DEFINE_JIT_OPERATION(operationTryOSREnterAtCatch, char*, (VM* vmPointer, uint32_t bytecodeIndexBits)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + BytecodeIndex bytecodeIndex = BytecodeIndex::fromBits(bytecodeIndexBits); + + CodeBlock* codeBlock = callFrame->codeBlock(); + CodeBlock* optimizedReplacement = codeBlock->replacement(); + if (UNLIKELY(!optimizedReplacement)) + return nullptr; + + switch (optimizedReplacement->jitType()) { + case JITType::DFGJIT: + case JITType::FTLJIT: { + MacroAssemblerCodePtr entry = DFG::prepareCatchOSREntry(vm, callFrame, codeBlock, optimizedReplacement, bytecodeIndex); + return entry.executableAddress(); + } + default: + break; + } + return nullptr; +} + +JSC_DEFINE_JIT_OPERATION(operationTryOSREnterAtCatchAndValueProfile, char*, (VM* vmPointer, uint32_t bytecodeIndexBits)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + BytecodeIndex bytecodeIndex = BytecodeIndex::fromBits(bytecodeIndexBits); + + CodeBlock* codeBlock = callFrame->codeBlock(); + CodeBlock* optimizedReplacement = codeBlock->replacement(); + if (UNLIKELY(!optimizedReplacement)) + return nullptr; + + switch (optimizedReplacement->jitType()) { + case JITType::DFGJIT: + case JITType::FTLJIT: { + MacroAssemblerCodePtr entry = DFG::prepareCatchOSREntry(vm, callFrame, codeBlock, optimizedReplacement, bytecodeIndex); + return entry.executableAddress(); + } + default: + break; + } + + codeBlock->ensureCatchLivenessIsComputedForBytecodeIndex(bytecodeIndex); + auto bytecode = codeBlock->instructions().at(bytecodeIndex)->as(); + auto& metadata = bytecode.metadata(codeBlock); + metadata.m_buffer->forEach([&] (ValueProfileAndVirtualRegister& profile) { + profile.m_buckets[0] = JSValue::encode(callFrame->uncheckedR(profile.m_operand).jsValue()); + }); + + return nullptr; +} + +#endif + +enum class AccessorType { + Getter, + Setter +}; + +static void putAccessorByVal(JSGlobalObject* globalObject, JSObject* base, JSValue subscript, int32_t attribute, JSObject* accessor, AccessorType accessorType) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + auto propertyKey = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, void()); + + scope.release(); + if (accessorType == AccessorType::Getter) + base->putGetter(globalObject, propertyKey, accessor, attribute); + else + base->putSetter(globalObject, propertyKey, accessor, attribute); +} + +JSC_DEFINE_JIT_OPERATION(operationPutGetterById, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* getter)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(object && object->isObject()); + JSObject* baseObj = object->getObject(); + + ASSERT(getter->isObject()); + baseObj->putGetter(globalObject, uid, getter, options); +} + +JSC_DEFINE_JIT_OPERATION(operationPutSetterById, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* setter)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(object && object->isObject()); + JSObject* baseObj = object->getObject(); + + ASSERT(setter->isObject()); + baseObj->putSetter(globalObject, uid, setter, options); +} + +JSC_DEFINE_JIT_OPERATION(operationPutGetterByVal, void, (JSGlobalObject* globalObject, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* getter)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + putAccessorByVal(globalObject, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(getter), AccessorType::Getter); +} + +JSC_DEFINE_JIT_OPERATION(operationPutSetterByVal, void, (JSGlobalObject* globalObject, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* setter)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + putAccessorByVal(globalObject, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(setter), AccessorType::Setter); +} + +#if USE(JSVALUE64) +JSC_DEFINE_JIT_OPERATION(operationPutGetterSetter, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t attribute, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(object && object->isObject()); + JSObject* baseObject = asObject(object); + + JSValue getter = JSValue::decode(encodedGetterValue); + JSValue setter = JSValue::decode(encodedSetterValue); + ASSERT(getter.isObject() || setter.isObject()); + GetterSetter* accessor = GetterSetter::create(vm, globalObject, getter, setter); + CommonSlowPaths::putDirectAccessorWithReify(vm, globalObject, baseObject, uid, accessor, attribute); +} + +#else +JSC_DEFINE_JIT_OPERATION(operationPutGetterSetter, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t attribute, JSCell* getterCell, JSCell* setterCell)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(object && object->isObject()); + JSObject* baseObject = asObject(object); + + ASSERT(getterCell || setterCell); + JSObject* getter = getterCell ? getterCell->getObject() : nullptr; + JSObject* setter = setterCell ? setterCell->getObject() : nullptr; + GetterSetter* accessor = GetterSetter::create(vm, globalObject, getter, setter); + CommonSlowPaths::putDirectAccessorWithReify(vm, globalObject, baseObject, uid, accessor, attribute); +} +#endif + +JSC_DEFINE_JIT_OPERATION(operationInstanceOfCustom, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue value = JSValue::decode(encodedValue); + JSValue hasInstanceValue = JSValue::decode(encodedHasInstance); + + if (constructor->hasInstance(globalObject, value, hasInstanceValue)) + return 1; + return 0; +} + +ALWAYS_INLINE static JSValue getByVal(JSGlobalObject* globalObject, CallFrame* callFrame, ArrayProfile* arrayProfile, JSValue baseValue, JSValue subscript) +{ + UNUSED_PARAM(callFrame); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (LIKELY(baseValue.isCell() && subscript.isString())) { + Structure& structure = *baseValue.asCell()->structure(vm); + if (JSCell::canUseFastGetOwnProperty(structure)) { + RefPtr existingAtomString = asString(subscript)->toExistingAtomString(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + if (existingAtomString) { + if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.get())) { + ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); + return result; + } + } + } + } + + if (std::optional index = subscript.tryGetAsUint32Index()) { + uint32_t i = *index; + if (isJSString(baseValue)) { + if (asString(baseValue)->canGetIndex(i)) + RELEASE_AND_RETURN(scope, asString(baseValue)->getIndex(globalObject, i)); + if (arrayProfile) + arrayProfile->setOutOfBounds(); + } else if (baseValue.isObject()) { + JSObject* object = asObject(baseValue); + if (JSValue result = object->tryGetIndexQuickly(i, arrayProfile)) + return result; + + bool skipMarkingOutOfBounds = false; + + if (object->indexingType() == ArrayWithContiguous + && static_cast(i) < object->butterfly()->publicLength()) { + // FIXME: expand this to ArrayStorage, Int32, and maybe Double: + // https://bugs.webkit.org/show_bug.cgi?id=182940 + auto* globalObject = object->globalObject(vm); + skipMarkingOutOfBounds = globalObject->isOriginalArrayStructure(object->structure(vm)) && globalObject->arrayPrototypeChainIsSane(); + } + + if (!skipMarkingOutOfBounds && !CommonSlowPaths::canAccessArgumentIndexQuickly(*object, i)) { + // FIXME: This will make us think that in-bounds typed array accesses are actually + // out-of-bounds. + // https://bugs.webkit.org/show_bug.cgi?id=149886 + if (arrayProfile) + arrayProfile->setOutOfBounds(); + } + } + + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, i)); + } else if (subscript.isNumber() && baseValue.isCell() && arrayProfile) + arrayProfile->setOutOfBounds(); + + baseValue.requireObjectCoercible(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + auto property = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + + ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); + RELEASE_AND_RETURN(scope, baseValue.get(globalObject, property)); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByValGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* profile, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue baseValue = JSValue::decode(encodedBase); + JSValue subscript = JSValue::decode(encodedSubscript); + + stubInfo->tookSlowPath = true; + + return JSValue::encode(getByVal(globalObject, callFrame, profile, baseValue, subscript)); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByValOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* profile, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBase); + JSValue subscript = JSValue::decode(encodedSubscript); + + CodeBlock* codeBlock = callFrame->codeBlock(); + + if (baseValue.isCell() && subscript.isInt32()) { + Structure* structure = baseValue.asCell()->structure(vm); + if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { + if (profile) { + ConcurrentJSLocker locker(codeBlock->m_lock); + profile->computeUpdatedPrediction(locker, codeBlock, structure); + } + repatchArrayGetByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo); + } + } + + if (baseValue.isCell() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { + const Identifier propertyName = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + if (subscript.isSymbol() || !parseIndex(propertyName)) { + scope.release(); + return JSValue::encode(baseValue.getPropertySlot(globalObject, propertyName, [&] (bool found, PropertySlot& slot) -> JSValue { + LOG_IC((ICEvent::OperationGetByValOptimize, baseValue.classInfoOrNull(vm), propertyName, baseValue == slot.slotBase())); + + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByVal); + return found ? slot.getValue(globalObject, propertyName) : jsUndefined(); + })); + } + } + + RELEASE_AND_RETURN(scope, JSValue::encode(getByVal(globalObject, callFrame, profile, baseValue, subscript))); +} + +JSC_DEFINE_JIT_OPERATION(operationGetByVal, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBase); + JSValue property = JSValue::decode(encodedProperty); + + if (LIKELY(baseValue.isCell())) { + JSCell* base = baseValue.asCell(); + + if (std::optional index = property.tryGetAsUint32Index()) + RELEASE_AND_RETURN(scope, getByValWithIndex(globalObject, base, *index)); + + if (property.isString()) { + Structure& structure = *base->structure(vm); + if (JSCell::canUseFastGetOwnProperty(structure)) { + RefPtr existingAtomString = asString(property)->toExistingAtomString(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + if (existingAtomString) { + if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.get())) + return JSValue::encode(result); + } + } + } + } + + baseValue.requireObjectCoercible(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + auto propertyName = property.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + RELEASE_AND_RETURN(scope, JSValue::encode(baseValue.get(globalObject, propertyName))); +} + +ALWAYS_INLINE static JSValue getPrivateName(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, JSValue fieldNameValue) +{ + UNUSED_PARAM(callFrame); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSObject* base = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + auto fieldName = fieldNameValue.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + + PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); + base->getPrivateField(globalObject, fieldName, slot); + RETURN_IF_EXCEPTION(scope, JSValue()); + + return slot.getValue(globalObject, fieldName); +} + +ALWAYS_INLINE static JSValue getPrivateName(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, Identifier fieldName) +{ + ASSERT(fieldName.isPrivateName()); + UNUSED_PARAM(callFrame); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + baseValue.requireObjectCoercible(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + + JSObject* base = baseValue.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue()); + + PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); + base->getPrivateField(globalObject, fieldName, slot); + RETURN_IF_EXCEPTION(scope, JSValue()); + + return slot.getValue(globalObject, fieldName); +} + +JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(encodedBase); + JSValue fieldNameValue = JSValue::decode(encodedFieldName); + ASSERT(CacheableIdentifier::isCacheableIdentifierCell(fieldNameValue)); + + CodeBlock* codeBlock = callFrame->codeBlock(); + + if (baseValue.isObject()) { + const Identifier fieldName = fieldNameValue.toPropertyKey(globalObject); + EXCEPTION_ASSERT(!scope.exception() || vm.hasPendingTerminationException()); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + ASSERT(fieldName.isSymbol()); + + JSObject* base = jsCast(baseValue.asCell()); + + PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); + base->getPrivateField(globalObject, fieldName, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + LOG_IC((ICEvent::OperationGetPrivateNameOptimize, baseValue.classInfoOrNull(vm), fieldName, true)); + + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(fieldNameValue.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::PrivateName); + return JSValue::encode(slot.getValue(globalObject, fieldName)); + } + + RELEASE_AND_RETURN(scope, JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldNameValue))); +} + +JSC_DEFINE_JIT_OPERATION(operationGetPrivateName, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(encodedBase); + JSValue fieldNameValue = JSValue::decode(encodedFieldName); + + if (stubInfo) + stubInfo->tookSlowPath = true; + + return JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldNameValue)); +} + +JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + stubInfo->tookSlowPath = true; + + JSValue baseValue = JSValue::decode(base); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier fieldName = Identifier::fromUid(vm, identifier.uid()); + + JSValue result = getPrivateName(globalObject, callFrame, baseValue, fieldName); + + LOG_IC((ICEvent::OperationGetPrivateNameById, baseValue.classInfoOrNull(vm), fieldName, true)); + + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue baseValue = JSValue::decode(base); + auto fieldName = Identifier::fromUid(vm, identifier.uid()); + + if (baseValue.isObject()) { + JSObject* base = jsCast(baseValue.asCell()); + + PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); + base->getPrivateField(globalObject, fieldName, slot); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + LOG_IC((ICEvent::OperationGetPrivateNameOptimize, baseValue.classInfoOrNull(vm), fieldName, true)); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::PrivateNameById); + return JSValue::encode(slot.getValue(globalObject, fieldName)); + } + + RELEASE_AND_RETURN(scope, JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldName))); +} + +JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) +{ + SuperSamplerScope superSamplerScope(false); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue baseValue = JSValue::decode(base); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier fieldName = Identifier::fromUid(vm, identifier.uid()); + + JSValue result = getPrivateName(globalObject, callFrame, baseValue, fieldName); + + LOG_IC((ICEvent::OperationGetPrivateNameByIdGeneric, baseValue.classInfoOrNull(vm), fieldName, true)); + + return JSValue::encode(result); +} + +static bool deleteById(JSGlobalObject* globalObject, VM& vm, DeletePropertySlot& slot, JSValue base, const Identifier& ident, ECMAMode ecmaMode) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + + JSObject* baseObj = base.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, false); + if (!baseObj) + return false; + bool couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, globalObject, ident, slot); + RETURN_IF_EXCEPTION(scope, false); + if (!couldDelete && ecmaMode.isStrict()) + throwTypeError(globalObject, scope, UnableToDeletePropertyError); + return couldDelete; +} + +JSC_DEFINE_JIT_OPERATION(operationDeleteByIdOptimize, size_t, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier, ECMAMode ecmaMode)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue baseValue = JSValue::decode(encodedBase); + + DeletePropertySlot slot; + Structure* oldStructure = baseValue.structureOrNull(vm); + + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + + bool result = deleteById(globalObject, vm, slot, baseValue, ident, ecmaMode); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + if (baseValue.isObject()) { + if (!parseIndex(ident)) { + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchDeleteBy(globalObject, codeBlock, slot, baseValue, oldStructure, identifier, *stubInfo, DelByKind::ById, ecmaMode); + } + } + + return result; +} + +JSC_DEFINE_JIT_OPERATION(operationDeleteByIdGeneric, size_t, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier, ECMAMode ecmaMode)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); + Identifier ident = Identifier::fromUid(vm, identifier.uid()); + DeletePropertySlot slot; + return deleteById(globalObject, vm, slot, JSValue::decode(encodedBase), ident, ecmaMode); +} + +static bool deleteByVal(JSGlobalObject* globalObject, VM& vm, DeletePropertySlot& slot, JSValue base, JSValue key, ECMAMode ecmaMode) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + + JSObject* baseObj = base.toObject(globalObject); + RETURN_IF_EXCEPTION(scope, false); + if (!baseObj) + return false; + + bool couldDelete; + uint32_t index; + if (key.getUInt32(index)) + couldDelete = baseObj->methodTable(vm)->deletePropertyByIndex(baseObj, globalObject, index); + else { + Identifier property = key.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, false); + couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, globalObject, property, slot); + } + RETURN_IF_EXCEPTION(scope, false); + if (!couldDelete && ecmaMode.isStrict()) + throwTypeError(globalObject, scope, UnableToDeletePropertyError); + return couldDelete; +} + +JSC_DEFINE_JIT_OPERATION(operationDeleteByValOptimize, size_t, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ECMAMode ecmaMode)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue baseValue = JSValue::decode(encodedBase); + JSValue subscript = JSValue::decode(encodedSubscript); + + DeletePropertySlot slot; + Structure* oldStructure = baseValue.structureOrNull(vm); + + bool result = deleteByVal(globalObject, vm, slot, baseValue, subscript, ecmaMode); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + if (baseValue.isObject() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { + const Identifier propertyName = subscript.toPropertyKey(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + + if (subscript.isSymbol() || !parseIndex(propertyName)) { + CodeBlock* codeBlock = callFrame->codeBlock(); + CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); + if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(vm), identifier)) + repatchDeleteBy(globalObject, codeBlock, slot, baseValue, oldStructure, identifier, *stubInfo, DelByKind::ByVal, ecmaMode); + } + } + + return result; +} + +JSC_DEFINE_JIT_OPERATION(operationDeleteByValGeneric, size_t, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ECMAMode ecmaMode)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + DeletePropertySlot slot; + return deleteByVal(globalObject, vm, slot, JSValue::decode(encodedBase), JSValue::decode(encodedSubscript), ecmaMode); +} + +JSC_DEFINE_JIT_OPERATION(operationPushWithScope, JSCell*, (JSGlobalObject* globalObject, JSCell* currentScopeCell, EncodedJSValue objectValue)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSObject* object = JSValue::decode(objectValue).toObject(globalObject); + RETURN_IF_EXCEPTION(scope, nullptr); + + JSScope* currentScope = jsCast(currentScopeCell); + + return JSWithScope::create(vm, globalObject, currentScope, object); +} + +JSC_DEFINE_JIT_OPERATION(operationPushWithScopeObject, JSCell*, (JSGlobalObject* globalObject, JSCell* currentScopeCell, JSObject* object)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSScope* currentScope = jsCast(currentScopeCell); + return JSWithScope::create(vm, globalObject, currentScope, object); +} + +JSC_DEFINE_JIT_OPERATION(operationInstanceOfGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue value = JSValue::decode(encodedValue); + JSValue proto = JSValue::decode(encodedProto); + + stubInfo->tookSlowPath = true; + + bool result = JSObject::defaultHasInstance(globalObject, value, proto); + return JSValue::encode(jsBoolean(result)); +} + +JSC_DEFINE_JIT_OPERATION(operationInstanceOfOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue value = JSValue::decode(encodedValue); + JSValue proto = JSValue::decode(encodedProto); + + bool result = JSObject::defaultHasInstance(globalObject, value, proto); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (stubInfo->considerCachingGeneric(vm, codeBlock, value.structureOrNull(vm))) + repatchInstanceOf(globalObject, codeBlock, value, proto, *stubInfo, result); + + return JSValue::encode(jsBoolean(result)); +} + +JSC_DEFINE_JIT_OPERATION(operationSizeFrameForForwardArguments, int32_t, (JSGlobalObject* globalObject, EncodedJSValue, int32_t numUsedStackSlots, int32_t)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return sizeFrameForForwardArguments(globalObject, callFrame, vm, numUsedStackSlots); +} + +JSC_DEFINE_JIT_OPERATION(operationSizeFrameForVarargs, int32_t, (JSGlobalObject* globalObject, EncodedJSValue encodedArguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue arguments = JSValue::decode(encodedArguments); + return sizeFrameForVarargs(globalObject, callFrame, vm, arguments, numUsedStackSlots, firstVarArgOffset); +} + +JSC_DEFINE_JIT_OPERATION(operationSetupForwardArgumentsFrame, CallFrame*, (JSGlobalObject* globalObject, CallFrame* newCallFrame, EncodedJSValue, int32_t, int32_t length)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + setupForwardArgumentsFrame(globalObject, callFrame, newCallFrame, length); + return newCallFrame; +} + +JSC_DEFINE_JIT_OPERATION(operationSetupVarargsFrame, CallFrame*, (JSGlobalObject* globalObject, CallFrame* newCallFrame, EncodedJSValue encodedArguments, int32_t firstVarArgOffset, int32_t length)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue arguments = JSValue::decode(encodedArguments); + setupVarargsFrame(globalObject, callFrame, newCallFrame, arguments, firstVarArgOffset, length); + return newCallFrame; +} + +JSC_DEFINE_JIT_OPERATION(operationSwitchCharWithUnknownKeyType, char*, (JSGlobalObject* globalObject, EncodedJSValue encodedKey, size_t tableIndex, int32_t min)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSValue key = JSValue::decode(encodedKey); + CodeBlock* codeBlock = callFrame->codeBlock(); + + const SimpleJumpTable& linkedTable = codeBlock->baselineSwitchJumpTable(tableIndex); + ASSERT(codeBlock->unlinkedSwitchJumpTable(tableIndex).m_min == min); + void* result = linkedTable.m_ctiDefault.executableAddress(); + + if (key.isString()) { + JSString* string = asString(key); + if (string->length() == 1) { + String value = string->value(globalObject); + RETURN_IF_EXCEPTION(throwScope, nullptr); + result = linkedTable.ctiForValue(min, value[0]).executableAddress(); + } + } + + assertIsTaggedWith(result); + return reinterpret_cast(result); +} + +JSC_DEFINE_JIT_OPERATION(operationSwitchImmWithUnknownKeyType, char*, (VM* vmPointer, EncodedJSValue encodedKey, size_t tableIndex, int32_t min)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue key = JSValue::decode(encodedKey); + CodeBlock* codeBlock = callFrame->codeBlock(); + + const SimpleJumpTable& linkedTable = codeBlock->baselineSwitchJumpTable(tableIndex); + ASSERT(codeBlock->unlinkedSwitchJumpTable(tableIndex).m_min == min); + void* result; + if (key.isInt32()) + result = linkedTable.ctiForValue(min, key.asInt32()).executableAddress(); + else if (key.isDouble() && key.asDouble() == static_cast(key.asDouble())) + result = linkedTable.ctiForValue(min, static_cast(key.asDouble())).executableAddress(); + else + result = linkedTable.m_ctiDefault.executableAddress(); + assertIsTaggedWith(result); + return reinterpret_cast(result); +} + +JSC_DEFINE_JIT_OPERATION(operationSwitchStringWithUnknownKeyType, char*, (JSGlobalObject* globalObject, EncodedJSValue encodedKey, size_t tableIndex)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + JSValue key = JSValue::decode(encodedKey); + CodeBlock* codeBlock = callFrame->codeBlock(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + void* result; + const StringJumpTable& linkedTable = codeBlock->baselineStringSwitchJumpTable(tableIndex); + + if (key.isString()) { + StringImpl* value = asString(key)->value(globalObject).impl(); + + RETURN_IF_EXCEPTION(throwScope, nullptr); + + const UnlinkedStringJumpTable& unlinkedTable = codeBlock->unlinkedStringSwitchJumpTable(tableIndex); + result = linkedTable.ctiForValue(unlinkedTable, value).executableAddress(); + } else + result = linkedTable.ctiDefault().executableAddress(); + + assertIsTaggedWith(result); + return reinterpret_cast(result); +} + +JSC_DEFINE_JIT_OPERATION(operationResolveScopeForBaseline, EncodedJSValue, (JSGlobalObject* globalObject, const Instruction* pc)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + CodeBlock* codeBlock = callFrame->codeBlock(); + + auto bytecode = pc->as(); + const Identifier& ident = codeBlock->identifier(bytecode.m_var); + JSScope* scope = callFrame->uncheckedR(bytecode.m_scope).Register::scope(); + JSObject* resolvedScope = JSScope::resolve(globalObject, scope, ident); + // Proxy can throw an error here, e.g. Proxy in with statement's @unscopables. + RETURN_IF_EXCEPTION(throwScope, { }); + + auto& metadata = bytecode.metadata(codeBlock); + ResolveType resolveType = metadata.m_resolveType; + + // ModuleVar does not keep the scope register value alive in DFG. + ASSERT(resolveType != ModuleVar); + + switch (resolveType) { + case GlobalProperty: + case GlobalPropertyWithVarInjectionChecks: + case UnresolvedProperty: + case UnresolvedPropertyWithVarInjectionChecks: { + if (resolvedScope->isGlobalObject()) { + JSGlobalObject* globalObject = jsCast(resolvedScope); + bool hasProperty = globalObject->hasProperty(globalObject, ident); + RETURN_IF_EXCEPTION(throwScope, { }); + if (hasProperty) { + ConcurrentJSLocker locker(codeBlock->m_lock); + metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalPropertyWithVarInjectionChecks : GlobalProperty; + metadata.m_globalObject.set(vm, codeBlock, globalObject); + metadata.m_globalLexicalBindingEpoch = globalObject->globalLexicalBindingEpoch(); + } + } else if (resolvedScope->isGlobalLexicalEnvironment()) { + JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast(resolvedScope); + ConcurrentJSLocker locker(codeBlock->m_lock); + metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; + metadata.m_globalLexicalEnvironment.set(vm, codeBlock, globalLexicalEnvironment); + } + break; + } + default: + break; + } + + return JSValue::encode(resolvedScope); +} + +JSC_DEFINE_JIT_OPERATION(operationGetFromScope, EncodedJSValue, (JSGlobalObject* globalObject, const Instruction* pc)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + CodeBlock* codeBlock = callFrame->codeBlock(); + + auto bytecode = pc->as(); + const Identifier& ident = codeBlock->identifier(bytecode.m_var); + JSObject* scope = jsCast(callFrame->uncheckedR(bytecode.m_scope).jsValue()); + GetPutInfo& getPutInfo = bytecode.metadata(codeBlock).m_getPutInfo; + + // ModuleVar is always converted to ClosureVar for get_from_scope. + ASSERT(getPutInfo.resolveType() != ModuleVar); + + RELEASE_AND_RETURN(throwScope, JSValue::encode(scope->getPropertySlot(globalObject, ident, [&] (bool found, PropertySlot& slot) -> JSValue { + if (!found) { + if (getPutInfo.resolveMode() == ThrowIfNotFound) + throwException(globalObject, throwScope, createUndefinedVariableError(globalObject, ident)); + return jsUndefined(); + } + + JSValue result = JSValue(); + if (scope->isGlobalLexicalEnvironment()) { + // When we can't statically prove we need a TDZ check, we must perform the check on the slow path. + result = slot.getValue(globalObject, ident); + if (result == jsTDZValue()) { + throwException(globalObject, throwScope, createTDZError(globalObject)); + return jsUndefined(); + } + } + + CommonSlowPaths::tryCacheGetFromScopeGlobal(globalObject, codeBlock, vm, bytecode, scope, slot, ident); + + if (!result) + return slot.getValue(globalObject, ident); + return result; + }))); +} + +JSC_DEFINE_JIT_OPERATION(operationPutToScope, void, (JSGlobalObject* globalObject, const Instruction* pc)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + CodeBlock* codeBlock = callFrame->codeBlock(); + auto bytecode = pc->as(); + auto& metadata = bytecode.metadata(codeBlock); + + const Identifier& ident = codeBlock->identifier(bytecode.m_var); + JSObject* scope = jsCast(callFrame->uncheckedR(bytecode.m_scope).jsValue()); + JSValue value = callFrame->r(bytecode.m_value).jsValue(); + GetPutInfo& getPutInfo = metadata.m_getPutInfo; + + // ModuleVar does not keep the scope register value alive in DFG. + ASSERT(getPutInfo.resolveType() != ModuleVar); + + if (getPutInfo.resolveType() == ResolvedClosureVar) { + JSLexicalEnvironment* environment = jsCast(scope); + environment->variableAt(ScopeOffset(metadata.m_operand)).set(vm, environment, value); + if (WatchpointSet* set = metadata.m_watchpointSet) + set->touch(vm, "Executed op_put_scope"); + return; + } + + bool hasProperty = scope->hasProperty(globalObject, ident); + RETURN_IF_EXCEPTION(throwScope, void()); + if (hasProperty + && scope->isGlobalLexicalEnvironment() + && !isInitialization(getPutInfo.initializationMode())) { + // When we can't statically prove we need a TDZ check, we must perform the check on the slow path. + PropertySlot slot(scope, PropertySlot::InternalMethodType::Get); + JSGlobalLexicalEnvironment::getOwnPropertySlot(scope, globalObject, ident, slot); + if (slot.getValue(globalObject, ident) == jsTDZValue()) { + throwException(globalObject, throwScope, createTDZError(globalObject)); + return; + } + } + + if (getPutInfo.resolveMode() == ThrowIfNotFound && !hasProperty) { + throwException(globalObject, throwScope, createUndefinedVariableError(globalObject, ident)); + return; + } + + PutPropertySlot slot(scope, getPutInfo.ecmaMode().isStrict(), PutPropertySlot::UnknownContext, isInitialization(getPutInfo.initializationMode())); + scope->methodTable(vm)->put(scope, globalObject, ident, value, slot); + + RETURN_IF_EXCEPTION(throwScope, void()); + + CommonSlowPaths::tryCachePutToScopeGlobal(globalObject, codeBlock, bytecode, scope, slot, ident); +} + +JSC_DEFINE_JIT_OPERATION(operationThrow, void, (JSGlobalObject* globalObject, EncodedJSValue encodedExceptionValue)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue exceptionValue = JSValue::decode(encodedExceptionValue); + throwException(globalObject, scope, exceptionValue); + + // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForCatch + genericUnwind(vm, callFrame); +} + +JSC_DEFINE_JIT_OPERATION(operationReallocateButterflyToHavePropertyStorageWithInitialCapacity, char*, (VM* vmPointer, JSObject* object)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(!object->structure(vm)->outOfLineCapacity()); + Butterfly* result = object->allocateMoreOutOfLineStorage(vm, 0, initialOutOfLineCapacity); + object->nukeStructureAndSetButterfly(vm, object->structureID(), result); + return reinterpret_cast(result); +} + +JSC_DEFINE_JIT_OPERATION(operationReallocateButterflyToGrowPropertyStorage, char*, (VM* vmPointer, JSObject* object, size_t newSize)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + Butterfly* result = object->allocateMoreOutOfLineStorage(vm, object->structure(vm)->outOfLineCapacity(), newSize); + object->nukeStructureAndSetButterfly(vm, object->structureID(), result); + return reinterpret_cast(result); +} + +JSC_DEFINE_JIT_OPERATION(operationOSRWriteBarrier, void, (VM* vmPointer, JSCell* cell)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + vm.writeBarrier(cell); +} + +JSC_DEFINE_JIT_OPERATION(operationWriteBarrierSlowPath, void, (VM* vmPointer, JSCell* cell)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + vm.writeBarrierSlowPath(cell); +} + +JSC_DEFINE_JIT_OPERATION(operationLookupExceptionHandler, void, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + genericUnwind(vm, callFrame); + ASSERT(vm.targetMachinePCForThrow); +} + +JSC_DEFINE_JIT_OPERATION(operationLookupExceptionHandlerFromCallerFrame, void, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + ASSERT(callFrame->isStackOverflowFrame()); + ASSERT(jsCast(vm.exceptionForInspection()->value().asCell())->isStackOverflowError()); + genericUnwind(vm, callFrame); + ASSERT(vm.targetMachinePCForThrow); +} + +JSC_DEFINE_JIT_OPERATION(operationVMHandleException, void, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + genericUnwind(vm, callFrame); +} + +// This function "should" just take the JSGlobalObject*, but doing so would make it more difficult +// to call from exception check sites. So, unlike all of our other functions, we allow +// ourselves to play some gnarly ABI tricks just to simplify the calling convention. This is +// particularly safe here since this is never called on the critical path - it's only for +// testing. +JSC_DEFINE_JIT_OPERATION(operationExceptionFuzz, void, (JSGlobalObject* globalObject)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(scope); +#if COMPILER(GCC_COMPATIBLE) + void* returnPC = __builtin_return_address(0); + // FIXME (see rdar://72897291): Work around a Clang bug where __builtin_return_address() + // sometimes gives us a signed pointer, and sometimes does not. + returnPC = removeCodePtrTag(returnPC); + doExceptionFuzzing(globalObject, scope, "JITOperations", returnPC); +#endif // COMPILER(GCC_COMPATIBLE) +} + +JSC_DEFINE_JIT_OPERATION(operationExceptionFuzzWithCallFrame, void, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(scope); +#if COMPILER(GCC_COMPATIBLE) + void* returnPC = __builtin_return_address(0); + // FIXME (see rdar://72897291): Work around a Clang bug where __builtin_return_address() + // sometimes gives us a signed pointer, and sometimes does not. + returnPC = removeCodePtrTag(returnPC); + doExceptionFuzzing(callFrame->lexicalGlobalObject(vm), scope, "JITOperations", returnPC); +#endif // COMPILER(GCC_COMPATIBLE) +} + +JSC_DEFINE_JIT_OPERATION(operationValueAdd, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return JSValue::encode(jsAdd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2))); +} + +JSC_DEFINE_JIT_OPERATION(operationValueAddProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile* arithProfile)) +{ + ASSERT(arithProfile); + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return JSValue::encode(profiledAdd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2), *arithProfile)); +} + +JSC_DEFINE_JIT_OPERATION(operationValueAddProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + BinaryArithProfile* arithProfile = addIC->arithProfile(); + ASSERT(arithProfile); + arithProfile->observeLHSAndRHS(op1, op2); + auto nonOptimizeVariant = operationValueAddProfiledNoOptimize; + addIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + JSValue result = jsAdd(globalObject, op1, op2); + arithProfile->observeResult(result); + + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationValueAddProfiledNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + BinaryArithProfile* arithProfile = addIC->arithProfile(); + ASSERT(arithProfile); + return JSValue::encode(profiledAdd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2), *arithProfile)); +} + +JSC_DEFINE_JIT_OPERATION(operationValueAddOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + auto nonOptimizeVariant = operationValueAddNoOptimize; + if (BinaryArithProfile* arithProfile = addIC->arithProfile()) + arithProfile->observeLHSAndRHS(op1, op2); + addIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + return JSValue::encode(jsAdd(globalObject, op1, op2)); +} + +JSC_DEFINE_JIT_OPERATION(operationValueAddNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + JSValue result = jsAdd(globalObject, op1, op2); + + return JSValue::encode(result); +} + +ALWAYS_INLINE static EncodedJSValue unprofiledMul(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) +{ + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + return JSValue::encode(jsMul(globalObject, op1, op2)); +} + +ALWAYS_INLINE static EncodedJSValue profiledMul(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + if (shouldObserveLHSAndRHSTypes) + arithProfile.observeLHSAndRHS(op1, op2); + + JSValue result = jsMul(globalObject, op1, op2); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + arithProfile.observeResult(result); + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationValueMul, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return unprofiledMul(globalObject, encodedOp1, encodedOp2); +} + +JSC_DEFINE_JIT_OPERATION(operationValueMulNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return unprofiledMul(globalObject, encodedOp1, encodedOp2); +} + +JSC_DEFINE_JIT_OPERATION(operationValueMulOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + auto nonOptimizeVariant = operationValueMulNoOptimize; + if (BinaryArithProfile* arithProfile = mulIC->arithProfile()) + arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); + mulIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + return unprofiledMul(globalObject, encodedOp1, encodedOp2); +} + +JSC_DEFINE_JIT_OPERATION(operationValueMulProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile* arithProfile)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + ASSERT(arithProfile); + return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile); +} + +JSC_DEFINE_JIT_OPERATION(operationValueMulProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + BinaryArithProfile* arithProfile = mulIC->arithProfile(); + ASSERT(arithProfile); + arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); + auto nonOptimizeVariant = operationValueMulProfiledNoOptimize; + mulIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile, false); +} + +JSC_DEFINE_JIT_OPERATION(operationValueMulProfiledNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + BinaryArithProfile* arithProfile = mulIC->arithProfile(); + ASSERT(arithProfile); + return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile); +} + +// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt +JSC_DEFINE_JIT_OPERATION(operationArithNegate, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue operand = JSValue::decode(encodedOperand); + + JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + +#if USE(BIGINT32) + if (primValue.isBigInt32()) + RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()))); +#endif + if (primValue.isHeapBigInt()) + RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()))); + + double number = primValue.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(jsNumber(-number)); + +} + +// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt +JSC_DEFINE_JIT_OPERATION(operationArithNegateProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand, UnaryArithProfile* arithProfile)) +{ + ASSERT(arithProfile); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue operand = JSValue::decode(encodedOperand); + arithProfile->observeArg(operand); + + JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + +#if USE(BIGINT32) + if (primValue.isBigInt32()) { + JSValue result = JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()); + RETURN_IF_EXCEPTION(scope, { }); + arithProfile->observeResult(result); + return JSValue::encode(result); + } +#endif + if (primValue.isHeapBigInt()) { + JSValue result = JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()); + RETURN_IF_EXCEPTION(scope, { }); + arithProfile->observeResult(result); + return JSValue::encode(result); + } + + double number = primValue.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + JSValue result = jsNumber(-number); + arithProfile->observeResult(result); + return JSValue::encode(result); +} + +// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt +JSC_DEFINE_JIT_OPERATION(operationArithNegateProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand, JITNegIC* negIC)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue operand = JSValue::decode(encodedOperand); + + UnaryArithProfile* arithProfile = negIC->arithProfile(); + ASSERT(arithProfile); + arithProfile->observeArg(operand); + negIC->generateOutOfLine(callFrame->codeBlock(), operationArithNegateProfiled); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + +#if USE(BIGINT32) + if (primValue.isBigInt32()) { + JSValue result = JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()); + RETURN_IF_EXCEPTION(scope, { }); + arithProfile->observeResult(result); + return JSValue::encode(result); + } +#endif + if (primValue.isHeapBigInt()) { + JSValue result = JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()); + RETURN_IF_EXCEPTION(scope, { }); + arithProfile->observeResult(result); + return JSValue::encode(result); + } + + double number = primValue.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + JSValue result = jsNumber(-number); + arithProfile->observeResult(result); + return JSValue::encode(result); +} + +// FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt +JSC_DEFINE_JIT_OPERATION(operationArithNegateOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand, JITNegIC* negIC)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + JSValue operand = JSValue::decode(encodedOperand); + + if (UnaryArithProfile* arithProfile = negIC->arithProfile()) + arithProfile->observeArg(operand); + negIC->generateOutOfLine(callFrame->codeBlock(), operationArithNegate); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + +#if USE(BIGINT32) + // FIXME: why does this function profile the argument but not the result? + if (primValue.isBigInt32()) + RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()))); +#endif + if (primValue.isHeapBigInt()) + RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()))); + + double number = primValue.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(jsNumber(-number)); +} + +ALWAYS_INLINE static EncodedJSValue unprofiledSub(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) +{ + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + return JSValue::encode(jsSub(globalObject, op1, op2)); +} + +ALWAYS_INLINE static EncodedJSValue profiledSub(VM& vm, JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue op1 = JSValue::decode(encodedOp1); + JSValue op2 = JSValue::decode(encodedOp2); + + if (shouldObserveLHSAndRHSTypes) + arithProfile.observeLHSAndRHS(op1, op2); + + JSValue result = jsSub(globalObject, op1, op2); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + arithProfile.observeResult(result); + return JSValue::encode(result); +} + +JSC_DEFINE_JIT_OPERATION(operationValueSub, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + return unprofiledSub(globalObject, encodedOp1, encodedOp2); +} + +JSC_DEFINE_JIT_OPERATION(operationValueSubProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile* arithProfile)) +{ + ASSERT(arithProfile); + + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return profiledSub(vm, globalObject, encodedOp1, encodedOp2, *arithProfile); +} + +JSC_DEFINE_JIT_OPERATION(operationValueSubOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + auto nonOptimizeVariant = operationValueSubNoOptimize; + if (BinaryArithProfile* arithProfile = subIC->arithProfile()) + arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); + subIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + return unprofiledSub(globalObject, encodedOp1, encodedOp2); +} + +JSC_DEFINE_JIT_OPERATION(operationValueSubNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC*)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + return unprofiledSub(globalObject, encodedOp1, encodedOp2); +} + +JSC_DEFINE_JIT_OPERATION(operationValueSubProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + BinaryArithProfile* arithProfile = subIC->arithProfile(); + ASSERT(arithProfile); + arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); + auto nonOptimizeVariant = operationValueSubProfiledNoOptimize; + subIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); + +#if ENABLE(MATH_IC_STATS) + callFrame->codeBlock()->dumpMathICStats(); +#endif + + return profiledSub(vm, globalObject, encodedOp1, encodedOp2, *arithProfile, false); +} + +JSC_DEFINE_JIT_OPERATION(operationValueSubProfiledNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)) +{ + VM& vm = globalObject->vm(); + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + + BinaryArithProfile* arithProfile = subIC->arithProfile(); + ASSERT(arithProfile); + return profiledSub(vm, globalObject, encodedOp1, encodedOp2, *arithProfile); +} + +JSC_DEFINE_JIT_OPERATION(operationProcessTypeProfilerLog, void, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + vm.typeProfilerLog()->processLogEntries(vm, "Log Full, called from inside baseline JIT"_s); +} + +JSC_DEFINE_JIT_OPERATION(operationProcessShadowChickenLog, void, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + RELEASE_ASSERT(vm.shadowChicken()); + vm.shadowChicken()->update(vm, callFrame); +} + +JSC_DEFINE_JIT_OPERATION(operationRetrieveAndClearExceptionIfCatchable, JSCell*, (VM* vmPointer)) +{ + VM& vm = *vmPointer; + CallFrame* callFrame = DECLARE_CALL_FRAME(vm); + JITOperationPrologueCallFrameTracer tracer(vm, callFrame); + auto scope = DECLARE_THROW_SCOPE(vm); + RELEASE_ASSERT(!!scope.exception()); + + Exception* exception = scope.exception(); + if (UNLIKELY(vm.isTerminationException(exception))) { + genericUnwind(vm, callFrame); + return nullptr; + } + + // We want to clear the exception here rather than in the catch prologue + // JIT code because clearing it also entails clearing a bit in an Atomic + // bit field in VMTraps. + scope.clearException(); + return exception; +} + +} // namespace JSC + +IGNORE_WARNINGS_END + +#endif // ENABLE(JIT) diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h index 4b5d011f906a3..bbb1d229e1abe 100644 --- a/Source/JavaScriptCore/jit/JITOperations.h +++ b/Source/JavaScriptCore/jit/JITOperations.h @@ -155,6 +155,7 @@ JSC_DECLARE_JIT_OPERATION(operationLookupExceptionHandlerFromCallerFrame, void, JSC_DECLARE_JIT_OPERATION(operationVMHandleException, void, (VM*)); JSC_DECLARE_JIT_OPERATION(operationThrowStackOverflowErrorFromThunk, void, (JSGlobalObject*)); JSC_DECLARE_JIT_OPERATION(operationThrowIteratorResultIsNotObject, void, (JSGlobalObject*)); +JSC_DECLARE_JIT_OPERATION(operationGetWrappedValue, EncodedJSValue, (JSFunction*, EncodedJSValue)); JSC_DECLARE_JIT_OPERATION(operationThrowStackOverflowError, void, (CodeBlock*)); JSC_DECLARE_JIT_OPERATION(operationCallArityCheck, int32_t, (JSGlobalObject*)); diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.cpp b/Source/JavaScriptCore/jit/ThunkGenerators.cpp index 8eb57c097b30b..b50a3337b636b 100644 --- a/Source/JavaScriptCore/jit/ThunkGenerators.cpp +++ b/Source/JavaScriptCore/jit/ThunkGenerators.cpp @@ -29,6 +29,7 @@ #include "JITOperations.h" #include "JITThunks.h" #include "JSBoundFunction.h" +#include "JSRemoteFunction.h" #include "LLIntThunks.h" #include "MaxFrameExtentForSlowPathCall.h" #include "SpecializedThunkJIT.h" @@ -37,6 +38,8 @@ #include #include +#include "MacroAssemblerPrinter.h" + #if ENABLE(JIT) namespace JSC { @@ -1416,6 +1419,137 @@ MacroAssemblerCodeRef boundFunctionCallGenerator(VM& vm) linkBuffer, JITThunkPtrTag, "Specialized thunk for bound function calls with no arguments"); } +MacroAssemblerCodeRef remoteFunctionCallGenerator(VM& vm) +{ + CCallHelpers jit; + jit.emitFunctionPrologue(); + + // Set up our call frame. + jit.storePtr(CCallHelpers::TrustedImmPtr(nullptr), CCallHelpers::addressFor(CallFrameSlot::codeBlock)); + jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::tagFor(CallFrameSlot::argumentCountIncludingThis)); + + unsigned extraStackNeeded = 0; + if (unsigned stackMisalignment = sizeof(CallerFrameAndPC) % stackAlignmentBytes()) + extraStackNeeded = stackAlignmentBytes() - stackMisalignment; + + // We need to forward all of the arguments that we were passed. We aren't allowed to do a tail + // call here as far as I can tell. At least not so long as the generic path doesn't do a tail + // call, since that would be way too weird. + + // The formula for the number of stack bytes needed given some number of parameters (including + // this) is: + // + // stackAlign((numParams + CallFrameHeaderSize) * sizeof(Register) - sizeof(CallerFrameAndPC)) + // + // Probably we want to write this as: + // + // stackAlign((numParams + (CallFrameHeaderSize - CallerFrameAndPCSize)) * sizeof(Register)) + // + // That's really all there is to this. We have all the registers we need to do it. + + jit.loadCell(CCallHelpers::addressFor(CallFrameSlot::callee), GPRInfo::regT0); // t0 = callee + jit.load32(CCallHelpers::payloadFor(CallFrameSlot::argumentCountIncludingThis), GPRInfo::regT1); // t1 = arguments+this on stack + jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1); // t1 -= 1 (remove `this` from arguments count) + + jit.add32(CCallHelpers::TrustedImm32(CallFrame::headerSizeInRegisters - CallerFrameAndPC::sizeInRegisters + 1), GPRInfo::regT1, GPRInfo::regT2); // t2 += space for callee, codeblock and argument count + jit.lshift32(CCallHelpers::TrustedImm32(3), GPRInfo::regT2); // t2 *= 8 (64 bits per reg) + jit.add32(CCallHelpers::TrustedImm32(stackAlignmentBytes() - 1), GPRInfo::regT2); // grow to satckAlignmentBytes in size + jit.and32(CCallHelpers::TrustedImm32(-stackAlignmentBytes()), GPRInfo::regT2); + + if (extraStackNeeded) + jit.add32(CCallHelpers::TrustedImm32(extraStackNeeded), GPRInfo::regT2); // add extra if needed + + // At this point regT1 has the actual argument count, regT2 has the amount of stack we will need, and regT3 has the passed argument count. + // Check to see if we have enough stack space. + + jit.negPtr(GPRInfo::regT2); // t2 = -t2 + jit.addPtr(CCallHelpers::stackPointerRegister, GPRInfo::regT2); // t2 += sp + CCallHelpers::Jump haveStackSpace = jit.branchPtr(CCallHelpers::BelowOrEqual, CCallHelpers::AbsoluteAddress(vm.addressOfSoftStackLimit()), GPRInfo::regT2); // stackcheck + + // Throw Stack Overflow exception + jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame); + jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, JSBoundFunction::offsetOfScopeChain()), GPRInfo::regT3); + jit.setupArguments(GPRInfo::regT3); + jit.prepareCallOperation(vm); + jit.move(CCallHelpers::TrustedImmPtr(tagCFunction(operationThrowStackOverflowErrorFromThunk)), GPRInfo::nonArgGPR0); + emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag); + jit.call(GPRInfo::nonArgGPR0, OperationPtrTag); + jit.jumpToExceptionHandler(vm); + + haveStackSpace.link(&jit); + jit.move(GPRInfo::regT2, CCallHelpers::stackPointerRegister); + + // Do basic callee frame setup, including 'this'. + + jit.store32(GPRInfo::regT1, CCallHelpers::calleeFramePayloadSlot(CallFrameSlot::argumentCountIncludingThis));// t1 = argumentCountIncludingThis + + JSValueRegs valueRegs = JSValueRegs::withTwoAvailableRegs(GPRInfo::regT4, GPRInfo::regT2); + jit.storeTrustedValue(jsUndefined(), CCallHelpers::calleeArgumentSlot(0)); + + // OK, now we can start copying. This is a simple matter of copying parameters from the caller's + // frame to the callee's frame. Note that we know that regT3 (the argument count) must be at + // least 1. + CCallHelpers::Jump done = jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT1); + + CCallHelpers::Label loop = jit.label(); + jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1); + jit.loadValue(CCallHelpers::addressFor(virtualRegisterForArgumentIncludingThis(1)).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight), valueRegs); + + // Check the passed value (in valueRegs) and convert to a JSRemoteFunction if necessary. + // (regT3 and regT5 are free, regT0 is callee JSRemoteFunction) + CCallHelpers::JumpList valueIsPrimitive; + valueIsPrimitive.append(jit.branchIfNotCell(valueRegs)); + valueIsPrimitive.append(jit.branchIfNotObject(valueRegs.payloadGPR())); + jit.pushToSave(GPRInfo::regT1); + + jit.move(CCallHelpers::TrustedImmPtr(tagCFunction(operationGetWrappedValue)), GPRInfo::nonArgGPR0); + emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag); + + jit.loadCell(CCallHelpers::addressFor(CallFrameSlot::callee), GPRInfo::regT0); // t0 = callee + jit.setupArguments(GPRInfo::regT0, valueRegs); + jit.prepareCallOperation(vm); + jit.call(GPRInfo::nonArgGPR0, OperationPtrTag); + jit.popToRestore(GPRInfo::regT1); + auto checkException = jit.emitJumpIfException(vm); + jit.setupResults(valueRegs); + + valueIsPrimitive.link(&jit); + jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(1).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight)); + jit.branchTest32(CCallHelpers::NonZero, GPRInfo::regT1).linkTo(loop, &jit); + + done.link(&jit); + + jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, JSRemoteFunction::offsetOfTarget()), GPRInfo::regT2); + jit.storeCell(GPRInfo::regT2, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee)); + + jit.loadPtr(CCallHelpers::Address(GPRInfo::regT2, JSFunction::offsetOfExecutableOrRareData()), GPRInfo::regT0); + auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT0, CCallHelpers::TrustedImm32(JSFunction::rareDataTag)); + jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), GPRInfo::regT0); + hasExecutable.link(&jit); + + jit.loadPtr( + CCallHelpers::Address( + GPRInfo::regT0, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)), + GPRInfo::regT0); + CCallHelpers::Jump noCode = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT0); + + emitPointerValidation(jit, GPRInfo::regT0, JSEntryPtrTag); + jit.call(GPRInfo::regT0, JSEntryPtrTag); + + jit.emitFunctionEpilogue(); + jit.ret(); + + checkException.link(&jit); + jit.print("EXCEPTION HANDLED\n"); + jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame); + jit.jumpToExceptionHandler(vm); + + LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::BoundFunctionThunk); + linkBuffer.link(noCode, CodeLocationLabel(vm.jitStubs->ctiNativeTailCallWithoutSavedTags(vm))); + return FINALIZE_CODE( + linkBuffer, JITThunkPtrTag, "Specialized thunk for bound function calls with no arguments"); +} + } // namespace JSC #endif // ENABLE(JIT) diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.h b/Source/JavaScriptCore/jit/ThunkGenerators.h index 631a596f2fd64..74d7671149e8e 100644 --- a/Source/JavaScriptCore/jit/ThunkGenerators.h +++ b/Source/JavaScriptCore/jit/ThunkGenerators.h @@ -79,6 +79,8 @@ MacroAssemblerCodeRef truncThunkGenerator(VM&); MacroAssemblerCodeRef boundFunctionCallGenerator(VM&); +MacroAssemblerCodeRef remoteFunctionCallGenerator(VM&); + #if ENABLE(EXTRA_CTI_THUNKS) MacroAssemblerCodeRef checkExceptionGenerator(VM&); #endif diff --git a/Source/JavaScriptCore/runtime/Intrinsic.cpp b/Source/JavaScriptCore/runtime/Intrinsic.cpp index ff5f1924690f6..b81759bca7ea8 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.cpp +++ b/Source/JavaScriptCore/runtime/Intrinsic.cpp @@ -205,6 +205,8 @@ const char* intrinsicName(Intrinsic intrinsic) return "IsTypedArrayViewIntrinsic"; case BoundFunctionCallIntrinsic: return "BoundFunctionCallIntrinsic"; + case RemoteFunctionCallIntrinsic: + return "RemoteFunctionCallIntrinsic"; case JSMapGetIntrinsic: return "JSMapGetIntrinsic"; case JSMapHasIntrinsic: diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h index 8e68e7e74fb78..4ae3651b575e2 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.h +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -118,6 +118,7 @@ enum Intrinsic : uint8_t { TypedArrayEntriesIntrinsic, IsTypedArrayViewIntrinsic, BoundFunctionCallIntrinsic, + RemoteFunctionCallIntrinsic, JSMapGetIntrinsic, JSMapHasIntrinsic, JSMapSetIntrinsic, diff --git a/Source/JavaScriptCore/runtime/JSCast.h b/Source/JavaScriptCore/runtime/JSCast.h index 68c0e5bc2ea04..a6993159099cc 100644 --- a/Source/JavaScriptCore/runtime/JSCast.h +++ b/Source/JavaScriptCore/runtime/JSCast.h @@ -99,6 +99,7 @@ struct JSTypeRange { macro(JSSymbolTableObject, JSType::GlobalObjectType, JSType::ModuleEnvironmentType) \ macro(JSScope, JSType::GlobalObjectType, JSType::WithScopeType) \ macro(StringObject, JSType::StringObjectType, JSType::DerivedStringObjectType) \ + macro(ShadowRealmObject, JSType::ShadowRealmType, JSType::ShadowRealmType) \ // Forward declare the classes because they may not already exist. diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h index abbcbd2a68ad5..34181bdd86890 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.h +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -150,6 +150,7 @@ class JSFunction : public JSCallee { bool isBuiltinFunction() const; JS_EXPORT_PRIVATE bool isHostFunctionNonInline() const; bool isClassConstructorFunction() const; + bool isRemoteFunction() const; void setFunctionName(JSGlobalObject*, JSValue name); diff --git a/Source/JavaScriptCore/runtime/JSFunctionInlines.h b/Source/JavaScriptCore/runtime/JSFunctionInlines.h index 25d609430bb7d..2ced82d6f3155 100644 --- a/Source/JavaScriptCore/runtime/JSFunctionInlines.h +++ b/Source/JavaScriptCore/runtime/JSFunctionInlines.h @@ -27,6 +27,7 @@ #include "FunctionExecutable.h" #include "JSBoundFunction.h" +#include "JSRemoteFunction.h" #include "JSFunction.h" #include "NativeExecutable.h" @@ -78,6 +79,11 @@ inline bool JSFunction::isClassConstructorFunction() const return !isHostFunction() && jsExecutable()->isClassConstructorFunction(); } +inline bool JSFunction::isRemoteFunction() const +{ + return static_cast(jsDynamicCast(globalObject()->vm(), this)); +} + inline TaggedNativeFunction JSFunction::nativeFunction() { ASSERT(isHostFunctionNonInline()); @@ -98,6 +104,14 @@ inline bool isHostFunction(JSValue value, TaggedNativeFunction nativeFunction) return function->nativeFunction() == nativeFunction; } +inline bool isRemoteFunction(JSValue value) +{ + JSFunction* function = jsCast(getJSFunction(value)); + if (!function) + return false; + return function->isRemoteFunction(); +} + inline bool JSFunction::hasReifiedLength() const { if (FunctionRareData* rareData = this->rareData()) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index cff47b0510797..b027fc06198d1 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -140,6 +140,7 @@ #include "JSPromise.h" #include "JSPromiseConstructor.h" #include "JSPromisePrototype.h" +#include "JSRemoteFunction.h" #include "JSSet.h" #include "JSSetIterator.h" #include "JSStringIterator.h" @@ -769,6 +770,10 @@ void JSGlobalObject::init(VM& vm) [] (const Initializer& init) { init.set(JSNativeStdFunction::createStructure(init.vm, init.owner, init.owner->m_functionPrototype.get())); }); + m_remoteFunctionStructure.initLater( + [] (const Initializer& init) { + init.set(JSRemoteFunction::createStructure(init.vm, init.owner, init.owner->m_functionPrototype.get())); + }); JSFunction* callFunction = nullptr; JSFunction* applyFunction = nullptr; JSFunction* hasInstanceSymbolFunction = nullptr; @@ -1527,6 +1532,14 @@ capitalName ## Constructor* lowerName ## Constructor = featureFlag ? capitalName init.set(JSFunction::create(init.vm, jsCast(init.owner), 0, String(), createPrivateSymbol)); }); + // ShadowRealms + m_linkTimeConstants[static_cast(LinkTimeConstant::createRemoteFunction)].initLater([] (const Initializer& init) { + init.set(JSFunction::create(init.vm, jsCast(init.owner), 0, String(), createRemoteFunction)); + }); + m_linkTimeConstants[static_cast(LinkTimeConstant::isRemoteFunction)].initLater([] (const Initializer& init) { + init.set(JSFunction::create(init.vm, jsCast(init.owner), 0, String(), isRemoteFunction)); + }); + #if ENABLE(WEBASSEMBLY) // WebAssembly Streaming API m_linkTimeConstants[static_cast(LinkTimeConstant::webAssemblyCompileStreamingInternal)].initLater([] (const Initializer& init) { @@ -2255,6 +2268,7 @@ void JSGlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_customSetterFunctionStructure.visit(visitor); thisObject->m_boundFunctionStructure.visit(visitor); thisObject->m_nativeStdFunctionStructure.visit(visitor); + thisObject->m_remoteFunctionStructure.visit(visitor); visitor.append(thisObject->m_shadowRealmObjectStructure); visitor.append(thisObject->m_regExpStructure); visitor.append(thisObject->m_generatorFunctionStructure); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index f650a3d89ef2a..e063438cc67c8 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -418,6 +418,7 @@ class JSGlobalObject : public JSSegmentedVariableObject { LazyProperty m_customGetterFunctionStructure; LazyProperty m_customSetterFunctionStructure; LazyProperty m_nativeStdFunctionStructure; + LazyProperty m_remoteFunctionStructure; PropertyOffset m_functionNameOffset; WriteBarrier m_shadowRealmObjectStructure; WriteBarrier m_regExpStructure; @@ -861,6 +862,7 @@ class JSGlobalObject : public JSSegmentedVariableObject { Structure* regExpMatchesArrayStructure() const { return m_regExpMatchesArrayStructure.get(); } Structure* regExpMatchesArrayWithIndicesStructure() const { return m_regExpMatchesArrayWithIndicesStructure.get(); } Structure* regExpMatchesIndicesArrayStructure() const { return m_regExpMatchesIndicesArrayStructure.get(); } + Structure* remoteFunctionStructure() const { return m_remoteFunctionStructure.get(this); } Structure* moduleRecordStructure() const { return m_moduleRecordStructure.get(this); } Structure* moduleNamespaceObjectStructure() const { return m_moduleNamespaceObjectStructure.get(this); } Structure* proxyObjectStructure() const { return m_proxyObjectStructure.get(this); } diff --git a/Source/JavaScriptCore/runtime/JSRemoteFunction.cpp b/Source/JavaScriptCore/runtime/JSRemoteFunction.cpp new file mode 100644 index 0000000000000..2cdf9436c1ffd --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSRemoteFunction.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2021 Igalia S.L. + * Author: Caitlin Potter + * + * 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. AND ITS CONTRIBUTORS ``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 ITS 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 "JSFunctionInlines.h" +#include "JSRemoteFunction.h" + +#include "ExecutableBaseInlines.h" +#include "JSCInlines.h" +#include "ShadowRealmObject.h" + +#include + +namespace JSC { + +const ClassInfo JSRemoteFunction::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSRemoteFunction) }; + +JSRemoteFunction::JSRemoteFunction(VM& vm, NativeExecutable* executable, JSGlobalObject* globalObject, Structure* structure, JSCell* target) + : Base(vm, executable, globalObject, structure) + , m_target(vm, this, target) +{ +} + +JSValue wrapValue(JSGlobalObject* globalObject, JSGlobalObject* targetGlobalObject, JSValue value) +{ + VM& vm = globalObject->vm(); + + if (value.isPrimitive()) + return value; + + if (value.isCallable(vm)) { + JSCallee* target = static_cast(value.asCell()); + return JSValue(JSRemoteFunction::create(vm, targetGlobalObject, target)); + } + + return JSValue(); +} + +static inline JSValue wrapArgument(JSGlobalObject* globalObject, JSGlobalObject* targetGlobalObject, JSValue value) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue result = wrapValue(globalObject, targetGlobalObject, value); + if (result.isEmpty()) + throwTypeError(globalObject, scope, "value passing between realms must be callable or primitive"); + return result; +} + +static inline JSValue wraprReturnValue(JSGlobalObject* globalObject, JSGlobalObject* targetGlobalObject, JSValue value) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue result = wrapValue(globalObject, targetGlobalObject, value); + if (result.isEmpty()) + throwTypeError(globalObject, scope, "value passing between realms must be callable or primitive"); + return result; +} + +JSC_DEFINE_HOST_FUNCTION(remoteJSFunctionCall, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + dataLog("remoteJSFunctionCall\n"); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSRemoteFunction* remoteFunction = jsCast(callFrame->jsCallee()); + JSFunction* target = remoteFunction->targetFunction(); + ASSERT(target); + + JSGlobalObject* targetGlobalObject = target->structure()->globalObject(); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < callFrame->argumentCount(); ++i) { + JSValue wrappedValue = wrapArgument(globalObject, targetGlobalObject, callFrame->uncheckedArgument(i)); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + args.append(wrappedValue); + } + if (UNLIKELY(args.hasOverflowed())) { + throwOutOfMemoryError(globalObject, scope); + return encodedJSValue(); + } + ExecutableBase* executable = target->executable(); + if (executable->hasJITCodeForCall()) { + // Force the executable to cache its arity entrypoint. + executable->entrypointFor(CodeForCall, MustCheckArity); + } + + auto callData = getCallData(vm, target); + ASSERT(callData.type != CallData::Type::None); + auto result = call(targetGlobalObject, target, callData, jsUndefined(), args); + + // Hide exceptions from calling realm + if (scope.exception()) { + scope.clearException(); + throwTypeError(globalObject, scope, "an error occurred in remote realm"); + return encodedJSValue(); + } + + auto wrappedResult = wraprReturnValue(globalObject, globalObject, result); + RELEASE_AND_RETURN(scope, JSValue::encode(wrappedResult)); +} + +JSC_DEFINE_HOST_FUNCTION(remoteFunctionCall, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + dataLog("remoteFunctionCall\n"); + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSRemoteFunction* remoteFunction = jsCast(callFrame->jsCallee()); + JSCell* target = remoteFunction->target(); + JSGlobalObject* targetGlobalObject = target->structure()->globalObject(); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < callFrame->argumentCount(); ++i) { + JSValue wrappedValue = wrapValue(globalObject, targetGlobalObject, callFrame->uncheckedArgument(i)); + args.append(wrappedValue); + } + if (UNLIKELY(args.hasOverflowed())) { + throwOutOfMemoryError(globalObject, scope); + return encodedJSValue(); + } + + auto callData = getCallData(vm, target); + ASSERT(callData.type != CallData::Type::None); + auto result = call(targetGlobalObject, target, callData, jsUndefined(), args); + + // Hide exceptions from calling realm + if (scope.exception()) { + scope.clearException(); + throwTypeError(globalObject, scope, "an error occurred in remote realm"); + return encodedJSValue(); + } + + auto wrappedResult = wrapValue(targetGlobalObject, globalObject, result); + RELEASE_AND_RETURN(scope, JSValue::encode(wrappedResult)); +} + +JSC_DEFINE_HOST_FUNCTION(isRemoteFunction, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(JSValue(static_cast(jsDynamicCast(globalObject->vm(), callFrame->uncheckedArgument(0))))); +} + +JSC_DEFINE_HOST_FUNCTION(createRemoteFunction, (JSGlobalObject* globalObject, CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + ASSERT(callFrame->argumentCount() == 2); + JSValue target = callFrame->uncheckedArgument(0); + ASSERT(target.isCallable(vm)); + + JSCell* targetCallable = target.asCell(); + JSGlobalObject* destinationGlobalObject = globalObject; + if (!callFrame->uncheckedArgument(1).isUndefinedOrNull()) { + if (auto shadowRealm = jsDynamicCast(vm, callFrame->uncheckedArgument(1))) + destinationGlobalObject = shadowRealm->globalObject(); + else + destinationGlobalObject = jsCast(callFrame->uncheckedArgument(1)); + } + + auto result = JSRemoteFunction::create(vm, destinationGlobalObject, targetCallable); + RELEASE_AND_RETURN(scope, JSValue::encode(result)); +} + +inline Structure* getRemoteFunctionStructure(JSGlobalObject* globalObject) +{ + // FIXME: implement globalObject-aware structure caching + return globalObject->remoteFunctionStructure(); +} + +JSRemoteFunction* JSRemoteFunction::create(VM& vm, JSGlobalObject* globalObject, JSCell* targetCallable) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + + ASSERT(targetCallable && targetCallable->isCallable(vm)); + if (auto remote = jsDynamicCast(vm, targetCallable)) { + targetCallable = remote->target(); + ASSERT(!JSC::isRemoteFunction(targetCallable)); + } + + bool isJSFunction = getJSFunction(targetCallable); + NativeExecutable* executable = vm.getRemoteFunction(isJSFunction); + Structure* structure = getRemoteFunctionStructure(globalObject); + RETURN_IF_EXCEPTION(scope, nullptr); + JSRemoteFunction* function = new (NotNull, allocateCell(vm)) JSRemoteFunction(vm, executable, globalObject, structure, targetCallable); + + function->finishCreation(vm); + return function; +} + +void JSRemoteFunction::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); +} + +template +void JSRemoteFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSRemoteFunction* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + visitor.append(thisObject->m_target); +} + +DEFINE_VISIT_CHILDREN(JSRemoteFunction); + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSRemoteFunction.h b/Source/JavaScriptCore/runtime/JSRemoteFunction.h new file mode 100644 index 0000000000000..123d84f45d2ef --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSRemoteFunction.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 Igalia S.L. + * Author: Caitlin Potter + * + * 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. AND ITS CONTRIBUTORS ``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 ITS 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 "AuxiliaryBarrier.h" +#include "JSObject.h" +#include + +namespace JSC { + +JSC_DECLARE_HOST_FUNCTION(remoteJSFunctionCall); +JSC_DECLARE_HOST_FUNCTION(remoteFunctionCall); +JSC_DECLARE_HOST_FUNCTION(isRemoteFunction); +JSC_DECLARE_HOST_FUNCTION(createRemoteFunction); + +// JSRemoteFunction creates a bridge between its native Realm and a remote one. +// When invoked, arguments are wrapped to prevent leaking information across the realm boundary. +// The return value and any abrupt completions are also filtered. +class JSRemoteFunction final : public JSFunction { +public: + typedef JSFunction Base; + static constexpr unsigned StructureFlags = Base::StructureFlags; + + template + static IsoSubspace* subspaceFor(VM& vm) + { + return vm.remoteFunctionSpace(); + } + + JS_EXPORT_PRIVATE static JSRemoteFunction* create(VM&, JSGlobalObject*, JSCell* targetCallable); + + JSCell* target() { return m_target.get(); } + JSFunction* targetFunction() { + return static_cast(getJSFunction(target())); + } + JSGlobalObject* targetGlobalObject() { return targetFunction()->globalObject(); } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + ASSERT(globalObject); + return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info()); + } + + static ptrdiff_t offsetOfTarget() { return OBJECT_OFFSETOF(JSRemoteFunction, m_target); } + + DECLARE_INFO; + +private: + JSRemoteFunction(VM&, NativeExecutable*, JSGlobalObject*, Structure*, JSCell* targetCallable); + + void finishCreation(VM&); + DECLARE_VISIT_CHILDREN; + + WriteBarrier m_target; +}; + +} diff --git a/Source/JavaScriptCore/runtime/ShadowRealmPrototype.cpp b/Source/JavaScriptCore/runtime/ShadowRealmPrototype.cpp index 0438db4acb9c5..d9d1a4c914698 100644 --- a/Source/JavaScriptCore/runtime/ShadowRealmPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ShadowRealmPrototype.cpp @@ -116,10 +116,11 @@ JSC_DEFINE_HOST_FUNCTION(evalInRealm, (JSGlobalObject* globalObject, CallFrame* RETURN_IF_EXCEPTION(scope, { }); JSValue result = vm.interpreter->execute(eval, realmGlobalObject, realmGlobalObject->globalThis(), realmGlobalObject->globalScope()); - if (UNLIKELY(scope.exception())) { - scope.clearException(); - return throwVMError(globalObject, scope, createTypeError(globalObject, "Error encountered during evaluation"_s)); - } + // FIXME: uncomment this, just using this to get a closer idea what's going wrong during evaluation. + //if (UNLIKELY(scope.exception())) { + // scope.clearException(); + // return throwVMError(globalObject, scope, createTypeError(globalObject, "Error encountered during evaluation"_s)); + // } RELEASE_AND_RETURN(scope, JSValue::encode(result)); } diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp index 65233a4fe956b..34320acf4ca91 100644 --- a/Source/JavaScriptCore/runtime/VM.cpp +++ b/Source/JavaScriptCore/runtime/VM.cpp @@ -116,6 +116,7 @@ #include "JSPromise.h" #include "JSPropertyNameEnumerator.h" #include "JSProxy.h" +#include "JSRemoteFunction.h" #include "JSScriptFetchParameters.h" #include "JSScriptFetcher.h" #include "JSSet.h" @@ -821,6 +822,8 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) return randomThunkGenerator; case BoundFunctionCallIntrinsic: return boundFunctionCallGenerator; + case RemoteFunctionCallIntrinsic: + return remoteFunctionCallGenerator; default: return nullptr; } @@ -898,6 +901,26 @@ NativeExecutable* VM::getBoundFunction(bool isJSFunction, bool canConstruct) return getOrCreate(m_fastBoundExecutable); } +NativeExecutable* VM::getRemoteFunction(bool isJSFunction) +{ + bool slowCase = !isJSFunction; + + auto getOrCreate = [&] (Weak& slot) -> NativeExecutable* { + if (auto* cached = slot.get()) + return cached; + NativeExecutable* result = getHostFunction( + slowCase ? remoteFunctionCall : remoteJSFunctionCall, + slowCase ? NoIntrinsic : RemoteFunctionCallIntrinsic, + callHostFunctionAsConstructor, nullptr, String()); + slot = Weak(result); + return result; + }; + + if (slowCase) + return getOrCreate(m_slowRemoteFunctionExecutable); + return getOrCreate(m_fastRemoteFunctionExecutable); +} + MacroAssemblerCodePtr VM::getCTIInternalFunctionTrampolineFor(CodeSpecializationKind kind) { #if ENABLE(JIT) @@ -1620,6 +1643,7 @@ DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(moduleNamespaceObjectSpace, *moduleNames DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(nativeStdFunctionSpace, *nativeStdFunctionHeapCellType, JSNativeStdFunction) // Hash:0x70ed61e4 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(proxyObjectSpace, *cellHeapCellType, ProxyObject) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(proxyRevokeSpace, *cellHeapCellType, ProxyRevoke) // Hash:0xb506a939 +DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(remoteFunctionSpace, *cellHeapCellType, JSRemoteFunction) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(scopedArgumentsTableSpace, *destructibleCellHeapCellType, ScopedArgumentsTable) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(scriptFetchParametersSpace, *destructibleCellHeapCellType, JSScriptFetchParameters) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(scriptFetcherSpace, *destructibleCellHeapCellType, JSScriptFetcher) diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h index 6dbe1555fedee..d4bd46a002482 100644 --- a/Source/JavaScriptCore/runtime/VM.h +++ b/Source/JavaScriptCore/runtime/VM.h @@ -599,6 +599,7 @@ class VM : public ThreadSafeRefCounted, public DoublyLinkedListNode { DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(nativeStdFunctionSpace) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(proxyObjectSpace) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(proxyRevokeSpace) + DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(remoteFunctionSpace) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(scopedArgumentsTableSpace) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(scriptFetchParametersSpace) DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(scriptFetcherSpace) @@ -791,6 +792,9 @@ class VM : public ThreadSafeRefCounted, public DoublyLinkedListNode { Weak m_slowBoundExecutable; Weak m_slowCanConstructBoundExecutable; + Weak m_fastRemoteFunctionExecutable; + Weak m_slowRemoteFunctionExecutable; + Ref deferredWorkTimer; JSCell* currentlyDestructingCallbackObject; @@ -904,6 +908,7 @@ class VM : public ThreadSafeRefCounted, public DoublyLinkedListNode { NativeExecutable* getHostFunction(NativeFunction, Intrinsic, NativeFunction constructor, const DOMJIT::Signature*, const String& name); NativeExecutable* getBoundFunction(bool isJSFunction, bool canConstruct); + NativeExecutable* getRemoteFunction(bool isJSFunction); MacroAssemblerCodePtr getCTIInternalFunctionTrampolineFor(CodeSpecializationKind); MacroAssemblerCodeRef getCTILinkCall(); diff --git a/test.js b/test.js new file mode 100644 index 0000000000000..a47ee08e72e40 --- /dev/null +++ b/test.js @@ -0,0 +1,49 @@ +function assertThrows(thing, kind) { + let error; + let threw = false; + let kindString = () => { + return kind.name; + } + try { + thing(); + } catch (e) { + error = e; + threw = true; + } + + if (!threw) + throw new Error("Expected to throw " + kindString() + ", but did not throw"); + + if (!(error instanceof kind)) + throw new Error("Expected to throw " + kindString() + ", but threw " + error); +} +function assert(expr, message) { + if (!expr) + throw new Error(message || "Assertion failed"); +} +print("creating ShadowRealm"); +let r = new ShadowRealm; + +print("test remote-calling a function which throws from outside a ShadowRealm"); +r.evaluate("globalThis.lastValue = 1;"); +if (globalThis.lastValue === 1) + throw new Error("ran in wrong realm"); + +print("shadowrealm evaluate function which invokes its argument with `lastValue`"); +let f = r.evaluate("(function(f) { return f(lastValue); })"); + +assert(!r.evaluate("!!globalThis.print")); +let exportValue = r.evaluate("(function(name, value) { this[name] = value; })"); +print("exporting function print"); +exportValue("print", globalThis.print); +print("testing that the export worked"); +assert(r.evaluate("!!this.print")); + +print("shadowrealm evaluate function which throws a TypeError"); +let thrower = r.evaluate("(function thrower() { globalThis.print('throwing \"butts\"'); /*throw new TypeError('butts');*/ })"); + +print("test remote-calling a function which throws from outside a ShadowRealm"); +assertThrows(() => { f(thrower); }, TypeError); + + +print("done!"); \ No newline at end of file diff --git a/test2.js b/test2.js new file mode 100644 index 0000000000000..626b6bfd0d4f1 --- /dev/null +++ b/test2.js @@ -0,0 +1,12 @@ +let r = new ShadowRealm; + +let exportValue = r.evaluate(` + (function(exportName, exportValue) { + globalThis[exportName] = exportValue; + }) +`); + +print("typeof globalThis.print: ", typeof globalThis.print); +print("typeof shadowRealm globalThis.print: ", r.evaluate("typeof globalThis.print")); +exportValue("print", globalThis.print); +print("typeof shadowRealm globalThis.print: ", r.evaluate("typeof globalThis.print"));