/* -*- 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 "mozilla/Utf8.h" // mozilla::Utf8Unit #include "jsfriendapi.h" #include "js/BuildId.h" // JS::BuildIdCharVector, JS::SetProcessBuildIdOp #include "js/CompilationAndEvaluation.h" // JS::Compile #include "js/CompileOptions.h" // JS::CompileOptions #include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/Transcoding.h" #include "jsapi-tests/tests.h" #include "util/Text.h" #include "vm/JSScript.h" #include "vm/JSScript-inl.h" static bool GetBuildId(JS::BuildIdCharVector* buildId) { const char buildid[] = "testXDR"; return buildId->append(buildid, sizeof(buildid)); } static JSScript* FreezeThaw(JSContext* cx, JS::CompileOptions& options, JS::HandleScript script) { JS::SetProcessBuildIdOp(::GetBuildId); // freeze JS::TranscodeBuffer buffer; JS::TranscodeResult rs = JS::EncodeScript(cx, buffer, script); if (rs != JS::TranscodeResult_Ok) { return nullptr; } // thaw JS::RootedScript script2(cx); rs = JS::DecodeScript(cx, options, buffer, &script2); if (rs != JS::TranscodeResult_Ok) { return nullptr; } return script2; } enum TestCase { TEST_FIRST, TEST_SCRIPT = TEST_FIRST, TEST_FUNCTION, TEST_SERIALIZED_FUNCTION, TEST_END }; BEGIN_TEST(testXDR_bug506491) { static const char s[] = "function makeClosure(s, name, value) {\n" " eval(s);\n" " Math.sin(value);\n" " let n = name, v = value;\n" " return function () { return String(v); };\n" "}\n" "var f = makeClosure('0;', 'status', 'ok');\n"; // compile JS::CompileOptions options(cx); options.setFileAndLine(__FILE__, __LINE__); JS::SourceText srcBuf; CHECK(srcBuf.init(cx, s, js_strlen(s), JS::SourceOwnership::Borrowed)); JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf)); CHECK(script); script = FreezeThaw(cx, options, script); CHECK(script); // execute JS::RootedValue v2(cx); CHECK(JS_ExecuteScript(cx, script, &v2)); // try to break the Block object that is the parent of f JS_GC(cx); // confirm EVAL("f() === 'ok';\n", &v2); JS::RootedValue trueval(cx, JS::TrueValue()); CHECK_SAME(v2, trueval); return true; } END_TEST(testXDR_bug506491) BEGIN_TEST(testXDR_bug516827) { // compile an empty script JS::CompileOptions options(cx); options.setFileAndLine(__FILE__, __LINE__); JS::SourceText srcBuf; CHECK(srcBuf.init(cx, "", 0, JS::SourceOwnership::Borrowed)); JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf)); CHECK(script); script = FreezeThaw(cx, options, script); CHECK(script); // execute with null result meaning no result wanted CHECK(JS_ExecuteScript(cx, script)); return true; } END_TEST(testXDR_bug516827) BEGIN_TEST(testXDR_source) { const char* samples[] = { // This can't possibly fail to compress well, can it? "function f(x) { return x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + " "x + x + x + x + x + x + x + x + x + x + x + x }", "short", nullptr}; for (const char** s = samples; *s; s++) { JS::CompileOptions options(cx); options.setFileAndLine(__FILE__, __LINE__); JS::SourceText srcBuf; CHECK(srcBuf.init(cx, *s, strlen(*s), JS::SourceOwnership::Borrowed)); JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf)); CHECK(script); script = FreezeThaw(cx, options, script); CHECK(script); JSString* out = JS_DecompileScript(cx, script); CHECK(out); bool equal; CHECK(JS_StringEqualsAscii(cx, out, *s, &equal)); CHECK(equal); } return true; } END_TEST(testXDR_source) BEGIN_TEST(testXDR_sourceMap) { const char* sourceMaps[] = {"http://example.com/source-map.json", "file:///var/source-map.json", nullptr}; JS::RootedScript script(cx); for (const char** sm = sourceMaps; *sm; sm++) { JS::CompileOptions options(cx); options.setFileAndLine(__FILE__, __LINE__); JS::SourceText srcBuf; CHECK(srcBuf.init(cx, "", 0, JS::SourceOwnership::Borrowed)); script = JS::Compile(cx, options, srcBuf); CHECK(script); size_t len = strlen(*sm); JS::UniqueTwoByteChars expected_wrapper(js::InflateString(cx, *sm, len)); char16_t* expected = expected_wrapper.get(); CHECK(expected); // The script source takes responsibility of free'ing |expected|. CHECK(script->scriptSource()->setSourceMapURL(cx, expected)); script = FreezeThaw(cx, options, script); CHECK(script); CHECK(script->scriptSource()); CHECK(script->scriptSource()->hasSourceMapURL()); const char16_t* actual = script->scriptSource()->sourceMapURL(); CHECK(actual); while (*expected) { CHECK(*actual); CHECK(*expected == *actual); expected++; actual++; } CHECK(!*actual); } return true; } END_TEST(testXDR_sourceMap)