WebAssemblyFunction.cpp [plain text]
#include "config.h"
#include "WebAssemblyFunction.h"
#if ENABLE(WEBASSEMBLY)
#include "B3Compilation.h"
#include "JSCInlines.h"
#include "JSFunctionInlines.h"
#include "JSObject.h"
#include "JSWebAssemblyInstance.h"
#include "JSWebAssemblyMemory.h"
#include "JSWebAssemblyRuntimeError.h"
#include "LLIntThunks.h"
#include "ProtoCallFrame.h"
#include "VM.h"
#include "WasmCallee.h"
#include "WasmContext.h"
#include "WasmFormat.h"
#include "WasmMemory.h"
#include <wtf/FastTLS.h>
#include <wtf/SystemTracing.h>
namespace JSC {
const ClassInfo WebAssemblyFunction::s_info = { "WebAssemblyFunction", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunction) };
static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
{
VM& vm = exec->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, exec->jsCallee());
if (!wasmFunction)
return JSValue::encode(throwException(exec, scope, createTypeError(exec, "expected a WebAssembly function", defaultSourceAppender, runtimeTypeForValue(exec->jsCallee()))));
Wasm::SignatureIndex signatureIndex = wasmFunction->signatureIndex();
const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
ASSERT(wasmFunction->instance()->codeBlock()->isSafeToRun(wasmFunction->instance()->memory()));
{
for (unsigned argIndex = 0; argIndex < signature.argumentCount(); ++argIndex) {
if (signature.argument(argIndex) == Wasm::I64) {
JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyRuntimeErrorStructure(),
"WebAssembly function with an i64 argument can't be called from JavaScript");
return JSValue::encode(throwException(exec, scope, error));
}
}
if (signature.returnType() == Wasm::I64) {
JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyRuntimeErrorStructure(),
"WebAssembly function that returns i64 can't be called from JavaScript");
return JSValue::encode(throwException(exec, scope, error));
}
}
TraceScope traceScope(WebAssemblyExecuteStart, WebAssemblyExecuteEnd);
Vector<JSValue> boxedArgs;
Wasm::Context* wasmContext = wasmFunction->instance();
if (!Wasm::useFastTLSForContext())
boxedArgs.append(wasmContext);
for (unsigned argIndex = 0; argIndex < signature.argumentCount(); ++argIndex) {
JSValue arg = exec->argument(argIndex);
switch (signature.argument(argIndex)) {
case Wasm::I32:
arg = JSValue::decode(arg.toInt32(exec));
break;
case Wasm::F32:
arg = JSValue::decode(bitwise_cast<uint32_t>(arg.toFloat(exec)));
break;
case Wasm::F64:
arg = JSValue::decode(bitwise_cast<uint64_t>(arg.toNumber(exec)));
break;
case Wasm::Void:
case Wasm::I64:
case Wasm::Func:
case Wasm::Anyfunc:
RELEASE_ASSERT_NOT_REACHED();
}
RETURN_IF_EXCEPTION(scope, encodedJSValue());
boxedArgs.append(arg);
}
JSValue firstArgument = JSValue();
int argCount = 1;
JSValue* remainingArgs = nullptr;
if (boxedArgs.size()) {
remainingArgs = boxedArgs.data();
firstArgument = *remainingArgs;
remainingArgs++;
argCount = boxedArgs.size();
}
ProtoCallFrame protoCallFrame;
protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
Wasm::Context* prevWasmContext = Wasm::loadContext(vm);
{
const intptr_t sp = bitwise_cast<intptr_t>(&sp); const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
const intptr_t stackSpaceUsed = 2 * frameSize; if (UNLIKELY((sp < stackSpaceUsed) || ((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit()))))
return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
}
Wasm::storeContext(vm, wasmContext);
ASSERT(wasmFunction->instance());
ASSERT(wasmFunction->instance() == Wasm::loadContext(vm));
EncodedJSValue rawResult = vmEntryToWasm(wasmFunction->jsEntrypoint(), &vm, &protoCallFrame);
wasmContext->use();
if (prevWasmContext != wasmContext) {
wasmContext->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
}
Wasm::storeContext(vm, prevWasmContext);
RETURN_IF_EXCEPTION(scope, { });
switch (signature.returnType()) {
case Wasm::Void:
return JSValue::encode(jsUndefined());
case Wasm::I32:
return JSValue::encode(jsNumber(static_cast<int32_t>(rawResult)));
case Wasm::F32:
return JSValue::encode(jsNumber(purifyNaN(static_cast<double>(bitwise_cast<float>(static_cast<int32_t>(rawResult))))));
case Wasm::F64:
return JSValue::encode(jsNumber(purifyNaN(bitwise_cast<double>(rawResult))));
case Wasm::I64:
case Wasm::Func:
case Wasm::Anyfunc:
RELEASE_ASSERT_NOT_REACHED();
}
return EncodedJSValue();
}
WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, unsigned length, const String& name, JSWebAssemblyInstance* instance, Wasm::Callee& jsEntrypoint, Wasm::WasmEntrypointLoadLocation wasmEntrypoint, Wasm::SignatureIndex signatureIndex)
{
NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
Structure* structure = globalObject->webAssemblyFunctionStructure();
WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, jsEntrypoint, wasmEntrypoint, signatureIndex);
function->finishCreation(vm, executable, length, name, instance);
ASSERT_WITH_MESSAGE(!function->isLargeAllocation(), "WebAssemblyFunction should be allocated not in large allocation since it is JSCallee.");
return function;
}
Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
ASSERT(globalObject);
return Structure::create(vm, globalObject, prototype, TypeInfo(WebAssemblyFunctionType, StructureFlags), info());
}
WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, Wasm::Callee& jsEntrypoint, Wasm::WasmEntrypointLoadLocation wasmEntrypoint, Wasm::SignatureIndex signatureIndex)
: Base(vm, globalObject, structure)
, m_jsEntrypoint(jsEntrypoint.entrypoint())
, m_wasmFunction(Wasm::CallableFunction(signatureIndex, wasmEntrypoint))
{ }
}
#endif // ENABLE(WEBASSEMBLY)