| /* |
| * Copyright (C) 2021-2022 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include "ErrorInstance.h" |
| #include "FrameTracers.h" |
| #include "LLIntEntrypoint.h" |
| #include "Repatch.h" |
| |
| #include "VMTrapsInlines.h" |
| |
| namespace JSC { |
| |
| inline void* throwNotAFunctionErrorFromCallIC(JSGlobalObject* globalObject, JSCell* owner, JSValue callee, CallLinkInfo* callLinkInfo) |
| { |
| VM& vm = getVM(globalObject); |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| auto errorMessage = constructErrorMessage(globalObject, callee, "is not a function"_s); |
| RETURN_IF_EXCEPTION(scope, nullptr); |
| if (!errorMessage) [[unlikely]] { |
| throwOutOfMemoryError(globalObject, scope); |
| return nullptr; |
| } |
| |
| // Call IC will throw these errors after throwing away the caller's frame when it is a tail-call. |
| // But we would like to have error information for them from the thrown frame. |
| // This frame information can be reconstructed easily since we have CodeOrigin and owner CodeBlock for CallLinkInfo. |
| auto [codeBlock, bytecodeIndex] = callLinkInfo->retrieveCaller(owner); |
| if (codeBlock) |
| errorMessage = appendSourceToErrorMessage(codeBlock, bytecodeIndex, errorMessage, runtimeTypeForValue(callee), notAFunctionSourceAppender); |
| auto* error = ErrorInstance::create(vm, globalObject->errorStructure(ErrorType::TypeError), errorMessage, JSValue(), ErrorType::TypeError, owner, callLinkInfo); |
| throwException(globalObject, scope, error); |
| return nullptr; |
| } |
| |
| inline void* throwNotAConstructorErrorFromCallIC(JSGlobalObject* globalObject, JSCell* owner, JSValue callee, CallLinkInfo* callLinkInfo) |
| { |
| VM& vm = getVM(globalObject); |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| auto errorMessage = constructErrorMessage(globalObject, callee, "is not a constructor"_s); |
| RETURN_IF_EXCEPTION(scope, nullptr); |
| if (!errorMessage) [[unlikely]] { |
| throwOutOfMemoryError(globalObject, scope); |
| return nullptr; |
| } |
| // Call IC will throw these errors after throwing away the caller's frame when it is a tail-call. |
| // But we would like to have error information for them from the thrown frame. |
| // This frame information can be reconstructed easily since we have CodeOrigin and owner CodeBlock for CallLinkInfo. |
| auto [codeBlock, bytecodeIndex] = callLinkInfo->retrieveCaller(owner); |
| if (codeBlock) |
| errorMessage = appendSourceToErrorMessage(codeBlock, bytecodeIndex, errorMessage, runtimeTypeForValue(callee), defaultSourceAppender); |
| auto* error = ErrorInstance::create(vm, globalObject->errorStructure(ErrorType::TypeError), errorMessage, JSValue(), ErrorType::TypeError, owner, callLinkInfo); |
| throwException(globalObject, scope, error); |
| return nullptr; |
| } |
| |
| inline void* handleHostCall(VM& vm, JSCell* owner, CallFrame* calleeFrame, JSValue callee, CallLinkInfo* callLinkInfo) |
| { |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| |
| calleeFrame->setCodeBlock(nullptr); |
| |
| if (callLinkInfo->specializationKind() == CodeSpecializationKind::CodeForCall) { |
| auto callData = JSC::getCallData(callee); |
| ASSERT(callData.type != CallData::Type::JS); |
| |
| if (callData.type == CallData::Type::Native) { |
| NativeCallFrameTracer tracer(vm, calleeFrame); |
| calleeFrame->setCallee(asObject(callee)); |
| vm.encodedHostCallReturnValue = callData.native.function(asObject(callee)->globalObject(), calleeFrame); |
| AssertNoGC assertNoGC; |
| if (scope.exception()) [[unlikely]] |
| return nullptr; |
| return LLInt::getHostCallReturnValueEntrypoint().code().taggedPtr(); |
| } |
| |
| auto* globalObject = callLinkInfo->globalObjectForSlowPath(owner); |
| calleeFrame->setCallee(globalObject->partiallyInitializedFrameCallee()); |
| ASSERT(callData.type == CallData::Type::None); |
| RELEASE_AND_RETURN(scope, throwNotAFunctionErrorFromCallIC(globalObject, owner, callee, callLinkInfo)); |
| } |
| |
| ASSERT(callLinkInfo->specializationKind() == CodeSpecializationKind::CodeForConstruct); |
| |
| auto constructData = JSC::getConstructData(callee); |
| ASSERT(constructData.type != CallData::Type::JS); |
| |
| if (constructData.type == CallData::Type::Native) { |
| NativeCallFrameTracer tracer(vm, calleeFrame); |
| calleeFrame->setCallee(asObject(callee)); |
| vm.encodedHostCallReturnValue = constructData.native.function(asObject(callee)->globalObject(), calleeFrame); |
| AssertNoGC assertNoGC; |
| if (scope.exception()) [[unlikely]] |
| return nullptr; |
| return LLInt::getHostCallReturnValueEntrypoint().code().taggedPtr(); |
| } |
| |
| auto* globalObject = callLinkInfo->globalObjectForSlowPath(owner); |
| calleeFrame->setCallee(globalObject->partiallyInitializedFrameCallee()); |
| ASSERT(constructData.type == CallData::Type::None); |
| RELEASE_AND_RETURN(scope, throwNotAConstructorErrorFromCallIC(globalObject, owner, callee, callLinkInfo)); |
| } |
| |
| ALWAYS_INLINE void* linkFor(VM& vm, JSCell* owner, CallFrame* calleeFrame, CallLinkInfo* callLinkInfo) |
| { |
| auto throwScope = DECLARE_THROW_SCOPE(vm); |
| |
| CodeSpecializationKind kind = callLinkInfo->specializationKind(); |
| |
| JSValue calleeAsValue = calleeFrame->guaranteedJSValueCallee(); |
| JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue); |
| if (!calleeAsFunctionCell) { |
| if (auto* internalFunction = jsDynamicCast<InternalFunction*>(calleeAsValue)) { |
| CodePtr<JSEntryPtrTag> codePtr = vm.getCTIInternalFunctionTrampolineFor(kind); |
| RELEASE_ASSERT(!!codePtr); |
| |
| switch (callLinkInfo->mode()) { |
| case CallLinkInfo::Mode::Init: { |
| if (!callLinkInfo->seenOnce()) |
| callLinkInfo->setSeen(); |
| else |
| linkMonomorphicCall(vm, owner, *callLinkInfo, nullptr, internalFunction, codePtr); |
| break; |
| } |
| case CallLinkInfo::Mode::Monomorphic: |
| case CallLinkInfo::Mode::Polymorphic: { |
| if (kind == CodeSpecializationKind::CodeForCall) { |
| linkPolymorphicCall(vm, owner, calleeFrame, *callLinkInfo, CallVariant(internalFunction)); |
| break; |
| } |
| callLinkInfo->setVirtualCall(vm); |
| break; |
| } |
| case CallLinkInfo::Mode::Virtual: |
| break; |
| } |
| |
| return codePtr.taggedPtr(); |
| } |
| RELEASE_AND_RETURN(throwScope, handleHostCall(vm, owner, calleeFrame, calleeAsValue, callLinkInfo)); |
| } |
| |
| JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell); |
| JSScope* scope = callee->scopeUnchecked(); |
| ExecutableBase* executable = callee->executable(); |
| |
| CodePtr<JSEntryPtrTag> codePtr; |
| CodeBlock* codeBlock = nullptr; |
| |
| DeferTraps deferTraps(vm); // We can't jettison any code until after we link the call. |
| |
| if (executable->isHostFunction()) { |
| codePtr = jsToWasmICCodePtr(kind, callee); |
| if (!codePtr) |
| codePtr = executable->entrypointFor(kind, ArityCheckMode::MustCheckArity); |
| } else { |
| FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable); |
| |
| if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) { |
| auto* globalObject = callLinkInfo->globalObjectForSlowPath(owner); |
| calleeFrame->setCallee(globalObject->partiallyInitializedFrameCallee()); |
| RELEASE_AND_RETURN(throwScope, throwNotAConstructorErrorFromCallIC(globalObject, owner, callee, callLinkInfo)); |
| } |
| |
| CodeBlock** codeBlockSlot = calleeFrame->addressOfCodeBlock(); |
| functionExecutable->prepareForExecution<FunctionExecutable>(vm, callee, scope, kind, *codeBlockSlot); |
| RETURN_IF_EXCEPTION(throwScope, nullptr); |
| |
| codeBlock = *codeBlockSlot; |
| ASSERT(codeBlock); |
| |
| ArityCheckMode arity; |
| if (calleeFrame->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo->isVarargs()) |
| arity = ArityCheckMode::MustCheckArity; |
| else |
| arity = ArityCheckMode::ArityCheckNotRequired; |
| codePtr = functionExecutable->entrypointFor(kind, arity); |
| } |
| |
| switch (callLinkInfo->mode()) { |
| case CallLinkInfo::Mode::Init: { |
| if (!callLinkInfo->seenOnce()) |
| callLinkInfo->setSeen(); |
| else |
| linkMonomorphicCall(vm, owner, *callLinkInfo, codeBlock, callee, codePtr); |
| break; |
| } |
| case CallLinkInfo::Mode::Monomorphic: |
| case CallLinkInfo::Mode::Polymorphic: { |
| if (kind == CodeSpecializationKind::CodeForCall) { |
| linkPolymorphicCall(vm, owner, calleeFrame, *callLinkInfo, CallVariant(callee)); |
| break; |
| } |
| callLinkInfo->setVirtualCall(vm); |
| break; |
| } |
| case CallLinkInfo::Mode::Virtual: |
| break; |
| } |
| |
| return codePtr.taggedPtr(); |
| } |
| |
| ALWAYS_INLINE void* virtualForWithFunction(VM& vm, JSCell* owner, CallFrame* calleeFrame, CallLinkInfo* callLinkInfo, JSCell*& calleeAsFunctionCell) |
| { |
| auto throwScope = DECLARE_THROW_SCOPE(vm); |
| |
| CodeSpecializationKind kind = callLinkInfo->specializationKind(); |
| |
| JSValue calleeAsValue = calleeFrame->guaranteedJSValueCallee(); |
| calleeAsFunctionCell = getJSFunction(calleeAsValue); |
| if (!calleeAsFunctionCell) [[unlikely]] { |
| if (jsDynamicCast<InternalFunction*>(calleeAsValue)) { |
| CodePtr<JSEntryPtrTag> codePtr = vm.getCTIInternalFunctionTrampolineFor(kind); |
| ASSERT(!!codePtr); |
| return codePtr.taggedPtr(); |
| } |
| RELEASE_AND_RETURN(throwScope, handleHostCall(vm, owner, calleeFrame, calleeAsValue, callLinkInfo)); |
| } |
| |
| JSFunction* function = jsCast<JSFunction*>(calleeAsFunctionCell); |
| JSScope* scope = function->scopeUnchecked(); |
| ExecutableBase* executable = function->executable(); |
| |
| DeferTraps deferTraps(vm); // We can't jettison if we're going to call this CodeBlock. |
| |
| if (!executable->isHostFunction()) { |
| FunctionExecutable* functionExecutable = jsCast<FunctionExecutable*>(executable); |
| |
| if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) { |
| auto* globalObject = callLinkInfo->globalObjectForSlowPath(owner); |
| calleeFrame->setCallee(globalObject->partiallyInitializedFrameCallee()); |
| RELEASE_AND_RETURN(throwScope, throwNotAConstructorErrorFromCallIC(globalObject, owner, function, callLinkInfo)); |
| } |
| |
| CodeBlock** codeBlockSlot = calleeFrame->addressOfCodeBlock(); |
| functionExecutable->prepareForExecution<FunctionExecutable>(vm, function, scope, kind, *codeBlockSlot); |
| RETURN_IF_EXCEPTION(throwScope, nullptr); |
| } |
| |
| // FIXME: Support wasm IC. |
| // https://bugs.webkit.org/show_bug.cgi?id=220339 |
| return executable->entrypointFor(kind, ArityCheckMode::MustCheckArity).taggedPtr(); |
| } |
| |
| } // namespace JSC |