1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/RefPtr.h" // RefPtr
6 #include "mozilla/ScopeExit.h" // MakeScopeExit
7 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
9 #include "frontend/CompilationStencil.h" // JS::Stencil
10 #include "js/CompileOptions.h" // JS::CompileOptions, JS::InstantiateOptions
11 #include "js/experimental/CompileScript.h" // JS::NewFrontendContext
12 #include "js/SourceText.h" // JS::Source{Ownership,Text}
13 #include "jsapi-tests/tests.h"
14 #include "vm/ErrorReporting.h"
15 #include "vm/JSONPrinter.h" // js::JSONPrinter
20 static void dump(const T
& data
) {
21 js::Fprinter
printer(stderr
);
22 js::JSONPrinter
json(printer
, true);
28 BEGIN_TEST(testCompileScript
) {
30 CHECK(testNonsyntacticCompile());
31 CHECK(testCompileModule());
32 CHECK(testPrepareForInstantiate());
38 static constexpr std::string_view src
= "42\n";
39 static constexpr std::u16string_view src_16
= u
"42\n";
41 static_assert(src
.length() == src_16
.length(),
42 "Source buffers must be same length");
44 JS::CompileOptions
options(cx
);
46 JS::FrontendContext
* fc
= JS::NewFrontendContext();
49 mozilla::MakeScopeExit([fc
] { JS::DestroyFrontendContext(fc
); });
51 { // 16-bit characters
52 JS::SourceText
<char16_t
> buf16
;
53 CHECK(buf16
.init(cx
, src_16
.data(), src_16
.length(),
54 JS::SourceOwnership::Borrowed
));
56 RefPtr
<JS::Stencil
> stencil
=
57 CompileGlobalScriptToStencil(fc
, options
, buf16
);
59 CHECK(stencil
->scriptExtra
.size() == 1);
60 CHECK(stencil
->scriptExtra
[0].extent
.sourceStart
== 0);
61 CHECK(stencil
->scriptExtra
[0].extent
.sourceEnd
== 3);
62 CHECK(stencil
->scriptData
.size() == 1);
63 CHECK(stencil
->scriptData
[0].hasSharedData()); // has generated bytecode
64 CHECK(stencil
->scriptData
[0].gcThingsLength
== 1);
68 JS::SourceText
<mozilla::Utf8Unit
> buf8
;
70 buf8
.init(cx
, src
.data(), src
.length(), JS::SourceOwnership::Borrowed
));
72 RefPtr
<JS::Stencil
> stencil
=
73 CompileGlobalScriptToStencil(fc
, options
, buf8
);
75 CHECK(stencil
->scriptExtra
.size() == 1);
76 CHECK(stencil
->scriptExtra
[0].extent
.sourceStart
== 0);
77 CHECK(stencil
->scriptExtra
[0].extent
.sourceEnd
== 3);
78 CHECK(stencil
->scriptData
.size() == 1);
79 CHECK(stencil
->scriptData
[0].hasSharedData()); // has generated bytecode
80 CHECK(stencil
->scriptData
[0].gcThingsLength
== 1);
83 { // propagates failures
84 static constexpr std::string_view badSrc
= "{ a: 1, b: 3\n";
85 JS::SourceText
<mozilla::Utf8Unit
> srcBuf
;
86 CHECK(srcBuf
.init(cx
, badSrc
.data(), badSrc
.length(),
87 JS::SourceOwnership::Borrowed
));
89 RefPtr
<JS::Stencil
> stencil
=
90 CompileGlobalScriptToStencil(fc
, options
, srcBuf
);
92 CHECK(fc
->maybeError().isSome());
93 const js::CompileError
& error
= fc
->maybeError().ref();
94 CHECK(JSEXN_SYNTAXERR
== error
.exnType
);
95 CHECK(error
.lineno
== 1);
96 CHECK(error
.column
.oneOriginValue() == 10);
102 bool testNonsyntacticCompile() {
104 "function f() { return x; }"
107 JS::SourceText
<mozilla::Utf8Unit
> srcBuf
;
108 CHECK(srcBuf
.init(cx
, chars
, strlen(chars
), JS::SourceOwnership::Borrowed
));
110 JS::CompileOptions
options(cx
);
111 options
.setNonSyntacticScope(true);
113 JS::FrontendContext
* fc
= JS::NewFrontendContext();
116 mozilla::MakeScopeExit([fc
] { JS::DestroyFrontendContext(fc
); });
118 RefPtr
<JS::Stencil
> stencil
=
119 CompileGlobalScriptToStencil(fc
, options
, srcBuf
);
122 JS::InstantiateOptions
instantiateOptions(options
);
123 JS::RootedScript
script(
124 cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
, stencil
));
126 CHECK(script
->hasNonSyntacticScope());
131 bool testCompileModule() {
132 static constexpr std::string_view src
= "import 'a'; 42\n";
133 static constexpr std::u16string_view src_16
= u
"import 'a'; 42\n";
135 static_assert(src
.length() == src_16
.length(),
136 "Source buffers must be same length");
138 JS::CompileOptions
options(cx
);
140 JS::FrontendContext
* fc
= JS::NewFrontendContext();
143 mozilla::MakeScopeExit([fc
] { JS::DestroyFrontendContext(fc
); });
145 { // 16-bit characters
146 JS::SourceText
<char16_t
> buf16
;
147 CHECK(buf16
.init(cx
, src_16
.data(), src_16
.length(),
148 JS::SourceOwnership::Borrowed
));
150 RefPtr
<JS::Stencil
> stencil
=
151 CompileModuleScriptToStencil(fc
, options
, buf16
);
153 CHECK(stencil
->isModule());
154 CHECK(stencil
->scriptExtra
.size() == 1);
155 CHECK(stencil
->scriptExtra
[0].extent
.sourceStart
== 0);
156 CHECK(stencil
->scriptExtra
[0].extent
.sourceEnd
== 15);
157 CHECK(stencil
->scriptData
.size() == 1);
158 CHECK(stencil
->scriptData
[0].hasSharedData()); // has generated bytecode
159 CHECK(stencil
->scriptData
[0].gcThingsLength
== 1);
162 { // 8-bit characters
163 JS::SourceText
<mozilla::Utf8Unit
> buf8
;
165 buf8
.init(cx
, src
.data(), src
.length(), JS::SourceOwnership::Borrowed
));
167 RefPtr
<JS::Stencil
> stencil
=
168 CompileModuleScriptToStencil(fc
, options
, buf8
);
170 CHECK(stencil
->scriptExtra
.size() == 1);
171 CHECK(stencil
->scriptExtra
[0].extent
.sourceStart
== 0);
172 CHECK(stencil
->scriptExtra
[0].extent
.sourceEnd
== 15);
173 CHECK(stencil
->scriptData
.size() == 1);
174 CHECK(stencil
->scriptData
[0].hasSharedData()); // has generated bytecode
175 CHECK(stencil
->scriptData
[0].gcThingsLength
== 1);
178 { // propagates failures
179 static constexpr std::string_view badSrc
= "{ a: 1, b: 3\n";
180 JS::SourceText
<mozilla::Utf8Unit
> srcBuf
;
181 CHECK(srcBuf
.init(cx
, badSrc
.data(), badSrc
.length(),
182 JS::SourceOwnership::Borrowed
));
184 RefPtr
<JS::Stencil
> stencil
=
185 CompileModuleScriptToStencil(fc
, options
, srcBuf
);
187 CHECK(fc
->maybeError().isSome());
188 const js::CompileError
& error
= fc
->maybeError().ref();
189 CHECK(JSEXN_SYNTAXERR
== error
.exnType
);
190 CHECK(error
.lineno
== 1);
191 CHECK(error
.column
.oneOriginValue() == 10);
197 bool testPrepareForInstantiate() {
198 static constexpr std::u16string_view src_16
=
199 u
"function f() { return {'field': 42};}; f()['field']\n";
201 JS::CompileOptions
options(cx
);
203 JS::SourceText
<char16_t
> buf16
;
204 CHECK(buf16
.init(cx
, src_16
.data(), src_16
.length(),
205 JS::SourceOwnership::Borrowed
));
207 JS::FrontendContext
* fc
= JS::NewFrontendContext();
210 mozilla::MakeScopeExit([fc
] { JS::DestroyFrontendContext(fc
); });
212 RefPtr
<JS::Stencil
> stencil
=
213 CompileGlobalScriptToStencil(fc
, options
, buf16
);
215 CHECK(stencil
->scriptData
.size() == 2);
216 CHECK(stencil
->scopeData
.size() == 1); // function f
217 CHECK(stencil
->parserAtomData
.size() == 1); // 'field'
219 JS::InstantiationStorage storage
;
220 CHECK(JS::PrepareForInstantiate(fc
, *stencil
, storage
));
221 CHECK(storage
.isValid());
222 // TODO storage.gcOutput_ is private, so there isn't a good way to check the
223 // scriptData and scopeData capacities
227 END_TEST(testCompileScript
);