summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testCompileScript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testCompileScript.cpp')
-rw-r--r--js/src/jsapi-tests/testCompileScript.cpp242
1 files changed, 242 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testCompileScript.cpp b/js/src/jsapi-tests/testCompileScript.cpp
new file mode 100644
index 0000000000..886af8dbe7
--- /dev/null
+++ b/js/src/jsapi-tests/testCompileScript.cpp
@@ -0,0 +1,242 @@
+/* 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 "mozilla/RefPtr.h" // RefPtr
+#include "mozilla/ScopeExit.h" // MakeScopeExit
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+
+#include "frontend/CompilationStencil.h" // JS::Stencil
+#include "js/CompileOptions.h" // JS::CompileOptions, JS::InstantiateOptions
+#include "js/experimental/CompileScript.h" // JS::NewFrontendContext
+#include "js/SourceText.h" // JS::Source{Ownership,Text}
+#include "jsapi-tests/tests.h"
+#include "vm/ErrorReporting.h"
+#include "vm/JSONPrinter.h" // js::JSONPrinter
+
+using namespace JS;
+
+template <typename T>
+static void dump(const T& data) {
+ js::Fprinter printer(stderr);
+ js::JSONPrinter json(printer, true);
+
+ data->dump(json);
+ printer.put("\n");
+}
+
+BEGIN_TEST(testCompileScript) {
+ CHECK(testCompile());
+ CHECK(testNonsyntacticCompile());
+ CHECK(testCompileModule());
+ CHECK(testPrepareForInstantiate());
+
+ return true;
+}
+
+bool testCompile() {
+ static constexpr std::string_view src = "42\n";
+ static constexpr std::u16string_view src_16 = u"42\n";
+
+ static_assert(src.length() == src_16.length(),
+ "Source buffers must be same length");
+
+ JS::CompileOptions options(cx);
+
+ JS::FrontendContext* fc = JS::NewFrontendContext();
+ CHECK(fc);
+ auto destroyFc =
+ mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); });
+
+ { // 16-bit characters
+ JS::SourceText<char16_t> buf16;
+ CHECK(buf16.init(cx, src_16.data(), src_16.length(),
+ JS::SourceOwnership::Borrowed));
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileGlobalScriptToStencil(fc, options, buf16, compileStorage);
+ CHECK(stencil);
+ CHECK(stencil->scriptExtra.size() == 1);
+ CHECK(stencil->scriptExtra[0].extent.sourceStart == 0);
+ CHECK(stencil->scriptExtra[0].extent.sourceEnd == 3);
+ CHECK(stencil->scriptData.size() == 1);
+ CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode
+ CHECK(stencil->scriptData[0].gcThingsLength == 1);
+ CHECK(compileStorage.hasInput());
+ }
+
+ { // 8-bit characters
+ JS::SourceText<mozilla::Utf8Unit> buf8;
+ CHECK(
+ buf8.init(cx, src.data(), src.length(), JS::SourceOwnership::Borrowed));
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileGlobalScriptToStencil(fc, options, buf8, compileStorage);
+ CHECK(stencil);
+ CHECK(stencil->scriptExtra.size() == 1);
+ CHECK(stencil->scriptExtra[0].extent.sourceStart == 0);
+ CHECK(stencil->scriptExtra[0].extent.sourceEnd == 3);
+ CHECK(stencil->scriptData.size() == 1);
+ CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode
+ CHECK(stencil->scriptData[0].gcThingsLength == 1);
+ CHECK(compileStorage.hasInput());
+ }
+
+ { // propagates failures
+ static constexpr std::string_view badSrc = "{ a: 1, b: 3\n";
+ JS::SourceText<mozilla::Utf8Unit> srcBuf;
+ CHECK(srcBuf.init(cx, badSrc.data(), badSrc.length(),
+ JS::SourceOwnership::Borrowed));
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage);
+ CHECK(!stencil);
+ CHECK(fc->maybeError().isSome());
+ const js::CompileError& error = fc->maybeError().ref();
+ CHECK(JSEXN_SYNTAXERR == error.exnType);
+ CHECK(error.lineno == 1);
+ CHECK(error.column == 9);
+ }
+
+ return true;
+}
+
+bool testNonsyntacticCompile() {
+ 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);
+
+ JS::FrontendContext* fc = JS::NewFrontendContext();
+ CHECK(fc);
+ auto destroyFc =
+ mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); });
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileGlobalScriptToStencil(fc, options, srcBuf, compileStorage);
+ CHECK(stencil);
+
+ JS::InstantiateOptions instantiateOptions(options);
+ JS::RootedScript script(
+ cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil));
+ CHECK(script);
+ CHECK(script->hasNonSyntacticScope());
+
+ return true;
+}
+
+bool testCompileModule() {
+ static constexpr std::string_view src = "import 'a'; 42\n";
+ static constexpr std::u16string_view src_16 = u"import 'a'; 42\n";
+
+ static_assert(src.length() == src_16.length(),
+ "Source buffers must be same length");
+
+ JS::CompileOptions options(cx);
+
+ JS::FrontendContext* fc = JS::NewFrontendContext();
+ CHECK(fc);
+ auto destroyFc =
+ mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); });
+
+ { // 16-bit characters
+ JS::SourceText<char16_t> buf16;
+ CHECK(buf16.init(cx, src_16.data(), src_16.length(),
+ JS::SourceOwnership::Borrowed));
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileModuleScriptToStencil(fc, options, buf16, compileStorage);
+ CHECK(stencil);
+ CHECK(stencil->isModule());
+ CHECK(stencil->scriptExtra.size() == 1);
+ CHECK(stencil->scriptExtra[0].extent.sourceStart == 0);
+ CHECK(stencil->scriptExtra[0].extent.sourceEnd == 15);
+ CHECK(stencil->scriptData.size() == 1);
+ CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode
+ CHECK(stencil->scriptData[0].gcThingsLength == 1);
+ CHECK(compileStorage.hasInput());
+ }
+
+ { // 8-bit characters
+ JS::SourceText<mozilla::Utf8Unit> buf8;
+ CHECK(
+ buf8.init(cx, src.data(), src.length(), JS::SourceOwnership::Borrowed));
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileModuleScriptToStencil(fc, options, buf8, compileStorage);
+ CHECK(stencil);
+ CHECK(stencil->scriptExtra.size() == 1);
+ CHECK(stencil->scriptExtra[0].extent.sourceStart == 0);
+ CHECK(stencil->scriptExtra[0].extent.sourceEnd == 15);
+ CHECK(stencil->scriptData.size() == 1);
+ CHECK(stencil->scriptData[0].hasSharedData()); // has generated bytecode
+ CHECK(stencil->scriptData[0].gcThingsLength == 1);
+ CHECK(compileStorage.hasInput());
+ }
+
+ { // propagates failures
+ static constexpr std::string_view badSrc = "{ a: 1, b: 3\n";
+ JS::SourceText<mozilla::Utf8Unit> srcBuf;
+ CHECK(srcBuf.init(cx, badSrc.data(), badSrc.length(),
+ JS::SourceOwnership::Borrowed));
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileModuleScriptToStencil(fc, options, srcBuf, compileStorage);
+ CHECK(!stencil);
+ CHECK(fc->maybeError().isSome());
+ const js::CompileError& error = fc->maybeError().ref();
+ CHECK(JSEXN_SYNTAXERR == error.exnType);
+ CHECK(error.lineno == 1);
+ CHECK(error.column == 9);
+ }
+
+ return true;
+}
+
+bool testPrepareForInstantiate() {
+ static constexpr std::u16string_view src_16 =
+ u"function f() { return {'field': 42};}; f()['field']\n";
+
+ JS::CompileOptions options(cx);
+
+ JS::SourceText<char16_t> buf16;
+ CHECK(buf16.init(cx, src_16.data(), src_16.length(),
+ JS::SourceOwnership::Borrowed));
+
+ JS::FrontendContext* fc = JS::NewFrontendContext();
+ CHECK(fc);
+ auto destroyFc =
+ mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); });
+
+ JS::CompilationStorage compileStorage;
+ RefPtr<JS::Stencil> stencil =
+ CompileGlobalScriptToStencil(fc, options, buf16, compileStorage);
+ CHECK(stencil);
+ CHECK(stencil->scriptData.size() == 2);
+ CHECK(stencil->scopeData.size() == 1); // function f
+ CHECK(stencil->parserAtomData.size() == 1); // 'field'
+ CHECK(compileStorage.hasInput());
+ CHECK(compileStorage.getInput().atomCache.empty());
+
+ JS::InstantiationStorage storage;
+ CHECK(JS::PrepareForInstantiate(fc, compileStorage, *stencil, storage));
+ CHECK(compileStorage.getInput().atomCache.size() == 1); // 'field'
+ CHECK(storage.isValid());
+ // TODO storage.gcOutput_ is private, so there isn't a good way to check the
+ // scriptData and scopeData capacities
+
+ return true;
+}
+END_TEST(testCompileScript);