tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CompilationAndEvaluation.cpp (22798B)


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