blob: 1cc7ff43ae96cef079c3368bce433695ace2826f [file] [log] [blame]
/*
* Copyright (C) 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.
*/
#pragma once
#include "Repatch.h"
#include "VMTrapsInlines.h"
namespace JSC {
inline SlowPathReturnType handleHostCall(JSGlobalObject* globalObject, CallFrame* calleeFrame, JSValue callee, CallLinkInfo* callLinkInfo)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
calleeFrame->setCodeBlock(nullptr);
if (callLinkInfo->specializationKind() == 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);
DisallowGC disallowGC;
if (UNLIKELY(scope.exception())) {
return encodeResult(
vm.getCTIThrowExceptionFromCallSlowPath().code().executableAddress(),
reinterpret_cast<void*>(KeepTheFrame));
}
return encodeResult(
LLInt::getHostCallReturnValueEntrypoint().code().executableAddress(),
reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
ASSERT(callData.type == CallData::Type::None);
throwException(globalObject, scope, createNotAFunctionError(globalObject, callee));
return encodeResult(
vm.getCTIThrowExceptionFromCallSlowPath().code().executableAddress(),
reinterpret_cast<void*>(KeepTheFrame));
}
ASSERT(callLinkInfo->specializationKind() == 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);
DisallowGC disallowGC;
if (UNLIKELY(scope.exception())) {
return encodeResult(
vm.getCTIThrowExceptionFromCallSlowPath().code().executableAddress(),
reinterpret_cast<void*>(KeepTheFrame));
}
return encodeResult(LLInt::getHostCallReturnValueEntrypoint().code().executableAddress(), reinterpret_cast<void*>(KeepTheFrame));
}
ASSERT(constructData.type == CallData::Type::None);
throwException(globalObject, scope, createNotAConstructorError(globalObject, callee));
return encodeResult(
vm.getCTIThrowExceptionFromCallSlowPath().code().executableAddress(),
reinterpret_cast<void*>(KeepTheFrame));
}
ALWAYS_INLINE SlowPathReturnType linkFor(CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)
{
CallFrame* callFrame = calleeFrame->callerFrame();
VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
CodeSpecializationKind kind = callLinkInfo->specializationKind();
NativeCallFrameTracer tracer(vm, callFrame);
RELEASE_ASSERT(!callLinkInfo->isDirect());
JSValue calleeAsValue = calleeFrame->guaranteedJSValueCallee();
JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue);
if (!calleeAsFunctionCell) {
if (auto* internalFunction = jsDynamicCast<InternalFunction*>(calleeAsValue)) {
MacroAssemblerCodePtr<JSEntryPtrTag> codePtr = vm.getCTIInternalFunctionTrampolineFor(kind);
RELEASE_ASSERT(!!codePtr);
if (!callLinkInfo->seenOnce())
callLinkInfo->setSeen();
else
linkMonomorphicCall(vm, calleeFrame, *callLinkInfo, nullptr, internalFunction, codePtr);
void* linkedTarget = codePtr.executableAddress();
return encodeResult(linkedTarget, reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
RELEASE_AND_RETURN(throwScope, handleHostCall(globalObject, calleeFrame, calleeAsValue, callLinkInfo));
}
JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell);
JSScope* scope = callee->scopeUnchecked();
ExecutableBase* executable = callee->executable();
MacroAssemblerCodePtr<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, MustCheckArity);
} else {
FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
auto handleThrowException = [&] () {
void* throwTarget = vm.getCTIThrowExceptionFromCallSlowPath().code().executableAddress();
return encodeResult(throwTarget, reinterpret_cast<void*>(KeepTheFrame));
};
if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) {
throwException(globalObject, throwScope, createNotAConstructorError(globalObject, callee));
return handleThrowException();
}
CodeBlock** codeBlockSlot = calleeFrame->addressOfCodeBlock();
functionExecutable->prepareForExecution<FunctionExecutable>(vm, callee, scope, kind, *codeBlockSlot);
RETURN_IF_EXCEPTION(throwScope, handleThrowException());
codeBlock = *codeBlockSlot;
ASSERT(codeBlock);
ArityCheckMode arity;
if (calleeFrame->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo->isVarargs())
arity = MustCheckArity;
else
arity = ArityCheckNotRequired;
codePtr = functionExecutable->entrypointFor(kind, arity);
}
if (!callLinkInfo->seenOnce())
callLinkInfo->setSeen();
else
linkMonomorphicCall(vm, calleeFrame, *callLinkInfo, codeBlock, callee, codePtr);
return encodeResult(codePtr.executableAddress(), reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
ALWAYS_INLINE SlowPathReturnType virtualForWithFunction(JSGlobalObject* globalObject, CallFrame* calleeFrame, CallLinkInfo* callLinkInfo, JSCell*& calleeAsFunctionCell)
{
CallFrame* callFrame = calleeFrame->callerFrame();
VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
CodeSpecializationKind kind = callLinkInfo->specializationKind();
NativeCallFrameTracer tracer(vm, callFrame);
JSValue calleeAsValue = calleeFrame->guaranteedJSValueCallee();
calleeAsFunctionCell = getJSFunction(calleeAsValue);
if (UNLIKELY(!calleeAsFunctionCell)) {
if (jsDynamicCast<InternalFunction*>(calleeAsValue)) {
MacroAssemblerCodePtr<JSEntryPtrTag> codePtr = vm.getCTIInternalFunctionTrampolineFor(kind);
ASSERT(!!codePtr);
return encodeResult(codePtr.executableAddress(), reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
RELEASE_AND_RETURN(throwScope, handleHostCall(globalObject, 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);
auto handleThrowException = [&] () {
void* throwTarget = vm.getCTIThrowExceptionFromCallSlowPath().code().executableAddress();
return encodeResult(throwTarget, reinterpret_cast<void*>(KeepTheFrame));
};
if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) {
throwException(globalObject, throwScope, createNotAConstructorError(globalObject, function));
return handleThrowException();
}
CodeBlock** codeBlockSlot = calleeFrame->addressOfCodeBlock();
functionExecutable->prepareForExecution<FunctionExecutable>(vm, function, scope, kind, *codeBlockSlot);
RETURN_IF_EXCEPTION(throwScope, handleThrowException());
}
// FIXME: Support wasm IC.
// https://bugs.webkit.org/show_bug.cgi?id=220339
return encodeResult(executable->entrypointFor(
kind, MustCheckArity).executableAddress(),
reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
} // namespace JSC