[wasm] Make {WebAssembly.Function} work on any iterable.

This makes sure the "parameters" and "results" properties of the passed
FunctionType object can be arbitrary iterable objects, not just plain
JavaScript arrays.

R=clemensh@chromium.org
TEST=mjsunit/wasm/type-reflection
BUG=v8:7742

Change-Id: Icba18c418e549deba9fff1855be4956813b1a953
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1733071
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63049}
This commit is contained in:
Michael Starzinger 2019-08-02 13:54:50 +02:00 committed by Commit Bot
parent f51e0368ea
commit d335cb6a11
2 changed files with 65 additions and 8 deletions

View File

@ -1378,6 +1378,21 @@ void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
thrower.TypeError("WebAssembly.Exception cannot be called");
}
namespace {
uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context,
Local<Object> iterable) {
Local<String> length = Utils::ToLocal(isolate->factory()->length_string());
MaybeLocal<Value> property = iterable->Get(context, length);
if (property.IsEmpty()) return i::kMaxUInt32;
MaybeLocal<Uint32> number = property.ToLocalChecked()->ToArrayIndex(context);
if (number.IsEmpty()) return i::kMaxUInt32;
DCHECK_NE(i::kMaxUInt32, number.ToLocalChecked()->Value());
return number.ToLocalChecked()->Value();
}
} // namespace
// WebAssembly.Function
void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
@ -1402,13 +1417,16 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
function_type->Get(context, parameters_key);
v8::Local<v8::Value> parameters_value;
if (!parameters_maybe.ToLocal(&parameters_value)) return;
// TODO(7742): Allow any iterable, not just {Array} here.
if (!parameters_value->IsArray()) {
if (!parameters_value->IsObject()) {
thrower.TypeError("Argument 0 must be a function type with 'parameters'");
return;
}
Local<Array> parameters = parameters_value.As<Array>();
uint32_t parameters_len = parameters->Length();
Local<Object> parameters = parameters_value.As<Object>();
uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
if (parameters_len == i::kMaxUInt32) {
thrower.TypeError("Argument 0 contains parameters without 'length'");
return;
}
if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
thrower.TypeError("Argument 0 contains too many parameters");
return;
@ -1420,13 +1438,16 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
function_type->Get(context, results_key);
v8::Local<v8::Value> results_value;
if (!results_maybe.ToLocal(&results_value)) return;
// TODO(7742): Allow any iterable, not just {Array} here.
if (!results_value->IsArray()) {
if (!results_value->IsObject()) {
thrower.TypeError("Argument 0 must be a function type with 'results'");
return;
}
Local<Array> results = results_value.As<Array>();
uint32_t results_len = results->Length();
Local<Object> results = results_value.As<Object>();
uint32_t results_len = GetIterableLength(i_isolate, context, results);
if (results_len == i::kMaxUInt32) {
thrower.TypeError("Argument 0 contains results without 'length'");
return;
}
if (results_len > (enabled_features.mv
? i::wasm::kV8MaxWasmFunctionMultiReturns
: i::wasm::kV8MaxWasmFunctionReturns)) {

View File

@ -209,6 +209,42 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
() => new WebAssembly.Function({parameters:[], results:[]}, _ => 0));
})();
(function TestFunctionConstructorNonArray1() {
let log = []; // Populated with a log of accesses.
let two = { toString: () => "2" }; // Just a fancy "2".
let logger = new Proxy({ length: two, "0": "i32", "1": "f32"}, {
get: function(obj, prop) { log.push(prop); return Reflect.get(obj, prop); },
set: function(obj, prop, val) { assertUnreachable(); }
});
let fun = new WebAssembly.Function({parameters:logger, results:[]}, _ => 0);
assertArrayEquals(["i32", "f32"], WebAssembly.Function.type(fun).parameters);
assertArrayEquals(["length", "0", "1"], log);
})();
(function TestFunctionConstructorNonArray2() {
let throw1 = { get length() { throw new Error("cannot see length"); }};
let throw2 = { length: { toString: _ => { throw new Error("no length") } } };
let throw3 = { length: "not a length value, this also throws" };
assertThrows(
() => new WebAssembly.Function({parameters:throw1, results:[]}), Error,
/cannot see length/);
assertThrows(
() => new WebAssembly.Function({parameters:throw2, results:[]}), Error,
/no length/);
assertThrows(
() => new WebAssembly.Function({parameters:throw3, results:[]}), TypeError,
/Argument 0 contains parameters without 'length'/);
assertThrows(
() => new WebAssembly.Function({parameters:[], results:throw1}), Error,
/cannot see length/);
assertThrows(
() => new WebAssembly.Function({parameters:[], results:throw2}), Error,
/no length/);
assertThrows(
() => new WebAssembly.Function({parameters:[], results:throw3}), TypeError,
/Argument 0 contains results without 'length'/);
})();
(function TestFunctionConstructedFunction() {
let fun = new WebAssembly.Function({parameters:[], results:[]}, _ => 0);
assertTrue(fun instanceof WebAssembly.Function);