summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testStencil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testStencil.cpp')
-rw-r--r--js/src/jsapi-tests/testStencil.cpp881
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)