tor-browser

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

Eval.cpp (18961B)


      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 #include "builtin/Eval.h"
      8 
      9 #include "mozilla/HashFunctions.h"
     10 #include "mozilla/Range.h"
     11 
     12 #include "frontend/BytecodeCompiler.h"  // frontend::CompileEvalScript
     13 #include "gc/HashUtil.h"
     14 #include "js/CompilationAndEvaluation.h"
     15 #include "js/EnvironmentChain.h"       // JS::EnvironmentChain
     16 #include "js/friend/ErrorMessages.h"   // js::GetErrorMessage, JSMSG_*
     17 #include "js/friend/JSMEnvironment.h"  // JS::NewJSMEnvironment, JS::ExecuteInJSMEnvironment, JS::GetJSMEnvironmentOfScriptedCaller, JS::IsJSMEnvironment
     18 #include "js/friend/WindowProxy.h"     // js::IsWindowProxy
     19 #include "js/SourceText.h"
     20 #include "js/StableStringChars.h"
     21 #include "vm/EnvironmentObject.h"
     22 #include "vm/FrameIter.h"
     23 #include "vm/GlobalObject.h"
     24 #include "vm/Interpreter.h"
     25 #include "vm/JSContext.h"
     26 #include "vm/JSONParser.h"
     27 
     28 #include "gc/Marking-inl.h"
     29 #include "vm/EnvironmentObject-inl.h"
     30 #include "vm/JSContext-inl.h"
     31 #include "vm/Stack-inl.h"
     32 
     33 using namespace js;
     34 
     35 using mozilla::AddToHash;
     36 
     37 using JS::AutoCheckCannotGC;
     38 using JS::AutoStableStringChars;
     39 using JS::CompileOptions;
     40 using JS::SourceText;
     41 
     42 // We should be able to assert this for *any* fp->environmentChain().
     43 static void AssertInnerizedEnvironmentChain(JSContext* cx, JSObject& env) {
     44 #ifdef DEBUG
     45  RootedObject obj(cx);
     46  for (obj = &env; obj; obj = obj->enclosingEnvironment()) {
     47    MOZ_ASSERT(!IsWindowProxy(obj));
     48  }
     49 #endif
     50 }
     51 
     52 static bool IsEvalCacheCandidate(JSScript* script) {
     53  if (!script->isDirectEvalInFunction()) {
     54    return false;
     55  }
     56 
     57  // Make sure there are no inner objects (which may be used directly by script
     58  // and clobbered) or inner functions (which may have wrong scope).
     59  for (JS::GCCellPtr gcThing : script->gcthings()) {
     60    if (gcThing.is<JSObject>()) {
     61      return false;
     62    }
     63  }
     64 
     65  return true;
     66 }
     67 
     68 /* static */
     69 HashNumber EvalCacheHashPolicy::hash(const EvalCacheLookup& l) {
     70  HashNumber hash = HashStringChars(l.str);
     71  return AddToHash(hash, l.callerScript, l.pc);
     72 }
     73 
     74 /* static */
     75 bool EvalCacheHashPolicy::match(const EvalCacheEntry& cacheEntry,
     76                                const EvalCacheLookup& l) {
     77  MOZ_ASSERT(IsEvalCacheCandidate(cacheEntry.script));
     78 
     79  return EqualStrings(cacheEntry.str, l.str) &&
     80         cacheEntry.callerScript == l.callerScript && cacheEntry.pc == l.pc;
     81 }
     82 
     83 void EvalCacheLookup::trace(JSTracer* trc) {
     84  TraceNullableRoot(trc, &str, "EvalCacheLookup::str");
     85  TraceNullableRoot(trc, &callerScript, "EvalCacheLookup::callerScript");
     86 }
     87 
     88 // Add the script to the eval cache when EvalKernel is finished
     89 class EvalScriptGuard {
     90  JSContext* cx_;
     91  Rooted<JSScript*> script_;
     92 
     93  /* These fields are only valid if lookup_.str is non-nullptr. */
     94  Rooted<EvalCacheLookup> lookup_;
     95  mozilla::Maybe<DependentAddPtr<EvalCache>> p_;
     96 
     97  Rooted<JSLinearString*> lookupStr_;
     98 
     99 public:
    100  explicit EvalScriptGuard(JSContext* cx)
    101      : cx_(cx), script_(cx), lookup_(cx), lookupStr_(cx) {}
    102 
    103  ~EvalScriptGuard() {
    104    if (script_ && !cx_->isExceptionPending()) {
    105      script_->cacheForEval();
    106      EvalCacheLookup& lookup = lookup_.get();
    107      EvalCacheEntry cacheEntry = {lookupStr_, script_, lookup.callerScript,
    108                                   lookup.pc};
    109      lookup.str = lookupStr_;
    110      if (lookup.str && IsEvalCacheCandidate(script_)) {
    111        // Ignore failure to add cache entry.
    112        if (!p_->add(cx_, cx_->caches().evalCache, lookup, cacheEntry)) {
    113          cx_->recoverFromOutOfMemory();
    114        }
    115      }
    116    }
    117  }
    118 
    119  void lookupInEvalCache(JSLinearString* str, JSScript* callerScript,
    120                         jsbytecode* pc) {
    121    lookupStr_ = str;
    122    EvalCacheLookup& lookup = lookup_.get();
    123    lookup.str = str;
    124    lookup.callerScript = callerScript;
    125    lookup.pc = pc;
    126    p_.emplace(cx_, cx_->caches().evalCache, lookup);
    127    if (*p_) {
    128      script_ = (*p_)->script;
    129      p_->remove(cx_, cx_->caches().evalCache, lookup);
    130    }
    131  }
    132 
    133  void setNewScript(JSScript* script) {
    134    // JSScript::fullyInitFromStencil has already called js_CallNewScriptHook.
    135    MOZ_ASSERT(!script_ && script);
    136    script_ = script;
    137  }
    138 
    139  bool foundScript() { return !!script_; }
    140 
    141  HandleScript script() {
    142    MOZ_ASSERT(script_);
    143    return script_;
    144  }
    145 };
    146 
    147 enum class EvalJSONResult { Failure, Success, NotJSON };
    148 
    149 template <typename CharT>
    150 static bool EvalStringMightBeJSON(const mozilla::Range<const CharT> chars) {
    151  // If the eval string starts with '(' or '[' and ends with ')' or ']', it
    152  // may be JSON.  Try the JSON parser first because it's much faster.  If
    153  // the eval string isn't JSON, JSON parsing will probably fail quickly, so
    154  // little time will be lost.
    155  size_t length = chars.length();
    156  if (length < 2) {
    157    return false;
    158  }
    159 
    160  // It used to be that strings in JavaScript forbid U+2028 LINE SEPARATOR
    161  // and U+2029 PARAGRAPH SEPARATOR, so something like
    162  //
    163  //   eval("['" + "\u2028" + "']");
    164  //
    165  // i.e. an array containing a string with a line separator in it, *would*
    166  // be JSON but *would not* be valid JavaScript.  Handing such a string to
    167  // the JSON parser would then fail to recognize a syntax error.  As of
    168  // <https://tc39.github.io/proposal-json-superset/> JavaScript strings may
    169  // contain these two code points, so it's safe to JSON-parse eval strings
    170  // that contain them.
    171 
    172  CharT first = chars[0], last = chars[length - 1];
    173  return (first == '[' && last == ']') || (first == '(' && last == ')');
    174 }
    175 
    176 template <typename CharT>
    177 static EvalJSONResult ParseEvalStringAsJSON(
    178    JSContext* cx, const mozilla::Range<const CharT> chars,
    179    MutableHandleValue rval) {
    180  size_t len = chars.length();
    181  MOZ_ASSERT((chars[0] == '(' && chars[len - 1] == ')') ||
    182             (chars[0] == '[' && chars[len - 1] == ']'));
    183 
    184  auto jsonChars = (chars[0] == '[') ? chars
    185                                     : mozilla::Range<const CharT>(
    186                                           chars.begin().get() + 1U, len - 2);
    187 
    188  Rooted<JSONParser<CharT>> parser(
    189      cx, cx, jsonChars, JSONParser<CharT>::ParseType::AttemptForEval);
    190  if (!parser.parse(rval)) {
    191    return EvalJSONResult::Failure;
    192  }
    193 
    194  return rval.isUndefined() ? EvalJSONResult::NotJSON : EvalJSONResult::Success;
    195 }
    196 
    197 static EvalJSONResult TryEvalJSON(JSContext* cx, JSLinearString* str,
    198                                  MutableHandleValue rval) {
    199  if (str->hasLatin1Chars()) {
    200    AutoCheckCannotGC nogc;
    201    if (!EvalStringMightBeJSON(str->latin1Range(nogc))) {
    202      return EvalJSONResult::NotJSON;
    203    }
    204  } else {
    205    AutoCheckCannotGC nogc;
    206    if (!EvalStringMightBeJSON(str->twoByteRange(nogc))) {
    207      return EvalJSONResult::NotJSON;
    208    }
    209  }
    210 
    211  AutoStableStringChars linearChars(cx);
    212  if (!linearChars.init(cx, str)) {
    213    return EvalJSONResult::Failure;
    214  }
    215 
    216  return linearChars.isLatin1()
    217             ? ParseEvalStringAsJSON(cx, linearChars.latin1Range(), rval)
    218             : ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
    219 }
    220 
    221 enum EvalType { DIRECT_EVAL, INDIRECT_EVAL };
    222 
    223 // 18.2.1.1 PerformEval
    224 //
    225 // Common code implementing direct and indirect eval.
    226 //
    227 // Evaluate v, if it is a string, in the context of the given calling
    228 // frame, with the provided scope chain, with the semantics of either a direct
    229 // or indirect eval (see ES5 10.4.2).  If this is an indirect eval, env
    230 // must be the global lexical environment.
    231 //
    232 // On success, store the completion value in call.rval and return true.
    233 static bool EvalKernel(JSContext* cx, HandleValue v, EvalType evalType,
    234                       AbstractFramePtr caller, HandleObject env,
    235                       jsbytecode* pc, MutableHandleValue vp) {
    236  MOZ_ASSERT((evalType == INDIRECT_EVAL) == !caller);
    237  MOZ_ASSERT((evalType == INDIRECT_EVAL) == !pc);
    238  MOZ_ASSERT_IF(evalType == INDIRECT_EVAL,
    239                env->is<GlobalLexicalEnvironmentObject>());
    240  AssertInnerizedEnvironmentChain(cx, *env);
    241 
    242  // "Dynamic Code Brand Checks" adds support for Object values.
    243  // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-performeval
    244  // Steps 2-4.
    245  RootedString str(cx);
    246  if (v.isString()) {
    247    str = v.toString();
    248  } else if (v.isObject()) {
    249    RootedObject obj(cx, &v.toObject());
    250    if (!cx->getCodeForEval(obj, &str)) {
    251      return false;
    252    }
    253  }
    254  if (!str) {
    255    vp.set(v);
    256    return true;
    257  }
    258 
    259  // Steps 6-8.
    260  JS::RootedVector<JSString*> parameterStrings(cx);
    261  JS::RootedVector<Value> parameterArgs(cx);
    262  bool canCompileStrings = cx->bypassCSPForDebugger;
    263 
    264  if (!canCompileStrings &&
    265      !cx->isRuntimeCodeGenEnabled(
    266          JS::RuntimeCode::JS, str,
    267          evalType == DIRECT_EVAL ? JS::CompilationType::DirectEval
    268                                  : JS::CompilationType::IndirectEval,
    269          parameterStrings, str, parameterArgs, v, &canCompileStrings)) {
    270    return false;
    271  }
    272  if (!canCompileStrings) {
    273    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    274                              JSMSG_CSP_BLOCKED_EVAL);
    275    return false;
    276  }
    277 
    278  // Step 9 ff.
    279 
    280  // Per ES5, indirect eval runs in the global scope. (eval is specified this
    281  // way so that the compiler can make assumptions about what bindings may or
    282  // may not exist in the current frame if it doesn't see 'eval'.)
    283  MOZ_ASSERT_IF(
    284      evalType != DIRECT_EVAL,
    285      cx->global() == &env->as<GlobalLexicalEnvironmentObject>().global());
    286 
    287  Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
    288  if (!linearStr) {
    289    return false;
    290  }
    291 
    292  RootedScript callerScript(cx, caller ? caller.script() : nullptr);
    293  EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp);
    294  if (ejr != EvalJSONResult::NotJSON) {
    295    return ejr == EvalJSONResult::Success;
    296  }
    297 
    298  EvalScriptGuard esg(cx);
    299 
    300  if (evalType == DIRECT_EVAL && caller.isFunctionFrame()) {
    301    esg.lookupInEvalCache(linearStr, callerScript, pc);
    302  }
    303 
    304  if (!esg.foundScript()) {
    305    RootedScript maybeScript(cx);
    306    uint32_t lineno;
    307    const char* filename;
    308    bool mutedErrors;
    309    uint32_t pcOffset;
    310    if (evalType == DIRECT_EVAL) {
    311      DescribeScriptedCallerForDirectEval(cx, callerScript, pc, &filename,
    312                                          &lineno, &pcOffset, &mutedErrors);
    313      maybeScript = callerScript;
    314    } else {
    315      DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno,
    316                                           &pcOffset, &mutedErrors);
    317    }
    318 
    319    const char* introducerFilename = filename;
    320    if (maybeScript && maybeScript->scriptSource()->introducerFilename()) {
    321      introducerFilename = maybeScript->scriptSource()->introducerFilename();
    322    }
    323 
    324    Rooted<Scope*> enclosing(cx);
    325    if (evalType == DIRECT_EVAL) {
    326      enclosing = callerScript->innermostScope(pc);
    327    } else {
    328      enclosing = &cx->global()->emptyGlobalScope();
    329    }
    330 
    331    CompileOptions options(cx);
    332    options.setIsRunOnce(true)
    333        .setNoScriptRval(false)
    334        .setMutedErrors(mutedErrors)
    335        .setDeferDebugMetadata();
    336 
    337    RootedScript introScript(cx);
    338 
    339    if (evalType == DIRECT_EVAL && IsStrictEvalPC(pc)) {
    340      options.setForceStrictMode();
    341    }
    342 
    343    if (introducerFilename) {
    344      options.setFileAndLine(filename, 1);
    345      options.setIntroductionInfo(introducerFilename, "eval", lineno, pcOffset);
    346      introScript = maybeScript;
    347    } else {
    348      options.setFileAndLine("eval", 1);
    349      options.setIntroductionType("eval");
    350    }
    351    options.setNonSyntacticScope(
    352        enclosing->hasOnChain(ScopeKind::NonSyntactic));
    353 
    354    AutoStableStringChars linearChars(cx);
    355    if (!linearChars.initTwoByte(cx, linearStr)) {
    356      return false;
    357    }
    358 
    359    SourceText<char16_t> srcBuf;
    360    if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
    361      return false;
    362    }
    363 
    364    RootedScript script(
    365        cx, frontend::CompileEvalScript(cx, options, srcBuf, enclosing, env));
    366    if (!script) {
    367      return false;
    368    }
    369 
    370    RootedValue undefValue(cx);
    371    JS::InstantiateOptions instantiateOptions(options);
    372    if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, undefValue,
    373                                 nullptr, introScript, maybeScript)) {
    374      return false;
    375    }
    376 
    377    esg.setNewScript(script);
    378  }
    379 
    380  return ExecuteKernel(cx, esg.script(), env, NullFramePtr() /* evalInFrame */,
    381                       vp);
    382 }
    383 
    384 bool js::IndirectEval(JSContext* cx, unsigned argc, Value* vp) {
    385  CallArgs args = CallArgsFromVp(argc, vp);
    386 
    387  RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
    388 
    389  // Note we'll just pass |undefined| here, then return it directly (or throw
    390  // if runtime codegen is disabled), if no argument is provided.
    391  return EvalKernel(cx, args.get(0), INDIRECT_EVAL, NullFramePtr(),
    392                    globalLexical, nullptr, args.rval());
    393 }
    394 
    395 bool js::DirectEval(JSContext* cx, HandleValue v, MutableHandleValue vp) {
    396  // Direct eval can assume it was called from an interpreted or baseline frame.
    397  ScriptFrameIter iter(cx);
    398  AbstractFramePtr caller = iter.abstractFramePtr();
    399 
    400  MOZ_ASSERT(JSOp(*iter.pc()) == JSOp::Eval ||
    401             JSOp(*iter.pc()) == JSOp::StrictEval ||
    402             JSOp(*iter.pc()) == JSOp::SpreadEval ||
    403             JSOp(*iter.pc()) == JSOp::StrictSpreadEval);
    404  MOZ_ASSERT(caller.realm() == caller.script()->realm());
    405 
    406  RootedObject envChain(cx, caller.environmentChain());
    407  return EvalKernel(cx, v, DIRECT_EVAL, caller, envChain, iter.pc(), vp);
    408 }
    409 
    410 bool js::IsAnyBuiltinEval(JSFunction* fun) {
    411  return fun->maybeNative() == IndirectEval;
    412 }
    413 
    414 static bool ExecuteInExtensibleLexicalEnvironment(
    415    JSContext* cx, HandleScript scriptArg,
    416    Handle<ExtensibleLexicalEnvironmentObject*> env) {
    417  CHECK_THREAD(cx);
    418  cx->check(env, scriptArg);
    419  MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
    420 
    421  RootedValue rval(cx);
    422  return ExecuteKernel(cx, scriptArg, env, NullFramePtr() /* evalInFrame */,
    423                       &rval);
    424 }
    425 
    426 JS_PUBLIC_API bool js::ExecuteInFrameScriptEnvironment(
    427    JSContext* cx, HandleObject objArg, HandleScript scriptArg,
    428    MutableHandleObject envArg) {
    429  Rooted<NonSyntacticVariablesObject*> varEnv(
    430      cx, NonSyntacticVariablesObject::create(cx));
    431  if (!varEnv) {
    432    return false;
    433  }
    434 
    435  JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
    436  if (!envChain.append(objArg)) {
    437    return false;
    438  }
    439 
    440  Rooted<WithEnvironmentObject*> env(
    441      cx, js::CreateObjectsForEnvironmentChain(cx, envChain, varEnv));
    442  if (!env) {
    443    return false;
    444  }
    445 
    446  // Create lexical environment with |this| == objArg, which should be a Gecko
    447  // MessageManager.
    448  // NOTE: This is required behavior for Gecko FrameScriptLoader, where some
    449  // callers try to bind methods from the message manager in their scope chain
    450  // to |this|, and will fail if it is not bound to a message manager.
    451  ObjectRealm& realm = ObjectRealm::get(varEnv);
    452  Rooted<NonSyntacticLexicalEnvironmentObject*> lexicalEnv(
    453      cx, realm.getOrCreateNonSyntacticLexicalEnvironment(cx, env, varEnv));
    454  if (!lexicalEnv) {
    455    return false;
    456  }
    457 
    458  if (!ExecuteInExtensibleLexicalEnvironment(cx, scriptArg, lexicalEnv)) {
    459    return false;
    460  }
    461 
    462  envArg.set(lexicalEnv);
    463  return true;
    464 }
    465 
    466 JS_PUBLIC_API JSObject* JS::NewJSMEnvironment(JSContext* cx) {
    467  Rooted<NonSyntacticVariablesObject*> varEnv(
    468      cx, NonSyntacticVariablesObject::create(cx));
    469  if (!varEnv) {
    470    return nullptr;
    471  }
    472 
    473  // Force the NonSyntacticLexicalEnvironmentObject to be created.
    474  ObjectRealm& realm = ObjectRealm::get(varEnv);
    475  MOZ_ASSERT(!realm.getNonSyntacticLexicalEnvironment(varEnv));
    476  if (!realm.getOrCreateNonSyntacticLexicalEnvironment(cx, varEnv)) {
    477    return nullptr;
    478  }
    479 
    480  return varEnv;
    481 }
    482 
    483 JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(JSContext* cx,
    484                                               HandleScript scriptArg,
    485                                               HandleObject varEnv) {
    486  JS::EnvironmentChain emptyChain(cx, JS::SupportUnscopables::No);
    487  return ExecuteInJSMEnvironment(cx, scriptArg, varEnv, emptyChain);
    488 }
    489 
    490 JS_PUBLIC_API bool JS::ExecuteInJSMEnvironment(
    491    JSContext* cx, HandleScript scriptArg, HandleObject varEnv,
    492    const EnvironmentChain& targetObj) {
    493  cx->check(varEnv);
    494  MOZ_ASSERT(
    495      ObjectRealm::get(varEnv).getNonSyntacticLexicalEnvironment(varEnv));
    496  MOZ_DIAGNOSTIC_ASSERT(scriptArg->noScriptRval());
    497 
    498  Rooted<ExtensibleLexicalEnvironmentObject*> env(
    499      cx, ExtensibleLexicalEnvironmentObject::forVarEnvironment(varEnv));
    500 
    501  // If the Gecko subscript loader specifies target objects, we need to add
    502  // them to the environment. These are added after the NSVO environment.
    503  if (!targetObj.empty()) {
    504    // The environment chain will be as follows:
    505    //      GlobalObject / SystemGlobal
    506    //      GlobalLexicalEnvironmentObject[this=global]
    507    //      NonSyntacticVariablesObject (the JSMEnvironment)
    508    //      NonSyntacticLexicalEnvironmentObject[this=nsvo]
    509    //      WithEnvironmentObject[target=targetObj]
    510    //      NonSyntacticLexicalEnvironmentObject[this=targetObj] (*)
    511    //
    512    //  (*) This environment intercepts JSOp::GlobalThis.
    513 
    514    // Wrap the target objects in WithEnvironments.
    515    Rooted<WithEnvironmentObject*> envChain(
    516        cx, js::CreateObjectsForEnvironmentChain(cx, targetObj, env));
    517    if (!envChain) {
    518      return false;
    519    }
    520 
    521    // See CreateNonSyntacticEnvironmentChain
    522    if (!JSObject::setQualifiedVarObj(cx, envChain)) {
    523      return false;
    524    }
    525 
    526    // Create an extensible lexical environment for the target object.
    527    env = ObjectRealm::get(envChain).getOrCreateNonSyntacticLexicalEnvironment(
    528        cx, envChain);
    529    if (!env) {
    530      return false;
    531    }
    532  }
    533 
    534  return ExecuteInExtensibleLexicalEnvironment(cx, scriptArg, env);
    535 }
    536 
    537 JS_PUBLIC_API JSObject* JS::GetJSMEnvironmentOfScriptedCaller(JSContext* cx) {
    538  FrameIter iter(cx);
    539  if (iter.done()) {
    540    return nullptr;
    541  }
    542 
    543  // WASM frames don't always provide their environment, but we also shouldn't
    544  // expect to see any calling into here.
    545  MOZ_RELEASE_ASSERT(!iter.isWasm());
    546 
    547  RootedObject env(cx, iter.environmentChain(cx));
    548  while (env && !env->is<NonSyntacticVariablesObject>()) {
    549    env = env->enclosingEnvironment();
    550  }
    551 
    552  return env;
    553 }
    554 
    555 JS_PUBLIC_API bool JS::IsJSMEnvironment(JSObject* obj) {
    556  // NOTE: This also returns true if the NonSyntacticVariablesObject was
    557  // created for reasons other than the JSM loader.
    558  return obj->is<NonSyntacticVariablesObject>();
    559 }
    560 
    561 #ifdef JSGC_HASH_TABLE_CHECKS
    562 void RuntimeCaches::checkEvalCacheAfterMinorGC() {
    563  gc::CheckTableAfterMovingGC(evalCache, [](const auto& entry) {
    564    CheckGCThingAfterMovingGC(entry.str);
    565    CheckGCThingAfterMovingGC(entry.script);
    566    CheckGCThingAfterMovingGC(entry.callerScript);
    567    return EvalCacheLookup(entry.str, entry.callerScript, entry.pc);
    568  });
    569 }
    570 #endif