WebAssemblyPrototype.cpp [plain text]
#include "config.h"
#include "WebAssemblyPrototype.h"
#if ENABLE(WEBASSEMBLY)
#include "CatchScope.h"
#include "Exception.h"
#include "FunctionPrototype.h"
#include "JSCInlines.h"
#include "JSPromiseDeferred.h"
#include "JSWebAssemblyHelpers.h"
#include "JSWebAssemblyInstance.h"
#include "JSWebAssemblyModule.h"
#include "ObjectConstructor.h"
#include "PromiseDeferredTimer.h"
#include "StrongInlines.h"
#include "WasmBBQPlan.h"
#include "WasmWorklist.h"
#include "WebAssemblyInstanceConstructor.h"
#include "WebAssemblyModuleConstructor.h"
using JSC::Wasm::Plan;
using JSC::Wasm::BBQPlan;
namespace JSC {
static EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState*);
static EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState*);
static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState*);
}
#include "WebAssemblyPrototype.lut.h"
namespace JSC {
const ClassInfo WebAssemblyPrototype::s_info = { "WebAssembly", &Base::s_info, &prototypeTableWebAssembly, nullptr, CREATE_METHOD_TABLE(WebAssemblyPrototype) };
static void reject(ExecState* exec, CatchScope& catchScope, JSPromiseDeferred* promise)
{
Exception* exception = catchScope.exception();
ASSERT(exception);
catchScope.clearException();
promise->reject(exec, exception->value());
}
static EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState* exec)
{
VM& vm = exec->vm();
auto scope = DECLARE_CATCH_SCOPE(vm);
auto* globalObject = exec->lexicalGlobalObject();
JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, globalObject);
RETURN_IF_EXCEPTION(scope, { });
Vector<Strong<JSCell>> dependencies;
dependencies.append(Strong<JSCell>(vm, globalObject));
vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
Vector<uint8_t> source = createSourceBufferFromValue(vm, exec, exec->argument(0));
if (UNLIKELY(scope.exception()))
reject(exec, scope, promise);
else {
Wasm::Module::validateAsync(vm, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, globalObject] (VM& vm, Wasm::Module::ValidationResult&& result) mutable {
vm.promiseDeferredTimer->scheduleWorkSoon(promise, [promise, globalObject, result = WTFMove(result), &vm] () mutable {
auto scope = DECLARE_CATCH_SCOPE(vm);
ExecState* exec = globalObject->globalExec();
JSValue module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(result));
if (scope.exception()) {
reject(exec, scope, promise);
return;
}
promise->resolve(exec, module);
});
}));
}
return JSValue::encode(promise->promise());
}
enum class Resolve { WithInstance, WithModuleAndInstance };
static void resolve(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyInstance* instance, JSWebAssemblyModule* module, Ref<Wasm::CodeBlock>&& codeBlock, Resolve resolveKind)
{
auto scope = DECLARE_CATCH_SCOPE(vm);
instance->finalizeCreation(vm, exec, WTFMove(codeBlock));
RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
if (resolveKind == Resolve::WithInstance)
promise->resolve(exec, instance);
else {
JSObject* result = constructEmptyObject(exec);
result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("module")), module);
result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("instance")), instance);
promise->resolve(exec, result);
}
}
static void instantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyModule* module, JSObject* importObject, Resolve resolveKind)
{
auto scope = DECLARE_CATCH_SCOPE(vm);
JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
Vector<Strong<JSCell>> dependencies;
dependencies.append(Strong<JSCell>(vm, instance));
vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
module->module().compileAsync(vm, instance->memoryMode(), createSharedTask<Wasm::CodeBlock::CallbackType>([promise, instance, module, resolveKind] (VM& vm, Ref<Wasm::CodeBlock>&& refCodeBlock) mutable {
RefPtr<Wasm::CodeBlock> codeBlock = WTFMove(refCodeBlock);
vm.promiseDeferredTimer->scheduleWorkSoon(promise, [promise, instance, module, resolveKind, &vm, codeBlock = WTFMove(codeBlock)] () mutable {
ExecState* exec = instance->globalObject()->globalExec();
resolve(vm, exec, promise, instance, module, codeBlock.releaseNonNull(), resolveKind);
});
}));
}
static void compileAndInstantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSValue buffer, JSObject* importObject)
{
auto scope = DECLARE_CATCH_SCOPE(vm);
auto* globalObject = exec->lexicalGlobalObject();
Vector<Strong<JSCell>> dependencies;
dependencies.append(Strong<JSCell>(vm, importObject));
vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
Vector<uint8_t> source = createSourceBufferFromValue(vm, exec, buffer);
RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
Wasm::Module::validateAsync(vm, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, importObject, globalObject] (VM& vm, Wasm::Module::ValidationResult&& result) mutable {
vm.promiseDeferredTimer->scheduleWorkSoon(promise, [promise, importObject, globalObject, result = WTFMove(result), &vm] () mutable {
auto scope = DECLARE_CATCH_SCOPE(vm);
ExecState* exec = globalObject->globalExec();
JSWebAssemblyModule* module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(result));
if (scope.exception())
return reject(exec, scope, promise);
instantiate(vm, exec, promise, module, importObject, Resolve::WithModuleAndInstance);
});
}));
}
static EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState* exec)
{
VM& vm = exec->vm();
auto scope = DECLARE_CATCH_SCOPE(vm);
JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, exec->lexicalGlobalObject());
RETURN_IF_EXCEPTION(scope, { });
JSValue importArgument = exec->argument(1);
JSObject* importObject = importArgument.getObject();
if (!importArgument.isUndefined() && !importObject) {
promise->reject(exec, createTypeError(exec,
ASCIILiteral("second argument to WebAssembly.instantiate must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument)));
return JSValue::encode(promise->promise());
}
JSValue firstArgument = exec->argument(0);
if (auto* module = jsDynamicCast<JSWebAssemblyModule*>(vm, firstArgument))
instantiate(vm, exec, promise, module, importObject, Resolve::WithInstance);
else
compileAndInstantiate(vm, exec, promise, firstArgument, importObject);
return JSValue::encode(promise->promise());
}
static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState* exec)
{
VM& vm = exec->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
size_t byteOffset;
size_t byteSize;
uint8_t* base = getWasmBufferFromValue(exec, exec->argument(0), byteOffset, byteSize);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
BBQPlan plan(&vm, base + byteOffset, byteSize, BBQPlan::Validation, Plan::dontFinalize());
return JSValue::encode(jsBoolean(plan.parseAndValidateModule()));
}
WebAssemblyPrototype* WebAssemblyPrototype::create(VM& vm, JSGlobalObject*, Structure* structure)
{
auto* object = new (NotNull, allocateCell<WebAssemblyPrototype>(vm.heap)) WebAssemblyPrototype(vm, structure);
object->finishCreation(vm);
return object;
}
Structure* WebAssemblyPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
}
void WebAssemblyPrototype::finishCreation(VM& vm)
{
Base::finishCreation(vm);
}
WebAssemblyPrototype::WebAssemblyPrototype(VM& vm, Structure* structure)
: Base(vm, structure)
{
}
}
#endif // ENABLE(WEBASSEMBLY)