Coverage Report

Created: 2018-09-25 14:53

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