[wasm][eh] Encode values in WebAssembly.Exception
R=jkummerow@chromium.org Bug: v8:11992 Change-Id: If62f2cdc080364dec796a836321110bf571769ef Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3049075 Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#75937}
This commit is contained in:
parent
d938c10891
commit
b86db1396a
@ -20,6 +20,7 @@
|
||||
#include "src/handles/handles.h"
|
||||
#include "src/heap/factory.h"
|
||||
#include "src/init/v8.h"
|
||||
#include "src/objects/fixed-array.h"
|
||||
#include "src/objects/js-promise-inl.h"
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/objects/templates.h"
|
||||
@ -1231,6 +1232,48 @@ bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool ToI32(Local<v8::Value> value, Local<Context> context, int32_t* i32_value) {
|
||||
if (!value->IsUndefined()) {
|
||||
v8::Local<v8::Int32> int32_value;
|
||||
if (!value->ToInt32(context).ToLocal(&int32_value)) return false;
|
||||
if (!int32_value->Int32Value(context).To(i32_value)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToI64(Local<v8::Value> value, Local<Context> context, int64_t* i64_value) {
|
||||
if (!value->IsUndefined()) {
|
||||
v8::Local<v8::BigInt> bigint_value;
|
||||
if (!value->ToBigInt(context).ToLocal(&bigint_value)) return false;
|
||||
*i64_value = bigint_value->Int64Value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToF32(Local<v8::Value> value, Local<Context> context, float* f32_value) {
|
||||
if (!value->IsUndefined()) {
|
||||
double f64_value = 0;
|
||||
v8::Local<v8::Number> number_value;
|
||||
if (!value->ToNumber(context).ToLocal(&number_value)) return false;
|
||||
if (!number_value->NumberValue(context).To(&f64_value)) return false;
|
||||
*f32_value = i::DoubleToFloat32(f64_value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToF64(Local<v8::Value> value, Local<Context> context, double* f64_value) {
|
||||
if (!value->IsUndefined()) {
|
||||
v8::Local<v8::Number> number_value;
|
||||
if (!value->ToNumber(context).ToLocal(&number_value)) return false;
|
||||
if (!number_value->NumberValue(context).To(f64_value)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// WebAssembly.Global
|
||||
void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
@ -1296,43 +1339,25 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
switch (type.kind()) {
|
||||
case i::wasm::kI32: {
|
||||
int32_t i32_value = 0;
|
||||
if (!value->IsUndefined()) {
|
||||
v8::Local<v8::Int32> int32_value;
|
||||
if (!value->ToInt32(context).ToLocal(&int32_value)) return;
|
||||
if (!int32_value->Int32Value(context).To(&i32_value)) return;
|
||||
}
|
||||
if (!ToI32(value, context, &i32_value)) return;
|
||||
global_obj->SetI32(i32_value);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kI64: {
|
||||
int64_t i64_value = 0;
|
||||
if (!value->IsUndefined()) {
|
||||
v8::Local<v8::BigInt> bigint_value;
|
||||
if (!value->ToBigInt(context).ToLocal(&bigint_value)) return;
|
||||
i64_value = bigint_value->Int64Value();
|
||||
}
|
||||
if (!ToI64(value, context, &i64_value)) return;
|
||||
global_obj->SetI64(i64_value);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kF32: {
|
||||
float f32_value = 0;
|
||||
if (!value->IsUndefined()) {
|
||||
double f64_value = 0;
|
||||
v8::Local<v8::Number> number_value;
|
||||
if (!value->ToNumber(context).ToLocal(&number_value)) return;
|
||||
if (!number_value->NumberValue(context).To(&f64_value)) return;
|
||||
f32_value = i::DoubleToFloat32(f64_value);
|
||||
}
|
||||
if (!ToF32(value, context, &f32_value)) return;
|
||||
global_obj->SetF32(f32_value);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kF64: {
|
||||
double f64_value = 0;
|
||||
if (!value->IsUndefined()) {
|
||||
v8::Local<v8::Number> number_value;
|
||||
if (!value->ToNumber(context).ToLocal(&number_value)) return;
|
||||
if (!number_value->NumberValue(context).To(&f64_value)) return;
|
||||
}
|
||||
if (!ToF64(value, context, &f64_value)) return;
|
||||
global_obj->SetF64(f64_value);
|
||||
break;
|
||||
}
|
||||
@ -1471,6 +1496,93 @@ void WebAssemblyTag(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
args.GetReturnValue().Set(Utils::ToLocal(exception));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t GetEncodedSize(i::Handle<i::WasmExceptionObject> tag_object) {
|
||||
auto serialized_sig = tag_object->serialized_signature();
|
||||
i::wasm::WasmExceptionSig sig{0, static_cast<size_t>(serialized_sig.length()),
|
||||
reinterpret_cast<i::wasm::ValueType*>(
|
||||
serialized_sig.GetDataStartAddress())};
|
||||
i::wasm::WasmException exception(&sig);
|
||||
return i::WasmExceptionPackage::GetEncodedSize(&exception);
|
||||
}
|
||||
|
||||
void EncodeExceptionValues(v8::Isolate* isolate,
|
||||
i::PodArray<i::wasm::ValueType> signature,
|
||||
const Local<Value>& arg,
|
||||
ScheduledErrorThrower* thrower,
|
||||
i::Handle<i::FixedArray> values_out) {
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
uint32_t index = 0;
|
||||
if (!arg->IsObject()) {
|
||||
thrower->TypeError("Exception values must be an iterable object");
|
||||
return;
|
||||
}
|
||||
auto values = arg.As<Object>();
|
||||
for (int i = 0; i < signature.length(); ++i) {
|
||||
MaybeLocal<Value> maybe_value = values->Get(context, i);
|
||||
Local<Value> value = maybe_value.ToLocalChecked();
|
||||
i::wasm::ValueType type = signature.get(i);
|
||||
switch (type.kind()) {
|
||||
case i::wasm::kI32: {
|
||||
int32_t i32 = 0;
|
||||
if (!ToI32(value, context, &i32)) return;
|
||||
i::EncodeI32ExceptionValue(values_out, &index, i32);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kI64: {
|
||||
int64_t i64 = 0;
|
||||
if (!ToI64(value, context, &i64)) return;
|
||||
i::EncodeI64ExceptionValue(values_out, &index, i64);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kF32: {
|
||||
float f32 = 0;
|
||||
if (!ToF32(value, context, &f32)) return;
|
||||
int32_t i32 = bit_cast<int32_t>(f32);
|
||||
i::EncodeI32ExceptionValue(values_out, &index, i32);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kF64: {
|
||||
double f64 = 0;
|
||||
if (!ToF64(value, context, &f64)) return;
|
||||
int64_t i64 = bit_cast<int64_t>(f64);
|
||||
i::EncodeI64ExceptionValue(values_out, &index, i64);
|
||||
break;
|
||||
}
|
||||
case i::wasm::kRef:
|
||||
case i::wasm::kOptRef:
|
||||
switch (type.heap_representation()) {
|
||||
case i::wasm::HeapType::kExtern:
|
||||
case i::wasm::HeapType::kFunc:
|
||||
case i::wasm::HeapType::kAny:
|
||||
case i::wasm::HeapType::kEq:
|
||||
case i::wasm::HeapType::kI31:
|
||||
case i::wasm::HeapType::kData:
|
||||
values_out->set(index++, *Utils::OpenHandle(*value));
|
||||
break;
|
||||
case internal::wasm::HeapType::kBottom:
|
||||
UNREACHABLE();
|
||||
default:
|
||||
// TODO(7748): Add support for custom struct/array types.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
break;
|
||||
case i::wasm::kRtt:
|
||||
case i::wasm::kRttWithDepth:
|
||||
case i::wasm::kI8:
|
||||
case i::wasm::kI16:
|
||||
case i::wasm::kVoid:
|
||||
case i::wasm::kBottom:
|
||||
case i::wasm::kS128:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
@ -1493,10 +1605,16 @@ void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
auto tag_object = i::Handle<i::WasmExceptionObject>::cast(arg0);
|
||||
auto tag = i::Handle<i::WasmExceptionTag>(
|
||||
i::WasmExceptionTag::cast(tag_object->exception_tag()), i_isolate);
|
||||
// TODO(thibaudm): Encode arguments in the exception package.
|
||||
uint32_t size = 0;
|
||||
uint32_t size = GetEncodedSize(tag_object);
|
||||
i::Handle<i::WasmExceptionPackage> runtime_exception =
|
||||
i::WasmExceptionPackage::New(i_isolate, tag, size);
|
||||
// The constructor above should guarantee that the cast below succeeds.
|
||||
auto values = i::Handle<i::FixedArray>::cast(
|
||||
i::WasmExceptionPackage::GetExceptionValues(i_isolate,
|
||||
runtime_exception));
|
||||
auto signature = tag_object->serialized_signature();
|
||||
EncodeExceptionValues(isolate, signature, args[1], &thrower, values);
|
||||
if (thrower.error()) return;
|
||||
args.GetReturnValue().Set(
|
||||
Utils::ToLocal(i::Handle<i::Object>::cast(runtime_exception)));
|
||||
}
|
||||
|
@ -1812,6 +1812,20 @@ Handle<Object> WasmExceptionPackage::GetExceptionValues(
|
||||
return ReadOnlyRoots(isolate).undefined_value_handle();
|
||||
}
|
||||
|
||||
void EncodeI32ExceptionValue(Handle<FixedArray> encoded_values,
|
||||
uint32_t* encoded_index, uint32_t value) {
|
||||
encoded_values->set((*encoded_index)++, Smi::FromInt(value >> 16));
|
||||
encoded_values->set((*encoded_index)++, Smi::FromInt(value & 0xffff));
|
||||
}
|
||||
|
||||
void EncodeI64ExceptionValue(Handle<FixedArray> encoded_values,
|
||||
uint32_t* encoded_index, uint64_t value) {
|
||||
EncodeI32ExceptionValue(encoded_values, encoded_index,
|
||||
static_cast<uint32_t>(value >> 32));
|
||||
EncodeI32ExceptionValue(encoded_values, encoded_index,
|
||||
static_cast<uint32_t>(value));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
namespace {
|
||||
|
@ -590,6 +590,12 @@ class V8_EXPORT_PRIVATE WasmExceptionPackage : public JSObject {
|
||||
OBJECT_CONSTRUCTORS(WasmExceptionPackage, JSObject);
|
||||
};
|
||||
|
||||
void V8_EXPORT_PRIVATE EncodeI32ExceptionValue(
|
||||
Handle<FixedArray> encoded_values, uint32_t* encoded_index, uint32_t value);
|
||||
|
||||
void V8_EXPORT_PRIVATE EncodeI64ExceptionValue(
|
||||
Handle<FixedArray> encoded_values, uint32_t* encoded_index, uint64_t value);
|
||||
|
||||
// A Wasm function that is wrapped and exported to JavaScript.
|
||||
// Representation of WebAssembly.Function JavaScript-level object.
|
||||
class WasmExportedFunction : public JSFunction {
|
||||
|
@ -3083,20 +3083,6 @@ class WasmInterpreterInternals {
|
||||
return false;
|
||||
}
|
||||
|
||||
void EncodeI32ExceptionValue(Handle<FixedArray> encoded_values,
|
||||
uint32_t* encoded_index, uint32_t value) {
|
||||
encoded_values->set((*encoded_index)++, Smi::FromInt(value >> 16));
|
||||
encoded_values->set((*encoded_index)++, Smi::FromInt(value & 0xffff));
|
||||
}
|
||||
|
||||
void EncodeI64ExceptionValue(Handle<FixedArray> encoded_values,
|
||||
uint32_t* encoded_index, uint64_t value) {
|
||||
EncodeI32ExceptionValue(encoded_values, encoded_index,
|
||||
static_cast<uint32_t>(value >> 32));
|
||||
EncodeI32ExceptionValue(encoded_values, encoded_index,
|
||||
static_cast<uint32_t>(value));
|
||||
}
|
||||
|
||||
// Allocate, initialize and throw a new exception. The exception values are
|
||||
// being popped off the operand stack. Returns true if the exception is being
|
||||
// handled locally by the interpreter, false otherwise (interpreter exits).
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --experimental-wasm-eh
|
||||
// Flags: --experimental-wasm-eh --experimental-wasm-reftypes
|
||||
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
@ -69,7 +69,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
/Argument 0 must be a WebAssembly tag/);
|
||||
assertThrows(() => WebAssembly.Exception(js_tag), TypeError,
|
||||
/WebAssembly.Exception must be invoked with 'new'/);
|
||||
let js_exception = new WebAssembly.Exception(js_tag);
|
||||
let js_exception = new WebAssembly.Exception(js_tag, []);
|
||||
|
||||
// Check prototype.
|
||||
assertSame(WebAssembly.Exception.prototype, js_exception.__proto__);
|
||||
@ -87,3 +87,86 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
assertTrue(e instanceof WebAssembly.Exception);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestExceptionConstructorWithPayload() {
|
||||
print(arguments.callee.name);
|
||||
let tag = new WebAssembly.Tag(
|
||||
{parameters: ['i32', 'f32', 'i64', 'f64', 'externref']});
|
||||
assertThrows(() => new WebAssembly.Exception(
|
||||
tag, [1n, 2, 3n, 4, {}]), TypeError);
|
||||
assertDoesNotThrow(() => new WebAssembly.Exception(tag, [3, 4, 5n, 6, {}]));
|
||||
})();
|
||||
|
||||
(function TestCatchJSException() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
let js_tag = new WebAssembly.Tag({parameters: []});
|
||||
let js_func_index = builder.addImport('m', 'js_func', kSig_v_v);
|
||||
let js_tag_index = builder.addImportedException("m", "js_tag", kSig_v_v);
|
||||
let tag_index = builder.addException(kSig_v_v);
|
||||
builder.addExportOfKind("wasm_tag", kExternalException, tag_index);
|
||||
builder.addFunction("catch", kSig_i_v)
|
||||
.addBody([
|
||||
kExprTry, kWasmI32,
|
||||
kExprCallFunction, js_func_index,
|
||||
kExprI32Const, 0,
|
||||
kExprCatch, js_tag_index,
|
||||
kExprI32Const, 1,
|
||||
kExprCatch, tag_index,
|
||||
kExprI32Const, 2,
|
||||
kExprEnd
|
||||
]).exportFunc();
|
||||
let tag;
|
||||
function js_func() {
|
||||
throw new WebAssembly.Exception(tag, []);
|
||||
}
|
||||
let instance = builder.instantiate({m: {js_func, js_tag}});
|
||||
tag = js_tag;
|
||||
assertEquals(1, instance.exports.catch());
|
||||
tag = instance.exports.wasm_tag;
|
||||
assertEquals(2, instance.exports.catch());
|
||||
})();
|
||||
|
||||
function TestCatchJS(types_str, types, values) {
|
||||
// Create a JS exception, catch it in wasm and check the unpacked value(s).
|
||||
let builder = new WasmModuleBuilder();
|
||||
let js_tag = new WebAssembly.Tag({parameters: types_str});
|
||||
let js_func_index = builder.addImport('m', 'js_func', kSig_v_v);
|
||||
let sig1 = makeSig(types, []);
|
||||
let sig2 = makeSig([], types);
|
||||
let js_tag_index = builder.addImportedException("m", "js_tag", sig1);
|
||||
let tag_index = builder.addException(sig1);
|
||||
let return_type = builder.addType(sig2);
|
||||
builder.addExportOfKind("wasm_tag", kExternalException, tag_index);
|
||||
builder.addFunction("catch", sig2)
|
||||
.addBody([
|
||||
kExprTry, return_type,
|
||||
kExprCallFunction, js_func_index,
|
||||
kExprUnreachable,
|
||||
kExprCatch, js_tag_index,
|
||||
kExprCatch, tag_index,
|
||||
kExprEnd
|
||||
]).exportFunc();
|
||||
let exception;
|
||||
function js_func() {
|
||||
throw exception;
|
||||
}
|
||||
let expected = values.length == 1 ? values[0] : values;
|
||||
let instance = builder.instantiate({m: {js_func, js_tag}});
|
||||
exception = new WebAssembly.Exception(js_tag, values);
|
||||
assertEquals(expected, instance.exports.catch());
|
||||
exception = new WebAssembly.Exception(instance.exports.wasm_tag, values);
|
||||
assertEquals(expected, instance.exports.catch());
|
||||
}
|
||||
|
||||
(function TestCatchJSExceptionWithPayload() {
|
||||
print(arguments.callee.name);
|
||||
TestCatchJS(['i32'], [kWasmI32], [1]);
|
||||
TestCatchJS(['i64'], [kWasmI64], [2n]);
|
||||
TestCatchJS(['f32'], [kWasmF32], [3]);
|
||||
TestCatchJS(['f64'], [kWasmF64], [4]);
|
||||
TestCatchJS(['externref'], [kWasmExternRef], [{value: 5}]);
|
||||
TestCatchJS(['i32', 'i64', 'f32', 'f64', 'externref'],
|
||||
[kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmExternRef],
|
||||
[6, 7n, 8, 9, {value: 10}]);
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user