// 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. #include #include "include/v8-context.h" #include "include/v8-function-callback.h" #include "include/v8-local-handle.h" #include "include/v8-object.h" #include "include/v8-persistent-handle.h" #include "include/v8-promise.h" #include "include/v8-wasm.h" #include "src/api/api-inl.h" #include "src/handles/global-handles.h" #include "test/cctest/cctest.h" namespace { bool wasm_streaming_callback_got_called = false; bool wasm_streaming_data_got_collected = false; // The bytes of a minimal WebAssembly module. const uint8_t kMinimalWasmModuleBytes[]{0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00}; void WasmStreamingTestFinalizer(const v8::WeakCallbackInfo& data) { CHECK(!wasm_streaming_data_got_collected); wasm_streaming_data_got_collected = true; i::GlobalHandles::Destroy(reinterpret_cast(data.GetParameter())); } void WasmStreamingCallbackTestCallbackIsCalled( const v8::FunctionCallbackInfo& args) { CHECK(!wasm_streaming_callback_got_called); wasm_streaming_callback_got_called = true; i::Handle global_handle = reinterpret_cast(args.GetIsolate()) ->global_handles() ->Create(*v8::Utils::OpenHandle(*args.Data())); i::GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), WasmStreamingTestFinalizer, v8::WeakCallbackType::kParameter); } void WasmStreamingCallbackTestOnBytesReceived( const v8::FunctionCallbackInfo& args) { std::shared_ptr streaming = v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); // The first bytes of the WebAssembly magic word. const uint8_t bytes[]{0x00, 0x61, 0x73}; streaming->OnBytesReceived(bytes, arraysize(bytes)); } void WasmStreamingCallbackTestFinishWithSuccess( const v8::FunctionCallbackInfo& args) { std::shared_ptr streaming = v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); streaming->OnBytesReceived(kMinimalWasmModuleBytes, arraysize(kMinimalWasmModuleBytes)); streaming->Finish(); } void WasmStreamingCallbackTestFinishWithFailure( const v8::FunctionCallbackInfo& args) { std::shared_ptr streaming = v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); streaming->Finish(); } void WasmStreamingCallbackTestAbortWithReject( const v8::FunctionCallbackInfo& args) { std::shared_ptr streaming = v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); streaming->Abort(v8::Object::New(args.GetIsolate())); } void WasmStreamingCallbackTestAbortNoReject( const v8::FunctionCallbackInfo& args) { std::shared_ptr streaming = v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); streaming->Abort({}); } void TestWasmStreaming(v8::WasmStreamingCallback callback, v8::Promise::PromiseState expected_state) { CcTest::isolate()->SetWasmStreamingCallback(callback); LocalContext env; v8::Isolate* isolate = env->GetIsolate(); v8::HandleScope scope(isolate); // Call {WebAssembly.compileStreaming} with {null} as parameter. The parameter // is only really processed by the embedder, so for this test the value is // irrelevant. v8::Local promise = v8::Local::Cast( CompileRun("WebAssembly.compileStreaming(null)")); EmptyMessageQueues(isolate); CHECK_EQ(expected_state, promise->State()); } } // namespace TEST(WasmStreamingCallback) { TestWasmStreaming(WasmStreamingCallbackTestCallbackIsCalled, v8::Promise::kPending); CHECK(wasm_streaming_callback_got_called); CcTest::CollectAllAvailableGarbage(); CHECK(wasm_streaming_data_got_collected); } TEST(WasmStreamingOnBytesReceived) { TestWasmStreaming(WasmStreamingCallbackTestOnBytesReceived, v8::Promise::kPending); } TEST(WasmStreamingFinishWithSuccess) { TestWasmStreaming(WasmStreamingCallbackTestFinishWithSuccess, v8::Promise::kFulfilled); } TEST(WasmStreamingFinishWithFailure) { TestWasmStreaming(WasmStreamingCallbackTestFinishWithFailure, v8::Promise::kRejected); } TEST(WasmStreamingAbortWithReject) { TestWasmStreaming(WasmStreamingCallbackTestAbortWithReject, v8::Promise::kRejected); } TEST(WasmStreamingAbortWithoutReject) { TestWasmStreaming(WasmStreamingCallbackTestAbortNoReject, v8::Promise::kPending); } TEST(WasmCompileToWasmModuleObject) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); v8::HandleScope scope(isolate); auto maybe_module = v8::WasmModuleObject::Compile( CcTest::isolate(), {kMinimalWasmModuleBytes, arraysize(kMinimalWasmModuleBytes)}); CHECK(!maybe_module.IsEmpty()); } namespace { bool wasm_simd_enabled_value = false; bool wasm_exceptions_enabled_value = false; bool MockWasmSimdEnabledCallback(v8::Local) { return wasm_simd_enabled_value; } bool MockWasmExceptionsEnabledCallback(v8::Local) { return wasm_exceptions_enabled_value; } } // namespace TEST(TestSetWasmSimdEnabledCallback) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); i::Isolate* i_isolate = reinterpret_cast(isolate); v8::HandleScope scope(isolate); v8::Local context = v8::Context::New(CcTest::isolate()); i::Handle i_context = v8::Utils::OpenHandle(*context); // {Isolate::IsWasmSimdEnabled} calls the callback set by the embedder if // such a callback exists. Otherwise it returns // {FLAG_experimental_wasm_simd}. First we test that the flag is returned // correctly if no callback is set. Then we test that the flag is ignored if // the callback is set. i::FLAG_experimental_wasm_simd = false; CHECK(!i_isolate->IsWasmSimdEnabled(i_context)); i::FLAG_experimental_wasm_simd = true; CHECK(i_isolate->IsWasmSimdEnabled(i_context)); isolate->SetWasmSimdEnabledCallback(MockWasmSimdEnabledCallback); wasm_simd_enabled_value = false; CHECK(!i_isolate->IsWasmSimdEnabled(i_context)); wasm_simd_enabled_value = true; i::FLAG_experimental_wasm_simd = false; CHECK(i_isolate->IsWasmSimdEnabled(i_context)); } TEST(TestSetWasmExceptionsEnabledCallback) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); i::Isolate* i_isolate = reinterpret_cast(isolate); v8::HandleScope scope(isolate); v8::Local context = v8::Context::New(CcTest::isolate()); i::Handle i_context = v8::Utils::OpenHandle(*context); // {Isolate::AreWasmExceptionsEnabled} calls the callback set by the embedder // if such a callback exists. Otherwise it returns // {FLAG_experimental_wasm_eh}. First we test that the flag is returned // correctly if no callback is set. Then we test that the flag is ignored if // the callback is set. i::FLAG_experimental_wasm_eh = false; CHECK(!i_isolate->AreWasmExceptionsEnabled(i_context)); i::FLAG_experimental_wasm_eh = true; CHECK(i_isolate->AreWasmExceptionsEnabled(i_context)); isolate->SetWasmExceptionsEnabledCallback(MockWasmExceptionsEnabledCallback); wasm_exceptions_enabled_value = false; CHECK(!i_isolate->AreWasmExceptionsEnabled(i_context)); wasm_exceptions_enabled_value = true; i::FLAG_experimental_wasm_eh = false; CHECK(i_isolate->AreWasmExceptionsEnabled(i_context)); }