1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:38:11 +00:00

LibWeb: Replace GlobalObject with VM in WebAssembly AOs [Part 1/4]

This commit is contained in:
Linus Groh 2022-08-21 21:16:30 +01:00
parent 40a70461a0
commit 5f1fe4b012
7 changed files with 34 additions and 37 deletions

View file

@ -29,14 +29,13 @@ JS::ThrowCompletionOr<JS::Value> WebAssemblyInstanceConstructor::call()
JS::ThrowCompletionOr<JS::Object*> WebAssemblyInstanceConstructor::construct(FunctionObject&) JS::ThrowCompletionOr<JS::Object*> WebAssemblyInstanceConstructor::construct(FunctionObject&)
{ {
auto& vm = this->vm(); auto& vm = this->vm();
auto& global_object = this->global_object(); auto& realm = *vm.current_realm();
auto& realm = *global_object.associated_realm();
auto* module_argument = TRY(vm.argument(0).to_object(vm)); auto* module_argument = TRY(vm.argument(0).to_object(vm));
if (!is<WebAssemblyModuleObject>(module_argument)) if (!is<WebAssemblyModuleObject>(module_argument))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "WebAssembly.Module"); return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "WebAssembly.Module");
auto& module_object = static_cast<WebAssemblyModuleObject&>(*module_argument); auto& module_object = static_cast<WebAssemblyModuleObject&>(*module_argument);
auto result = TRY(WebAssemblyObject::instantiate_module(module_object.module(), vm, global_object)); auto result = TRY(WebAssemblyObject::instantiate_module(vm, module_object.module()));
return heap().allocate<WebAssemblyInstanceObject>(realm, realm, result); return heap().allocate<WebAssemblyInstanceObject>(realm, realm, result);
} }

View file

