blob: 7c308c6a69657fa428a4c5c047e28eafa4839145 [file] [log] [blame]
/*
* 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