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/. */
7 #include "builtin/TestingUtility.h"
9 #include <stdint.h> // uint32_t
11 #include "jsapi.h" // JS_NewPlainObject, JS_WrapValue
13 #include "frontend/CompilationStencil.h" // js::frontend::CompilationStencil
14 #include "js/CharacterEncoding.h" // JS_EncodeStringToUTF8
15 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
16 #include "js/CompileOptions.h" // JS::CompileOptions
17 #include "js/Conversions.h" // JS::ToBoolean, JS::ToString, JS::ToUint32, JS::ToInt32
18 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_DefineProperty
19 #include "js/PropertyDescriptor.h" // JSPROP_ENUMERATE
20 #include "js/RealmOptions.h" // JS::RealmBehaviors
21 #include "js/RootingAPI.h" // JS::Rooted, JS::Handle
22 #include "js/Utility.h" // JS::UniqueChars
23 #include "js/Value.h" // JS::Value, JS::StringValue
24 #include "vm/JSContext.h" // JS::ReportUsageErrorASCII
25 #include "vm/JSScript.h"
26 #include "vm/Realm.h" // JS::Realm
28 bool js::ParseCompileOptions(JSContext
* cx
, JS::CompileOptions
& options
,
29 JS::Handle
<JSObject
*> opts
,
30 JS::UniqueChars
* fileNameBytes
) {
31 JS::Rooted
<JS::Value
> v(cx
);
32 JS::Rooted
<JSString
*> s(cx
);
34 if (!JS_GetProperty(cx
, opts
, "isRunOnce", &v
)) {
37 if (!v
.isUndefined()) {
38 options
.setIsRunOnce(JS::ToBoolean(v
));
41 if (!JS_GetProperty(cx
, opts
, "noScriptRval", &v
)) {
44 if (!v
.isUndefined()) {
45 options
.setNoScriptRval(JS::ToBoolean(v
));
48 if (!JS_GetProperty(cx
, opts
, "fileName", &v
)) {
52 options
.setFile(nullptr);
53 } else if (!v
.isUndefined()) {
54 s
= JS::ToString(cx
, v
);
59 *fileNameBytes
= JS_EncodeStringToUTF8(cx
, s
);
60 if (!*fileNameBytes
) {
63 options
.setFile(fileNameBytes
->get());
67 if (!JS_GetProperty(cx
, opts
, "skipFileNameValidation", &v
)) {
70 if (!v
.isUndefined()) {
71 options
.setSkipFilenameValidation(JS::ToBoolean(v
));
74 if (!JS_GetProperty(cx
, opts
, "lineNumber", &v
)) {
77 if (!v
.isUndefined()) {
79 if (!JS::ToUint32(cx
, v
, &u
)) {
85 if (!JS_GetProperty(cx
, opts
, "columnNumber", &v
)) {
88 if (!v
.isUndefined()) {
90 if (!JS::ToInt32(cx
, v
, &c
)) {
96 options
.setColumn(JS::ColumnNumberOneOrigin(c
));
99 if (!JS_GetProperty(cx
, opts
, "sourceIsLazy", &v
)) {
103 options
.setSourceIsLazy(v
.toBoolean());
106 if (!JS_GetProperty(cx
, opts
, "forceFullParse", &v
)) {
109 bool forceFullParseIsSet
= !v
.isUndefined();
110 if (v
.isBoolean() && v
.toBoolean()) {
111 options
.setForceFullParse();
114 if (!JS_GetProperty(cx
, opts
, "eagerDelazificationStrategy", &v
)) {
117 if (forceFullParseIsSet
&& !v
.isUndefined()) {
119 cx
, "forceFullParse and eagerDelazificationStrategy are both set.");
123 s
= JS::ToString(cx
, v
);
128 JSLinearString
* str
= JS_EnsureLinearString(cx
, s
);
134 JS::DelazificationOption strategy
= JS::DelazificationOption::OnDemandOnly
;
136 #define MATCH_AND_SET_STRATEGY_(NAME) \
137 if (!found && JS_LinearStringEqualsLiteral(str, #NAME)) { \
138 strategy = JS::DelazificationOption::NAME; \
142 FOREACH_DELAZIFICATION_STRATEGY(MATCH_AND_SET_STRATEGY_
);
143 #undef MATCH_AND_SET_STRATEGY_
144 #undef FOR_STRATEGY_NAMES
147 JS_ReportErrorASCII(cx
,
148 "eagerDelazificationStrategy does not match any "
149 "DelazificationOption.");
152 options
.setEagerDelazificationStrategy(strategy
);
158 bool js::ParseSourceOptions(JSContext
* cx
, JS::Handle
<JSObject
*> opts
,
159 JS::MutableHandle
<JSString
*> displayURL
,
160 JS::MutableHandle
<JSString
*> sourceMapURL
) {
161 JS::Rooted
<JS::Value
> v(cx
);
163 if (!JS_GetProperty(cx
, opts
, "displayURL", &v
)) {
166 if (!v
.isUndefined()) {
167 displayURL
.set(ToString(cx
, v
));
173 if (!JS_GetProperty(cx
, opts
, "sourceMapURL", &v
)) {
176 if (!v
.isUndefined()) {
177 sourceMapURL
.set(ToString(cx
, v
));
186 bool js::SetSourceOptions(JSContext
* cx
, FrontendContext
* fc
,
187 ScriptSource
* source
,
188 JS::Handle
<JSString
*> displayURL
,
189 JS::Handle
<JSString
*> sourceMapURL
) {
190 if (displayURL
&& !source
->hasDisplayURL()) {
191 JS::UniqueTwoByteChars chars
= JS_CopyStringCharsZ(cx
, displayURL
);
195 if (!source
->setDisplayURL(fc
, std::move(chars
))) {
199 if (sourceMapURL
&& !source
->hasSourceMapURL()) {
200 JS::UniqueTwoByteChars chars
= JS_CopyStringCharsZ(cx
, sourceMapURL
);
204 if (!source
->setSourceMapURL(fc
, std::move(chars
))) {
212 JSObject
* js::CreateScriptPrivate(JSContext
* cx
,
213 JS::Handle
<JSString
*> path
/* = nullptr */) {
214 JS::Rooted
<JSObject
*> info(cx
, JS_NewPlainObject(cx
));
220 JS::Rooted
<JS::Value
> pathValue(cx
, JS::StringValue(path
));
221 if (!JS_DefineProperty(cx
, info
, "path", pathValue
, JSPROP_ENUMERATE
)) {
229 bool js::ParseDebugMetadata(JSContext
* cx
, JS::Handle
<JSObject
*> opts
,
230 JS::MutableHandle
<JS::Value
> privateValue
,
231 JS::MutableHandle
<JSString
*> elementAttributeName
) {
232 JS::Rooted
<JS::Value
> v(cx
);
233 JS::Rooted
<JSString
*> s(cx
);
235 if (!JS_GetProperty(cx
, opts
, "element", &v
)) {
239 JS::Rooted
<JSObject
*> infoObject(cx
, CreateScriptPrivate(cx
));
243 JS::Rooted
<JS::Value
> elementValue(cx
, v
);
244 if (!JS_WrapValue(cx
, &elementValue
)) {
247 if (!JS_DefineProperty(cx
, infoObject
, "element", elementValue
, 0)) {
250 privateValue
.set(JS::ObjectValue(*infoObject
));
253 if (!JS_GetProperty(cx
, opts
, "elementAttributeName", &v
)) {
256 if (!v
.isUndefined()) {
261 elementAttributeName
.set(s
);
267 JS::UniqueChars
js::StringToLocale(JSContext
* cx
, JS::Handle
<JSObject
*> callee
,
268 JS::Handle
<JSString
*> str_
) {
269 Rooted
<JSLinearString
*> str(cx
, str_
->ensureLinear(cx
));
274 if (!StringIsAscii(str
)) {
275 ReportUsageErrorASCII(cx
, callee
,
276 "First argument contains non-ASCII characters");
280 UniqueChars locale
= JS_EncodeStringToASCII(cx
, str
);
285 bool containsOnlyValidBCP47Characters
=
286 mozilla::IsAsciiAlpha(locale
[0]) &&
287 std::all_of(locale
.get(), locale
.get() + str
->length(), [](auto c
) {
288 return mozilla::IsAsciiAlphanumeric(c
) || c
== '-';
291 if (!containsOnlyValidBCP47Characters
) {
292 ReportUsageErrorASCII(cx
, callee
,
293 "First argument should be a BCP47 language tag");
300 bool js::ValidateLazinessOfStencilAndGlobal(
301 JSContext
* cx
, const js::frontend::CompilationStencil
& stencil
) {
302 if (cx
->realm()->behaviors().discardSource() && stencil
.canLazilyParse
) {
303 JS_ReportErrorASCII(cx
,
304 "Stencil compiled with with lazy parse option cannot "
305 "be used in a realm with discardSource");
312 bool js::ValidateModuleCompileOptions(JSContext
* cx
,
313 JS::CompileOptions
& options
) {
314 if (options
.lineno
== 0) {
315 JS_ReportErrorASCII(cx
, "Module cannot be compiled with lineNumber == 0");
319 if (!options
.filename()) {
320 JS_ReportErrorASCII(cx
, "Module should have filename");