[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:
Andreas Haas 2019-03-22 10:13:27 +01:00 committed by Commit Bot
parent d2fec28e22
commit 8cbcae37db
2 changed files with 90 additions and 2 deletions

View File

@ -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);

View 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"));
})();