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