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 /* Same-thread compilation and evaluation APIs. */
8
9 #include "js/CompilationAndEvaluation.h"
10
11 #include "mozilla/Maybe.h" // mozilla::None, mozilla::Some
12 #include "mozilla/TextUtils.h" // mozilla::IsAscii
13 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
14
15 #include <utility> // std::move
16
17 #include "jstypes.h" // JS_PUBLIC_API
18
19 #include "frontend/BytecodeCompilation.h" // frontend::CompileGlobalScript
20 #include "frontend/CompilationStencil.h" // for frontened::{CompilationStencil, BorrowingCompilationStencil, CompilationGCOutput}
21 #include "frontend/FullParseHandler.h" // frontend::FullParseHandler
22 #include "frontend/ParseContext.h" // frontend::UsedNameTracker
23 #include "frontend/Parser.h" // frontend::Parser, frontend::ParseGoal
24 #include "js/CharacterEncoding.h" // JS::UTF8Chars, JS::UTF8CharsToNewTwoByteCharsZ
25 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
26 #include "js/RootingAPI.h" // JS::Rooted
27 #include "js/SourceText.h" // JS::SourceText
28 #include "js/TypeDecls.h" // JS::HandleObject, JS::MutableHandleScript
29 #include "js/Utility.h" // js::MallocArena, JS::UniqueTwoByteChars
30 #include "js/Value.h" // JS::Value
31 #include "util/CompleteFile.h" // js::FileContents, js::ReadCompleteFile
32 #include "util/StringBuffer.h" // js::StringBuffer
33 #include "vm/EnvironmentObject.h" // js::CreateNonSyntacticEnvironmentChain
34 #include "vm/FunctionFlags.h" // js::FunctionFlags
35 #include "vm/Interpreter.h" // js::Execute
36 #include "vm/JSContext.h" // JSContext
37
38 #include "debugger/DebugAPI-inl.h" // js::DebugAPI
39 #include "vm/JSContext-inl.h" // JSContext::check
40
41 using mozilla::Utf8Unit;
42
43 using JS::CompileOptions;
44 using JS::HandleObject;
45 using JS::ReadOnlyCompileOptions;
46 using JS::SourceOwnership;
47 using JS::SourceText;
48 using JS::UniqueTwoByteChars;
49 using JS::UTF8Chars;
50 using JS::UTF8CharsToNewTwoByteCharsZ;
51
52 using namespace js;
53
ReportSourceTooLong(JSContext * cx)54 JS_PUBLIC_API void JS::detail::ReportSourceTooLong(JSContext* cx) {
55 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
56 JSMSG_SOURCE_TOO_LONG);
57 }
58
59 template <typename Unit>
CompileSourceBuffer(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<Unit> & srcBuf)60 static JSScript* CompileSourceBuffer(JSContext* cx,
61 const ReadOnlyCompileOptions& options,
62 SourceText<Unit>& srcBuf) {
63 ScopeKind scopeKind =
64 options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
65
66 MOZ_ASSERT(!cx->zone()->isAtomsZone());
67 AssertHeapIsIdle();
68 CHECK_THREAD(cx);
69
70 return frontend::CompileGlobalScript(cx, options, srcBuf, scopeKind);
71 }
72
Compile(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<char16_t> & srcBuf)73 JSScript* JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
74 SourceText<char16_t>& srcBuf) {
75 return CompileSourceBuffer(cx, options, srcBuf);
76 }
77
Compile(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<Utf8Unit> & srcBuf)78 JSScript* JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
79 SourceText<Utf8Unit>& srcBuf) {
80 return CompileSourceBuffer(cx, options, srcBuf);
81 }
82
83 template <typename Unit>
CompileSourceBufferAndStartIncrementalEncoding(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<Unit> & srcBuf)84 static JSScript* CompileSourceBufferAndStartIncrementalEncoding(
85 JSContext* cx, const ReadOnlyCompileOptions& options,
86 SourceText<Unit>& srcBuf) {
87 MOZ_ASSERT(!cx->zone()->isAtomsZone());
88 AssertHeapIsIdle();
89 CHECK_THREAD(cx);
90
91 ScopeKind scopeKind =
92 options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
93
94 Rooted<frontend::CompilationInput> input(cx,
95 frontend::CompilationInput(options));
96 auto stencil = frontend::CompileGlobalScriptToExtensibleStencil(
97 cx, input.get(), srcBuf, scopeKind);
98 if (!stencil) {
99 return nullptr;
100 }
101
102 RootedScript script(cx);
103 {
104 frontend::BorrowingCompilationStencil borrowingStencil(*stencil);
105
106 Rooted<frontend::CompilationGCOutput> gcOutput(cx);
107 if (!frontend::InstantiateStencils(cx, input.get(), borrowingStencil,
108 gcOutput.get())) {
109 return nullptr;
110 }
111
112 script = gcOutput.get().script;
113 if (!script) {
114 return nullptr;
115 }
116 }
117
118 MOZ_DIAGNOSTIC_ASSERT(options.useStencilXDR);
119 if (!script->scriptSource()->startIncrementalEncoding(cx, options,
120 std::move(stencil))) {
121 return nullptr;
122 }
123
124 return script;
125 }
126
CompileAndStartIncrementalEncoding(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<char16_t> & srcBuf)127 JSScript* JS::CompileAndStartIncrementalEncoding(
128 JSContext* cx, const ReadOnlyCompileOptions& options,
129 SourceText<char16_t>& srcBuf) {
130 return CompileSourceBufferAndStartIncrementalEncoding(cx, options, srcBuf);
131 }
132
CompileAndStartIncrementalEncoding(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<Utf8Unit> & srcBuf)133 JSScript* JS::CompileAndStartIncrementalEncoding(
134 JSContext* cx, const ReadOnlyCompileOptions& options,
135 SourceText<Utf8Unit>& srcBuf) {
136 return CompileSourceBufferAndStartIncrementalEncoding(cx, options, srcBuf);
137 }
138
CompileUtf8File(JSContext * cx,const ReadOnlyCompileOptions & options,FILE * file)139 JSScript* JS::CompileUtf8File(JSContext* cx,
140 const ReadOnlyCompileOptions& options,
141 FILE* file) {
142 FileContents buffer(cx);
143 if (!ReadCompleteFile(cx, file, buffer)) {
144 return nullptr;
145 }
146
147 SourceText<Utf8Unit> srcBuf;
148 if (!srcBuf.init(cx, reinterpret_cast<const char*>(buffer.begin()),
149 buffer.length(), SourceOwnership::Borrowed)) {
150 return nullptr;
151 }
152
153 return CompileSourceBuffer(cx, options, srcBuf);
154 }
155
CompileUtf8Path(JSContext * cx,const ReadOnlyCompileOptions & optionsArg,const char * filename)156 JSScript* JS::CompileUtf8Path(JSContext* cx,
157 const ReadOnlyCompileOptions& optionsArg,
158 const char* filename) {
159 AutoFile file;
160 if (!file.open(cx, filename)) {
161 return nullptr;
162 }
163
164 CompileOptions options(cx, optionsArg);
165 options.setFileAndLine(filename, 1);
166 return CompileUtf8File(cx, options, file.fp());
167 }
168
JS_Utf8BufferIsCompilableUnit(JSContext * cx,HandleObject obj,const char * utf8,size_t length)169 JS_PUBLIC_API bool JS_Utf8BufferIsCompilableUnit(JSContext* cx,
170 HandleObject obj,
171 const char* utf8,
172 size_t length) {
173 AssertHeapIsIdle();
174 CHECK_THREAD(cx);
175 cx->check(obj);
176
177 cx->clearPendingException();
178
179 JS::UniqueTwoByteChars chars{
180 UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(utf8, length), &length,
181 js::MallocArena)
182 .get()};
183 if (!chars) {
184 return true;
185 }
186
187 // Return true on any out-of-memory error or non-EOF-related syntax error, so
188 // our caller doesn't try to collect more buffered source.
189 bool result = true;
190
191 using frontend::FullParseHandler;
192 using frontend::ParseGoal;
193 using frontend::Parser;
194
195 CompileOptions options(cx);
196 Rooted<frontend::CompilationInput> input(cx,
197 frontend::CompilationInput(options));
198 if (!input.get().initForGlobal(cx)) {
199 return false;
200 }
201
202 LifoAllocScope allocScope(&cx->tempLifoAlloc());
203 frontend::CompilationState compilationState(cx, allocScope, input.get());
204 if (!compilationState.init(cx)) {
205 return false;
206 }
207
208 JS::AutoSuppressWarningReporter suppressWarnings(cx);
209 Parser<FullParseHandler, char16_t> parser(cx, options, chars.get(), length,
210 /* foldConstants = */ true,
211 compilationState,
212 /* syntaxParser = */ nullptr);
213 if (!parser.checkOptions() || !parser.parse()) {
214 // We ran into an error. If it was because we ran out of source, we
215 // return false so our caller knows to try to collect more buffered
216 // source.
217 if (parser.isUnexpectedEOF()) {
218 result = false;
219 }
220
221 cx->clearPendingException();
222 }
223
224 return result;
225 }
226
227 class FunctionCompiler {
228 private:
229 JSContext* const cx_;
230 RootedAtom nameAtom_;
231 StringBuffer funStr_;
232
233 uint32_t parameterListEnd_ = 0;
234 bool nameIsIdentifier_ = true;
235
236 public:
FunctionCompiler(JSContext * cx)237 explicit FunctionCompiler(JSContext* cx)
238 : cx_(cx), nameAtom_(cx), funStr_(cx) {
239 AssertHeapIsIdle();
240 CHECK_THREAD(cx);
241 MOZ_ASSERT(!cx->zone()->isAtomsZone());
242 }
243
init(const char * name,unsigned nargs,const char * const * argnames)244 [[nodiscard]] bool init(const char* name, unsigned nargs,
245 const char* const* argnames) {
246 if (!funStr_.ensureTwoByteChars()) {
247 return false;
248 }
249 if (!funStr_.append("function ")) {
250 return false;
251 }
252
253 if (name) {
254 size_t nameLen = strlen(name);
255
256 nameAtom_ = Atomize(cx_, name, nameLen);
257 if (!nameAtom_) {
258 return false;
259 }
260
261 // If the name is an identifier, we can just add it to source text.
262 // Otherwise we'll have to set it manually later.
263 nameIsIdentifier_ = js::frontend::IsIdentifier(
264 reinterpret_cast<const Latin1Char*>(name), nameLen);
265 if (nameIsIdentifier_) {
266 if (!funStr_.append(nameAtom_)) {
267 return false;
268 }
269 }
270 }
271
272 if (!funStr_.append("(")) {
273 return false;
274 }
275
276 for (unsigned i = 0; i < nargs; i++) {
277 if (i != 0) {
278 if (!funStr_.append(", ")) {
279 return false;
280 }
281 }
282 if (!funStr_.append(argnames[i], strlen(argnames[i]))) {
283 return false;
284 }
285 }
286
287 // Remember the position of ")".
288 parameterListEnd_ = funStr_.length();
289 MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')');
290
291 return funStr_.append(FunctionConstructorMedialSigils);
292 }
293
294 template <typename Unit>
addFunctionBody(const SourceText<Unit> & srcBuf)295 [[nodiscard]] inline bool addFunctionBody(const SourceText<Unit>& srcBuf) {
296 return funStr_.append(srcBuf.get(), srcBuf.length());
297 }
298
finish(HandleObjectVector envChain,const ReadOnlyCompileOptions & optionsArg)299 JSFunction* finish(HandleObjectVector envChain,
300 const ReadOnlyCompileOptions& optionsArg) {
301 using js::frontend::FunctionSyntaxKind;
302
303 if (!funStr_.append(FunctionConstructorFinalBrace)) {
304 return nullptr;
305 }
306
307 size_t newLen = funStr_.length();
308 UniqueTwoByteChars stolen(funStr_.stealChars());
309 if (!stolen) {
310 return nullptr;
311 }
312
313 SourceText<char16_t> newSrcBuf;
314 if (!newSrcBuf.init(cx_, std::move(stolen), newLen)) {
315 return nullptr;
316 }
317
318 RootedObject enclosingEnv(cx_);
319 ScopeKind kind;
320 if (envChain.empty()) {
321 // A compiled function has a burned-in environment chain, so if no exotic
322 // environment was requested, we can use the global lexical environment
323 // directly and not need to worry about any potential non-syntactic scope.
324 enclosingEnv.set(&cx_->global()->lexicalEnvironment());
325 kind = ScopeKind::Global;
326 } else {
327 if (!CreateNonSyntacticEnvironmentChain(cx_, envChain, &enclosingEnv)) {
328 return nullptr;
329 }
330 kind = ScopeKind::NonSyntactic;
331 }
332
333 cx_->check(enclosingEnv);
334
335 // Make sure the static scope chain matches up when we have a
336 // non-syntactic scope.
337 MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(enclosingEnv),
338 kind == ScopeKind::NonSyntactic);
339
340 CompileOptions options(cx_, optionsArg);
341 options.setNonSyntacticScope(kind == ScopeKind::NonSyntactic);
342
343 FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
344 RootedFunction fun(cx_);
345 if (kind == ScopeKind::NonSyntactic) {
346 RootedScope enclosingScope(
347 cx_, GlobalScope::createEmpty(cx_, ScopeKind::NonSyntactic));
348 if (!enclosingScope) {
349 return nullptr;
350 }
351
352 fun = js::frontend::CompileStandaloneFunctionInNonSyntacticScope(
353 cx_, options, newSrcBuf, mozilla::Some(parameterListEnd_), syntaxKind,
354 enclosingScope);
355 } else {
356 fun = js::frontend::CompileStandaloneFunction(
357 cx_, options, newSrcBuf, mozilla::Some(parameterListEnd_),
358 syntaxKind);
359 }
360 if (!fun) {
361 return nullptr;
362 }
363
364 // When the function name isn't a valid identifier, the generated function
365 // source in srcBuf won't include the name, so name the function manually.
366 if (!nameIsIdentifier_) {
367 fun->setAtom(nameAtom_);
368 }
369
370 if (fun->isInterpreted()) {
371 fun->initEnvironment(enclosingEnv);
372 }
373
374 return fun;
375 }
376 };
377
CompileFunction(JSContext * cx,HandleObjectVector envChain,const ReadOnlyCompileOptions & options,const char * name,unsigned nargs,const char * const * argnames,SourceText<char16_t> & srcBuf)378 JS_PUBLIC_API JSFunction* JS::CompileFunction(
379 JSContext* cx, HandleObjectVector envChain,
380 const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
381 const char* const* argnames, SourceText<char16_t>& srcBuf) {
382 FunctionCompiler compiler(cx);
383 if (!compiler.init(name, nargs, argnames) ||
384 !compiler.addFunctionBody(srcBuf)) {
385 return nullptr;
386 }
387
388 return compiler.finish(envChain, options);
389 }
390
CompileFunction(JSContext * cx,HandleObjectVector envChain,const ReadOnlyCompileOptions & options,const char * name,unsigned nargs,const char * const * argnames,SourceText<Utf8Unit> & srcBuf)391 JS_PUBLIC_API JSFunction* JS::CompileFunction(
392 JSContext* cx, HandleObjectVector envChain,
393 const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
394 const char* const* argnames, SourceText<Utf8Unit>& srcBuf) {
395 FunctionCompiler compiler(cx);
396 if (!compiler.init(name, nargs, argnames) ||
397 !compiler.addFunctionBody(srcBuf)) {
398 return nullptr;
399 }
400
401 return compiler.finish(envChain, options);
402 }
403
CompileFunctionUtf8(JSContext * cx,HandleObjectVector envChain,const ReadOnlyCompileOptions & options,const char * name,unsigned nargs,const char * const * argnames,const char * bytes,size_t length)404 JS_PUBLIC_API JSFunction* JS::CompileFunctionUtf8(
405 JSContext* cx, HandleObjectVector envChain,
406 const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
407 const char* const* argnames, const char* bytes, size_t length) {
408 SourceText<Utf8Unit> srcBuf;
409 if (!srcBuf.init(cx, bytes, length, SourceOwnership::Borrowed)) {
410 return nullptr;
411 }
412
413 return CompileFunction(cx, envChain, options, name, nargs, argnames, srcBuf);
414 }
415
ExposeScriptToDebugger(JSContext * cx,HandleScript script)416 JS_PUBLIC_API void JS::ExposeScriptToDebugger(JSContext* cx,
417 HandleScript script) {
418 MOZ_ASSERT(cx);
419 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
420
421 DebugAPI::onNewScript(cx, script);
422 }
423
UpdateDebugMetadata(JSContext * cx,Handle<JSScript * > script,const ReadOnlyCompileOptions & options,HandleValue privateValue,HandleString elementAttributeName,HandleScript introScript,HandleScript scriptOrModule)424 JS_PUBLIC_API bool JS::UpdateDebugMetadata(
425 JSContext* cx, Handle<JSScript*> script,
426 const ReadOnlyCompileOptions& options, HandleValue privateValue,
427 HandleString elementAttributeName, HandleScript introScript,
428 HandleScript scriptOrModule) {
429 RootedScriptSourceObject sso(cx, script->sourceObject());
430
431 if (!ScriptSourceObject::initElementProperties(cx, sso,
432 elementAttributeName)) {
433 return false;
434 }
435
436 // There is no equivalent of cross-compartment wrappers for scripts. If the
437 // introduction script and ScriptSourceObject are in different compartments,
438 // we would be creating a cross-compartment script reference, which is
439 // forbidden. We can still store a CCW to the script source object though.
440 RootedValue introductionScript(cx);
441 if (introScript) {
442 if (introScript->compartment() == cx->compartment()) {
443 introductionScript.setPrivateGCThing(introScript);
444 }
445 }
446 sso->setIntroductionScript(introductionScript);
447
448 RootedValue privateValueStore(cx, UndefinedValue());
449 if (privateValue.isUndefined()) {
450 // Set the private value to that of the script or module that this source is
451 // part of, if any.
452 if (scriptOrModule) {
453 privateValueStore = scriptOrModule->sourceObject()->canonicalPrivate();
454 }
455 } else {
456 privateValueStore = privateValue;
457 }
458
459 if (!privateValueStore.isUndefined()) {
460 if (!JS_WrapValue(cx, &privateValueStore)) {
461 return false;
462 }
463 }
464 sso->setPrivate(cx->runtime(), privateValueStore);
465
466 if (!options.hideScriptFromDebugger) {
467 JS::ExposeScriptToDebugger(cx, script);
468 }
469
470 return true;
471 }
472
SetSourceElementCallback(JSContext * cx,JSSourceElementCallback callback)473 JS_PUBLIC_API void JS::SetSourceElementCallback(
474 JSContext* cx, JSSourceElementCallback callback) {
475 MOZ_ASSERT(cx->runtime());
476 cx->runtime()->setSourceElementCallback(cx->runtime(), callback);
477 }
478
ExecuteScript(JSContext * cx,HandleObject envChain,HandleScript script,MutableHandleValue rval)479 MOZ_NEVER_INLINE static bool ExecuteScript(JSContext* cx, HandleObject envChain,
480 HandleScript script,
481 MutableHandleValue rval) {
482 MOZ_ASSERT(!cx->zone()->isAtomsZone());
483 AssertHeapIsIdle();
484 CHECK_THREAD(cx);
485 cx->check(envChain, script);
486
487 if (!IsGlobalLexicalEnvironment(envChain)) {
488 MOZ_RELEASE_ASSERT(script->hasNonSyntacticScope());
489 }
490
491 return Execute(cx, script, envChain, rval);
492 }
493
ExecuteScript(JSContext * cx,HandleObjectVector envChain,HandleScript script,MutableHandleValue rval)494 static bool ExecuteScript(JSContext* cx, HandleObjectVector envChain,
495 HandleScript script, MutableHandleValue rval) {
496 RootedObject env(cx);
497 if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env)) {
498 return false;
499 }
500
501 return ExecuteScript(cx, env, script, rval);
502 }
503
JS_ExecuteScript(JSContext * cx,HandleScript scriptArg,MutableHandleValue rval)504 MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
505 HandleScript scriptArg,
506 MutableHandleValue rval) {
507 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
508 return ExecuteScript(cx, globalLexical, scriptArg, rval);
509 }
510
JS_ExecuteScript(JSContext * cx,HandleScript scriptArg)511 MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
512 HandleScript scriptArg) {
513 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
514 RootedValue rval(cx);
515 return ExecuteScript(cx, globalLexical, scriptArg, &rval);
516 }
517
JS_ExecuteScript(JSContext * cx,HandleObjectVector envChain,HandleScript scriptArg,MutableHandleValue rval)518 MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(
519 JSContext* cx, HandleObjectVector envChain, HandleScript scriptArg,
520 MutableHandleValue rval) {
521 return ExecuteScript(cx, envChain, scriptArg, rval);
522 }
523
JS_ExecuteScript(JSContext * cx,HandleObjectVector envChain,HandleScript scriptArg)524 MOZ_NEVER_INLINE JS_PUBLIC_API bool JS_ExecuteScript(
525 JSContext* cx, HandleObjectVector envChain, HandleScript scriptArg) {
526 RootedValue rval(cx);
527 return ExecuteScript(cx, envChain, scriptArg, &rval);
528 }
529
CloneAndExecuteScript(JSContext * cx,HandleScript scriptArg,JS::MutableHandleValue rval)530 JS_PUBLIC_API bool JS::CloneAndExecuteScript(JSContext* cx,
531 HandleScript scriptArg,
532 JS::MutableHandleValue rval) {
533 CHECK_THREAD(cx);
534 RootedScript script(cx, scriptArg);
535 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
536 if (script->realm() != cx->realm()) {
537 script = CloneGlobalScript(cx, script);
538 if (!script) {
539 return false;
540 }
541 }
542 return ExecuteScript(cx, globalLexical, script, rval);
543 }
544
CloneAndExecuteScript(JSContext * cx,JS::HandleObjectVector envChain,HandleScript scriptArg,JS::MutableHandleValue rval)545 JS_PUBLIC_API bool JS::CloneAndExecuteScript(JSContext* cx,
546 JS::HandleObjectVector envChain,
547 HandleScript scriptArg,
548 JS::MutableHandleValue rval) {
549 CHECK_THREAD(cx);
550 MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
551 RootedScript script(cx, scriptArg);
552 if (script->realm() != cx->realm()) {
553 script = CloneGlobalScript(cx, script);
554 if (!script) {
555 return false;
556 }
557 }
558 return ExecuteScript(cx, envChain, script, rval);
559 }
560
561 template <typename Unit>
EvaluateSourceBuffer(JSContext * cx,ScopeKind scopeKind,Handle<JSObject * > env,const ReadOnlyCompileOptions & optionsArg,SourceText<Unit> & srcBuf,MutableHandle<Value> rval)562 static bool EvaluateSourceBuffer(JSContext* cx, ScopeKind scopeKind,
563 Handle<JSObject*> env,
564 const ReadOnlyCompileOptions& optionsArg,
565 SourceText<Unit>& srcBuf,
566 MutableHandle<Value> rval) {
567 CompileOptions options(cx, optionsArg);
568 MOZ_ASSERT(!cx->zone()->isAtomsZone());
569 AssertHeapIsIdle();
570 CHECK_THREAD(cx);
571 cx->check(env);
572 MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env),
573 scopeKind == ScopeKind::NonSyntactic);
574
575 options.setNonSyntacticScope(scopeKind == ScopeKind::NonSyntactic);
576 options.setIsRunOnce(true);
577
578 RootedScript script(
579 cx, frontend::CompileGlobalScript(cx, options, srcBuf, scopeKind));
580 if (!script) {
581 return false;
582 }
583
584 return Execute(cx, script, env, rval);
585 }
586
Evaluate(JSContext * cx,const ReadOnlyCompileOptions & options,SourceText<Utf8Unit> & srcBuf,MutableHandle<Value> rval)587 JS_PUBLIC_API bool JS::Evaluate(JSContext* cx,
588 const ReadOnlyCompileOptions& options,
589 SourceText<Utf8Unit>& srcBuf,
590 MutableHandle<Value> rval) {
591 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
592 return EvaluateSourceBuffer(cx, ScopeKind::Global, globalLexical, options,
593 srcBuf, rval);
594 }
595
Evaluate(JSContext * cx,const ReadOnlyCompileOptions & optionsArg,SourceText<char16_t> & srcBuf,MutableHandleValue rval)596 JS_PUBLIC_API bool JS::Evaluate(JSContext* cx,
597 const ReadOnlyCompileOptions& optionsArg,
598 SourceText<char16_t>& srcBuf,
599 MutableHandleValue rval) {
600 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
601 return EvaluateSourceBuffer(cx, ScopeKind::Global, globalLexical, optionsArg,
602 srcBuf, rval);
603 }
604
Evaluate(JSContext * cx,HandleObjectVector envChain,const ReadOnlyCompileOptions & options,SourceText<char16_t> & srcBuf,MutableHandleValue rval)605 JS_PUBLIC_API bool JS::Evaluate(JSContext* cx, HandleObjectVector envChain,
606 const ReadOnlyCompileOptions& options,
607 SourceText<char16_t>& srcBuf,
608 MutableHandleValue rval) {
609 RootedObject env(cx);
610 if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env)) {
611 return false;
612 }
613
614 return EvaluateSourceBuffer(cx, ScopeKind::NonSyntactic, env, options, srcBuf,
615 rval);
616 }
617
EvaluateUtf8Path(JSContext * cx,const ReadOnlyCompileOptions & optionsArg,const char * filename,MutableHandleValue rval)618 JS_PUBLIC_API bool JS::EvaluateUtf8Path(
619 JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
620 const char* filename, MutableHandleValue rval) {
621 FileContents buffer(cx);
622 {
623 AutoFile file;
624 if (!file.open(cx, filename) || !file.readAll(cx, buffer)) {
625 return false;
626 }
627 }
628
629 CompileOptions options(cx, optionsArg);
630 options.setFileAndLine(filename, 1);
631
632 auto contents = reinterpret_cast<const char*>(buffer.begin());
633 size_t length = buffer.length();
634
635 JS::SourceText<Utf8Unit> srcBuf;
636 if (!srcBuf.init(cx, contents, length, JS::SourceOwnership::Borrowed)) {
637 return false;
638 }
639
640 return Evaluate(cx, options, srcBuf, rval);
641 }
642