Commit e5af9c4f authored by Andy Wingo's avatar Andy Wingo Committed by V8 LUCI CQ

[stringref] Add support for passing stringref values to/from JS

Bug: v8:12868
Change-Id: Ib4540352c7db4b4bdbf88b559da44b08e71dd483
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3650603Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andy Wingo <wingo@igalia.com>
Cr-Commit-Position: refs/heads/main@{#80580}
parent 6faa6d55
...@@ -232,6 +232,7 @@ void WasmGraphBuilder::TerminateThrow(Node* effect, Node* control) { ...@@ -232,6 +232,7 @@ void WasmGraphBuilder::TerminateThrow(Node* effect, Node* control) {
Node* terminate = Node* terminate =
graph()->NewNode(mcgraph()->common()->Throw(), effect, control); graph()->NewNode(mcgraph()->common()->Throw(), effect, control);
gasm_->MergeControlToEnd(terminate); gasm_->MergeControlToEnd(terminate);
gasm_->InitializeEffectControl(nullptr, nullptr);
} }
bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) { bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
...@@ -6050,6 +6051,11 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { ...@@ -6050,6 +6051,11 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
} else { } else {
return BuildAllocateObjectWrapper(node, context); return BuildAllocateObjectWrapper(node, context);
} }
case wasm::HeapType::kString:
// Either {node} is already a tagged JS string, or if type.kind() is
// wasm::kOptRef, it's the null object. Either way it's good to go
// already to JS.
return node;
case wasm::HeapType::kAny: { case wasm::HeapType::kAny: {
if (!enabled_features_.has_gc()) return node; if (!enabled_features_.has_gc()) return node;
// Wrap {node} in object wrapper if it is an array/struct. // Wrap {node} in object wrapper if it is an array/struct.
...@@ -6227,6 +6233,25 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { ...@@ -6227,6 +6233,25 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
type_check.merge); type_check.merge);
} }
Node* BuildCheckString(Node* input, Node* js_context, wasm::ValueType type) {
auto done = gasm_->MakeLabel(MachineRepresentation::kTagged);
auto type_error = gasm_->MakeLabel();
gasm_->GotoIf(IsSmi(input), &type_error, BranchHint::kFalse);
if (type.is_nullable()) gasm_->GotoIf(IsNull(input), &done, input);
Node* map = gasm_->LoadMap(input);
Node* instance_type = gasm_->LoadInstanceType(map);
Node* check = gasm_->Uint32LessThan(
instance_type, gasm_->Uint32Constant(FIRST_NONSTRING_TYPE));
gasm_->GotoIf(check, &done, BranchHint::kTrue, input);
gasm_->Goto(&type_error);
gasm_->Bind(&type_error);
BuildCallToRuntimeWithContext(Runtime::kWasmThrowJSTypeError, js_context,
nullptr, 0);
TerminateThrow(effect(), control());
gasm_->Bind(&done);
return done.PhiAt(0);
}
Node* FromJS(Node* input, Node* js_context, wasm::ValueType type, Node* FromJS(Node* input, Node* js_context, wasm::ValueType type,
Node* frame_state = nullptr) { Node* frame_state = nullptr) {
switch (type.kind()) { switch (type.kind()) {
...@@ -6262,6 +6287,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { ...@@ -6262,6 +6287,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
// anyway). // anyway).
return BuildUnpackObjectWrapper(input, js_context, return BuildUnpackObjectWrapper(input, js_context,
kLeaveFunctionsAlone); kLeaveFunctionsAlone);
case wasm::HeapType::kString:
return BuildCheckString(input, js_context, type);
default: default:
if (module_->has_signature(type.ref_index())) { if (module_->has_signature(type.ref_index())) {
BuildCheckValidRefValue(input, js_context, type); BuildCheckValidRefValue(input, js_context, type);
......
...@@ -42,6 +42,10 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmModule* module, ...@@ -42,6 +42,10 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmModule* module,
(type.has_index() && !module->has_signature(type.ref_index()))) { (type.has_index() && !module->has_signature(type.ref_index()))) {
return false; return false;
} }
if (type == kWasmStringViewWtf8 || type == kWasmStringViewWtf16 ||
type == kWasmStringViewIter) {
return false;
}
} }
return true; return true;
} }
......
// Copyright 2022 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: --experimental-wasm-stringref
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
function assertInvalid(fn, message) {
let builder = new WasmModuleBuilder();
fn(builder);
assertThrows(() => builder.toModule(), WebAssembly.CompileError,
`WebAssembly.Module(): ${message}`);
}
(function TestPassthrough() {
let kSig_w_w = makeSig([kWasmStringRef], [kWasmStringRef]);
let builder = new WasmModuleBuilder();
builder.addFunction("passthrough", kSig_w_w)
.exportFunc()
.addBody([
kExprLocalGet, 0,
]);
let instance = builder.instantiate()
assertEquals('foo', instance.exports.passthrough('foo'));
assertEquals(null, instance.exports.passthrough(null));
assertThrows(()=>instance.exports.passthrough(3),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.passthrough({}),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.passthrough({valueOf: ()=>'foo'}),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.passthrough(undefined),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.passthrough(),
TypeError, "type incompatibility when transforming from/to JS");
})();
(function TestSwap() {
let kSig_ww_ww = makeSig([kWasmStringRef, kWasmStringRef],
[kWasmStringRef, kWasmStringRef]);
let builder = new WasmModuleBuilder();
builder.addFunction("swap", kSig_ww_ww)
.exportFunc()
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
]);
let instance = builder.instantiate()
assertArrayEquals(['bar', 'foo'], instance.exports.swap('foo', 'bar'));
assertArrayEquals(['bar', null], instance.exports.swap(null, 'bar'));
assertArrayEquals([null, 'foo'], instance.exports.swap('foo', null));
})();
(function TestViewsUnsupported() {
let kSig_x_v = makeSig([], [kWasmStringViewWtf8]);
let kSig_y_v = makeSig([], [kWasmStringViewWtf16]);
let kSig_z_v = makeSig([], [kWasmStringViewIter]);
let builder = new WasmModuleBuilder();
builder.addFunction("stringview_wtf8", kSig_x_v)
.exportFunc()
.addLocals(kWasmStringViewWtf8, 1)
.addBody([
kExprLocalGet, 0,
]);
builder.addFunction("stringview_wtf16", kSig_y_v)
.exportFunc()
.addLocals(kWasmStringViewWtf16, 1)
.addBody([
kExprLocalGet, 0,
]);
builder.addFunction("stringview_iter", kSig_z_v)
.exportFunc()
.addLocals(kWasmStringViewIter, 1)
.addBody([
kExprLocalGet, 0,
]);
let instance = builder.instantiate()
assertThrows(()=>instance.exports.stringview_wtf8(),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.stringview_wtf16(),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.stringview_iter(),
TypeError, "type incompatibility when transforming from/to JS");
})();
// TODO(wingo): Test stringref-valued globals (defined and imported).
// TODO(wingo): Test calls from wasm to JS.
// TODO(wingo): Test stringrefs in tables.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment