blob: 2b5900b7365b643a29aafb7a473ba320c8794e0c [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "tests.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jsfriendapi.h"
#include "jsscriptinlines.h"
static JSScript *
CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JS::HandleObject obj,
JSPrincipals *principals, JSPrincipals *originPrincipals,
const char *bytes, size_t nbytes,
const char *filename, unsigned lineno,
JSVersion version)
{
size_t nchars;
if (!JS_DecodeBytes(cx, bytes, nbytes, NULL, &nchars))
return NULL;
jschar *chars = static_cast<jschar *>(JS_malloc(cx, nchars * sizeof(jschar)));
if (!chars)
return NULL;
JS_ALWAYS_TRUE(JS_DecodeBytes(cx, bytes, nbytes, chars, &nchars));
JS::CompileOptions options(cx);
options.setPrincipals(principals)
.setOriginPrincipals(originPrincipals)
.setFileAndLine(filename, lineno)
.setVersion(version);
JSScript *script = JS::Compile(cx, obj, options, chars, nchars);
free(chars);
return script;
}
JSScript *
FreezeThaw(JSContext *cx, JS::HandleScript script)
{
// freeze
uint32_t nbytes;
void *memory = JS_EncodeScript(cx, script, &nbytes);
if (!memory)
return NULL;
// thaw
JSScript *script2 = JS_DecodeScript(cx, memory, nbytes,
script->principals(), script->originPrincipals);
js_free(memory);
return script2;
}
static JSScript *
GetScript(JSContext *cx, JS::HandleObject funobj)
{
return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
}
JSObject *
FreezeThaw(JSContext *cx, JS::HandleObject funobj)
{
// freeze
uint32_t nbytes;
void *memory = JS_EncodeInterpretedFunction(cx, funobj, &nbytes);
if (!memory)
return NULL;
// thaw
JSScript *script = GetScript(cx, funobj);
JSObject *funobj2 = JS_DecodeInterpretedFunction(cx, memory, nbytes,
script->principals(),
script->originPrincipals);
js_free(memory);
return funobj2;
}
static JSPrincipals testPrincipals[] = {
{ 1 },
{ 1 },
};
BEGIN_TEST(testXDR_principals)
{
JSScript *script;
JSCompartment *compartment = js::GetContextCompartment(cx);
for (int i = TEST_FIRST; i != TEST_END; ++i) {
// Appease the new JSAPI assertions. The stuff being tested here is
// going away anyway.
JS_SetCompartmentPrincipals(compartment, &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], NULL, i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[0], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[1], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(script) == &testPrincipals[1]);
}
return true;
}
enum TestCase {
TEST_FIRST,
TEST_SCRIPT = TEST_FIRST,
TEST_FUNCTION,
TEST_SERIALIZED_FUNCTION,
TEST_END
};
JSScript *createScriptViaXDR(JSPrincipals *prin, JSPrincipals *orig, int testCase)
{
const char src[] =
"function f() { return 1; }\n"
"f;\n";
JS::RootedObject global(cx, JS_GetGlobalForScopeChain(cx));
JS::RootedScript script(cx, CompileScriptForPrincipalsVersionOrigin(cx, global, prin, orig,
src, strlen(src), "test", 1,
JSVERSION_DEFAULT));
if (!script)
return NULL;
if (testCase == TEST_SCRIPT || testCase == TEST_SERIALIZED_FUNCTION) {
script = FreezeThaw(cx, script);
if (!script)
return NULL;
if (testCase == TEST_SCRIPT)
return script;
}
JS::RootedValue v(cx);
JSBool ok = JS_ExecuteScript(cx, global, script, v.address());
if (!ok || !v.isObject())
return NULL;
JS::RootedObject funobj(cx, &v.toObject());
if (testCase == TEST_FUNCTION) {
funobj = FreezeThaw(cx, funobj);
if (!funobj)
return NULL;
}
return GetScript(cx, funobj);
}
END_TEST(testXDR_principals)
BEGIN_TEST(testXDR_bug506491)
{
const char *s =
"function makeClosure(s, name, value) {\n"
" eval(s);\n"
" Math.sin(value);\n"
" return let (n = name, v = value) function () { return String(v); };\n"
"}\n"
"var f = makeClosure('0;', 'status', 'ok');\n";
// compile
JS::RootedScript script(cx, JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__));
CHECK(script);
script = FreezeThaw(cx, script);
CHECK(script);
// execute
JS::RootedValue v2(cx);
CHECK(JS_ExecuteScript(cx, global, script, v2.address()));
// try to break the Block object that is the parent of f
JS_GC(rt);
// confirm
EVAL("f() === 'ok';\n", v2.address());
JS::RootedValue trueval(cx, JSVAL_TRUE);
CHECK_SAME(v2, trueval);
return true;
}
END_TEST(testXDR_bug506491)
BEGIN_TEST(testXDR_bug516827)
{
// compile an empty script
JS::RootedScript script(cx, JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__));
CHECK(script);
script = FreezeThaw(cx, script);
CHECK(script);
// execute with null result meaning no result wanted
CHECK(JS_ExecuteScript(cx, global, script, NULL));
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",
NULL
};
for (const char **s = samples; *s; s++) {
JS::RootedScript script(cx, JS_CompileScript(cx, global, *s, strlen(*s), __FILE__, __LINE__));
CHECK(script);
script = FreezeThaw(cx, script);
CHECK(script);
JSString *out = JS_DecompileScript(cx, script, "testing", 0);
CHECK(out);
JSBool 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",
NULL
};
JS::RootedScript script(cx);
for (const char **sm = sourceMaps; *sm; sm++) {
script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
CHECK(script);
size_t len = strlen(*sm);
jschar *expected = js::InflateString(cx, *sm, &len);
CHECK(expected);
// The script source takes responsibility of free'ing |expected|.
CHECK(script->scriptSource()->setSourceMap(cx, expected, script->filename()));
script = FreezeThaw(cx, script);
CHECK(script);
CHECK(script->scriptSource());
CHECK(script->scriptSource()->hasSourceMap());
const jschar *actual = script->scriptSource()->sourceMap();
CHECK(actual);
while (*expected) {
CHECK(*actual);
CHECK(*expected == *actual);
expected++;
actual++;
}
CHECK(!*actual);
}
return true;
}
END_TEST(testXDR_sourceMap)