@ -27,6 +27,8 @@ void WebAssemblyInstanceObject::initialize(JS::Realm& realm)
{ {
Object::initialize(realm); Object::initialize(realm);
auto& vm = this->vm();
VERIFY(!m_exports_object); VERIFY(!m_exports_object);
m_exports_object = create(realm, nullptr); m_exports_object = create(realm, nullptr);
auto& instance = this->instance(); auto& instance = this->instance();
@ -36,7 +38,7 @@ void WebAssemblyInstanceObject::initialize(JS::Realm& realm)
[&](Wasm::FunctionAddress const& address) { [&](Wasm::FunctionAddress const& address) {
Optional<JS::FunctionObject*> object = cache.function_instances.get(address); Optional<JS::FunctionObject*> object = cache.function_instances.get(address);
if (!object.has_value()) { if (!object.has_value()) {
object = create_native_function(realm.global_object(), address, export_.name()); object = create_native_function(vm, address, export_.name());
cache.function_instances.set(address, *object); cache.function_instances.set(address, *object);
} }
m_exports_object->define_direct_property(export_.name(), *object, JS::default_attributes); m_exports_object->define_direct_property(export_.name(), *object, JS::default_attributes);

View file

@ -29,11 +29,10 @@ JS::ThrowCompletionOr<JS::Value> WebAssemblyModuleConstructor::call()
JS::ThrowCompletionOr<JS::Object*> WebAssemblyModuleConstructor::construct(FunctionObject&) JS::ThrowCompletionOr<JS::Object*> WebAssemblyModuleConstructor::construct(FunctionObject&)
{ {
auto& vm = this->vm(); auto& vm = this->vm();
auto& global_object = this->global_object(); auto& realm = *vm.current_realm();
auto& realm = *global_object.associated_realm();
auto* buffer_object = TRY(vm.argument(0).to_object(vm)); auto* buffer_object = TRY(vm.argument(0).to_object(vm));
auto result = TRY(parse_module(global_object, buffer_object)); auto result = TRY(parse_module(vm, buffer_object));
return heap().allocate<WebAssemblyModuleObject>(realm, realm, result); return heap().allocate<WebAssemblyModuleObject>(realm, realm, result);
} }

View file

@ -95,7 +95,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::validate)
auto buffer = TRY(vm.argument(0).to_object(vm)); auto buffer = TRY(vm.argument(0).to_object(vm));
// 2. Compile stableBytes as a WebAssembly module and store the results as module. // 2. Compile stableBytes as a WebAssembly module and store the results as module.
auto maybe_module = parse_module(global_object, buffer); auto maybe_module = parse_module(vm, buffer);
// 3. If module is error, return false. // 3. If module is error, return false.
if (maybe_module.is_error()) if (maybe_module.is_error())
@ -116,10 +116,8 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::validate)
return JS::Value(true); return JS::Value(true);
} }
JS::ThrowCompletionOr<size_t> parse_module(JS::GlobalObject& global_object, JS::Object* buffer_object) JS::ThrowCompletionOr<size_t> parse_module(JS::VM& vm, JS::Object* buffer_object)
{ {
auto& vm = global_object.vm();
ReadonlyBytes data; ReadonlyBytes data;
if (is<JS::ArrayBuffer>(buffer_object)) { if (is<JS::ArrayBuffer>(buffer_object)) {
auto& buffer = static_cast<JS::ArrayBuffer&>(*buffer_object); auto& buffer = static_cast<JS::ArrayBuffer&>(*buffer_object);
@ -170,7 +168,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::compile)
return promise; return promise;
} }
auto* buffer = buffer_or_error.release_value(); auto* buffer = buffer_or_error.release_value();
auto result = parse_module(global_object, buffer); auto result = parse_module(vm, buffer);
if (result.is_error()) if (result.is_error())
promise->reject(*result.release_error().value()); promise->reject(*result.release_error().value());
else else
@ -178,7 +176,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::compile)
return promise; return promise;
} }
JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(Wasm::Module const& module, JS::VM& vm, JS::GlobalObject& global_object) JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(JS::VM& vm, Wasm::Module const& module)
{ {
Wasm::Linker linker { module }; Wasm::Linker linker { module };
HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports; HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
@ -214,7 +212,7 @@ JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(Wasm::Module
[&](auto&, auto& arguments) -> Wasm::Result { [&](auto&, auto& arguments) -> Wasm::Result {
JS::MarkedVector<JS::Value> argument_values { vm.heap() }; JS::MarkedVector<JS::Value> argument_values { vm.heap() };
for (auto& entry : arguments) for (auto& entry : arguments)
argument_values.append(to_js_value(global_object, entry)); argument_values.append(to_js_value(vm, entry));
auto result_or_error = JS::call(vm, function, JS::js_undefined(), move(argument_values)); auto result_or_error = JS::call(vm, function, JS::js_undefined(), move(argument_values));
if (result_or_error.is_error()) { if (result_or_error.is_error()) {
@ -224,7 +222,7 @@ JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(Wasm::Module
return Wasm::Result { Vector<Wasm::Value> {} }; return Wasm::Result { Vector<Wasm::Value> {} };
if (type.results().size() == 1) { if (type.results().size() == 1) {
auto value_or_error = to_webassembly_value(global_object, result_or_error.release_value(), type.results().first()); auto value_or_error = to_webassembly_value(vm, result_or_error.release_value(), type.results().first());
if (value_or_error.is_error()) if (value_or_error.is_error())
return Wasm::Trap {}; return Wasm::Trap {};
@ -256,7 +254,7 @@ JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(Wasm::Module
// FIXME: Throw a LinkError instead. // FIXME: Throw a LinkError instead.
return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a BigInteger to a Number"); return vm.throw_completion<JS::TypeError>("LinkError: Import resolution attempted to cast a BigInteger to a Number");
} }
auto cast_value = TRY(to_webassembly_value(global_object, import_, type.type())); auto cast_value = TRY(to_webassembly_value(vm, import_, type.type()));
address = s_abstract_machine.store().allocate({ type.type(), false }, cast_value); address = s_abstract_machine.store().allocate({ type.type(), false }, cast_value);
} else { } else {
// FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2 // FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
@ -334,7 +332,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
Wasm::Module const* module { nullptr }; Wasm::Module const* module { nullptr };
if (is<JS::ArrayBuffer>(buffer) || is<JS::TypedArrayBase>(buffer)) { if (is<JS::ArrayBuffer>(buffer) || is<JS::TypedArrayBase>(buffer)) {
auto result = parse_module(global_object, buffer); auto result = parse_module(vm, buffer);
if (result.is_error()) { if (result.is_error()) {
promise->reject(*result.release_error().value()); promise->reject(*result.release_error().value());
return promise; return promise;
@ -350,7 +348,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
} }
VERIFY(module); VERIFY(module);
auto result = instantiate_module(*module, vm, global_object); auto result = instantiate_module(vm, *module);
if (result.is_error()) { if (result.is_error()) {
promise->reject(*result.release_error().value()); promise->reject(*result.release_error().value());
} else { } else {
@ -367,9 +365,9 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
return promise; return promise;
} }
JS::Value to_js_value(JS::GlobalObject& global_object, Wasm::Value& wasm_value) JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value)
{ {
auto& realm = *global_object.associated_realm(); auto& realm = *vm.current_realm();
switch (wasm_value.type().kind()) { switch (wasm_value.type().kind()) {
case Wasm::ValueType::I64: case Wasm::ValueType::I64:
return realm.heap().allocate<JS::BigInt>(realm, ::Crypto::SignedBigInteger::create_from(wasm_value.to<i64>().value())); return realm.heap().allocate<JS::BigInt>(realm, ::Crypto::SignedBigInteger::create_from(wasm_value.to<i64>().value()));
@ -381,7 +379,7 @@ JS::Value to_js_value(JS::GlobalObject& global_object, Wasm::Value& wasm_value)
return JS::Value(static_cast<double>(wasm_value.to<float>().value())); return JS::Value(static_cast<double>(wasm_value.to<float>().value()));
case Wasm::ValueType::FunctionReference: case Wasm::ValueType::FunctionReference:
// FIXME: What's the name of a function reference that isn't exported? // FIXME: What's the name of a function reference that isn't exported?
return create_native_function(global_object, wasm_value.to<Wasm::Reference::Func>().value().address, "FIXME_IHaveNoIdeaWhatThisShouldBeCalled"); return create_native_function(vm, wasm_value.to<Wasm::Reference::Func>().value().address, "FIXME_IHaveNoIdeaWhatThisShouldBeCalled");
case Wasm::ValueType::NullFunctionReference: case Wasm::ValueType::NullFunctionReference:
return JS::js_null(); return JS::js_null();
case Wasm::ValueType::ExternReference: case Wasm::ValueType::ExternReference:
@ -391,10 +389,9 @@ JS::Value to_js_value(JS::GlobalObject& global_object, Wasm::Value& wasm_value)
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::GlobalObject& global_object, JS::Value value, Wasm::ValueType const& type) JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value value, Wasm::ValueType const& type)
{ {
static ::Crypto::SignedBigInteger two_64 = "1"_sbigint.shift_left(64); static ::Crypto::SignedBigInteger two_64 = "1"_sbigint.shift_left(64);
auto& vm = global_object.vm();
switch (type.kind()) { switch (type.kind()) {
case Wasm::ValueType::I64: { case Wasm::ValueType::I64: {
@ -441,9 +438,9 @@ JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::GlobalObject& global
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
JS::NativeFunction* create_native_function(JS::GlobalObject& global_object, Wasm::FunctionAddress address, String const& name) JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress address, String const& name)
{ {
auto& realm = *global_object.associated_realm(); auto& realm = *vm.current_realm();
Optional<Wasm::FunctionType> type; Optional<Wasm::FunctionType> type;
WebAssemblyObject::s_abstract_machine.store().get(address)->visit([&](auto const& value) { type = value.type(); }); WebAssemblyObject::s_abstract_machine.store().get(address)->visit([&](auto const& value) { type = value.type(); });
if (auto entry = WebAssemblyObject::s_global_cache.function_instances.get(address); entry.has_value()) if (auto entry = WebAssemblyObject::s_global_cache.function_instances.get(address); entry.has_value())
@ -460,7 +457,7 @@ JS::NativeFunction* create_native_function(JS::GlobalObject& global_object, Wasm
// Grab as many values as needed and convert them. // Grab as many values as needed and convert them.
size_t index = 0; size_t index = 0;
for (auto& type : type.parameters()) for (auto& type : type.parameters())
values.append(TRY(to_webassembly_value(global_object, vm.argument(index++), type))); values.append(TRY(to_webassembly_value(vm, vm.argument(index++), type)));
auto result = WebAssemblyObject::s_abstract_machine.invoke(address, move(values)); auto result = WebAssemblyObject::s_abstract_machine.invoke(address, move(values));
// FIXME: Use the convoluted mapping of errors defined in the spec. // FIXME: Use the convoluted mapping of errors defined in the spec.
@ -471,11 +468,11 @@ JS::NativeFunction* create_native_function(JS::GlobalObject& global_object, Wasm
return JS::js_undefined(); return JS::js_undefined();
if (result.values().size() == 1) if (result.values().size() == 1)
return to_js_value(global_object, result.values().first()); return to_js_value(vm, result.values().first());
Vector<JS::Value> result_values; Vector<JS::Value> result_values;
for (auto& entry : result.values()) for (auto& entry : result.values())
result_values.append(to_js_value(global_object, entry)); result_values.append(to_js_value(vm, entry));
return JS::Value(JS::Array::create_from(realm, result_values)); return JS::Value(JS::Array::create_from(realm, result_values));
}); });

View file

@ -14,10 +14,10 @@
namespace Web::Bindings { namespace Web::Bindings {
class WebAssemblyMemoryObject; class WebAssemblyMemoryObject;
JS::ThrowCompletionOr<size_t> parse_module(JS::GlobalObject& global_object, JS::Object* buffer); JS::ThrowCompletionOr<size_t> parse_module(JS::VM&, JS::Object* buffer);
JS::NativeFunction* create_native_function(JS::GlobalObject& global_object, Wasm::FunctionAddress address, String const& name); JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, String const& name);
JS::Value to_js_value(JS::GlobalObject& global_object, Wasm::Value& wasm_value); JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value);
JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::GlobalObject& global_object, JS::Value value, Wasm::ValueType const& type); JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type);
class WebAssemblyObject final : public JS::Object { class WebAssemblyObject final : public JS::Object {
JS_OBJECT(WebAssemblyObject, JS::Object); JS_OBJECT(WebAssemblyObject, JS::Object);
@ -29,7 +29,7 @@ public:
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
static JS::ThrowCompletionOr<size_t> instantiate_module(Wasm::Module const&, JS::VM&, JS::GlobalObject&); static JS::ThrowCompletionOr<size_t> instantiate_module(JS::VM&, Wasm::Module const&);
struct CompiledWebAssemblyModule { struct CompiledWebAssemblyModule {
explicit CompiledWebAssemblyModule(Wasm::Module&& module) explicit CompiledWebAssemblyModule(Wasm::Module&& module)

View file

@ -65,7 +65,7 @@ JS::ThrowCompletionOr<JS::Object*> WebAssemblyTableConstructor::construct(Functi
if (value_value.is_undefined()) if (value_value.is_undefined())
return Wasm::Value(*reference_type, 0ull); return Wasm::Value(*reference_type, 0ull);
return to_webassembly_value(global_object, value_value, *reference_type); return to_webassembly_value(vm, value_value, *reference_type);
}()); }());
auto& reference = reference_value.value().get<Wasm::Reference>(); auto& reference = reference_value.value().get<Wasm::Reference>();

View file

@ -38,7 +38,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::grow)
if (value_value.is_undefined()) if (value_value.is_undefined())
return Wasm::Value(table->type().element_type(), 0ull); return Wasm::Value(table->type().element_type(), 0ull);
return to_webassembly_value(global_object, value_value, table->type().element_type()); return to_webassembly_value(vm, value_value, table->type().element_type());
}()); }());
auto& reference = reference_value.value().get<Wasm::Reference>(); auto& reference = reference_value.value().get<Wasm::Reference>();
@ -70,7 +70,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::get)
return JS::js_undefined(); return JS::js_undefined();
Wasm::Value wasm_value { ref.value() }; Wasm::Value wasm_value { ref.value() };
return to_js_value(global_object, wasm_value); return to_js_value(vm, wasm_value);
} }
JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::set) JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::set)
@ -94,7 +94,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::set)
if (value_value.is_undefined()) if (value_value.is_undefined())
return Wasm::Value(table->type().element_type(), 0ull); return Wasm::Value(table->type().element_type(), 0ull);
return to_webassembly_value(global_object, value_value, table->type().element_type()); return to_webassembly_value(vm, value_value, table->type().element_type());
}()); }());
auto& reference = reference_value.value().get<Wasm::Reference>(); auto& reference = reference_value.value().get<Wasm::Reference>();