diff options
Diffstat (limited to 'js/src/jsapi-tests/testCompileScript.cpp')
-rw-r--r-- | js/src/jsapi-tests/testCompileScript.cpp | 242 |
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); |