michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jsfriendapi.h" michael@0: #include "jsscript.h" michael@0: #include "jsstr.h" michael@0: michael@0: #include "jsapi-tests/tests.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: michael@0: static JSScript * michael@0: CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JS::HandleObject obj, michael@0: JSPrincipals *originPrincipals, michael@0: const char *bytes, size_t nbytes, michael@0: const char *filename, unsigned lineno, michael@0: JSVersion version) michael@0: { michael@0: size_t nchars; michael@0: if (!JS_DecodeBytes(cx, bytes, nbytes, nullptr, &nchars)) michael@0: return nullptr; michael@0: jschar *chars = static_cast(JS_malloc(cx, nchars * sizeof(jschar))); michael@0: if (!chars) michael@0: return nullptr; michael@0: JS_ALWAYS_TRUE(JS_DecodeBytes(cx, bytes, nbytes, chars, &nchars)); michael@0: JS::CompileOptions options(cx); michael@0: options.setOriginPrincipals(originPrincipals) michael@0: .setFileAndLine(filename, lineno) michael@0: .setVersion(version); michael@0: JSScript *script = JS::Compile(cx, obj, options, chars, nchars); michael@0: free(chars); michael@0: return script; michael@0: } michael@0: michael@0: static JSScript * michael@0: FreezeThaw(JSContext *cx, JS::HandleScript script) michael@0: { michael@0: // freeze michael@0: uint32_t nbytes; michael@0: void *memory = JS_EncodeScript(cx, script, &nbytes); michael@0: if (!memory) michael@0: return nullptr; michael@0: michael@0: // thaw michael@0: JSScript *script2 = JS_DecodeScript(cx, memory, nbytes, michael@0: script->originPrincipals()); michael@0: js_free(memory); michael@0: return script2; michael@0: } michael@0: michael@0: static JSScript * michael@0: GetScript(JSContext *cx, JS::HandleObject funobj) michael@0: { michael@0: JS::RootedFunction fun(cx, JS_GetObjectFunction(funobj)); michael@0: return JS_GetFunctionScript(cx, fun); michael@0: } michael@0: michael@0: static JSObject * michael@0: FreezeThaw(JSContext *cx, JS::HandleObject funobj) michael@0: { michael@0: // freeze michael@0: uint32_t nbytes; michael@0: void *memory = JS_EncodeInterpretedFunction(cx, funobj, &nbytes); michael@0: if (!memory) michael@0: return nullptr; michael@0: michael@0: // thaw michael@0: JSScript *script = GetScript(cx, funobj); michael@0: JSObject *funobj2 = JS_DecodeInterpretedFunction(cx, memory, nbytes, michael@0: script->originPrincipals()); michael@0: js_free(memory); michael@0: return funobj2; michael@0: } michael@0: michael@0: static TestJSPrincipals testPrincipal0(1); michael@0: static TestJSPrincipals testPrincipal1(1); michael@0: michael@0: BEGIN_TEST(testXDR_principals) michael@0: { michael@0: JSScript *script; michael@0: JSCompartment *compartment = js::GetContextCompartment(cx); michael@0: for (int i = TEST_FIRST; i != TEST_END; ++i) { michael@0: // Appease the new JSAPI assertions. The stuff being tested here is michael@0: // going away anyway. michael@0: JS_SetCompartmentPrincipals(compartment, &testPrincipal0); michael@0: script = createScriptViaXDR(nullptr, i); michael@0: CHECK(script); michael@0: CHECK(JS_GetScriptPrincipals(script) == &testPrincipal0); michael@0: CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipal0); michael@0: michael@0: script = createScriptViaXDR(&testPrincipal0, i); michael@0: CHECK(script); michael@0: CHECK(JS_GetScriptPrincipals(script) == &testPrincipal0); michael@0: CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipal0); michael@0: michael@0: script = createScriptViaXDR(&testPrincipal1, i); michael@0: CHECK(script); michael@0: CHECK(JS_GetScriptPrincipals(script) == &testPrincipal0); michael@0: CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipal1); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: enum TestCase { michael@0: TEST_FIRST, michael@0: TEST_SCRIPT = TEST_FIRST, michael@0: TEST_FUNCTION, michael@0: TEST_SERIALIZED_FUNCTION, michael@0: TEST_END michael@0: }; michael@0: michael@0: JSScript *createScriptViaXDR(JSPrincipals *orig, int testCase) michael@0: { michael@0: const char src[] = michael@0: "function f() { return 1; }\n" michael@0: "f;\n"; michael@0: michael@0: JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); michael@0: JS::RootedScript script(cx, CompileScriptForPrincipalsVersionOrigin(cx, global, orig, michael@0: src, strlen(src), "test", 1, michael@0: JSVERSION_DEFAULT)); michael@0: if (!script) michael@0: return nullptr; michael@0: michael@0: if (testCase == TEST_SCRIPT || testCase == TEST_SERIALIZED_FUNCTION) { michael@0: script = FreezeThaw(cx, script); michael@0: if (!script) michael@0: return nullptr; michael@0: if (testCase == TEST_SCRIPT) michael@0: return script; michael@0: } michael@0: michael@0: JS::RootedValue v(cx); michael@0: bool ok = JS_ExecuteScript(cx, global, script, &v); michael@0: if (!ok || !v.isObject()) michael@0: return nullptr; michael@0: JS::RootedObject funobj(cx, &v.toObject()); michael@0: if (testCase == TEST_FUNCTION) { michael@0: funobj = FreezeThaw(cx, funobj); michael@0: if (!funobj) michael@0: return nullptr; michael@0: } michael@0: return GetScript(cx, funobj); michael@0: } michael@0: michael@0: END_TEST(testXDR_principals) michael@0: michael@0: BEGIN_TEST(testXDR_bug506491) michael@0: { michael@0: const char *s = michael@0: "function makeClosure(s, name, value) {\n" michael@0: " eval(s);\n" michael@0: " Math.sin(value);\n" michael@0: " return let (n = name, v = value) function () { return String(v); };\n" michael@0: "}\n" michael@0: "var f = makeClosure('0;', 'status', 'ok');\n"; michael@0: michael@0: // compile michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(__FILE__, __LINE__); michael@0: JS::RootedScript script(cx, JS_CompileScript(cx, global, s, strlen(s), michael@0: options)); michael@0: CHECK(script); michael@0: michael@0: script = FreezeThaw(cx, script); michael@0: CHECK(script); michael@0: michael@0: // execute michael@0: JS::RootedValue v2(cx); michael@0: CHECK(JS_ExecuteScript(cx, global, script, &v2)); michael@0: michael@0: // try to break the Block object that is the parent of f michael@0: JS_GC(rt); michael@0: michael@0: // confirm michael@0: EVAL("f() === 'ok';\n", &v2); michael@0: JS::RootedValue trueval(cx, JSVAL_TRUE); michael@0: CHECK_SAME(v2, trueval); michael@0: return true; michael@0: } michael@0: END_TEST(testXDR_bug506491) michael@0: michael@0: BEGIN_TEST(testXDR_bug516827) michael@0: { michael@0: // compile an empty script michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(__FILE__, __LINE__); michael@0: JS::RootedScript script(cx, JS_CompileScript(cx, global, "", 0, options)); michael@0: CHECK(script); michael@0: michael@0: script = FreezeThaw(cx, script); michael@0: CHECK(script); michael@0: michael@0: // execute with null result meaning no result wanted michael@0: CHECK(JS_ExecuteScript(cx, global, script)); michael@0: return true; michael@0: } michael@0: END_TEST(testXDR_bug516827) michael@0: michael@0: BEGIN_TEST(testXDR_source) michael@0: { michael@0: const char *samples[] = { michael@0: // This can't possibly fail to compress well, can it? michael@0: "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 }", michael@0: "short", michael@0: nullptr michael@0: }; michael@0: for (const char **s = samples; *s; s++) { michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(__FILE__, __LINE__); michael@0: JS::RootedScript script(cx, JS_CompileScript(cx, global, *s, strlen(*s), michael@0: options)); michael@0: CHECK(script); michael@0: script = FreezeThaw(cx, script); michael@0: CHECK(script); michael@0: JSString *out = JS_DecompileScript(cx, script, "testing", 0); michael@0: CHECK(out); michael@0: bool equal; michael@0: CHECK(JS_StringEqualsAscii(cx, out, *s, &equal)); michael@0: CHECK(equal); michael@0: } michael@0: return true; michael@0: } michael@0: END_TEST(testXDR_source) michael@0: michael@0: BEGIN_TEST(testXDR_sourceMap) michael@0: { michael@0: const char *sourceMaps[] = { michael@0: "http://example.com/source-map.json", michael@0: "file:///var/source-map.json", michael@0: nullptr michael@0: }; michael@0: JS::RootedScript script(cx); michael@0: for (const char **sm = sourceMaps; *sm; sm++) { michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(__FILE__, __LINE__); michael@0: script = JS_CompileScript(cx, global, "", 0, options); michael@0: CHECK(script); michael@0: michael@0: size_t len = strlen(*sm); michael@0: jschar *expected = js::InflateString(cx, *sm, &len); michael@0: CHECK(expected); michael@0: michael@0: // The script source takes responsibility of free'ing |expected|. michael@0: CHECK(script->scriptSource()->setSourceMapURL(cx, expected)); michael@0: script = FreezeThaw(cx, script); michael@0: CHECK(script); michael@0: CHECK(script->scriptSource()); michael@0: CHECK(script->scriptSource()->hasSourceMapURL()); michael@0: michael@0: const jschar *actual = script->scriptSource()->sourceMapURL(); michael@0: CHECK(actual); michael@0: michael@0: while (*expected) { michael@0: CHECK(*actual); michael@0: CHECK(*expected == *actual); michael@0: expected++; michael@0: actual++; michael@0: } michael@0: CHECK(!*actual); michael@0: } michael@0: return true; michael@0: } michael@0: END_TEST(testXDR_sourceMap)