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