Source code

Revision control

Other Tools

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
#ifndef vm_JSContext_inl_h
8
#define vm_JSContext_inl_h
9
10
#include "vm/JSContext.h"
11
12
#include "builtin/Object.h"
13
#include "gc/Zone.h"
14
#include "jit/JitFrames.h"
15
#include "proxy/Proxy.h"
16
#include "util/DiagnosticAssertions.h"
17
#include "vm/BigIntType.h"
18
#include "vm/GlobalObject.h"
19
#include "vm/HelperThreads.h"
20
#include "vm/Interpreter.h"
21
#include "vm/Iteration.h"
22
#include "vm/Realm.h"
23
#include "vm/SymbolType.h"
24
25
#include "vm/Activation-inl.h" // js::Activation::hasWasmExitFP
26
27
namespace js {
28
29
class ContextChecks {
30
JSContext* cx;
31
32
JS::Realm* realm() const { return cx->realm(); }
33
JS::Compartment* compartment() const { return cx->compartment(); }
34
JS::Zone* zone() const { return cx->zone(); }
35
36
public:
37
explicit ContextChecks(JSContext* cx) : cx(cx) {
38
#ifdef DEBUG
39
if (realm()) {
40
GlobalObject* global = realm()->unsafeUnbarrieredMaybeGlobal();
41
if (global) {
42
checkObject(global);
43
}
44
}
45
#endif
46
}
47
48
/*
49
* Set a breakpoint here (break js::ContextChecks::fail) to debug
50
* realm/compartment/zone mismatches.
51
*/
52
static void fail(JS::Realm* r1, JS::Realm* r2, int argIndex) {
53
MOZ_CRASH_UNSAFE_PRINTF("*** Realm mismatch %p vs. %p at argument %d", r1,
54
r2, argIndex);
55
}
56
static void fail(JS::Compartment* c1, JS::Compartment* c2, int argIndex) {
57
MOZ_CRASH_UNSAFE_PRINTF("*** Compartment mismatch %p vs. %p at argument %d",
58
c1, c2, argIndex);
59
}
60
static void fail(JS::Zone* z1, JS::Zone* z2, int argIndex) {
61
MOZ_CRASH_UNSAFE_PRINTF("*** Zone mismatch %p vs. %p at argument %d", z1,
62
z2, argIndex);
63
}
64
65
void check(JS::Realm* r, int argIndex) {
66
if (r && r != realm()) {
67
fail(realm(), r, argIndex);
68
}
69
}
70
71
void check(JS::Compartment* c, int argIndex) {
72
if (c && c != compartment()) {
73
fail(compartment(), c, argIndex);
74
}
75
}
76
77
void check(JS::Zone* z, int argIndex) {
78
if (zone() && z != zone()) {
79
fail(zone(), z, argIndex);
80
}
81
}
82
83
void check(JSObject* obj, int argIndex) {
84
if (obj) {
85
checkObject(obj);
86
check(obj->compartment(), argIndex);
87
}
88
}
89
90
void checkObject(JSObject* obj) {
91
JS::AssertObjectIsNotGray(obj);
92
MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj));
93
}
94
95
template <typename T>
96
void checkAtom(T* thing, int argIndex) {
97
static_assert(mozilla::IsSame<T, JSAtom>::value ||
98
mozilla::IsSame<T, JS::Symbol>::value,
99
"Should only be called with JSAtom* or JS::Symbol* argument");
100
101
#ifdef DEBUG
102
// Atoms which move across zone boundaries need to be marked in the new
103
// zone, see JS_MarkCrossZoneId.
104
if (zone()) {
105
if (!cx->runtime()->gc.atomMarking.atomIsMarked(zone(), thing)) {
106
MOZ_CRASH_UNSAFE_PRINTF(
107
"*** Atom not marked for zone %p at argument %d", zone(), argIndex);
108
}
109
}
110
#endif
111
}
112
113
void check(JSString* str, int argIndex) {
114
JS::AssertCellIsNotGray(str);
115
if (str->isAtom()) {
116
checkAtom(&str->asAtom(), argIndex);
117
} else {
118
check(str->zone(), argIndex);
119
}
120
}
121
122
void check(JS::Symbol* symbol, int argIndex) { checkAtom(symbol, argIndex); }
123
124
void check(JS::BigInt* bi, int argIndex) { check(bi->zone(), argIndex); }
125
126
void check(const js::Value& v, int argIndex) {
127
if (v.isObject()) {
128
check(&v.toObject(), argIndex);
129
} else if (v.isString()) {
130
check(v.toString(), argIndex);
131
} else if (v.isSymbol()) {
132
check(v.toSymbol(), argIndex);
133
} else if (v.isBigInt()) {
134
check(v.toBigInt(), argIndex);
135
}
136
}
137
138
// Check the contents of any container class that supports the C++
139
// iteration protocol, eg GCVector<jsid>.
140
template <typename Container>
141
typename mozilla::EnableIf<
142
mozilla::IsSame<decltype(((Container*)nullptr)->begin()),
143
decltype(((Container*)nullptr)->end())>::value>::Type
144
check(const Container& container, int argIndex) {
145
for (auto i : container) {
146
check(i, argIndex);
147
}
148
}
149
150
void check(const JS::HandleValueArray& arr, int argIndex) {
151
for (size_t i = 0; i < arr.length(); i++) {
152
check(arr[i], argIndex);
153
}
154
}
155
156
void check(const CallArgs& args, int argIndex) {
157
for (Value* p = args.base(); p != args.end(); ++p) {
158
check(*p, argIndex);
159
}
160
}
161
162
void check(jsid id, int argIndex) {
163
if (JSID_IS_ATOM(id)) {
164
checkAtom(JSID_TO_ATOM(id), argIndex);
165
} else if (JSID_IS_SYMBOL(id)) {
166
checkAtom(JSID_TO_SYMBOL(id), argIndex);
167
} else {
168
MOZ_ASSERT(!JSID_IS_GCTHING(id));
169
}
170
}
171
172
void check(JSScript* script, int argIndex) {
173
JS::AssertCellIsNotGray(script);
174
if (script) {
175
check(script->realm(), argIndex);
176
}
177
}
178
179
void check(AbstractFramePtr frame, int argIndex);
180
181
void check(Handle<PropertyDescriptor> desc, int argIndex) {
182
check(desc.object(), argIndex);
183
if (desc.hasGetterObject()) {
184
check(desc.getterObject(), argIndex);
185
}
186
if (desc.hasSetterObject()) {
187
check(desc.setterObject(), argIndex);
188
}
189
check(desc.value(), argIndex);
190
}
191
192
void check(TypeSet::Type type, int argIndex) {
193
check(type.maybeCompartment(), argIndex);
194
}
195
};
196
197
} // namespace js
198
199
template <class Head, class... Tail>
200
inline void JSContext::checkImpl(int argIndex, const Head& head,
201
const Tail&... tail) {
202
js::ContextChecks(this).check(head, argIndex);
203
checkImpl(argIndex + 1, tail...);
204
}
205
206
template <class... Args>
207
inline void JSContext::check(const Args&... args) {
208
#ifdef JS_CRASH_DIAGNOSTICS
209
if (contextChecksEnabled()) {
210
checkImpl(0, args...);
211
}
212
#endif
213
}
214
215
template <class... Args>
216
inline void JSContext::releaseCheck(const Args&... args) {
217
if (contextChecksEnabled()) {
218
checkImpl(0, args...);
219
}
220
}
221
222
template <class... Args>
223
MOZ_ALWAYS_INLINE void JSContext::debugOnlyCheck(const Args&... args) {
224
#if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
225
if (contextChecksEnabled()) {
226
checkImpl(0, args...);
227
}
228
#endif
229
}
230
231
namespace js {
232
233
STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
234
MOZ_ALWAYS_INLINE bool CallNativeImpl(JSContext* cx, NativeImpl impl,
235
const CallArgs& args) {
236
#ifdef DEBUG
237
bool alreadyThrowing = cx->isExceptionPending();
238
#endif
239
cx->check(args);
240
bool ok = impl(cx, args);
241
if (ok) {
242
cx->check(args.rval());
243
MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
244
}
245
return ok;
246
}
247
248
MOZ_ALWAYS_INLINE bool CallJSGetterOp(JSContext* cx, GetterOp op,
249
HandleObject obj, HandleId id,
250
MutableHandleValue vp) {
251
if (!CheckRecursionLimit(cx)) {
252
return false;
253
}
254
255
cx->check(obj, id, vp);
256
bool ok = op(cx, obj, id, vp);
257
if (ok) {
258
cx->check(vp);
259
}
260
return ok;
261
}
262
263
MOZ_ALWAYS_INLINE bool CallJSSetterOp(JSContext* cx, SetterOp op,
264
HandleObject obj, HandleId id,
265
HandleValue v, ObjectOpResult& result) {
266
if (!CheckRecursionLimit(cx)) {
267
return false;
268
}
269
270
cx->check(obj, id, v);
271
return op(cx, obj, id, v, result);
272
}
273
274
inline bool CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op,
275
HandleObject obj, HandleId id, HandleValue v) {
276
if (!CheckRecursionLimit(cx)) {
277
return false;
278
}
279
280
cx->check(obj, id, v);
281
return op(cx, obj, id, v);
282
}
283
284
inline bool CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op,
285
HandleObject receiver, HandleId id,
286
ObjectOpResult& result) {
287
if (!CheckRecursionLimit(cx)) {
288
return false;
289
}
290
291
cx->check(receiver, id);
292
if (op) {
293
return op(cx, receiver, id, result);
294
}
295
return result.succeed();
296
}
297
298
MOZ_ALWAYS_INLINE bool CheckForInterrupt(JSContext* cx) {
299
MOZ_ASSERT(!cx->isExceptionPending());
300
// Add an inline fast-path since we have to check for interrupts in some hot
301
// C++ loops of library builtins.
302
if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt())) {
303
return cx->handleInterrupt();
304
}
305
306
JS_INTERRUPT_POSSIBLY_FAIL();
307
308
return true;
309
}
310
311
} /* namespace js */
312
313
inline js::LifoAlloc& JSContext::typeLifoAlloc() {
314
return zone()->types.typeLifoAlloc();
315
}
316
317
inline js::Nursery& JSContext::nursery() { return runtime()->gc.nursery(); }
318
319
inline void JSContext::minorGC(JS::GCReason reason) {
320
runtime()->gc.minorGC(reason);
321
}
322
323
inline bool JSContext::runningWithTrustedPrincipals() {
324
if (!realm()) {
325
return true;
326
}
327
if (!runtime()->trustedPrincipals()) {
328
return false;
329
}
330
return realm()->principals() == runtime()->trustedPrincipals();
331
}
332
333
inline void JSContext::enterRealm(JS::Realm* realm) {
334
// We should never enter a realm while in the atoms zone.
335
MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
336
337
realm->enter();
338
setRealm(realm);
339
}
340
341
inline void JSContext::enterAtomsZone() {
342
realm_ = nullptr;
343
setZone(runtime_->unsafeAtomsZone(), AtomsZone);
344
}
345
346
inline void JSContext::setZone(js::Zone* zone,
347
JSContext::IsAtomsZone isAtomsZone) {
348
if (zone_) {
349
zone_->addTenuredAllocsSinceMinorGC(allocsThisZoneSinceMinorGC_);
350
}
351
352
allocsThisZoneSinceMinorGC_ = 0;
353
354
zone_ = zone;
355
if (zone == nullptr) {
356
freeLists_ = nullptr;
357
return;
358
}
359
360
if (isAtomsZone == AtomsZone && isHelperThreadContext()) {
361
MOZ_ASSERT(!zone_->wasGCStarted());
362
freeLists_ = atomsZoneFreeLists_;
363
} else {
364
freeLists_ = &zone_->arenas.freeLists();
365
}
366
}
367
368
inline void JSContext::enterRealmOf(JSObject* target) {
369
JS::AssertCellIsNotGray(target);
370
enterRealm(target->nonCCWRealm());
371
}
372
373
inline void JSContext::enterRealmOf(JSScript* target) {
374
JS::AssertCellIsNotGray(target);
375
enterRealm(target->realm());
376
}
377
378
inline void JSContext::enterRealmOf(js::ObjectGroup* target) {
379
JS::AssertCellIsNotGray(target);
380
enterRealm(target->realm());
381
}
382
383
inline void JSContext::enterNullRealm() {
384
// We should never enter a realm while in the atoms zone.
385
MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
386
387
setRealm(nullptr);
388
}
389
390
inline void JSContext::leaveRealm(JS::Realm* oldRealm) {
391
// Only call leave() after we've setRealm()-ed away from the current realm.
392
JS::Realm* startingRealm = realm_;
393
394
// The current realm should be marked as entered-from-C++ at this point.
395
MOZ_ASSERT_IF(startingRealm, startingRealm->hasBeenEnteredIgnoringJit());
396
397
setRealm(oldRealm);
398
399
if (startingRealm) {
400
startingRealm->leave();
401
}
402
}
403
404
inline void JSContext::leaveAtomsZone(JS::Realm* oldRealm) {
405
setRealm(oldRealm);
406
}
407
408
inline void JSContext::setRealm(JS::Realm* realm) {
409
realm_ = realm;
410
if (realm) {
411
// This thread must have exclusive access to the zone.
412
MOZ_ASSERT(CurrentThreadCanAccessZone(realm->zone()));
413
MOZ_ASSERT(!realm->zone()->isAtomsZone());
414
setZone(realm->zone(), NotAtomsZone);
415
} else {
416
setZone(nullptr, NotAtomsZone);
417
}
418
}
419
420
inline void JSContext::setRealmForJitExceptionHandler(JS::Realm* realm) {
421
// JIT code enters (same-compartment) realms without calling realm->enter()
422
// so we don't call realm->leave() here.
423
MOZ_ASSERT(realm->compartment() == compartment());
424
realm_ = realm;
425
}
426
427
inline JSScript* JSContext::currentScript(
428
jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const {
429
if (ppc) {
430
*ppc = nullptr;
431
}
432
433
js::Activation* act = activation();
434
if (!act) {
435
return nullptr;
436
}
437
438
MOZ_ASSERT(act->cx() == this);
439
440
// Cross-compartment implies cross-realm.
441
if (allowCrossRealm == AllowCrossRealm::DontAllow &&
442
act->compartment() != compartment()) {
443
return nullptr;
444
}
445
446
JSScript* script = nullptr;
447
jsbytecode* pc = nullptr;
448
if (act->isJit()) {
449
if (act->hasWasmExitFP()) {
450
return nullptr;
451
}
452
js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc);
453
} else {
454
js::InterpreterFrame* fp = act->asInterpreter()->current();
455
MOZ_ASSERT(!fp->runningInJit());
456
script = fp->script();
457
pc = act->asInterpreter()->regs().pc;
458
}
459
460
MOZ_ASSERT(script->containsPC(pc));
461
462
if (allowCrossRealm == AllowCrossRealm::DontAllow &&
463
script->realm() != realm()) {
464
return nullptr;
465
}
466
467
if (ppc) {
468
*ppc = pc;
469
}
470
return script;
471
}
472
473
inline js::RuntimeCaches& JSContext::caches() { return runtime()->caches(); }
474
475
inline js::AutoKeepAtoms::AutoKeepAtoms(
476
JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
477
: cx(cx) {
478
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
479
cx->zone()->keepAtoms();
480
}
481
482
inline js::AutoKeepAtoms::~AutoKeepAtoms() { cx->zone()->releaseAtoms(); };
483
484
#endif /* vm_JSContext_inl_h */