diff --git a/include/v8-callbacks.h b/include/v8-callbacks.h index 0ffdfb6656..f3e96c3706 100644 --- a/include/v8-callbacks.h +++ b/include/v8-callbacks.h @@ -328,6 +328,10 @@ using WasmSimdEnabledCallback = bool (*)(Local context); // --- Callback for checking if WebAssembly exceptions are enabled --- using WasmExceptionsEnabledCallback = bool (*)(Local context); +// --- Callback for checking if WebAssembly GC is enabled --- +// If the callback returns true, it will also enable Wasm stringrefs. +using WasmGCEnabledCallback = bool (*)(Local context); + // --- Callback for checking if the SharedArrayBuffer constructor is enabled --- using SharedArrayBufferConstructorEnabledCallback = bool (*)(Local context); diff --git a/include/v8-isolate.h b/include/v8-isolate.h index da0a4ea2a9..a32a4e755e 100644 --- a/include/v8-isolate.h +++ b/include/v8-isolate.h @@ -1513,6 +1513,13 @@ class V8_EXPORT Isolate { V8_DEPRECATED("Wasm exceptions are always enabled") void SetWasmExceptionsEnabledCallback(WasmExceptionsEnabledCallback callback); + /** + * Register callback to control whehter Wasm GC is enabled. + * The callback overwrites the value of the flag. + * If the callback returns true, it will also enable Wasm stringrefs. + */ + void SetWasmGCEnabledCallback(WasmGCEnabledCallback callback); + void SetSharedArrayBufferConstructorEnabledCallback( SharedArrayBufferConstructorEnabledCallback callback); diff --git a/src/api/api.cc b/src/api/api.cc index 571acb5c45..b7b919d10c 100644 --- a/src/api/api.cc +++ b/src/api/api.cc @@ -9766,6 +9766,9 @@ CALLBACK_SETTER(WasmAsyncResolvePromiseCallback, CALLBACK_SETTER(WasmLoadSourceMapCallback, WasmLoadSourceMapCallback, wasm_load_source_map_callback) +CALLBACK_SETTER(WasmGCEnabledCallback, WasmGCEnabledCallback, + wasm_gc_enabled_callback) + CALLBACK_SETTER(SharedArrayBufferConstructorEnabledCallback, SharedArrayBufferConstructorEnabledCallback, sharedarraybuffer_constructor_enabled_callback) diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc index bd0453b2b7..251032ca10 100644 --- a/src/execution/isolate.cc +++ b/src/execution/isolate.cc @@ -2940,6 +2940,31 @@ bool Isolate::IsSharedArrayBufferConstructorEnabled(Handle context) { return false; } +bool Isolate::IsWasmGCEnabled(Handle context) { +#ifdef V8_ENABLE_WEBASSEMBLY + if (wasm_gc_enabled_callback()) { + v8::Local api_context = v8::Utils::ToLocal(context); + return wasm_gc_enabled_callback()(api_context); + } + return v8_flags.experimental_wasm_gc; +#else + return false; +#endif +} + +bool Isolate::IsWasmStringRefEnabled(Handle context) { + // If Wasm GC is explicitly enabled via a callback, also enable stringref. +#ifdef V8_ENABLE_WEBASSEMBLY + if (wasm_gc_enabled_callback()) { + v8::Local api_context = v8::Utils::ToLocal(context); + return wasm_gc_enabled_callback()(api_context); + } + return v8_flags.experimental_wasm_stringref; +#else + return false; +#endif +} + Handle Isolate::GetIncumbentContext() { JavaScriptFrameIterator it(this); diff --git a/src/execution/isolate.h b/src/execution/isolate.h index 78895afe10..0ca6d98e9c 100644 --- a/src/execution/isolate.h +++ b/src/execution/isolate.h @@ -510,6 +510,7 @@ using DebugObjectCache = std::vector>; V(WasmLoadSourceMapCallback, wasm_load_source_map_callback, nullptr) \ V(WasmSimdEnabledCallback, wasm_simd_enabled_callback, nullptr) \ V(WasmExceptionsEnabledCallback, wasm_exceptions_enabled_callback, nullptr) \ + V(WasmGCEnabledCallback, wasm_gc_enabled_callback, nullptr) \ /* State for Relocatable. */ \ V(Relocatable*, relocatable_top, nullptr) \ V(DebugObjectCache*, string_stream_debug_object_cache, nullptr) \ @@ -769,6 +770,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { bool IsSharedArrayBufferConstructorEnabled(Handle context); + bool IsWasmGCEnabled(Handle context); + bool IsWasmStringRefEnabled(Handle context); + THREAD_LOCAL_TOP_ADDRESS(Context, pending_handler_context) THREAD_LOCAL_TOP_ADDRESS(Address, pending_handler_entrypoint) THREAD_LOCAL_TOP_ADDRESS(Address, pending_handler_constant_pool) diff --git a/src/runtime/runtime-test-wasm.cc b/src/runtime/runtime-test-wasm.cc index 3aefe862c6..e6044fbf55 100644 --- a/src/runtime/runtime-test-wasm.cc +++ b/src/runtime/runtime-test-wasm.cc @@ -494,5 +494,17 @@ RUNTIME_FUNCTION(Runtime_FreezeWasmLazyCompilation) { return ReadOnlyRoots(isolate).undefined_value(); } +// This runtime function enables WebAssembly GC through an embedder +// callback and thereby bypasses the value in v8_flags. +RUNTIME_FUNCTION(Runtime_SetWasmGCEnabled) { + DCHECK_EQ(1, args.length()); + bool enable = args.at(0)->BooleanValue(isolate); + v8::Isolate* v8_isolate = reinterpret_cast(isolate); + WasmGCEnabledCallback enabled = [](v8::Local) { return true; }; + WasmGCEnabledCallback disabled = [](v8::Local) { return false; }; + v8_isolate->SetWasmGCEnabledCallback(enable ? enabled : disabled); + return ReadOnlyRoots(isolate).undefined_value(); +} + } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 860c1c4f8a..09e0a848d1 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -668,6 +668,7 @@ namespace internal { F(SerializeWasmModule, 1, 1) \ F(SetWasmCompileControls, 2, 1) \ F(SetWasmInstantiateControls, 0, 1) \ + F(SetWasmGCEnabled, 1, 1) \ F(WasmGetNumberOfInstances, 1, 1) \ F(WasmNumCodeSpaces, 1, 1) \ F(WasmEnterDebugging, 0, 1) \ diff --git a/src/wasm/wasm-features.cc b/src/wasm/wasm-features.cc index ab26412d89..1ecd5c8bb1 100644 --- a/src/wasm/wasm-features.cc +++ b/src/wasm/wasm-features.cc @@ -33,6 +33,12 @@ WasmFeatures WasmFeatures::FromIsolate(Isolate* isolate) { WasmFeatures WasmFeatures::FromContext(Isolate* isolate, Handle context) { WasmFeatures features = WasmFeatures::FromFlags(); + if (isolate->IsWasmGCEnabled(handle(isolate->context(), isolate))) { + features.Add(kFeature_gc); + } + if (isolate->IsWasmStringRefEnabled(handle(isolate->context(), isolate))) { + features.Add(kFeature_stringref); + } // This space intentionally left blank for future Wasm origin trials. return features; } diff --git a/test/mjsunit/wasm/origin-trial-flags.js b/test/mjsunit/wasm/origin-trial-flags.js new file mode 100644 index 0000000000..46363e1747 --- /dev/null +++ b/test/mjsunit/wasm/origin-trial-flags.js @@ -0,0 +1,46 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --noexperimental-wasm-gc --no-experimental-wasm-stringref +// Flags: --allow-natives-syntax + +load("test/mjsunit/wasm/wasm-module-builder.js"); + +function instantiateModuleWithGC() { + // Build a WebAssembly module which uses Wasm GC features. + const builder = new WasmModuleBuilder(); + builder.addFunction('main', makeSig([], [kWasmAnyRef])) + .addBody([ + kExprI32Const, 42, + kGCPrefix, kExprI31New, + ]) + .exportFunc(); + + return builder.instantiate(); +} + +function instantiateModuleWithStringRef() { + // Build a WebAssembly module which uses stringref features. + const builder = new WasmModuleBuilder(); + builder.addFunction("main", + makeSig([kWasmStringRef], [kWasmStringRef])) + .addBody([kExprLocalGet, 0]) + .exportFunc(); + return builder.instantiate(); +} + +// Due to --noexperimental-wasm-gc GC is disabled. +assertThrows(instantiateModuleWithGC, WebAssembly.CompileError); +// Due to --noexperimental-wasm-stringref stringrefs are not supported. +assertThrows(instantiateModuleWithStringRef, WebAssembly.CompileError); +// Disable WebAssembly GC explicitly. +%SetWasmGCEnabled(false); +assertThrows(instantiateModuleWithGC, WebAssembly.CompileError); +assertThrows(instantiateModuleWithStringRef, WebAssembly.CompileError); +// Enable WebAssembly GC explicitly. +%SetWasmGCEnabled(true); +assertEquals(42, instantiateModuleWithGC().exports.main()); +// Enabling Wasm GC via callback will also enable wasm stringref. +let str = "Hello World!"; +assertSame(str, instantiateModuleWithStringRef().exports.main(str)); diff --git a/test/unittests/api/api-wasm-unittest.cc b/test/unittests/api/api-wasm-unittest.cc index 85174ced7f..29e9e66d56 100644 --- a/test/unittests/api/api-wasm-unittest.cc +++ b/test/unittests/api/api-wasm-unittest.cc @@ -13,6 +13,7 @@ #include "include/v8-wasm.h" #include "src/api/api-inl.h" #include "src/handles/global-handles.h" +#include "test/common/flag-utils.h" #include "test/unittests/test-utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -156,4 +157,33 @@ TEST_F(ApiWasmTest, WasmStreamingSetCallback) { Promise::kPending); } +TEST_F(ApiWasmTest, WasmEnableDisableGC) { + Local context_local = Context::New(isolate()); + Context::Scope context_scope(context_local); + i::Handle context = v8::Utils::OpenHandle(*context_local); + // When using the flags, stringref and GC are controlled independently. + { + i::FlagScope flag_gc(&i::v8_flags.experimental_wasm_gc, false); + i::FlagScope flag_stringref(&i::v8_flags.experimental_wasm_stringref, + true); + EXPECT_FALSE(i_isolate()->IsWasmGCEnabled(context)); + EXPECT_TRUE(i_isolate()->IsWasmStringRefEnabled(context)); + } + { + i::FlagScope flag_gc(&i::v8_flags.experimental_wasm_gc, true); + i::FlagScope flag_stringref(&i::v8_flags.experimental_wasm_stringref, + false); + EXPECT_TRUE(i_isolate()->IsWasmGCEnabled(context)); + EXPECT_FALSE(i_isolate()->IsWasmStringRefEnabled(context)); + } + // When providing a callback, the callback will control GC and stringref. + isolate()->SetWasmGCEnabledCallback([](auto) { return true; }); + EXPECT_TRUE(i_isolate()->IsWasmGCEnabled(context)); + EXPECT_TRUE(i_isolate()->IsWasmStringRefEnabled(context)); + isolate()->SetWasmGCEnabledCallback([](auto) { return false; }); + EXPECT_FALSE(i_isolate()->IsWasmGCEnabled(context)); + EXPECT_FALSE(i_isolate()->IsWasmStringRefEnabled(context)); + isolate()->SetWasmGCEnabledCallback(nullptr); +} + } // namespace v8