/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include "jsapi.h" #include "frontend/CompilationStencil.h" #include "js/CompilationAndEvaluation.h" #include "js/experimental/CompileScript.h" #include "js/experimental/JSStencil.h" #include "js/Modules.h" #include "js/OffThreadScriptCompilation.h" #include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasOwnProperty, JS_SetProperty #include "js/Transcoding.h" #include "jsapi-tests/tests.h" #include "vm/HelperThreads.h" // js::RunPendingSourceCompressions #include "vm/Monitor.h" // js::Monitor, js::AutoLockMonitor BEGIN_TEST(testStencil_Basic) { const char* chars = "function f() { return 42; }" "f();"; auto result = basic_test(chars); CHECK(result); const char16_t* chars16 = u"function f() { return 42; }" u"f();"; auto result16 = basic_test(chars16); CHECK(result16); return true; } template bool basic_test(const CharT* chars) { size_t length = std::char_traits::length(chars); JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } END_TEST(testStencil_Basic) BEGIN_TEST(testStencil_Module) { const char* chars = "export function f() { return 42; }" "globalThis.x = f();"; auto result = basic_test(chars); CHECK(result); const char16_t* chars16 = u"export function f() { return 42; }" u"globalThis.x = f();"; auto result16 = basic_test(chars16); CHECK(result16); return true; } template bool basic_test(const CharT* chars) { size_t length = std::char_traits::length(chars); JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedObject moduleObject( cx, JS::InstantiateModuleStencil(cx, instantiateOptions, stencil)); CHECK(moduleObject); // Link and evaluate the module graph. The link step used to be call // "instantiate" but is unrelated to the concept in Stencil with same name. JS::RootedValue rval(cx); CHECK(JS::ModuleLink(cx, moduleObject)); CHECK(JS::ModuleEvaluate(cx, moduleObject, &rval)); CHECK(!rval.isUndefined()); js::RunJobs(cx); CHECK(JS_GetProperty(cx, global, "x", &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } END_TEST(testStencil_Module) BEGIN_TEST(testStencil_NonSyntactic) { const char* chars = "function f() { return x; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); options.setNonSyntacticScope(true); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedObject obj(cx, JS_NewPlainObject(cx)); JS::RootedValue val(cx, JS::Int32Value(42)); CHECK(obj); CHECK(JS_SetProperty(cx, obj, "x", val)); JS::RootedObjectVector chain(cx); CHECK(chain.append(obj)); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, chain, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } END_TEST(testStencil_NonSyntactic) BEGIN_TEST(testStencil_MultiGlobal) { const char* chars = "/**************************************/" "/**************************************/" "/**************************************/" "/**************************************/" "/**************************************/" "/**************************************/" "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); CHECK(RunInNewGlobal(cx, stencil)); CHECK(RunInNewGlobal(cx, stencil)); CHECK(RunInNewGlobal(cx, stencil)); // Start any pending SourceCompressionTasks now to confirm nothing fell apart // when using a JS::Stencil multiple times. CHECK(strlen(chars) > js::ScriptSource::MinimumCompressibleLength); js::RunPendingSourceCompressions(cx->runtime()); return true; } bool RunInNewGlobal(JSContext* cx, RefPtr stencil) { JS::RootedObject otherGlobal(cx, createGlobal()); CHECK(otherGlobal); JSAutoRealm ar(cx, otherGlobal); JS::InstantiateOptions instantiateOptions; JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } END_TEST(testStencil_MultiGlobal) BEGIN_TEST(testStencil_Transcode) { JS::SetProcessBuildIdOp(TestGetBuildId); JS::TranscodeBuffer buffer; { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); // Encode Stencil to XDR JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); CHECK(res == JS::TranscodeResult::Ok); CHECK(!buffer.empty()); // Instantiate and Run JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); } // Create a new global CHECK(createGlobal()); JSAutoRealm ar(cx, global); // Confirm it doesn't have the old code bool found = false; CHECK(JS_HasOwnProperty(cx, global, "f", &found)); CHECK(!found); { // Decode the stencil into new range RefPtr stencil; { JS::DecodeOptions decodeOptions; JS::TranscodeRange range(buffer.begin(), buffer.length()); JS::TranscodeResult res = JS::DecodeStencil(cx, decodeOptions, range, getter_AddRefs(stencil)); CHECK(res == JS::TranscodeResult::Ok); } { JS::FrontendContext* fc = JS::NewFrontendContext(); JS::DecodeOptions decodeOptions; JS::TranscodeRange range(buffer.begin(), buffer.length()); JS::TranscodeResult res = JS::DecodeStencil(fc, decodeOptions, range, getter_AddRefs(stencil)); CHECK(res == JS::TranscodeResult::Ok); JS::DestroyFrontendContext(fc); } // Delete the buffer to verify that the decoded stencil has no dependency // to the buffer. memset(buffer.begin(), 0, buffer.length()); buffer.clear(); // Instantiate and Run JS::InstantiateOptions instantiateOptions; JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); stencil = nullptr; JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); } return true; } static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } END_TEST(testStencil_Transcode) BEGIN_TEST(testStencil_TranscodeBorrowing) { JS::SetProcessBuildIdOp(TestGetBuildId); JS::TranscodeBuffer buffer; { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); // Encode Stencil to XDR JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); CHECK(res == JS::TranscodeResult::Ok); CHECK(!buffer.empty()); } JS::RootedScript script(cx); { JS::TranscodeRange range(buffer.begin(), buffer.length()); JS::DecodeOptions decodeOptions; decodeOptions.borrowBuffer = true; RefPtr stencil; JS::TranscodeResult res = JS::DecodeStencil(cx, decodeOptions, range, getter_AddRefs(stencil)); CHECK(res == JS::TranscodeResult::Ok); JS::InstantiateOptions instantiateOptions; script = JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil); CHECK(script); } // Delete the buffer to verify that the instantiated script has no dependency // to the buffer. memset(buffer.begin(), 0, buffer.length()); buffer.clear(); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } END_TEST(testStencil_TranscodeBorrowing) BEGIN_TEST(testStencil_OffThread) { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::CompileOptions options(cx); JS::OffThreadToken* token; // Force off-thread even though if this is a small file. options.forceAsync = true; CHECK(token = JS::CompileToStencilOffThread(cx, options, srcBuf, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } END_TEST(testStencil_OffThread) BEGIN_TEST(testStencil_OffThreadWithInstantiationStorage) { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::CompileOptions options(cx); JS::OffThreadToken* token; // Force off-thread even though if this is a small file. options.forceAsync = true; options.allocateInstantiationStorage = true; CHECK(token = JS::CompileToStencilOffThread(cx, options, srcBuf, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } JS::Rooted storage(cx); RefPtr stencil = JS::FinishOffThreadStencil(cx, token, storage.address()); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil, storage.address())); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } END_TEST(testStencil_OffThreadWithInstantiationStorage) BEGIN_TEST(testStencil_OffThreadModule) { const char* chars = "export function f() { return 42; }" "globalThis.x = f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::CompileOptions options(cx); JS::OffThreadToken* token; // Force off-thread even though if this is a small file. options.forceAsync = true; CHECK(token = JS::CompileModuleToStencilOffThread(cx, options, srcBuf, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedObject moduleObject( cx, JS::InstantiateModuleStencil(cx, instantiateOptions, stencil)); CHECK(moduleObject); JS::RootedValue rval(cx); CHECK(JS::ModuleLink(cx, moduleObject)); CHECK(JS::ModuleEvaluate(cx, moduleObject, &rval)); CHECK(!rval.isUndefined()); js::RunJobs(cx); CHECK(JS_GetProperty(cx, global, "x", &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } END_TEST(testStencil_OffThreadModule) BEGIN_TEST(testStencil_OffThreadModuleWithInstantiationStorage) { const char* chars = "export function f() { return 42; }" "globalThis.x = f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::CompileOptions options(cx); JS::OffThreadToken* token; // Force off-thread even though if this is a small file. options.forceAsync = true; options.allocateInstantiationStorage = true; CHECK(token = JS::CompileModuleToStencilOffThread(cx, options, srcBuf, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } JS::Rooted storage(cx); RefPtr stencil = JS::FinishOffThreadStencil(cx, token, storage.address()); CHECK(stencil); JS::InstantiateOptions instantiateOptions(options); JS::RootedObject moduleObject( cx, JS::InstantiateModuleStencil(cx, instantiateOptions, stencil, storage.address())); CHECK(moduleObject); JS::RootedValue rval(cx); CHECK(JS::ModuleLink(cx, moduleObject)); CHECK(JS::ModuleEvaluate(cx, moduleObject, &rval)); CHECK(!rval.isUndefined()); js::RunJobs(cx); CHECK(JS_GetProperty(cx, global, "x", &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } END_TEST(testStencil_OffThreadModuleWithInstantiationStorage) BEGIN_TEST(testStencil_OffThreadDecode) { JS::SetProcessBuildIdOp(TestGetBuildId); JS::TranscodeBuffer buffer; { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); // Encode Stencil to XDR JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); CHECK(res == JS::TranscodeResult::Ok); CHECK(!buffer.empty()); // Instantiate and Run JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); } JS::OffThreadToken* token; { JS::DecodeOptions decodeOptions; js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::TranscodeRange range(buffer.begin(), buffer.length()); // Force off-thread even though if this is a small file. decodeOptions.forceAsync = true; CHECK(token = JS::DecodeStencilOffThread(cx, decodeOptions, range, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } } RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); CHECK(!JS::StencilIsBorrowed(stencil)); JS::InstantiateOptions instantiateOptions; JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } END_TEST(testStencil_OffThreadDecode) BEGIN_TEST(testStencil_OffThreadDecodeWithInstantiationStorage) { JS::SetProcessBuildIdOp(TestGetBuildId); JS::TranscodeBuffer buffer; { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); // Encode Stencil to XDR JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); CHECK(res == JS::TranscodeResult::Ok); CHECK(!buffer.empty()); // Instantiate and Run JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); } JS::OffThreadToken* token; { JS::DecodeOptions decodeOptions; js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::TranscodeRange range(buffer.begin(), buffer.length()); // Force off-thread even though if this is a small file. decodeOptions.forceAsync = true; decodeOptions.allocateInstantiationStorage = true; CHECK(token = JS::DecodeStencilOffThread(cx, decodeOptions, range, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } } JS::Rooted storage(cx); RefPtr stencil = JS::FinishOffThreadStencil(cx, token, storage.address()); CHECK(stencil); CHECK(!JS::StencilIsBorrowed(stencil)); JS::InstantiateOptions instantiateOptions; JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil, storage.address())); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } END_TEST(testStencil_OffThreadDecodeWithInstantiationStorage) BEGIN_TEST(testStencil_OffThreadDecodeBorrow) { JS::SetProcessBuildIdOp(TestGetBuildId); JS::TranscodeBuffer buffer; { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); // Encode Stencil to XDR JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); CHECK(res == JS::TranscodeResult::Ok); CHECK(!buffer.empty()); // Instantiate and Run JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); } JS::OffThreadToken* token; { JS::DecodeOptions decodeOptions; js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::TranscodeRange range(buffer.begin(), buffer.length()); // Force off-thread even though if this is a small file. decodeOptions.forceAsync = true; decodeOptions.borrowBuffer = true; CHECK(token = JS::DecodeStencilOffThread(cx, decodeOptions, range, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } } RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); CHECK(JS::StencilIsBorrowed(stencil)); JS::InstantiateOptions instantiateOptions; JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } END_TEST(testStencil_OffThreadDecodeBorrow) constexpr size_t PinnedBufferMax = 1024; alignas(4) uint8_t pinnedBuffer[PinnedBufferMax]; size_t pinnedBufferSize = 0; BEGIN_TEST(testStencil_OffThreadDecodePinned) { JS::SetProcessBuildIdOp(TestGetBuildId); JS::TranscodeBuffer buffer; { const char* chars = "function f() { return 42; }" "f();"; JS::SourceText srcBuf; CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); JS::CompileOptions options(cx); RefPtr stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); // Encode Stencil to XDR JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); CHECK(res == JS::TranscodeResult::Ok); CHECK(!buffer.empty()); // Instantiate and Run JS::InstantiateOptions instantiateOptions(options); JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); } CHECK(buffer.length() < PinnedBufferMax); memcpy(pinnedBuffer, buffer.begin(), buffer.length()); pinnedBufferSize = buffer.length(); JS::OffThreadToken* token; { JS::DecodeOptions decodeOptions; js::Monitor monitor MOZ_UNANNOTATED(js::mutexid::ShellOffThreadState); JS::TranscodeRange range(pinnedBuffer, pinnedBufferSize); // Force off-thread even though if this is a small file. decodeOptions.forceAsync = true; decodeOptions.borrowBuffer = true; decodeOptions.usePinnedBytecode = true; CHECK(token = JS::DecodeStencilOffThread(cx, decodeOptions, range, callback, &monitor)); { js::AutoLockMonitor lock(monitor); lock.wait(); } } RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); CHECK(JS::StencilIsBorrowed(stencil)); JS::InstantiateOptions instantiateOptions; JS::RootedScript script( cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); CHECK(JS_ExecuteScript(cx, script, &rval)); CHECK(rval.isNumber() && rval.toNumber() == 42); return true; } static void callback(JS::OffThreadToken* token, void* context) { js::Monitor& monitor = *static_cast(context); js::AutoLockMonitor lock(monitor); lock.notify(); } static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } END_TEST(testStencil_OffThreadDecodePinned)