diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jsapi-tests/testStencil.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jsapi-tests/testStencil.cpp')
-rw-r--r-- | js/src/jsapi-tests/testStencil.cpp | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testStencil.cpp b/js/src/jsapi-tests/testStencil.cpp new file mode 100644 index 0000000000..fee4e6d881 --- /dev/null +++ b/js/src/jsapi-tests/testStencil.cpp @@ -0,0 +1,881 @@ +/* -*- 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 <string.h> + +#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<char, mozilla::Utf8Unit>(chars); + CHECK(result); + + const char16_t* chars16 = + u"function f() { return 42; }" + u"f();"; + auto result16 = basic_test<char16_t, char16_t>(chars16); + CHECK(result16); + + return true; +} + +template <typename CharT, typename SourceT> +bool basic_test(const CharT* chars) { + size_t length = std::char_traits<CharT>::length(chars); + + JS::SourceText<SourceT> srcBuf; + CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<char, mozilla::Utf8Unit>(chars); + CHECK(result); + + const char16_t* chars16 = + u"export function f() { return 42; }" + u"globalThis.x = f();"; + auto result16 = basic_test<char16_t, char16_t>(chars16); + CHECK(result16); + + return true; +} + +template <typename CharT, typename SourceT> +bool basic_test(const CharT* chars) { + size_t length = std::char_traits<CharT>::length(chars); + + JS::SourceText<SourceT> srcBuf; + CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + options.setNonSyntacticScope(true); + + RefPtr<JS::Stencil> 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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::Stencil> 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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::Stencil> 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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::Stencil> 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<mozilla::Utf8Unit> 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<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> 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<JS::InstantiationStorage> storage(cx); + RefPtr<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> 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<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> 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<JS::InstantiationStorage> storage(cx); + RefPtr<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::InstantiationStorage> storage(cx); + RefPtr<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::Stencil> 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<js::Monitor*>(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<mozilla::Utf8Unit> srcBuf; + CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); + + JS::CompileOptions options(cx); + RefPtr<JS::Stencil> 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<JS::Stencil> 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<js::Monitor*>(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) |