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