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 "jsfriendapi.h"
8
9 #include "mozilla/Atomics.h"
10 #include "mozilla/Maybe.h"
11 #include "mozilla/PodOperations.h"
12 #include "mozilla/TimeStamp.h"
13
14 #include <stdint.h>
15
16 #include "builtin/BigInt.h"
17 #include "builtin/MapObject.h"
18 #include "builtin/TestingFunctions.h"
19 #include "gc/GC.h"
20 #include "gc/PublicIterators.h"
21 #include "gc/WeakMap.h"
22 #include "js/CharacterEncoding.h"
23 #include "js/experimental/CodeCoverage.h" // js::EnableCodeCoverage
24 #include "js/Printf.h"
25 #include "js/Proxy.h"
26 #include "js/Wrapper.h"
27 #include "proxy/DeadObjectProxy.h"
28 #include "util/Poison.h"
29 #include "vm/ArgumentsObject.h"
30 #include "vm/DateObject.h"
31 #include "vm/ErrorObject.h"
32 #include "vm/FrameIter.h" // js::FrameIter
33 #include "vm/JSContext.h"
34 #include "vm/JSObject.h"
35 #include "vm/PlainObject.h" // js::PlainObject
36 #include "vm/Printer.h"
37 #include "vm/PromiseObject.h" // js::PromiseObject
38 #include "vm/Realm.h"
39 #include "vm/Time.h"
40 #include "vm/WrapperObject.h"
41
42 #include "gc/Nursery-inl.h"
43 #include "vm/Compartment-inl.h" // JS::Compartment::wrap
44 #include "vm/EnvironmentObject-inl.h"
45 #include "vm/JSObject-inl.h"
46 #include "vm/JSScript-inl.h"
47 #include "vm/NativeObject-inl.h"
48
49 using namespace js;
50
51 using mozilla::PodArrayZero;
52
RootingContext()53 JS::RootingContext::RootingContext() : realm_(nullptr), zone_(nullptr) {
54 for (auto& listHead : stackRoots_) {
55 listHead = nullptr;
56 }
57 for (auto& listHead : autoGCRooters_) {
58 listHead = nullptr;
59 }
60
61 PodArrayZero(nativeStackLimit);
62 #if JS_STACK_GROWTH_DIRECTION > 0
63 for (int i = 0; i < StackKindCount; i++) {
64 nativeStackLimit[i] = UINTPTR_MAX;
65 }
66 #endif
67 }
68
JS_SetGrayGCRootsTracer(JSContext * cx,JSTraceDataOp traceOp,void * data)69 JS_FRIEND_API void JS_SetGrayGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp,
70 void* data) {
71 cx->runtime()->gc.setGrayRootsTracer(traceOp, data);
72 }
73
JS_FindCompilationScope(JSContext * cx,HandleObject objArg)74 JS_FRIEND_API JSObject* JS_FindCompilationScope(JSContext* cx,
75 HandleObject objArg) {
76 cx->check(objArg);
77
78 RootedObject obj(cx, objArg);
79
80 /*
81 * We unwrap wrappers here. This is a little weird, but it's what's being
82 * asked of us.
83 */
84 if (obj->is<WrapperObject>()) {
85 obj = UncheckedUnwrap(obj);
86 }
87
88 /*
89 * Get the Window if `obj` is a WindowProxy so that we compile in the
90 * correct (global) scope.
91 */
92 return ToWindowIfWindowProxy(obj);
93 }
94
JS_GetObjectFunction(JSObject * obj)95 JS_FRIEND_API JSFunction* JS_GetObjectFunction(JSObject* obj) {
96 if (obj->is<JSFunction>()) {
97 return &obj->as<JSFunction>();
98 }
99 return nullptr;
100 }
101
JS_SplicePrototype(JSContext * cx,HandleObject obj,HandleObject proto)102 JS_FRIEND_API bool JS_SplicePrototype(JSContext* cx, HandleObject obj,
103 HandleObject proto) {
104 /*
105 * Change the prototype of an object which hasn't been used anywhere
106 * and does not share its type with another object. Unlike JS_SetPrototype,
107 * does not nuke type information for the object.
108 */
109 CHECK_THREAD(cx);
110 cx->check(obj, proto);
111
112 if (!obj->isSingleton()) {
113 /*
114 * We can see non-singleton objects when trying to splice prototypes
115 * due to mutable __proto__ (ugh).
116 */
117 return JS_SetPrototype(cx, obj, proto);
118 }
119
120 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
121 return JSObject::splicePrototype(cx, obj, tagged);
122 }
123
JS_NewObjectWithUniqueType(JSContext * cx,const JSClass * clasp,HandleObject proto)124 JS_FRIEND_API JSObject* JS_NewObjectWithUniqueType(JSContext* cx,
125 const JSClass* clasp,
126 HandleObject proto) {
127 /*
128 * Create our object with a null proto and then splice in the correct proto
129 * after we setSingleton, so that we don't pollute the default
130 * ObjectGroup attached to our proto with information about our object, since
131 * we're not going to be using that ObjectGroup anyway.
132 */
133 RootedObject obj(cx, NewSingletonObjectWithGivenProto(cx, clasp, nullptr));
134 if (!obj) {
135 return nullptr;
136 }
137 if (!JS_SplicePrototype(cx, obj, proto)) {
138 return nullptr;
139 }
140 return obj;
141 }
142
JS_NewObjectWithoutMetadata(JSContext * cx,const JSClass * clasp,JS::Handle<JSObject * > proto)143 JS_FRIEND_API JSObject* JS_NewObjectWithoutMetadata(
144 JSContext* cx, const JSClass* clasp, JS::Handle<JSObject*> proto) {
145 cx->check(proto);
146 AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
147 return JS_NewObjectWithGivenProto(cx, clasp, proto);
148 }
149
GetIsSecureContext(JS::Realm * realm)150 JS_FRIEND_API bool JS::GetIsSecureContext(JS::Realm* realm) {
151 return realm->creationOptions().secureContext();
152 }
153
AssertCompartmentHasSingleRealm(JS::Compartment * comp)154 JS_FRIEND_API void js::AssertCompartmentHasSingleRealm(JS::Compartment* comp) {
155 MOZ_RELEASE_ASSERT(comp->realms().length() == 1);
156 }
157
GetRealmPrincipals(JS::Realm * realm)158 JS_FRIEND_API JSPrincipals* JS::GetRealmPrincipals(JS::Realm* realm) {
159 return realm->principals();
160 }
161
SetRealmPrincipals(JS::Realm * realm,JSPrincipals * principals)162 JS_FRIEND_API void JS::SetRealmPrincipals(JS::Realm* realm,
163 JSPrincipals* principals) {
164 // Short circuit if there's no change.
165 if (principals == realm->principals()) {
166 return;
167 }
168
169 // We'd like to assert that our new principals is always same-origin
170 // with the old one, but JSPrincipals doesn't give us a way to do that.
171 // But we can at least assert that we're not switching between system
172 // and non-system.
173 const JSPrincipals* trusted =
174 realm->runtimeFromMainThread()->trustedPrincipals();
175 bool isSystem = principals && principals == trusted;
176 MOZ_RELEASE_ASSERT(realm->isSystem() == isSystem);
177
178 // Clear out the old principals, if any.
179 if (realm->principals()) {
180 JS_DropPrincipals(TlsContext.get(), realm->principals());
181 realm->setPrincipals(nullptr);
182 }
183
184 // Set up the new principals.
185 if (principals) {
186 JS_HoldPrincipals(principals);
187 realm->setPrincipals(principals);
188 }
189 }
190
JS_GetScriptPrincipals(JSScript * script)191 JS_FRIEND_API JSPrincipals* JS_GetScriptPrincipals(JSScript* script) {
192 return script->principals();
193 }
194
GetScriptRealm(JSScript * script)195 JS_FRIEND_API JS::Realm* js::GetScriptRealm(JSScript* script) {
196 return script->realm();
197 }
198
JS_ScriptHasMutedErrors(JSScript * script)199 JS_FRIEND_API bool JS_ScriptHasMutedErrors(JSScript* script) {
200 return script->mutedErrors();
201 }
202
JS_WrapPropertyDescriptor(JSContext * cx,JS::MutableHandle<JS::PropertyDescriptor> desc)203 JS_FRIEND_API bool JS_WrapPropertyDescriptor(
204 JSContext* cx, JS::MutableHandle<JS::PropertyDescriptor> desc) {
205 return cx->compartment()->wrap(cx, desc);
206 }
207
JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer * trc,JS::GCCellPtr shape)208 JS_FRIEND_API void JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc,
209 JS::GCCellPtr shape) {
210 MOZ_ASSERT(shape.is<Shape>());
211 TraceCycleCollectorChildren(trc, &shape.as<Shape>());
212 }
213
JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer * trc,JS::GCCellPtr group)214 JS_FRIEND_API void JS_TraceObjectGroupCycleCollectorChildren(
215 JS::CallbackTracer* trc, JS::GCCellPtr group) {
216 MOZ_ASSERT(group.is<ObjectGroup>());
217 TraceCycleCollectorChildren(trc, &group.as<ObjectGroup>());
218 }
219
DefineHelpProperty(JSContext * cx,HandleObject obj,const char * prop,const char * value)220 static bool DefineHelpProperty(JSContext* cx, HandleObject obj,
221 const char* prop, const char* value) {
222 RootedAtom atom(cx, Atomize(cx, value, strlen(value)));
223 if (!atom) {
224 return false;
225 }
226 return JS_DefineProperty(cx, obj, prop, atom,
227 JSPROP_READONLY | JSPROP_PERMANENT);
228 }
229
JS_DefineFunctionsWithHelp(JSContext * cx,HandleObject obj,const JSFunctionSpecWithHelp * fs)230 JS_FRIEND_API bool JS_DefineFunctionsWithHelp(
231 JSContext* cx, HandleObject obj, const JSFunctionSpecWithHelp* fs) {
232 MOZ_ASSERT(!cx->zone()->isAtomsZone());
233
234 CHECK_THREAD(cx);
235 cx->check(obj);
236 for (; fs->name; fs++) {
237 JSAtom* atom = Atomize(cx, fs->name, strlen(fs->name));
238 if (!atom) {
239 return false;
240 }
241
242 Rooted<jsid> id(cx, AtomToId(atom));
243 RootedFunction fun(cx, DefineFunction(cx, obj, id, fs->call, fs->nargs,
244 fs->flags | JSPROP_RESOLVING));
245 if (!fun) {
246 return false;
247 }
248
249 if (fs->jitInfo) {
250 fun->setJitInfo(fs->jitInfo);
251 }
252
253 if (fs->usage) {
254 if (!DefineHelpProperty(cx, fun, "usage", fs->usage)) {
255 return false;
256 }
257 }
258
259 if (fs->help) {
260 if (!DefineHelpProperty(cx, fun, "help", fs->help)) {
261 return false;
262 }
263 }
264 }
265
266 return true;
267 }
268
GetBuiltinClass(JSContext * cx,HandleObject obj,ESClass * cls)269 JS_FRIEND_API bool js::GetBuiltinClass(JSContext* cx, HandleObject obj,
270 ESClass* cls) {
271 if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
272 return Proxy::getBuiltinClass(cx, obj, cls);
273 }
274
275 if (obj->is<PlainObject>()) {
276 *cls = ESClass::Object;
277 } else if (obj->is<ArrayObject>()) {
278 *cls = ESClass::Array;
279 } else if (obj->is<NumberObject>()) {
280 *cls = ESClass::Number;
281 } else if (obj->is<StringObject>()) {
282 *cls = ESClass::String;
283 } else if (obj->is<BooleanObject>()) {
284 *cls = ESClass::Boolean;
285 } else if (obj->is<RegExpObject>()) {
286 *cls = ESClass::RegExp;
287 } else if (obj->is<ArrayBufferObject>()) {
288 *cls = ESClass::ArrayBuffer;
289 } else if (obj->is<SharedArrayBufferObject>()) {
290 *cls = ESClass::SharedArrayBuffer;
291 } else if (obj->is<DateObject>()) {
292 *cls = ESClass::Date;
293 } else if (obj->is<SetObject>()) {
294 *cls = ESClass::Set;
295 } else if (obj->is<MapObject>()) {
296 *cls = ESClass::Map;
297 } else if (obj->is<PromiseObject>()) {
298 *cls = ESClass::Promise;
299 } else if (obj->is<MapIteratorObject>()) {
300 *cls = ESClass::MapIterator;
301 } else if (obj->is<SetIteratorObject>()) {
302 *cls = ESClass::SetIterator;
303 } else if (obj->is<ArgumentsObject>()) {
304 *cls = ESClass::Arguments;
305 } else if (obj->is<ErrorObject>()) {
306 *cls = ESClass::Error;
307 } else if (obj->is<BigIntObject>()) {
308 *cls = ESClass::BigInt;
309 } else if (obj->is<JSFunction>()) {
310 *cls = ESClass::Function;
311 } else {
312 *cls = ESClass::Other;
313 }
314
315 return true;
316 }
317
IsArgumentsObject(HandleObject obj)318 JS_FRIEND_API bool js::IsArgumentsObject(HandleObject obj) {
319 return obj->is<ArgumentsObject>();
320 }
321
ObjectClassName(JSContext * cx,HandleObject obj)322 JS_FRIEND_API const char* js::ObjectClassName(JSContext* cx, HandleObject obj) {
323 cx->check(obj);
324 return GetObjectClassName(cx, obj);
325 }
326
GetRealmZone(JS::Realm * realm)327 JS_FRIEND_API JS::Zone* js::GetRealmZone(JS::Realm* realm) {
328 return realm->zone();
329 }
330
IsSystemCompartment(JS::Compartment * comp)331 JS_FRIEND_API bool js::IsSystemCompartment(JS::Compartment* comp) {
332 // Realms in the same compartment must either all be system realms or
333 // non-system realms. We assert this in NewRealm and SetRealmPrincipals,
334 // but do an extra sanity check here.
335 MOZ_ASSERT(comp->realms()[0]->isSystem() ==
336 comp->realms().back()->isSystem());
337 return comp->realms()[0]->isSystem();
338 }
339
IsSystemRealm(JS::Realm * realm)340 JS_FRIEND_API bool js::IsSystemRealm(JS::Realm* realm) {
341 return realm->isSystem();
342 }
343
IsSystemZone(Zone * zone)344 JS_FRIEND_API bool js::IsSystemZone(Zone* zone) { return zone->isSystemZone(); }
345
IsAtomsZone(JS::Zone * zone)346 JS_FRIEND_API bool js::IsAtomsZone(JS::Zone* zone) {
347 return zone->isAtomsZone();
348 }
349
IsFunctionObject(JSObject * obj)350 JS_FRIEND_API bool js::IsFunctionObject(JSObject* obj) {
351 return obj->is<JSFunction>();
352 }
353
IsSavedFrame(JSObject * obj)354 JS_FRIEND_API bool js::IsSavedFrame(JSObject* obj) {
355 return obj->is<SavedFrame>();
356 }
357
UninlinedIsCrossCompartmentWrapper(const JSObject * obj)358 JS_FRIEND_API bool js::UninlinedIsCrossCompartmentWrapper(const JSObject* obj) {
359 return js::IsCrossCompartmentWrapper(obj);
360 }
361
GetPrototypeNoProxy(JSObject * obj)362 JS_FRIEND_API JSObject* js::GetPrototypeNoProxy(JSObject* obj) {
363 MOZ_ASSERT(!obj->is<js::ProxyObject>());
364 return obj->staticPrototype();
365 }
366
AssertSameCompartment(JSContext * cx,JSObject * obj)367 JS_FRIEND_API void js::AssertSameCompartment(JSContext* cx, JSObject* obj) {
368 cx->check(obj);
369 }
370
AssertSameCompartment(JSContext * cx,JS::HandleValue v)371 JS_FRIEND_API void js::AssertSameCompartment(JSContext* cx, JS::HandleValue v) {
372 cx->check(v);
373 }
374
375 #ifdef DEBUG
AssertSameCompartment(JSObject * objA,JSObject * objB)376 JS_FRIEND_API void js::AssertSameCompartment(JSObject* objA, JSObject* objB) {
377 MOZ_ASSERT(objA->compartment() == objB->compartment());
378 }
379 #endif
380
NotifyAnimationActivity(JSObject * obj)381 JS_FRIEND_API void js::NotifyAnimationActivity(JSObject* obj) {
382 MOZ_ASSERT(obj->is<GlobalObject>());
383
384 auto timeNow = mozilla::TimeStamp::Now();
385 obj->as<GlobalObject>().realm()->lastAnimationTime = timeNow;
386 obj->runtimeFromMainThread()->lastAnimationTime = timeNow;
387 }
388
GetObjectSlotSpan(JSObject * obj)389 JS_FRIEND_API uint32_t js::GetObjectSlotSpan(JSObject* obj) {
390 return obj->as<NativeObject>().slotSpan();
391 }
392
IsObjectInContextCompartment(JSObject * obj,const JSContext * cx)393 JS_FRIEND_API bool js::IsObjectInContextCompartment(JSObject* obj,
394 const JSContext* cx) {
395 return obj->compartment() == cx->compartment();
396 }
397
RunningWithTrustedPrincipals(JSContext * cx)398 JS_FRIEND_API bool js::RunningWithTrustedPrincipals(JSContext* cx) {
399 return cx->runningWithTrustedPrincipals();
400 }
401
DefineFunctionWithReserved(JSContext * cx,JSObject * objArg,const char * name,JSNative call,unsigned nargs,unsigned attrs)402 JS_FRIEND_API JSFunction* js::DefineFunctionWithReserved(
403 JSContext* cx, JSObject* objArg, const char* name, JSNative call,
404 unsigned nargs, unsigned attrs) {
405 RootedObject obj(cx, objArg);
406 MOZ_ASSERT(!cx->zone()->isAtomsZone());
407 CHECK_THREAD(cx);
408 cx->check(obj);
409 JSAtom* atom = Atomize(cx, name, strlen(name));
410 if (!atom) {
411 return nullptr;
412 }
413 Rooted<jsid> id(cx, AtomToId(atom));
414 return DefineFunction(cx, obj, id, call, nargs, attrs,
415 gc::AllocKind::FUNCTION_EXTENDED);
416 }
417
NewFunctionWithReserved(JSContext * cx,JSNative native,unsigned nargs,unsigned flags,const char * name)418 JS_FRIEND_API JSFunction* js::NewFunctionWithReserved(JSContext* cx,
419 JSNative native,
420 unsigned nargs,
421 unsigned flags,
422 const char* name) {
423 MOZ_ASSERT(!cx->zone()->isAtomsZone());
424
425 CHECK_THREAD(cx);
426
427 RootedAtom atom(cx);
428 if (name) {
429 atom = Atomize(cx, name, strlen(name));
430 if (!atom) {
431 return nullptr;
432 }
433 }
434
435 return (flags & JSFUN_CONSTRUCTOR)
436 ? NewNativeConstructor(cx, native, nargs, atom,
437 gc::AllocKind::FUNCTION_EXTENDED)
438 : NewNativeFunction(cx, native, nargs, atom,
439 gc::AllocKind::FUNCTION_EXTENDED);
440 }
441
NewFunctionByIdWithReserved(JSContext * cx,JSNative native,unsigned nargs,unsigned flags,jsid id)442 JS_FRIEND_API JSFunction* js::NewFunctionByIdWithReserved(
443 JSContext* cx, JSNative native, unsigned nargs, unsigned flags, jsid id) {
444 MOZ_ASSERT(JSID_IS_STRING(id));
445 MOZ_ASSERT(!cx->zone()->isAtomsZone());
446 CHECK_THREAD(cx);
447 cx->check(id);
448
449 RootedAtom atom(cx, JSID_TO_ATOM(id));
450 return (flags & JSFUN_CONSTRUCTOR)
451 ? NewNativeConstructor(cx, native, nargs, atom,
452 gc::AllocKind::FUNCTION_EXTENDED)
453 : NewNativeFunction(cx, native, nargs, atom,
454 gc::AllocKind::FUNCTION_EXTENDED);
455 }
456
GetFunctionNativeReserved(JSObject * fun,size_t which)457 JS_FRIEND_API const Value& js::GetFunctionNativeReserved(JSObject* fun,
458 size_t which) {
459 MOZ_ASSERT(fun->as<JSFunction>().isNative());
460 return fun->as<JSFunction>().getExtendedSlot(which);
461 }
462
SetFunctionNativeReserved(JSObject * fun,size_t which,const Value & val)463 JS_FRIEND_API void js::SetFunctionNativeReserved(JSObject* fun, size_t which,
464 const Value& val) {
465 MOZ_ASSERT(fun->as<JSFunction>().isNative());
466 MOZ_ASSERT_IF(val.isObject(),
467 val.toObject().compartment() == fun->compartment());
468 fun->as<JSFunction>().setExtendedSlot(which, val);
469 }
470
FunctionHasNativeReserved(JSObject * fun)471 JS_FRIEND_API bool js::FunctionHasNativeReserved(JSObject* fun) {
472 MOZ_ASSERT(fun->as<JSFunction>().isNative());
473 return fun->as<JSFunction>().isExtended();
474 }
475
GetObjectProto(JSContext * cx,JS::Handle<JSObject * > obj,JS::MutableHandle<JSObject * > proto)476 JS_FRIEND_API bool js::GetObjectProto(JSContext* cx, JS::Handle<JSObject*> obj,
477 JS::MutableHandle<JSObject*> proto) {
478 cx->check(obj);
479
480 if (IsProxy(obj)) {
481 return JS_GetPrototype(cx, obj, proto);
482 }
483
484 proto.set(reinterpret_cast<const shadow::Object*>(obj.get())->group->proto);
485 return true;
486 }
487
GetStaticPrototype(JSObject * obj)488 JS_FRIEND_API JSObject* js::GetStaticPrototype(JSObject* obj) {
489 MOZ_ASSERT(obj->hasStaticPrototype());
490 return obj->staticPrototype();
491 }
492
GetRealmOriginalEval(JSContext * cx,MutableHandleObject eval)493 JS_FRIEND_API bool js::GetRealmOriginalEval(JSContext* cx,
494 MutableHandleObject eval) {
495 return GlobalObject::getOrCreateEval(cx, cx->global(), eval);
496 }
497
SetReservedSlotWithBarrier(JSObject * obj,size_t slot,const js::Value & value)498 JS_FRIEND_API void js::SetReservedSlotWithBarrier(JSObject* obj, size_t slot,
499 const js::Value& value) {
500 if (IsProxy(obj)) {
501 obj->as<ProxyObject>().setReservedSlot(slot, value);
502 } else {
503 obj->as<NativeObject>().setSlot(slot, value);
504 }
505 }
506
SetPreserveWrapperCallback(JSContext * cx,PreserveWrapperCallback callback)507 void js::SetPreserveWrapperCallback(JSContext* cx,
508 PreserveWrapperCallback callback) {
509 cx->runtime()->preserveWrapperCallback = callback;
510 }
511
JS_PCToLineNumber(JSScript * script,jsbytecode * pc,unsigned * columnp)512 JS_FRIEND_API unsigned JS_PCToLineNumber(JSScript* script, jsbytecode* pc,
513 unsigned* columnp) {
514 return PCToLineNumber(script, pc, columnp);
515 }
516
JS_IsDeadWrapper(JSObject * obj)517 JS_FRIEND_API bool JS_IsDeadWrapper(JSObject* obj) {
518 return IsDeadProxyObject(obj);
519 }
520
JS_NewDeadWrapper(JSContext * cx,JSObject * origObj)521 JS_FRIEND_API JSObject* JS_NewDeadWrapper(JSContext* cx, JSObject* origObj) {
522 return NewDeadProxyObject(cx, origObj);
523 }
524
TraceWeakMaps(WeakMapTracer * trc)525 void js::TraceWeakMaps(WeakMapTracer* trc) {
526 WeakMapBase::traceAllMappings(trc);
527 }
528
AreGCGrayBitsValid(JSRuntime * rt)529 extern JS_FRIEND_API bool js::AreGCGrayBitsValid(JSRuntime* rt) {
530 return rt->gc.areGrayBitsValid();
531 }
532
ZoneGlobalsAreAllGray(JS::Zone * zone)533 JS_FRIEND_API bool js::ZoneGlobalsAreAllGray(JS::Zone* zone) {
534 for (RealmsInZoneIter realm(zone); !realm.done(); realm.next()) {
535 JSObject* obj = realm->unsafeUnbarrieredMaybeGlobal();
536 if (!obj || !JS::ObjectIsMarkedGray(obj)) {
537 return false;
538 }
539 }
540 return true;
541 }
542
IsCompartmentZoneSweepingOrCompacting(JS::Compartment * comp)543 JS_FRIEND_API bool js::IsCompartmentZoneSweepingOrCompacting(
544 JS::Compartment* comp) {
545 MOZ_ASSERT(comp);
546 return comp->zone()->isGCSweepingOrCompacting();
547 }
548
VisitGrayWrapperTargets(Zone * zone,GCThingCallback callback,void * closure)549 JS_FRIEND_API void js::VisitGrayWrapperTargets(Zone* zone,
550 GCThingCallback callback,
551 void* closure) {
552 for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
553 for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) {
554 JSObject* target = e.front().key();
555 if (target->isMarkedGray()) {
556 JS::AutoSuppressGCAnalysis nogc;
557 callback(closure, JS::GCCellPtr(target));
558 }
559 }
560 }
561 }
562
StringToLinearStringSlow(JSContext * cx,JSString * str)563 JS_FRIEND_API JSLinearString* js::StringToLinearStringSlow(JSContext* cx,
564 JSString* str) {
565 return str->ensureLinear(cx);
566 }
567
JS_SetAccumulateTelemetryCallback(JSContext * cx,JSAccumulateTelemetryDataCallback callback)568 JS_FRIEND_API void JS_SetAccumulateTelemetryCallback(
569 JSContext* cx, JSAccumulateTelemetryDataCallback callback) {
570 cx->runtime()->setTelemetryCallback(cx->runtime(), callback);
571 }
572
JS_SetSetUseCounterCallback(JSContext * cx,JSSetUseCounterCallback callback)573 JS_FRIEND_API void JS_SetSetUseCounterCallback(
574 JSContext* cx, JSSetUseCounterCallback callback) {
575 cx->runtime()->setUseCounterCallback(cx->runtime(), callback);
576 }
577
CopyProxyObject(JSContext * cx,Handle<ProxyObject * > from,Handle<ProxyObject * > to)578 static bool CopyProxyObject(JSContext* cx, Handle<ProxyObject*> from,
579 Handle<ProxyObject*> to) {
580 MOZ_ASSERT(from->getClass() == to->getClass());
581
582 if (from->is<WrapperObject>() &&
583 (Wrapper::wrapperHandler(from)->flags() & Wrapper::CROSS_COMPARTMENT)) {
584 to->setCrossCompartmentPrivate(GetProxyPrivate(from));
585 } else {
586 RootedValue v(cx, GetProxyPrivate(from));
587 if (!cx->compartment()->wrap(cx, &v)) {
588 return false;
589 }
590 to->setSameCompartmentPrivate(v);
591 }
592
593 MOZ_ASSERT(from->numReservedSlots() == to->numReservedSlots());
594
595 RootedValue v(cx);
596 for (size_t n = 0; n < from->numReservedSlots(); n++) {
597 v = GetProxyReservedSlot(from, n);
598 if (!cx->compartment()->wrap(cx, &v)) {
599 return false;
600 }
601 SetProxyReservedSlot(to, n, v);
602 }
603
604 return true;
605 }
606
JS_CloneObject(JSContext * cx,HandleObject obj,HandleObject proto)607 JS_FRIEND_API JSObject* JS_CloneObject(JSContext* cx, HandleObject obj,
608 HandleObject proto) {
609 // |obj| might be in a different compartment.
610 cx->check(proto);
611
612 if (!obj->isNative() && !obj->is<ProxyObject>()) {
613 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
614 JSMSG_CANT_CLONE_OBJECT);
615 return nullptr;
616 }
617
618 RootedObject clone(cx);
619 if (obj->isNative()) {
620 // JS_CloneObject is used to create the target object for JSObject::swap().
621 // swap() requires its arguments are tenured, so ensure tenure allocation.
622 clone = NewTenuredObjectWithGivenProto(cx, obj->getClass(), proto);
623 if (!clone) {
624 return nullptr;
625 }
626
627 if (clone->is<JSFunction>() &&
628 (obj->compartment() != clone->compartment())) {
629 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
630 JSMSG_CANT_CLONE_OBJECT);
631 return nullptr;
632 }
633
634 if (obj->as<NativeObject>().hasPrivate()) {
635 clone->as<NativeObject>().setPrivate(
636 obj->as<NativeObject>().getPrivate());
637 }
638 } else {
639 auto* handler = GetProxyHandler(obj);
640
641 // Same as above, require tenure allocation of the clone. This means for
642 // proxy objects we need to reject nursery allocatable proxies.
643 if (handler->canNurseryAllocate()) {
644 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
645 JSMSG_CANT_CLONE_OBJECT);
646 return nullptr;
647 }
648
649 clone = ProxyObject::New(cx, handler, JS::NullHandleValue,
650 AsTaggedProto(proto), obj->getClass());
651 if (!clone) {
652 return nullptr;
653 }
654
655 if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>())) {
656 return nullptr;
657 }
658 }
659
660 return clone;
661 }
662
663 // We don't want jsfriendapi.h to depend on GenericPrinter,
664 // so these functions are declared directly in the cpp.
665
666 namespace js {
667
668 extern JS_FRIEND_API void DumpString(JSString* str, js::GenericPrinter& out);
669
670 extern JS_FRIEND_API void DumpAtom(JSAtom* atom, js::GenericPrinter& out);
671
672 extern JS_FRIEND_API void DumpObject(JSObject* obj, js::GenericPrinter& out);
673
674 extern JS_FRIEND_API void DumpChars(const char16_t* s, size_t n,
675 js::GenericPrinter& out);
676
677 extern JS_FRIEND_API void DumpValue(const JS::Value& val,
678 js::GenericPrinter& out);
679
680 extern JS_FRIEND_API void DumpId(jsid id, js::GenericPrinter& out);
681
682 extern JS_FRIEND_API void DumpInterpreterFrame(
683 JSContext* cx, js::GenericPrinter& out, InterpreterFrame* start = nullptr);
684
685 } // namespace js
686
DumpString(JSString * str,js::GenericPrinter & out)687 JS_FRIEND_API void js::DumpString(JSString* str, js::GenericPrinter& out) {
688 #if defined(DEBUG) || defined(JS_JITSPEW)
689 str->dump(out);
690 #endif
691 }
692
DumpAtom(JSAtom * atom,js::GenericPrinter & out)693 JS_FRIEND_API void js::DumpAtom(JSAtom* atom, js::GenericPrinter& out) {
694 #if defined(DEBUG) || defined(JS_JITSPEW)
695 atom->dump(out);
696 #endif
697 }
698
DumpChars(const char16_t * s,size_t n,js::GenericPrinter & out)699 JS_FRIEND_API void js::DumpChars(const char16_t* s, size_t n,
700 js::GenericPrinter& out) {
701 #if defined(DEBUG) || defined(JS_JITSPEW)
702 out.printf("char16_t * (%p) = ", (void*)s);
703 JSString::dumpChars(s, n, out);
704 out.putChar('\n');
705 #endif
706 }
707
DumpObject(JSObject * obj,js::GenericPrinter & out)708 JS_FRIEND_API void js::DumpObject(JSObject* obj, js::GenericPrinter& out) {
709 #if defined(DEBUG) || defined(JS_JITSPEW)
710 if (!obj) {
711 out.printf("NULL\n");
712 return;
713 }
714 obj->dump(out);
715 #endif
716 }
717
DumpString(JSString * str,FILE * fp)718 JS_FRIEND_API void js::DumpString(JSString* str, FILE* fp) {
719 #if defined(DEBUG) || defined(JS_JITSPEW)
720 Fprinter out(fp);
721 js::DumpString(str, out);
722 #endif
723 }
724
DumpAtom(JSAtom * atom,FILE * fp)725 JS_FRIEND_API void js::DumpAtom(JSAtom* atom, FILE* fp) {
726 #if defined(DEBUG) || defined(JS_JITSPEW)
727 Fprinter out(fp);
728 js::DumpAtom(atom, out);
729 #endif
730 }
731
DumpChars(const char16_t * s,size_t n,FILE * fp)732 JS_FRIEND_API void js::DumpChars(const char16_t* s, size_t n, FILE* fp) {
733 #if defined(DEBUG) || defined(JS_JITSPEW)
734 Fprinter out(fp);
735 js::DumpChars(s, n, out);
736 #endif
737 }
738
DumpObject(JSObject * obj,FILE * fp)739 JS_FRIEND_API void js::DumpObject(JSObject* obj, FILE* fp) {
740 #if defined(DEBUG) || defined(JS_JITSPEW)
741 Fprinter out(fp);
742 js::DumpObject(obj, out);
743 #endif
744 }
745
DumpId(jsid id,FILE * fp)746 JS_FRIEND_API void js::DumpId(jsid id, FILE* fp) {
747 #if defined(DEBUG) || defined(JS_JITSPEW)
748 Fprinter out(fp);
749 js::DumpId(id, out);
750 #endif
751 }
752
DumpValue(const JS::Value & val,FILE * fp)753 JS_FRIEND_API void js::DumpValue(const JS::Value& val, FILE* fp) {
754 #if defined(DEBUG) || defined(JS_JITSPEW)
755 Fprinter out(fp);
756 js::DumpValue(val, out);
757 #endif
758 }
759
DumpString(JSString * str)760 JS_FRIEND_API void js::DumpString(JSString* str) { DumpString(str, stderr); }
DumpAtom(JSAtom * atom)761 JS_FRIEND_API void js::DumpAtom(JSAtom* atom) { DumpAtom(atom, stderr); }
DumpObject(JSObject * obj)762 JS_FRIEND_API void js::DumpObject(JSObject* obj) { DumpObject(obj, stderr); }
DumpChars(const char16_t * s,size_t n)763 JS_FRIEND_API void js::DumpChars(const char16_t* s, size_t n) {
764 DumpChars(s, n, stderr);
765 }
DumpValue(const JS::Value & val)766 JS_FRIEND_API void js::DumpValue(const JS::Value& val) {
767 DumpValue(val, stderr);
768 }
DumpId(jsid id)769 JS_FRIEND_API void js::DumpId(jsid id) { DumpId(id, stderr); }
DumpInterpreterFrame(JSContext * cx,InterpreterFrame * start)770 JS_FRIEND_API void js::DumpInterpreterFrame(JSContext* cx,
771 InterpreterFrame* start) {
772 #if defined(DEBUG) || defined(JS_JITSPEW)
773 Fprinter out(stderr);
774 DumpInterpreterFrame(cx, out, start);
775 #endif
776 }
DumpPC(JSContext * cx)777 JS_FRIEND_API bool js::DumpPC(JSContext* cx) {
778 #if defined(DEBUG) || defined(JS_JITSPEW)
779 return DumpPC(cx, stdout);
780 #else
781 return true;
782 #endif
783 }
DumpScript(JSContext * cx,JSScript * scriptArg)784 JS_FRIEND_API bool js::DumpScript(JSContext* cx, JSScript* scriptArg) {
785 #if defined(DEBUG) || defined(JS_JITSPEW)
786 return DumpScript(cx, scriptArg, stdout);
787 #else
788 return true;
789 #endif
790 }
791
FormatValue(JSContext * cx,HandleValue v,UniqueChars & bytes)792 static const char* FormatValue(JSContext* cx, HandleValue v,
793 UniqueChars& bytes) {
794 if (v.isMagic()) {
795 MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_OUT ||
796 v.whyMagic() == JS_UNINITIALIZED_LEXICAL);
797 return "[unavailable]";
798 }
799
800 if (IsCallable(v)) {
801 return "[function]";
802 }
803
804 if (v.isObject() && IsCrossCompartmentWrapper(&v.toObject())) {
805 return "[cross-compartment wrapper]";
806 }
807
808 JSString* str;
809 {
810 mozilla::Maybe<AutoRealm> ar;
811 if (v.isObject()) {
812 ar.emplace(cx, &v.toObject());
813 }
814
815 str = ToString<CanGC>(cx, v);
816 if (!str) {
817 return nullptr;
818 }
819 }
820
821 bytes = QuoteString(cx, str, '"');
822 return bytes.get();
823 }
824
FormatFrame(JSContext * cx,const FrameIter & iter,Sprinter & sp,int num,bool showArgs,bool showLocals,bool showThisProps)825 static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
826 int num, bool showArgs, bool showLocals,
827 bool showThisProps) {
828 MOZ_ASSERT(!cx->isExceptionPending());
829 RootedScript script(cx, iter.script());
830 jsbytecode* pc = iter.pc();
831
832 RootedObject envChain(cx, iter.environmentChain(cx));
833 JSAutoRealm ar(cx, envChain);
834
835 const char* filename = script->filename();
836 unsigned column = 0;
837 unsigned lineno = PCToLineNumber(script, pc, &column);
838 RootedFunction fun(cx, iter.maybeCallee(cx));
839 RootedString funname(cx);
840 if (fun) {
841 funname = fun->displayAtom();
842 }
843
844 RootedValue thisVal(cx);
845 if (iter.hasUsableAbstractFramePtr() && iter.isFunctionFrame() && fun &&
846 !fun->isArrow() && !fun->isDerivedClassConstructor() &&
847 !(fun->isBoundFunction() && iter.isConstructing())) {
848 if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal)) {
849 return false;
850 }
851 }
852
853 // print the frame number and function name
854 if (funname) {
855 UniqueChars funbytes = QuoteString(cx, funname);
856 if (!funbytes) {
857 return false;
858 }
859 if (!sp.printf("%d %s(", num, funbytes.get())) {
860 return false;
861 }
862 } else if (fun) {
863 if (!sp.printf("%d anonymous(", num)) {
864 return false;
865 }
866 } else {
867 if (!sp.printf("%d <TOP LEVEL>", num)) {
868 return false;
869 }
870 }
871
872 if (showArgs && iter.hasArgs()) {
873 PositionalFormalParameterIter fi(script);
874 bool first = true;
875 for (unsigned i = 0; i < iter.numActualArgs(); i++) {
876 RootedValue arg(cx);
877 if (i < iter.numFormalArgs() && fi.closedOver()) {
878 if (iter.hasInitialEnvironment(cx)) {
879 arg = iter.callObj(cx).aliasedBinding(fi);
880 } else {
881 arg = MagicValue(JS_OPTIMIZED_OUT);
882 }
883 } else if (iter.hasUsableAbstractFramePtr()) {
884 if (!script->needsArgsAnalysis() && script->argsObjAliasesFormals() &&
885 iter.hasArgsObj()) {
886 arg = iter.argsObj().arg(i);
887 } else {
888 arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
889 }
890 } else {
891 arg = MagicValue(JS_OPTIMIZED_OUT);
892 }
893
894 UniqueChars valueBytes;
895 const char* value = FormatValue(cx, arg, valueBytes);
896 if (!value) {
897 if (cx->isThrowingOutOfMemory()) {
898 return false;
899 }
900 cx->clearPendingException();
901 }
902
903 UniqueChars nameBytes;
904 const char* name = nullptr;
905
906 if (i < iter.numFormalArgs()) {
907 MOZ_ASSERT(fi.argumentSlot() == i);
908 if (!fi.isDestructured()) {
909 nameBytes = StringToNewUTF8CharsZ(cx, *fi.name());
910 name = nameBytes.get();
911 if (!name) {
912 return false;
913 }
914 } else {
915 name = "(destructured parameter)";
916 }
917 fi++;
918 }
919
920 if (value) {
921 if (!sp.printf("%s%s%s%s%s%s", !first ? ", " : "", name ? name : "",
922 name ? " = " : "", arg.isString() ? "\"" : "", value,
923 arg.isString() ? "\"" : "")) {
924 return false;
925 }
926
927 first = false;
928 } else {
929 if (!sp.put(" <Failed to get argument while inspecting stack "
930 "frame>\n")) {
931 return false;
932 }
933 }
934 }
935 }
936
937 // print filename, line number and column
938 if (!sp.printf("%s [\"%s\":%u:%u]\n", fun ? ")" : "",
939 filename ? filename : "<unknown>", lineno, column)) {
940 return false;
941 }
942
943 // Note: Right now we don't dump the local variables anymore, because
944 // that is hard to support across all the JITs etc.
945
946 // print the value of 'this'
947 if (showLocals) {
948 if (!thisVal.isUndefined()) {
949 RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
950 if (!thisValStr) {
951 if (cx->isThrowingOutOfMemory()) {
952 return false;
953 }
954 cx->clearPendingException();
955 }
956 if (thisValStr) {
957 UniqueChars thisValBytes = QuoteString(cx, thisValStr);
958 if (!thisValBytes) {
959 return false;
960 }
961 if (!sp.printf(" this = %s\n", thisValBytes.get())) {
962 return false;
963 }
964 } else {
965 if (!sp.put(" <failed to get 'this' value>\n")) {
966 return false;
967 }
968 }
969 }
970 }
971
972 if (showThisProps && thisVal.isObject()) {
973 RootedObject obj(cx, &thisVal.toObject());
974
975 RootedIdVector keys(cx);
976 if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
977 if (cx->isThrowingOutOfMemory()) {
978 return false;
979 }
980 cx->clearPendingException();
981 }
982
983 for (size_t i = 0; i < keys.length(); i++) {
984 RootedId id(cx, keys[i]);
985 RootedValue key(cx, IdToValue(id));
986 RootedValue v(cx);
987
988 if (!GetProperty(cx, obj, obj, id, &v)) {
989 if (cx->isThrowingOutOfMemory()) {
990 return false;
991 }
992 cx->clearPendingException();
993 if (!sp.put(" <Failed to fetch property while inspecting stack "
994 "frame>\n")) {
995 return false;
996 }
997 continue;
998 }
999
1000 UniqueChars nameBytes;
1001 const char* name = FormatValue(cx, key, nameBytes);
1002 if (!name) {
1003 if (cx->isThrowingOutOfMemory()) {
1004 return false;
1005 }
1006 cx->clearPendingException();
1007 }
1008
1009 UniqueChars valueBytes;
1010 const char* value = FormatValue(cx, v, valueBytes);
1011 if (!value) {
1012 if (cx->isThrowingOutOfMemory()) {
1013 return false;
1014 }
1015 cx->clearPendingException();
1016 }
1017
1018 if (name && value) {
1019 if (!sp.printf(" this.%s = %s%s%s\n", name, v.isString() ? "\"" : "",
1020 value, v.isString() ? "\"" : "")) {
1021 return false;
1022 }
1023 } else {
1024 if (!sp.put(" <Failed to format values while inspecting stack "
1025 "frame>\n")) {
1026 return false;
1027 }
1028 }
1029 }
1030 }
1031
1032 MOZ_ASSERT(!cx->isExceptionPending());
1033 return true;
1034 }
1035
FormatWasmFrame(JSContext * cx,const FrameIter & iter,Sprinter & sp,int num)1036 static bool FormatWasmFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
1037 int num) {
1038 UniqueChars nameStr;
1039 if (JSAtom* functionDisplayAtom = iter.maybeFunctionDisplayAtom()) {
1040 nameStr = StringToNewUTF8CharsZ(cx, *functionDisplayAtom);
1041 if (!nameStr) {
1042 return false;
1043 }
1044 }
1045
1046 if (!sp.printf("%d %s()", num, nameStr ? nameStr.get() : "<wasm-function>")) {
1047 return false;
1048 }
1049
1050 if (!sp.printf(" [\"%s\":wasm-function[%u]:0x%x]\n",
1051 iter.filename() ? iter.filename() : "<unknown>",
1052 iter.wasmFuncIndex(), iter.wasmBytecodeOffset())) {
1053 return false;
1054 }
1055
1056 MOZ_ASSERT(!cx->isExceptionPending());
1057 return true;
1058 }
1059
FormatStackDump(JSContext * cx,bool showArgs,bool showLocals,bool showThisProps)1060 JS_FRIEND_API JS::UniqueChars JS::FormatStackDump(JSContext* cx, bool showArgs,
1061 bool showLocals,
1062 bool showThisProps) {
1063 int num = 0;
1064
1065 Sprinter sp(cx);
1066 if (!sp.init()) {
1067 return nullptr;
1068 }
1069
1070 for (AllFramesIter i(cx); !i.done(); ++i) {
1071 bool ok = i.hasScript() ? FormatFrame(cx, i, sp, num, showArgs, showLocals,
1072 showThisProps)
1073 : FormatWasmFrame(cx, i, sp, num);
1074 if (!ok) {
1075 return nullptr;
1076 }
1077 num++;
1078 }
1079
1080 if (num == 0) {
1081 if (!sp.put("JavaScript stack is empty\n")) {
1082 return nullptr;
1083 }
1084 }
1085
1086 return sp.release();
1087 }
1088
ForceLexicalInitialization(JSContext * cx,HandleObject obj)1089 extern JS_FRIEND_API bool JS::ForceLexicalInitialization(JSContext* cx,
1090 HandleObject obj) {
1091 AssertHeapIsIdle();
1092 CHECK_THREAD(cx);
1093 cx->check(obj);
1094
1095 bool initializedAny = false;
1096 NativeObject* nobj = &obj->as<NativeObject>();
1097
1098 for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
1099 Shape* s = &r.front();
1100 Value v = nobj->getSlot(s->slot());
1101 if (s->isDataProperty() && v.isMagic() &&
1102 v.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
1103 nobj->setSlot(s->slot(), UndefinedValue());
1104 initializedAny = true;
1105 }
1106 }
1107 return initializedAny;
1108 }
1109
IsGCPoisoning()1110 extern JS_FRIEND_API int JS::IsGCPoisoning() {
1111 #ifdef JS_GC_POISONING
1112 return !js::gDisablePoisoning;
1113 #else
1114 return false;
1115 #endif
1116 }
1117
1118 struct DumpHeapTracer final : public JS::CallbackTracer, public WeakMapTracer {
1119 const char* prefix;
1120 FILE* output;
1121 mozilla::MallocSizeOf mallocSizeOf;
1122
DumpHeapTracerDumpHeapTracer1123 DumpHeapTracer(FILE* fp, JSContext* cx, mozilla::MallocSizeOf mallocSizeOf)
1124 : JS::CallbackTracer(cx, DoNotTraceWeakMaps),
1125 js::WeakMapTracer(cx->runtime()),
1126 prefix(""),
1127 output(fp),
1128 mallocSizeOf(mallocSizeOf) {}
1129
1130 private:
traceDumpHeapTracer1131 void trace(JSObject* map, JS::GCCellPtr key, JS::GCCellPtr value) override {
1132 JSObject* kdelegate = nullptr;
1133 if (key.is<JSObject>()) {
1134 kdelegate = UncheckedUnwrapWithoutExpose(&key.as<JSObject>());
1135 }
1136
1137 fprintf(output, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n", map,
1138 key.asCell(), kdelegate, value.asCell());
1139 }
1140
1141 bool onChild(const JS::GCCellPtr& thing) override;
1142 };
1143
MarkDescriptor(gc::Cell * thing)1144 static char MarkDescriptor(gc::Cell* thing) {
1145 gc::TenuredCell* cell = &thing->asTenured();
1146 if (cell->isMarkedBlack()) {
1147 return 'B';
1148 }
1149 if (cell->isMarkedGray()) {
1150 return 'G';
1151 }
1152 if (cell->isMarkedAny()) {
1153 return 'X';
1154 }
1155 return 'W';
1156 }
1157
DumpHeapVisitZone(JSRuntime * rt,void * data,Zone * zone)1158 static void DumpHeapVisitZone(JSRuntime* rt, void* data, Zone* zone) {
1159 DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
1160 fprintf(dtrc->output, "# zone %p\n", (void*)zone);
1161 }
1162
DumpHeapVisitRealm(JSContext * cx,void * data,Handle<Realm * > realm)1163 static void DumpHeapVisitRealm(JSContext* cx, void* data,
1164 Handle<Realm*> realm) {
1165 char name[1024];
1166 if (auto nameCallback = cx->runtime()->realmNameCallback) {
1167 nameCallback(cx, realm, name, sizeof(name));
1168 } else {
1169 strcpy(name, "<unknown>");
1170 }
1171
1172 DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
1173 fprintf(dtrc->output, "# realm %s [in compartment %p, zone %p]\n", name,
1174 (void*)realm->compartment(), (void*)realm->zone());
1175 }
1176
DumpHeapVisitArena(JSRuntime * rt,void * data,gc::Arena * arena,JS::TraceKind traceKind,size_t thingSize)1177 static void DumpHeapVisitArena(JSRuntime* rt, void* data, gc::Arena* arena,
1178 JS::TraceKind traceKind, size_t thingSize) {
1179 DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
1180 fprintf(dtrc->output, "# arena allockind=%u size=%u\n",
1181 unsigned(arena->getAllocKind()), unsigned(thingSize));
1182 }
1183
DumpHeapVisitCell(JSRuntime * rt,void * data,JS::GCCellPtr cellptr,size_t thingSize)1184 static void DumpHeapVisitCell(JSRuntime* rt, void* data, JS::GCCellPtr cellptr,
1185 size_t thingSize) {
1186 DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
1187 char cellDesc[1024 * 32];
1188 JS_GetTraceThingInfo(cellDesc, sizeof(cellDesc), dtrc, cellptr.asCell(),
1189 cellptr.kind(), true);
1190
1191 fprintf(dtrc->output, "%p %c %s", cellptr.asCell(),
1192 MarkDescriptor(cellptr.asCell()), cellDesc);
1193 if (dtrc->mallocSizeOf) {
1194 auto size = JS::ubi::Node(cellptr).size(dtrc->mallocSizeOf);
1195 fprintf(dtrc->output, " SIZE:: %" PRIu64 "\n", size);
1196 } else {
1197 fprintf(dtrc->output, "\n");
1198 }
1199
1200 js::TraceChildren(dtrc, cellptr.asCell(), cellptr.kind());
1201 }
1202
onChild(const JS::GCCellPtr & thing)1203 bool DumpHeapTracer::onChild(const JS::GCCellPtr& thing) {
1204 if (gc::IsInsideNursery(thing.asCell())) {
1205 return true;
1206 }
1207
1208 char buffer[1024];
1209 getTracingEdgeName(buffer, sizeof(buffer));
1210 fprintf(output, "%s%p %c %s\n", prefix, thing.asCell(),
1211 MarkDescriptor(thing.asCell()), buffer);
1212 return true;
1213 }
1214
DumpHeap(JSContext * cx,FILE * fp,js::DumpHeapNurseryBehaviour nurseryBehaviour,mozilla::MallocSizeOf mallocSizeOf)1215 void js::DumpHeap(JSContext* cx, FILE* fp,
1216 js::DumpHeapNurseryBehaviour nurseryBehaviour,
1217 mozilla::MallocSizeOf mallocSizeOf) {
1218 if (nurseryBehaviour == js::CollectNurseryBeforeDump) {
1219 cx->runtime()->gc.evictNursery(JS::GCReason::API);
1220 }
1221
1222 DumpHeapTracer dtrc(fp, cx, mallocSizeOf);
1223
1224 fprintf(dtrc.output, "# Roots.\n");
1225 TraceRuntimeWithoutEviction(&dtrc);
1226
1227 fprintf(dtrc.output, "# Weak maps.\n");
1228 WeakMapBase::traceAllMappings(&dtrc);
1229
1230 fprintf(dtrc.output, "==========\n");
1231
1232 dtrc.prefix = "> ";
1233 IterateHeapUnbarriered(cx, &dtrc, DumpHeapVisitZone, DumpHeapVisitRealm,
1234 DumpHeapVisitArena, DumpHeapVisitCell);
1235
1236 fflush(dtrc.output);
1237 }
1238
NotifyGCRootsRemoved(JSContext * cx)1239 JS_FRIEND_API void JS::NotifyGCRootsRemoved(JSContext* cx) {
1240 cx->runtime()->gc.notifyRootsRemoved();
1241 }
1242
GetAnyRealmInZone(JS::Zone * zone)1243 JS_FRIEND_API JS::Realm* js::GetAnyRealmInZone(JS::Zone* zone) {
1244 if (zone->isAtomsZone()) {
1245 return nullptr;
1246 }
1247
1248 RealmsInZoneIter realm(zone);
1249 MOZ_ASSERT(!realm.done());
1250 return realm.get();
1251 }
1252
IsSharableCompartment(JS::Compartment * comp)1253 JS_FRIEND_API bool js::IsSharableCompartment(JS::Compartment* comp) {
1254 // If this compartment has nuked outgoing wrappers (because all its globals
1255 // got nuked), we won't be able to create any useful CCWs out of it in the
1256 // future, and so we shouldn't use it for any new globals.
1257 if (comp->nukedOutgoingWrappers) {
1258 return false;
1259 }
1260
1261 // If this compartment has no live globals, it might be in the middle of being
1262 // GCed. Don't create any new Realms inside. There's no point to doing that
1263 // anyway, since the idea would be to avoid CCWs from existing Realms in the
1264 // compartment to the new Realm, and there are no existing Realms.
1265 if (!CompartmentHasLiveGlobal(comp)) {
1266 return false;
1267 }
1268
1269 // Good to go.
1270 return true;
1271 }
1272
GetTestingFunctions(JSContext * cx)1273 JS_FRIEND_API JSObject* js::GetTestingFunctions(JSContext* cx) {
1274 RootedObject obj(cx, JS_NewPlainObject(cx));
1275 if (!obj) {
1276 return nullptr;
1277 }
1278
1279 if (!DefineTestingFunctions(cx, obj, false, false)) {
1280 return nullptr;
1281 }
1282
1283 return obj;
1284 }
1285
SetDOMCallbacks(JSContext * cx,const DOMCallbacks * callbacks)1286 JS_FRIEND_API void js::SetDOMCallbacks(JSContext* cx,
1287 const DOMCallbacks* callbacks) {
1288 cx->runtime()->DOMcallbacks = callbacks;
1289 }
1290
GetDOMCallbacks(JSContext * cx)1291 JS_FRIEND_API const DOMCallbacks* js::GetDOMCallbacks(JSContext* cx) {
1292 return cx->runtime()->DOMcallbacks;
1293 }
1294
1295 static const void* gDOMProxyHandlerFamily = nullptr;
1296 static DOMProxyShadowsCheck gDOMProxyShadowsCheck;
1297 static const void* gDOMRemoteProxyHandlerFamily = nullptr;
1298
SetDOMProxyInformation(const void * domProxyHandlerFamily,DOMProxyShadowsCheck domProxyShadowsCheck,const void * domRemoteProxyHandlerFamily)1299 JS_FRIEND_API void js::SetDOMProxyInformation(
1300 const void* domProxyHandlerFamily,
1301 DOMProxyShadowsCheck domProxyShadowsCheck,
1302 const void* domRemoteProxyHandlerFamily) {
1303 gDOMProxyHandlerFamily = domProxyHandlerFamily;
1304 gDOMProxyShadowsCheck = domProxyShadowsCheck;
1305 gDOMRemoteProxyHandlerFamily = domRemoteProxyHandlerFamily;
1306 }
1307
GetDOMProxyHandlerFamily()1308 const void* js::GetDOMProxyHandlerFamily() { return gDOMProxyHandlerFamily; }
1309
GetDOMProxyShadowsCheck()1310 DOMProxyShadowsCheck js::GetDOMProxyShadowsCheck() {
1311 return gDOMProxyShadowsCheck;
1312 }
1313
GetDOMRemoteProxyHandlerFamily()1314 const void* js::GetDOMRemoteProxyHandlerFamily() {
1315 return gDOMRemoteProxyHandlerFamily;
1316 }
1317
IsDOMRemoteProxyObject(JSObject * object)1318 JS_FRIEND_API bool js::IsDOMRemoteProxyObject(JSObject* object) {
1319 return js::IsProxy(object) && js::GetProxyHandler(object)->family() ==
1320 js::GetDOMRemoteProxyHandlerFamily();
1321 }
1322
1323 static XrayJitInfo* gXrayJitInfo = nullptr;
1324
SetXrayJitInfo(XrayJitInfo * info)1325 JS_FRIEND_API void js::SetXrayJitInfo(XrayJitInfo* info) {
1326 gXrayJitInfo = info;
1327 }
1328
GetXrayJitInfo()1329 XrayJitInfo* js::GetXrayJitInfo() { return gXrayJitInfo; }
1330
PrepareScriptEnvironmentAndInvoke(JSContext * cx,HandleObject global,ScriptEnvironmentPreparer::Closure & closure)1331 JS_FRIEND_API void js::PrepareScriptEnvironmentAndInvoke(
1332 JSContext* cx, HandleObject global,
1333 ScriptEnvironmentPreparer::Closure& closure) {
1334 MOZ_ASSERT(!cx->isExceptionPending());
1335 MOZ_ASSERT(global->is<GlobalObject>());
1336
1337 MOZ_RELEASE_ASSERT(
1338 cx->runtime()->scriptEnvironmentPreparer,
1339 "Embedding needs to set a scriptEnvironmentPreparer callback");
1340
1341 cx->runtime()->scriptEnvironmentPreparer->invoke(global, closure);
1342 }
1343
SetScriptEnvironmentPreparer(JSContext * cx,ScriptEnvironmentPreparer * preparer)1344 JS_FRIEND_API void js::SetScriptEnvironmentPreparer(
1345 JSContext* cx, ScriptEnvironmentPreparer* preparer) {
1346 cx->runtime()->scriptEnvironmentPreparer = preparer;
1347 }
1348
SetCTypesActivityCallback(JSContext * cx,CTypesActivityCallback cb)1349 JS_FRIEND_API void js::SetCTypesActivityCallback(JSContext* cx,
1350 CTypesActivityCallback cb) {
1351 cx->runtime()->ctypesActivityCallback = cb;
1352 }
1353
AutoCTypesActivityCallback(JSContext * cx,js::CTypesActivityType beginType,js::CTypesActivityType endType MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)1354 js::AutoCTypesActivityCallback::AutoCTypesActivityCallback(
1355 JSContext* cx, js::CTypesActivityType beginType,
1356 js::CTypesActivityType endType MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
1357 : cx(cx),
1358 callback(cx->runtime()->ctypesActivityCallback),
1359 endType(endType) {
1360 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1361
1362 if (callback) {
1363 callback(cx, beginType);
1364 }
1365 }
1366
SetAllocationMetadataBuilder(JSContext * cx,const AllocationMetadataBuilder * callback)1367 JS_FRIEND_API void js::SetAllocationMetadataBuilder(
1368 JSContext* cx, const AllocationMetadataBuilder* callback) {
1369 cx->realm()->setAllocationMetadataBuilder(callback);
1370 }
1371
GetAllocationMetadata(JSObject * obj)1372 JS_FRIEND_API JSObject* js::GetAllocationMetadata(JSObject* obj) {
1373 ObjectWeakMap* map = ObjectRealm::get(obj).objectMetadataTable.get();
1374 if (map) {
1375 return map->lookup(obj);
1376 }
1377 return nullptr;
1378 }
1379
ReportIsNotFunction(JSContext * cx,HandleValue v)1380 JS_FRIEND_API bool js::ReportIsNotFunction(JSContext* cx, HandleValue v) {
1381 cx->check(v);
1382 return ReportIsNotFunction(cx, v, -1);
1383 }
1384
1385 #ifdef DEBUG
HasObjectMovedOp(JSObject * obj)1386 bool js::HasObjectMovedOp(JSObject* obj) {
1387 return !!GetObjectClass(obj)->extObjectMovedOp();
1388 }
1389 #endif
1390
ForwardToNative(JSContext * cx,JSNative native,const CallArgs & args)1391 JS_FRIEND_API bool js::ForwardToNative(JSContext* cx, JSNative native,
1392 const CallArgs& args) {
1393 return native(cx, args.length(), args.base());
1394 }
1395
ConvertArgsToArray(JSContext * cx,const CallArgs & args)1396 JS_FRIEND_API JSObject* js::ConvertArgsToArray(JSContext* cx,
1397 const CallArgs& args) {
1398 RootedObject argsArray(cx,
1399 NewDenseCopiedArray(cx, args.length(), args.array()));
1400 return argsArray;
1401 }
1402
GetPropertyNameFromPC(JSScript * script,jsbytecode * pc)1403 JS_FRIEND_API JSAtom* js::GetPropertyNameFromPC(JSScript* script,
1404 jsbytecode* pc) {
1405 if (!IsGetPropPC(pc) && !IsSetPropPC(pc)) {
1406 return nullptr;
1407 }
1408 return script->getName(pc);
1409 }
1410
SetWindowProxyClass(JSContext * cx,const JSClass * clasp)1411 JS_FRIEND_API void js::SetWindowProxyClass(JSContext* cx,
1412 const JSClass* clasp) {
1413 MOZ_ASSERT(!cx->runtime()->maybeWindowProxyClass());
1414 cx->runtime()->setWindowProxyClass(clasp);
1415 }
1416
SetWindowProxy(JSContext * cx,HandleObject global,HandleObject windowProxy)1417 JS_FRIEND_API void js::SetWindowProxy(JSContext* cx, HandleObject global,
1418 HandleObject windowProxy) {
1419 AssertHeapIsIdle();
1420 CHECK_THREAD(cx);
1421
1422 cx->check(global, windowProxy);
1423 MOZ_ASSERT(IsWindowProxy(windowProxy));
1424
1425 GlobalObject& globalObj = global->as<GlobalObject>();
1426 globalObj.setWindowProxy(windowProxy);
1427 globalObj.lexicalEnvironment().setWindowProxyThisValue(windowProxy);
1428 }
1429
ToWindowIfWindowProxy(JSObject * obj)1430 JS_FRIEND_API JSObject* js::ToWindowIfWindowProxy(JSObject* obj) {
1431 if (IsWindowProxy(obj)) {
1432 return &obj->nonCCWGlobal();
1433 }
1434 return obj;
1435 }
1436
ToWindowProxyIfWindowSlow(JSObject * obj)1437 JS_FRIEND_API JSObject* js::detail::ToWindowProxyIfWindowSlow(JSObject* obj) {
1438 if (JSObject* windowProxy = obj->as<GlobalObject>().maybeWindowProxy()) {
1439 return windowProxy;
1440 }
1441 return obj;
1442 }
1443
IsWindowProxy(JSObject * obj)1444 JS_FRIEND_API bool js::IsWindowProxy(JSObject* obj) {
1445 // Note: simply checking `obj == obj->global().windowProxy()` is not
1446 // sufficient: we may have transplanted the window proxy with a CCW.
1447 // Check the Class to ensure we really have a window proxy.
1448 return obj->getClass() ==
1449 obj->runtimeFromAnyThread()->maybeWindowProxyClass();
1450 }
1451
IsWindowSlow(JSObject * obj)1452 JS_FRIEND_API bool js::detail::IsWindowSlow(JSObject* obj) {
1453 return obj->as<GlobalObject>().maybeWindowProxy();
1454 }
1455
AutoAssertNoContentJS(JSContext * cx)1456 AutoAssertNoContentJS::AutoAssertNoContentJS(JSContext* cx)
1457 : context_(cx), prevAllowContentJS_(cx->runtime()->allowContentJS_) {
1458 cx->runtime()->allowContentJS_ = false;
1459 }
1460
~AutoAssertNoContentJS()1461 AutoAssertNoContentJS::~AutoAssertNoContentJS() {
1462 context_->runtime()->allowContentJS_ = prevAllowContentJS_;
1463 }
1464
EnableAccessValidation(JSContext * cx,bool enabled)1465 JS_FRIEND_API void js::EnableAccessValidation(JSContext* cx, bool enabled) {
1466 cx->enableAccessValidation = enabled;
1467 }
1468
EnableCodeCoverage()1469 JS_FRIEND_API void js::EnableCodeCoverage() { js::coverage::EnableLCov(); }
1470
SetRealmValidAccessPtr(JSContext * cx,JS::HandleObject global,bool * accessp)1471 JS_FRIEND_API void js::SetRealmValidAccessPtr(JSContext* cx,
1472 JS::HandleObject global,
1473 bool* accessp) {
1474 MOZ_ASSERT(global->is<GlobalObject>());
1475 global->as<GlobalObject>().realm()->setValidAccessPtr(accessp);
1476 }
1477
SystemZoneAvailable(JSContext * cx)1478 JS_FRIEND_API bool js::SystemZoneAvailable(JSContext* cx) { return true; }
1479
1480 static LogCtorDtor sLogCtor = nullptr;
1481 static LogCtorDtor sLogDtor = nullptr;
1482
SetLogCtorDtorFunctions(LogCtorDtor ctor,LogCtorDtor dtor)1483 JS_FRIEND_API void js::SetLogCtorDtorFunctions(LogCtorDtor ctor,
1484 LogCtorDtor dtor) {
1485 MOZ_ASSERT(!sLogCtor && !sLogDtor);
1486 MOZ_ASSERT(ctor && dtor);
1487 sLogCtor = ctor;
1488 sLogDtor = dtor;
1489 }
1490
LogCtor(void * self,const char * type,uint32_t sz)1491 JS_FRIEND_API void js::LogCtor(void* self, const char* type, uint32_t sz) {
1492 if (LogCtorDtor fun = sLogCtor) {
1493 fun(self, type, sz);
1494 }
1495 }
1496
LogDtor(void * self,const char * type,uint32_t sz)1497 JS_FRIEND_API void js::LogDtor(void* self, const char* type, uint32_t sz) {
1498 if (LogCtorDtor fun = sLogDtor) {
1499 fun(self, type, sz);
1500 }
1501 }
1502
MaybeGetScriptPrivate(JSObject * object)1503 JS_FRIEND_API JS::Value js::MaybeGetScriptPrivate(JSObject* object) {
1504 if (!object->is<ScriptSourceObject>()) {
1505 return UndefinedValue();
1506 }
1507
1508 return object->as<ScriptSourceObject>().canonicalPrivate();
1509 }
1510
GetGCHeapUsageForObjectZone(JSObject * obj)1511 JS_FRIEND_API uint64_t js::GetGCHeapUsageForObjectZone(JSObject* obj) {
1512 return obj->zone()->gcHeapSize.bytes();
1513 }
1514
1515 #ifdef DEBUG
RuntimeIsBeingDestroyed()1516 JS_FRIEND_API bool js::RuntimeIsBeingDestroyed() {
1517 JSRuntime* runtime = TlsContext.get()->runtime();
1518 MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime));
1519 return runtime->isBeingDestroyed();
1520 }
1521 #endif
1522
1523 // No-op implementations of public API that would depend on --with-intl-api
1524
1525 #ifndef JS_HAS_INTL_API
1526
IntlNotEnabled(JSContext * cx)1527 static bool IntlNotEnabled(JSContext* cx) {
1528 JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
1529 JSMSG_SUPPORT_NOT_ENABLED, "Intl");
1530 return false;
1531 }
1532
AddMozDateTimeFormatConstructor(JSContext * cx,JS::HandleObject intl)1533 bool js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::HandleObject intl) {
1534 return IntlNotEnabled(cx);
1535 }
1536
AddMozDisplayNamesConstructor(JSContext * cx,JS::HandleObject intl)1537 bool js::AddMozDisplayNamesConstructor(JSContext* cx, JS::HandleObject intl) {
1538 return IntlNotEnabled(cx);
1539 }
1540
AddDisplayNamesConstructor(JSContext * cx,JS::HandleObject intl)1541 bool js::AddDisplayNamesConstructor(JSContext* cx, JS::HandleObject intl) {
1542 return IntlNotEnabled(cx);
1543 }
1544
1545 #endif // !JS_HAS_INTL_API
1546
GetObjectZoneFromAnyThread(const JSObject * obj)1547 JS_FRIEND_API JS::Zone* js::GetObjectZoneFromAnyThread(const JSObject* obj) {
1548 return MaybeForwarded(obj)->zoneFromAnyThread();
1549 }
1550