1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/ArrayUtils.h" // mozilla::ArrayLength
8 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
9
10 #include "jsfriendapi.h"
11
12 #include "js/BuildId.h" // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
13 #include "js/CompilationAndEvaluation.h" // JS::Compile
14 #include "js/SourceText.h" // JS::Source{Ownership,Text}
15 #include "js/Transcoding.h"
16 #include "jsapi-tests/tests.h"
17 #include "vm/JSScript.h"
18
19 #include "vm/JSScript-inl.h"
20
GetBuildId(JS::BuildIdCharVector * buildId)21 static bool GetBuildId(JS::BuildIdCharVector* buildId) {
22 const char buildid[] = "testXDR";
23 return buildId->append(buildid, sizeof(buildid));
24 }
25
FreezeThaw(JSContext * cx,JS::HandleScript script)26 static JSScript* FreezeThaw(JSContext* cx, JS::HandleScript script) {
27 JS::SetProcessBuildIdOp(::GetBuildId);
28
29 // freeze
30 JS::TranscodeBuffer buffer;
31 JS::TranscodeResult rs = JS::EncodeScript(cx, buffer, script);
32 if (rs != JS::TranscodeResult_Ok) {
33 return nullptr;
34 }
35
36 // thaw
37 JS::RootedScript script2(cx);
38 rs = JS::DecodeScript(cx, buffer, &script2);
39 if (rs != JS::TranscodeResult_Ok) {
40 return nullptr;
41 }
42 return script2;
43 }
44
45 enum TestCase {
46 TEST_FIRST,
47 TEST_SCRIPT = TEST_FIRST,
48 TEST_FUNCTION,
49 TEST_SERIALIZED_FUNCTION,
50 TEST_END
51 };
52
BEGIN_TEST(testXDR_bug506491)53 BEGIN_TEST(testXDR_bug506491) {
54 static const char s[] =
55 "function makeClosure(s, name, value) {\n"
56 " eval(s);\n"
57 " Math.sin(value);\n"
58 " let n = name, v = value;\n"
59 " return function () { return String(v); };\n"
60 "}\n"
61 "var f = makeClosure('0;', 'status', 'ok');\n";
62
63 // compile
64 JS::CompileOptions options(cx);
65 options.setFileAndLine(__FILE__, __LINE__);
66
67 JS::SourceText<mozilla::Utf8Unit> srcBuf;
68 CHECK(srcBuf.init(cx, s, mozilla::ArrayLength(s) - 1,
69 JS::SourceOwnership::Borrowed));
70
71 JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf));
72 CHECK(script);
73
74 script = FreezeThaw(cx, script);
75 CHECK(script);
76
77 // execute
78 JS::RootedValue v2(cx);
79 CHECK(JS_ExecuteScript(cx, script, &v2));
80
81 // try to break the Block object that is the parent of f
82 JS_GC(cx);
83
84 // confirm
85 EVAL("f() === 'ok';\n", &v2);
86 JS::RootedValue trueval(cx, JS::TrueValue());
87 CHECK_SAME(v2, trueval);
88 return true;
89 }
90 END_TEST(testXDR_bug506491)
91
BEGIN_TEST(testXDR_bug516827)92 BEGIN_TEST(testXDR_bug516827) {
93 // compile an empty script
94 JS::CompileOptions options(cx);
95 options.setFileAndLine(__FILE__, __LINE__);
96
97 JS::SourceText<mozilla::Utf8Unit> srcBuf;
98 CHECK(srcBuf.init(cx, "", 0, JS::SourceOwnership::Borrowed));
99
100 JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf));
101 CHECK(script);
102
103 script = FreezeThaw(cx, script);
104 CHECK(script);
105
106 // execute with null result meaning no result wanted
107 CHECK(JS_ExecuteScript(cx, script));
108 return true;
109 }
110 END_TEST(testXDR_bug516827)
111
BEGIN_TEST(testXDR_source)112 BEGIN_TEST(testXDR_source) {
113 const char* samples[] = {
114 // This can't possibly fail to compress well, can it?
115 "function f(x) { return x + x + x + x + x + x + x + x + x + x + x + x + "
116 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
117 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
118 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
119 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
120 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
121 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
122 "x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + "
123 "x + x + x + x + x + x + x + x + x + x + x + x }",
124 "short", nullptr};
125 for (const char** s = samples; *s; s++) {
126 JS::CompileOptions options(cx);
127 options.setFileAndLine(__FILE__, __LINE__);
128
129 JS::SourceText<mozilla::Utf8Unit> srcBuf;
130 CHECK(srcBuf.init(cx, *s, strlen(*s), JS::SourceOwnership::Borrowed));
131
132 JS::RootedScript script(cx, JS::Compile(cx, options, srcBuf));
133 CHECK(script);
134
135 script = FreezeThaw(cx, script);
136 CHECK(script);
137
138 JSString* out = JS_DecompileScript(cx, script);
139 CHECK(out);
140
141 bool equal;
142 CHECK(JS_StringEqualsAscii(cx, out, *s, &equal));
143 CHECK(equal);
144 }
145 return true;
146 }
147 END_TEST(testXDR_source)
148
BEGIN_TEST(testXDR_sourceMap)149 BEGIN_TEST(testXDR_sourceMap) {
150 const char* sourceMaps[] = {"http://example.com/source-map.json",
151 "file:///var/source-map.json", nullptr};
152 JS::RootedScript script(cx);
153 for (const char** sm = sourceMaps; *sm; sm++) {
154 JS::CompileOptions options(cx);
155 options.setFileAndLine(__FILE__, __LINE__);
156
157 JS::SourceText<mozilla::Utf8Unit> srcBuf;
158 CHECK(srcBuf.init(cx, "", 0, JS::SourceOwnership::Borrowed));
159
160 script = JS::Compile(cx, options, srcBuf);
161 CHECK(script);
162
163 size_t len = strlen(*sm);
164 JS::UniqueTwoByteChars expected_wrapper(js::InflateString(cx, *sm, len));
165 char16_t* expected = expected_wrapper.get();
166 CHECK(expected);
167
168 // The script source takes responsibility of free'ing |expected|.
169 CHECK(script->scriptSource()->setSourceMapURL(cx, expected));
170 script = FreezeThaw(cx, script);
171 CHECK(script);
172 CHECK(script->scriptSource());
173 CHECK(script->scriptSource()->hasSourceMapURL());
174
175 const char16_t* actual = script->scriptSource()->sourceMapURL();
176 CHECK(actual);
177
178 while (*expected) {
179 CHECK(*actual);
180 CHECK(*expected == *actual);
181 expected++;
182 actual++;
183 }
184 CHECK(!*actual);
185 }
186 return true;
187 }
188 END_TEST(testXDR_sourceMap)
189