[wasm] Handle rejected input promise in WebAssembly.compileStreaming
In the implementation of WebAssembly.compileStreaming and WebAssembly.instantiateStreaming, we did not handle the case where the input, which is a Promise, gets rejected. When this Promise got rejected, the Promise returned by compileStreaming remained pending forever. With this CL, the rejection object of the input Promise gets forwarded to the result Promise. I also extended the --wasm-test-streaming flag to provide WebAssembly.compileStreaming and WebAssembly.instantiateStreaming in d8. The difference to the Chrome versions of these function is that d8 does not know about Response objects. That's why in d8 compileStreaming and instantiateStreaming expect a Promise to an ArrayBuffer or a TypedArray and not to a Response object. Cq-Include-Trybots: luci.chromium.try:linux-blink-rel Bug: chromium:943487 Change-Id: I77f789e9ae5d50ae9c9bc92bf27dbfe338fe0f13 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1535817 Reviewed-by: Ben Titzer <titzer@chromium.org> Commit-Queue: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#60427}
This commit is contained in:
parent
d2fec28e22
commit
8cbcae37db
@ -533,6 +533,36 @@ void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
std::move(resolver), bytes, is_shared);
|
||||
}
|
||||
|
||||
void WasmStreamingCallbackForTesting(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
|
||||
HandleScope scope(isolate);
|
||||
ScheduledErrorThrower thrower(i_isolate, "WebAssembly.compile()");
|
||||
|
||||
std::shared_ptr<v8::WasmStreaming> streaming =
|
||||
v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
|
||||
|
||||
bool is_shared = false;
|
||||
i::wasm::ModuleWireBytes bytes =
|
||||
GetFirstArgumentAsBytes(args, &thrower, &is_shared);
|
||||
if (thrower.error()) {
|
||||
streaming->Abort(Utils::ToLocal(thrower.Reify()));
|
||||
return;
|
||||
}
|
||||
streaming->OnBytesReceived(bytes.start(), bytes.length());
|
||||
streaming->Finish();
|
||||
CHECK(!thrower.error());
|
||||
}
|
||||
|
||||
void WasmStreamingPromiseFailedCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
std::shared_ptr<v8::WasmStreaming> streaming =
|
||||
v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
|
||||
streaming->Abort(args[0]);
|
||||
}
|
||||
|
||||
// WebAssembly.compileStreaming(Promise<Response>) -> Promise
|
||||
void WebAssemblyCompileStreaming(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
@ -571,6 +601,10 @@ void WebAssemblyCompileStreaming(
|
||||
v8::Function, compile_callback,
|
||||
v8::Function::New(context, i_isolate->wasm_streaming_callback(),
|
||||
Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
|
||||
ASSIGN(
|
||||
v8::Function, reject_callback,
|
||||
v8::Function::New(context, WasmStreamingPromiseFailedCallback,
|
||||
Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
|
||||
|
||||
// The parameter may be of type {Response} or of type {Promise<Response>}.
|
||||
// Treat either case of parameter as Promise.resolve(parameter)
|
||||
@ -584,7 +618,8 @@ void WebAssemblyCompileStreaming(
|
||||
// We do not have any use of the result here. The {compile_callback} will
|
||||
// start streaming compilation, which will eventually resolve the promise we
|
||||
// set as result value.
|
||||
USE(input_resolver->GetPromise()->Then(context, compile_callback));
|
||||
USE(input_resolver->GetPromise()->Then(context, compile_callback,
|
||||
reject_callback));
|
||||
}
|
||||
|
||||
// WebAssembly.validate(bytes) -> bool
|
||||
@ -845,6 +880,10 @@ void WebAssemblyInstantiateStreaming(
|
||||
v8::Function, compile_callback,
|
||||
v8::Function::New(context, i_isolate->wasm_streaming_callback(),
|
||||
Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
|
||||
ASSIGN(
|
||||
v8::Function, reject_callback,
|
||||
v8::Function::New(context, WasmStreamingPromiseFailedCallback,
|
||||
Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
|
||||
|
||||
// The parameter may be of type {Response} or of type {Promise<Response>}.
|
||||
// Treat either case of parameter as Promise.resolve(parameter)
|
||||
@ -858,7 +897,8 @@ void WebAssemblyInstantiateStreaming(
|
||||
// We do not have any use of the result here. The {compile_callback} will
|
||||
// start streaming compilation, which will eventually resolve the promise we
|
||||
// set as result value.
|
||||
USE(input_resolver->GetPromise()->Then(context, compile_callback));
|
||||
USE(input_resolver->GetPromise()->Then(context, compile_callback,
|
||||
reject_callback));
|
||||
}
|
||||
|
||||
// WebAssembly.instantiate(module, imports) -> WebAssembly.Instance
|
||||
@ -1927,6 +1967,10 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
|
||||
InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
|
||||
InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1);
|
||||
|
||||
if (FLAG_wasm_test_streaming) {
|
||||
isolate->set_wasm_streaming_callback(WasmStreamingCallbackForTesting);
|
||||
}
|
||||
|
||||
if (isolate->wasm_streaming_callback() != nullptr) {
|
||||
InstallFunc(isolate, webassembly, "compileStreaming",
|
||||
WebAssemblyCompileStreaming, 1);
|
||||
|
44
test/mjsunit/wasm/streaming-api.js
Normal file
44
test/mjsunit/wasm/streaming-api.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2019 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: --wasm-test-streaming
|
||||
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
(function TestCompileStreaming() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addFunction("main", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0])
|
||||
.exportAs("main");
|
||||
let bytes = builder.toBuffer();
|
||||
assertPromiseResult(WebAssembly.compileStreaming(Promise.resolve(bytes)).then(
|
||||
module => WebAssembly.instantiate(module)).then(
|
||||
instance => assertEquals(5, instance.exports.main(5))));
|
||||
})();
|
||||
|
||||
(function TestInstantiateStreaming() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addFunction("main", kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0])
|
||||
.exportAs("main");
|
||||
let bytes = builder.toBuffer();
|
||||
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes)).then(
|
||||
({module, instance}) => assertEquals(5, instance.exports.main(5))));
|
||||
})();
|
||||
|
||||
(function TestCompileStreamingRejectedInputPromise() {
|
||||
print(arguments.callee.name);
|
||||
assertPromiseResult(WebAssembly.compileStreaming(Promise.reject("myError")),
|
||||
assertUnreachable,
|
||||
error => assertEquals(error, "myError"));
|
||||
})();
|
||||
|
||||
(function TestInstantiateStreamingRejectedInputPromise() {
|
||||
print(arguments.callee.name);
|
||||
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.reject("myError")),
|
||||
assertUnreachable,
|
||||
error => assertEquals(error, "myError"));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user