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