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