diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 28274fcd51..bb1a26461a 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -1378,6 +1378,21 @@ void WebAssemblyException(const v8::FunctionCallbackInfo& args) { thrower.TypeError("WebAssembly.Exception cannot be called"); } +namespace { + +uint32_t GetIterableLength(i::Isolate* isolate, Local context, + Local iterable) { + Local length = Utils::ToLocal(isolate->factory()->length_string()); + MaybeLocal property = iterable->Get(context, length); + if (property.IsEmpty()) return i::kMaxUInt32; + MaybeLocal 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& args) { v8::Isolate* isolate = args.GetIsolate(); @@ -1402,13 +1417,16 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo& args) { function_type->Get(context, parameters_key); v8::Local parameters_value; if (!parameters_maybe.ToLocal(¶meters_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 parameters = parameters_value.As(); - uint32_t parameters_len = parameters->Length(); + Local parameters = parameters_value.As(); + 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& args) { function_type->Get(context, results_key); v8::Local 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 results = results_value.As(); - uint32_t results_len = results->Length(); + Local results = results_value.As(); + 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)) { diff --git a/test/mjsunit/wasm/type-reflection.js b/test/mjsunit/wasm/type-reflection.js index 5004cf1e6d..a5bc5f41f5 100644 --- a/test/mjsunit/wasm/type-reflection.js +++ b/test/mjsunit/wasm/type-reflection.js @@ -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);