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