Line data Source code
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 "vm/TypeInference-inl.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/IntegerPrintfMacros.h"
11 : #include "mozilla/MemoryReporting.h"
12 : #include "mozilla/PodOperations.h"
13 : #include "mozilla/SizePrintfMacros.h"
14 : #include "mozilla/Sprintf.h"
15 :
16 : #include "jsapi.h"
17 : #include "jscntxt.h"
18 : #include "jsgc.h"
19 : #include "jshashutil.h"
20 : #include "jsobj.h"
21 : #include "jsprf.h"
22 : #include "jsscript.h"
23 : #include "jsstr.h"
24 :
25 : #include "gc/Marking.h"
26 : #include "jit/BaselineJIT.h"
27 : #include "jit/CompileInfo.h"
28 : #include "jit/Ion.h"
29 : #include "jit/IonAnalysis.h"
30 : #include "jit/JitCompartment.h"
31 : #include "jit/OptimizationTracking.h"
32 : #include "js/MemoryMetrics.h"
33 : #include "vm/HelperThreads.h"
34 : #include "vm/Opcodes.h"
35 : #include "vm/Shape.h"
36 : #include "vm/Time.h"
37 : #include "vm/UnboxedObject.h"
38 :
39 : #include "jsatominlines.h"
40 : #include "jsscriptinlines.h"
41 :
42 : #include "vm/NativeObject-inl.h"
43 :
44 : using namespace js;
45 : using namespace js::gc;
46 :
47 : using mozilla::DebugOnly;
48 : using mozilla::Maybe;
49 : using mozilla::PodArrayZero;
50 : using mozilla::PodCopy;
51 : using mozilla::PodZero;
52 :
53 : #ifdef DEBUG
54 :
55 : static inline jsid
56 16962 : id___proto__(JSContext* cx)
57 : {
58 16962 : return NameToId(cx->names().proto);
59 : }
60 :
61 : static inline jsid
62 16962 : id_constructor(JSContext* cx)
63 : {
64 16962 : return NameToId(cx->names().constructor);
65 : }
66 :
67 : static inline jsid
68 16962 : id_caller(JSContext* cx)
69 : {
70 16962 : return NameToId(cx->names().caller);
71 : }
72 :
73 : const char*
74 0 : js::TypeIdStringImpl(jsid id)
75 : {
76 0 : if (JSID_IS_VOID(id))
77 0 : return "(index)";
78 0 : if (JSID_IS_EMPTY(id))
79 0 : return "(new)";
80 0 : if (JSID_IS_SYMBOL(id))
81 0 : return "(symbol)";
82 : static char bufs[4][100];
83 : static unsigned which = 0;
84 0 : which = (which + 1) & 3;
85 0 : PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
86 0 : return bufs[which];
87 : }
88 :
89 : #endif
90 :
91 : /////////////////////////////////////////////////////////////////////
92 : // Logging
93 : /////////////////////////////////////////////////////////////////////
94 :
95 : /* static */ const char*
96 0 : TypeSet::NonObjectTypeString(TypeSet::Type type)
97 : {
98 0 : if (type.isPrimitive()) {
99 0 : switch (type.primitive()) {
100 : case JSVAL_TYPE_UNDEFINED:
101 0 : return "void";
102 : case JSVAL_TYPE_NULL:
103 0 : return "null";
104 : case JSVAL_TYPE_BOOLEAN:
105 0 : return "bool";
106 : case JSVAL_TYPE_INT32:
107 0 : return "int";
108 : case JSVAL_TYPE_DOUBLE:
109 0 : return "float";
110 : case JSVAL_TYPE_STRING:
111 0 : return "string";
112 : case JSVAL_TYPE_SYMBOL:
113 0 : return "symbol";
114 : case JSVAL_TYPE_MAGIC:
115 0 : return "lazyargs";
116 : default:
117 0 : MOZ_CRASH("Bad type");
118 : }
119 : }
120 0 : if (type.isUnknown())
121 0 : return "unknown";
122 :
123 0 : MOZ_ASSERT(type.isAnyObject());
124 0 : return "object";
125 : }
126 :
127 : /* static */ const char*
128 0 : TypeSet::TypeString(TypeSet::Type type)
129 : {
130 0 : if (type.isPrimitive() || type.isUnknown() || type.isAnyObject())
131 0 : return NonObjectTypeString(type);
132 :
133 : static char bufs[4][40];
134 : static unsigned which = 0;
135 0 : which = (which + 1) & 3;
136 :
137 0 : if (type.isSingleton()) {
138 0 : JSObject* singleton = type.singletonNoBarrier();
139 0 : snprintf(bufs[which], 40, "<%s %#" PRIxPTR ">",
140 0 : singleton->getClass()->name, uintptr_t(singleton));
141 : } else {
142 0 : snprintf(bufs[which], 40, "[%s * %#" PRIxPTR "]", type.groupNoBarrier()->clasp()->name, uintptr_t(type.groupNoBarrier()));
143 : }
144 :
145 0 : return bufs[which];
146 : }
147 :
148 : /* static */ const char*
149 0 : TypeSet::ObjectGroupString(ObjectGroup* group)
150 : {
151 0 : return TypeString(TypeSet::ObjectType(group));
152 : }
153 :
154 : #ifdef DEBUG
155 :
156 : bool
157 109891 : js::InferSpewActive(SpewChannel channel)
158 : {
159 : static bool active[SPEW_COUNT];
160 : static bool checked = false;
161 109891 : if (!checked) {
162 3 : checked = true;
163 3 : PodArrayZero(active);
164 3 : const char* env = getenv("INFERFLAGS");
165 3 : if (!env)
166 3 : return false;
167 0 : if (strstr(env, "ops"))
168 0 : active[ISpewOps] = true;
169 0 : if (strstr(env, "result"))
170 0 : active[ISpewResult] = true;
171 0 : if (strstr(env, "full")) {
172 0 : for (unsigned i = 0; i < SPEW_COUNT; i++)
173 0 : active[i] = true;
174 : }
175 : }
176 109888 : return active[channel];
177 : }
178 :
179 0 : static bool InferSpewColorable()
180 : {
181 : /* Only spew colors on xterm-color to not screw up emacs. */
182 : static bool colorable = false;
183 : static bool checked = false;
184 0 : if (!checked) {
185 0 : checked = true;
186 0 : const char* env = getenv("TERM");
187 0 : if (!env)
188 0 : return false;
189 0 : if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
190 0 : colorable = true;
191 : }
192 0 : return colorable;
193 : }
194 :
195 : const char*
196 0 : js::InferSpewColorReset()
197 : {
198 0 : if (!InferSpewColorable())
199 0 : return "";
200 0 : return "\x1b[0m";
201 : }
202 :
203 : const char*
204 0 : js::InferSpewColor(TypeConstraint* constraint)
205 : {
206 : /* Type constraints are printed out using foreground colors. */
207 : static const char * const colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m",
208 : "\x1b[34m", "\x1b[35m", "\x1b[36m",
209 : "\x1b[37m" };
210 0 : if (!InferSpewColorable())
211 0 : return "";
212 0 : return colors[DefaultHasher<TypeConstraint*>::hash(constraint) % 7];
213 : }
214 :
215 : const char*
216 0 : js::InferSpewColor(TypeSet* types)
217 : {
218 : /* Type sets are printed out using bold colors. */
219 : static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
220 : "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
221 : "\x1b[1;37m" };
222 0 : if (!InferSpewColorable())
223 0 : return "";
224 0 : return colors[DefaultHasher<TypeSet*>::hash(types) % 7];
225 : }
226 :
227 : #ifdef DEBUG
228 : void
229 0 : js::InferSpewImpl(const char* fmt, ...)
230 : {
231 : va_list ap;
232 0 : va_start(ap, fmt);
233 0 : fprintf(stderr, "[infer] ");
234 0 : vfprintf(stderr, fmt, ap);
235 0 : fprintf(stderr, "\n");
236 0 : va_end(ap);
237 0 : }
238 : #endif
239 :
240 : MOZ_NORETURN MOZ_COLD static void
241 : MOZ_FORMAT_PRINTF(2, 3)
242 0 : TypeFailure(JSContext* cx, const char* fmt, ...)
243 : {
244 : char msgbuf[1024]; /* Larger error messages will be truncated */
245 : char errbuf[1024];
246 :
247 : va_list ap;
248 0 : va_start(ap, fmt);
249 0 : VsprintfLiteral(errbuf, fmt, ap);
250 0 : va_end(ap);
251 :
252 0 : SprintfLiteral(msgbuf, "[infer failure] %s", errbuf);
253 :
254 : /* Dump type state, even if INFERFLAGS is unset. */
255 0 : PrintTypes(cx, cx->compartment(), true);
256 :
257 0 : MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
258 0 : MOZ_CRASH();
259 : }
260 :
261 : bool
262 22313 : js::ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value)
263 : {
264 : /*
265 : * Check the correctness of the type information in the object's property
266 : * against an actual value.
267 : */
268 22313 : if (!group->unknownProperties() && !value.isUndefined()) {
269 16962 : id = IdToTypeId(id);
270 :
271 : /* Watch for properties which inference does not monitor. */
272 16962 : if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
273 2174 : return true;
274 :
275 16962 : TypeSet::Type type = TypeSet::GetValueType(value);
276 :
277 31750 : AutoEnterAnalysis enter(cx);
278 :
279 : /*
280 : * We don't track types for properties inherited from prototypes which
281 : * haven't yet been accessed during analysis of the inheriting object.
282 : * Don't do the property instantiation now.
283 : */
284 16962 : TypeSet* types = group->maybeGetProperty(id);
285 16962 : if (!types)
286 0 : return true;
287 :
288 : // Type set guards might miss when an object's group changes and its
289 : // properties become unknown.
290 16962 : if (value.isObject()) {
291 9810 : if (types->unknownObject())
292 2103 : return true;
293 16472 : for (size_t i = 0; i < types->getObjectCount(); i++) {
294 8836 : if (TypeSet::ObjectKey* key = types->getObject(i)) {
295 8836 : if (key->unknownProperties())
296 71 : return true;
297 : }
298 : }
299 7636 : JSObject* obj = &value.toObject();
300 7636 : if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup())
301 0 : return true;
302 : }
303 :
304 14788 : if (!types->hasType(type)) {
305 0 : TypeFailure(cx, "Missing type in object %s %s: %s",
306 : TypeSet::ObjectGroupString(group), TypeIdString(id),
307 0 : TypeSet::TypeString(type));
308 : }
309 : }
310 20139 : return true;
311 : }
312 :
313 : #endif
314 :
315 :
316 : /////////////////////////////////////////////////////////////////////
317 : // TypeSet
318 : /////////////////////////////////////////////////////////////////////
319 :
320 2006 : TemporaryTypeSet::TemporaryTypeSet(LifoAlloc* alloc, Type type)
321 : {
322 2006 : if (type.isUnknown()) {
323 19 : flags |= TYPE_FLAG_BASE_MASK;
324 1987 : } else if (type.isPrimitive()) {
325 1317 : flags = PrimitiveTypeFlag(type.primitive());
326 1317 : if (flags == TYPE_FLAG_DOUBLE)
327 0 : flags |= TYPE_FLAG_INT32;
328 670 : } else if (type.isAnyObject()) {
329 3 : flags |= TYPE_FLAG_ANYOBJECT;
330 667 : } else if (type.isGroup() && type.group()->unknownProperties()) {
331 6 : flags |= TYPE_FLAG_ANYOBJECT;
332 : } else {
333 661 : setBaseObjectCount(1);
334 661 : objectSet = reinterpret_cast<ObjectKey**>(type.objectKey());
335 :
336 661 : if (type.isGroup()) {
337 86 : ObjectGroup* ngroup = type.group();
338 86 : if (ngroup->newScript() && ngroup->newScript()->initializedGroup())
339 0 : addType(ObjectType(ngroup->newScript()->initializedGroup()), alloc);
340 : }
341 : }
342 2006 : }
343 :
344 : bool
345 2887 : TypeSet::mightBeMIRType(jit::MIRType type) const
346 : {
347 2887 : if (unknown())
348 0 : return true;
349 :
350 2887 : if (type == jit::MIRType::Object)
351 1233 : return unknownObject() || baseObjectCount() != 0;
352 :
353 1654 : switch (type) {
354 : case jit::MIRType::Undefined:
355 122 : return baseFlags() & TYPE_FLAG_UNDEFINED;
356 : case jit::MIRType::Null:
357 63 : return baseFlags() & TYPE_FLAG_NULL;
358 : case jit::MIRType::Boolean:
359 41 : return baseFlags() & TYPE_FLAG_BOOLEAN;
360 : case jit::MIRType::Int32:
361 43 : return baseFlags() & TYPE_FLAG_INT32;
362 : case jit::MIRType::Float32: // Fall through, there's no JSVAL for Float32.
363 : case jit::MIRType::Double:
364 256 : return baseFlags() & TYPE_FLAG_DOUBLE;
365 : case jit::MIRType::String:
366 490 : return baseFlags() & TYPE_FLAG_STRING;
367 : case jit::MIRType::Symbol:
368 132 : return baseFlags() & TYPE_FLAG_SYMBOL;
369 : case jit::MIRType::MagicOptimizedArguments:
370 169 : return baseFlags() & TYPE_FLAG_LAZYARGS;
371 : case jit::MIRType::MagicHole:
372 : case jit::MIRType::MagicIsConstructing:
373 : // These magic constants do not escape to script and are not observed
374 : // in the type sets.
375 : //
376 : // The reason we can return false here is subtle: if Ion is asking the
377 : // type set if it has seen such a magic constant, then the MIR in
378 : // question is the most generic type, MIRType::Value. A magic constant
379 : // could only be emitted by a MIR of MIRType::Value if that MIR is a
380 : // phi, and we check that different magic constants do not flow to the
381 : // same join point in GuessPhiType.
382 338 : return false;
383 : default:
384 0 : MOZ_CRASH("Bad MIR type");
385 : }
386 : }
387 :
388 : bool
389 6 : TypeSet::objectsAreSubset(TypeSet* other)
390 : {
391 6 : if (other->unknownObject())
392 0 : return true;
393 :
394 6 : if (unknownObject())
395 6 : return false;
396 :
397 0 : for (unsigned i = 0; i < getObjectCount(); i++) {
398 0 : ObjectKey* key = getObject(i);
399 0 : if (!key)
400 0 : continue;
401 0 : if (!other->hasType(ObjectType(key)))
402 0 : return false;
403 : }
404 :
405 0 : return true;
406 : }
407 :
408 : bool
409 5657 : TypeSet::isSubset(const TypeSet* other) const
410 : {
411 5657 : if ((baseFlags() & other->baseFlags()) != baseFlags())
412 312 : return false;
413 :
414 5345 : if (unknownObject()) {
415 155 : MOZ_ASSERT(other->unknownObject());
416 : } else {
417 5870 : for (unsigned i = 0; i < getObjectCount(); i++) {
418 703 : ObjectKey* key = getObject(i);
419 703 : if (!key)
420 0 : continue;
421 703 : if (!other->hasType(ObjectType(key)))
422 23 : return false;
423 : }
424 : }
425 :
426 5322 : return true;
427 : }
428 :
429 : bool
430 6 : TypeSet::objectsIntersect(const TypeSet* other) const
431 : {
432 6 : if (unknownObject() || other->unknownObject())
433 0 : return true;
434 :
435 10 : for (unsigned i = 0; i < getObjectCount(); i++) {
436 6 : ObjectKey* key = getObject(i);
437 6 : if (!key)
438 0 : continue;
439 6 : if (other->hasType(ObjectType(key)))
440 2 : return true;
441 : }
442 :
443 4 : return false;
444 : }
445 :
446 : template <class TypeListT>
447 : bool
448 12 : TypeSet::enumerateTypes(TypeListT* list) const
449 : {
450 : /* If any type is possible, there's no need to worry about specifics. */
451 12 : if (flags & TYPE_FLAG_UNKNOWN)
452 0 : return list->append(UnknownType());
453 :
454 : /* Enqueue type set members stored as bits. */
455 108 : for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
456 96 : if (flags & flag) {
457 10 : Type type = PrimitiveType(TypeFlagPrimitive(flag));
458 10 : if (!list->append(type))
459 0 : return false;
460 : }
461 : }
462 :
463 : /* If any object is possible, skip specifics. */
464 12 : if (flags & TYPE_FLAG_ANYOBJECT)
465 2 : return list->append(AnyObjectType());
466 :
467 : /* Enqueue specific object types. */
468 10 : unsigned count = getObjectCount();
469 13 : for (unsigned i = 0; i < count; i++) {
470 3 : ObjectKey* key = getObject(i);
471 3 : if (key) {
472 3 : if (!list->append(ObjectType(key)))
473 0 : return false;
474 : }
475 : }
476 :
477 10 : return true;
478 : }
479 :
480 : template bool TypeSet::enumerateTypes<TypeSet::TypeList>(TypeList* list) const;
481 : template bool TypeSet::enumerateTypes<jit::TempTypeList>(jit::TempTypeList* list) const;
482 :
483 : inline bool
484 0 : TypeSet::addTypesToConstraint(JSContext* cx, TypeConstraint* constraint)
485 : {
486 : /*
487 : * Build all types in the set into a vector before triggering the
488 : * constraint, as doing so may modify this type set.
489 : */
490 0 : TypeList types;
491 0 : if (!enumerateTypes(&types))
492 0 : return false;
493 :
494 0 : for (unsigned i = 0; i < types.length(); i++)
495 0 : constraint->newType(cx, this, types[i]);
496 :
497 0 : return true;
498 : }
499 :
500 : #ifdef DEBUG
501 : static inline bool
502 41742 : CompartmentsMatch(JSCompartment* a, JSCompartment* b)
503 : {
504 41742 : return !a || !b || a == b;
505 : }
506 : #endif
507 :
508 : bool
509 866 : ConstraintTypeSet::addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting)
510 : {
511 866 : checkMagic();
512 :
513 866 : if (!constraint) {
514 : /* OOM failure while constructing the constraint. */
515 0 : return false;
516 : }
517 :
518 866 : MOZ_RELEASE_ASSERT(cx->zone()->types.activeAnalysis);
519 866 : MOZ_ASSERT(CompartmentsMatch(maybeCompartment(), constraint->maybeCompartment()));
520 :
521 866 : InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
522 : InferSpewColor(this), this, InferSpewColorReset(),
523 : InferSpewColor(constraint), constraint, InferSpewColorReset(),
524 : constraint->kind());
525 :
526 866 : MOZ_ASSERT(constraint->next() == nullptr);
527 866 : constraint->setNext(constraintList_);
528 866 : constraintList_ = constraint;
529 :
530 866 : if (callExisting)
531 0 : return addTypesToConstraint(cx, constraint);
532 866 : return true;
533 : }
534 :
535 : void
536 1890 : TypeSet::clearObjects()
537 : {
538 1890 : setBaseObjectCount(0);
539 1890 : objectSet = nullptr;
540 1890 : }
541 :
542 : JSCompartment*
543 41742 : TypeSet::maybeCompartment()
544 : {
545 41742 : if (unknownObject())
546 165 : return nullptr;
547 :
548 41577 : unsigned objectCount = getObjectCount();
549 41577 : for (unsigned i = 0; i < objectCount; i++) {
550 2117 : ObjectKey* key = getObject(i);
551 2117 : if (!key)
552 0 : continue;
553 :
554 2117 : JSCompartment* comp = key->maybeCompartment();
555 2117 : if (comp)
556 2117 : return comp;
557 : }
558 :
559 39460 : return nullptr;
560 : }
561 :
562 : void
563 40876 : TypeSet::addType(Type type, LifoAlloc* alloc)
564 : {
565 40876 : MOZ_ASSERT(CompartmentsMatch(maybeCompartment(), type.maybeCompartment()));
566 :
567 40876 : if (unknown())
568 0 : return;
569 :
570 40876 : if (type.isUnknown()) {
571 234 : flags |= TYPE_FLAG_BASE_MASK;
572 234 : clearObjects();
573 234 : MOZ_ASSERT(unknown());
574 234 : return;
575 : }
576 :
577 40642 : if (type.isPrimitive()) {
578 27663 : TypeFlags flag = PrimitiveTypeFlag(type.primitive());
579 27663 : if (flags & flag)
580 0 : return;
581 :
582 : /* If we add float to a type set it is also considered to contain int. */
583 27663 : if (flag == TYPE_FLAG_DOUBLE)
584 742 : flag |= TYPE_FLAG_INT32;
585 :
586 27663 : flags |= flag;
587 27663 : return;
588 : }
589 :
590 12979 : if (flags & TYPE_FLAG_ANYOBJECT)
591 0 : return;
592 12979 : if (type.isAnyObject())
593 2 : goto unknownObject;
594 :
595 : {
596 12977 : uint32_t objectCount = baseObjectCount();
597 12977 : ObjectKey* key = type.objectKey();
598 : ObjectKey** pentry = TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey>
599 12977 : (*alloc, objectSet, objectCount, key);
600 12977 : if (!pentry)
601 137 : goto unknownObject;
602 12977 : if (*pentry)
603 12 : return;
604 12965 : *pentry = key;
605 :
606 12965 : setBaseObjectCount(objectCount);
607 :
608 : // Limit the number of objects we track. There is a different limit
609 : // depending on whether the set only contains DOM objects, which can
610 : // have many different classes and prototypes but are still optimizable
611 : // by IonMonkey.
612 12965 : if (objectCount >= TYPE_FLAG_OBJECT_COUNT_LIMIT) {
613 : JS_STATIC_ASSERT(TYPE_FLAG_DOMOBJECT_COUNT_LIMIT >= TYPE_FLAG_OBJECT_COUNT_LIMIT);
614 : // Examining the entire type set is only required when we first hit
615 : // the normal object limit.
616 137 : if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT) {
617 137 : for (unsigned i = 0; i < objectCount; i++) {
618 137 : const Class* clasp = getObjectClass(i);
619 137 : if (clasp && !clasp->isDOMClass())
620 137 : goto unknownObject;
621 : }
622 : }
623 :
624 : // Make sure the newly added object is also a DOM object.
625 0 : if (!key->clasp()->isDOMClass())
626 0 : goto unknownObject;
627 :
628 : // Limit the number of DOM objects.
629 0 : if (objectCount == TYPE_FLAG_DOMOBJECT_COUNT_LIMIT)
630 0 : goto unknownObject;
631 : }
632 : }
633 :
634 12828 : if (type.isGroup()) {
635 7447 : ObjectGroup* ngroup = type.group();
636 7447 : MOZ_ASSERT(!ngroup->singleton());
637 7447 : if (ngroup->unknownProperties())
638 1491 : goto unknownObject;
639 :
640 : // If we add a partially initialized group to a type set, add the
641 : // corresponding fully initialized group, as an object's group may change
642 : // from the former to the latter via the acquired properties analysis.
643 5956 : if (ngroup->newScript() && ngroup->newScript()->initializedGroup())
644 6 : addType(ObjectType(ngroup->newScript()->initializedGroup()), alloc);
645 : }
646 :
647 : if (false) {
648 : unknownObject:
649 1630 : flags |= TYPE_FLAG_ANYOBJECT;
650 1630 : clearObjects();
651 : }
652 : }
653 :
654 : // This class is used for post barriers on type set contents. The only times
655 : // when type sets contain nursery references is when a nursery object has its
656 : // group dynamically changed to a singleton. In such cases the type set will
657 : // need to be traced at the next minor GC.
658 : //
659 : // There is no barrier used for TemporaryTypeSets. These type sets are only
660 : // used during Ion compilation, and if some ConstraintTypeSet contains nursery
661 : // pointers then any number of TemporaryTypeSets might as well. Thus, if there
662 : // are any such ConstraintTypeSets in existence, all off thread Ion
663 : // compilations are canceled by the next minor GC.
664 32 : class TypeSetRef : public BufferableRef
665 : {
666 : Zone* zone;
667 : ConstraintTypeSet* types;
668 :
669 : public:
670 32 : TypeSetRef(Zone* zone, ConstraintTypeSet* types)
671 32 : : zone(zone), types(types)
672 32 : {}
673 :
674 31 : void trace(JSTracer* trc) override {
675 31 : types->trace(zone, trc);
676 31 : }
677 : };
678 :
679 : void
680 34418 : ConstraintTypeSet::postWriteBarrier(JSContext* cx, Type type)
681 : {
682 34418 : if (type.isSingletonUnchecked() && IsInsideNursery(type.singletonNoBarrier())) {
683 32 : cx->zone()->group()->storeBuffer().putGeneric(TypeSetRef(cx->zone(), this));
684 32 : cx->zone()->group()->storeBuffer().setShouldCancelIonCompilations();
685 : }
686 34418 : }
687 :
688 : void
689 33464 : ConstraintTypeSet::addType(JSContext* cx, Type type)
690 : {
691 33464 : checkMagic();
692 :
693 33464 : MOZ_RELEASE_ASSERT(cx->zone()->types.activeAnalysis);
694 :
695 33464 : if (hasType(type))
696 0 : return;
697 :
698 33464 : TypeSet::addType(type, &cx->typeLifoAlloc());
699 :
700 33464 : if (type.isObjectUnchecked() && unknownObject())
701 1553 : type = AnyObjectType();
702 :
703 33464 : postWriteBarrier(cx, type);
704 :
705 33464 : InferSpew(ISpewOps, "addType: %sT%p%s %s",
706 : InferSpewColor(this), this, InferSpewColorReset(),
707 : TypeString(type));
708 :
709 : /* Propagate the type to all constraints. */
710 33464 : if (!cx->helperThread()) {
711 29502 : TypeConstraint* constraint = constraintList();
712 29506 : while (constraint) {
713 2 : constraint->newType(cx, this, type);
714 2 : constraint = constraint->next();
715 : }
716 : } else {
717 3962 : MOZ_ASSERT(!constraintList_);
718 : }
719 : }
720 :
721 : void
722 0 : TypeSet::print(FILE* fp)
723 : {
724 0 : bool fromDebugger = !fp;
725 0 : if (!fp)
726 0 : fp = stderr;
727 :
728 0 : if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
729 0 : fprintf(fp, " [non-data]");
730 :
731 0 : if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
732 0 : fprintf(fp, " [non-writable]");
733 :
734 0 : if (definiteProperty())
735 0 : fprintf(fp, " [definite:%d]", definiteSlot());
736 :
737 0 : if (baseFlags() == 0 && !baseObjectCount()) {
738 0 : fprintf(fp, " missing");
739 0 : return;
740 : }
741 :
742 0 : if (flags & TYPE_FLAG_UNKNOWN)
743 0 : fprintf(fp, " unknown");
744 0 : if (flags & TYPE_FLAG_ANYOBJECT)
745 0 : fprintf(fp, " object");
746 :
747 0 : if (flags & TYPE_FLAG_UNDEFINED)
748 0 : fprintf(fp, " void");
749 0 : if (flags & TYPE_FLAG_NULL)
750 0 : fprintf(fp, " null");
751 0 : if (flags & TYPE_FLAG_BOOLEAN)
752 0 : fprintf(fp, " bool");
753 0 : if (flags & TYPE_FLAG_INT32)
754 0 : fprintf(fp, " int");
755 0 : if (flags & TYPE_FLAG_DOUBLE)
756 0 : fprintf(fp, " float");
757 0 : if (flags & TYPE_FLAG_STRING)
758 0 : fprintf(fp, " string");
759 0 : if (flags & TYPE_FLAG_SYMBOL)
760 0 : fprintf(fp, " symbol");
761 0 : if (flags & TYPE_FLAG_LAZYARGS)
762 0 : fprintf(fp, " lazyargs");
763 :
764 0 : uint32_t objectCount = baseObjectCount();
765 0 : if (objectCount) {
766 0 : fprintf(fp, " object[%u]", objectCount);
767 :
768 0 : unsigned count = getObjectCount();
769 0 : for (unsigned i = 0; i < count; i++) {
770 0 : ObjectKey* key = getObject(i);
771 0 : if (key)
772 0 : fprintf(fp, " %s", TypeString(ObjectType(key)));
773 : }
774 : }
775 :
776 0 : if (fromDebugger)
777 0 : fprintf(fp, "\n");
778 : }
779 :
780 : /* static */ void
781 0 : TypeSet::readBarrier(const TypeSet* types)
782 : {
783 0 : if (types->unknownObject())
784 0 : return;
785 :
786 0 : for (unsigned i = 0; i < types->getObjectCount(); i++) {
787 0 : if (ObjectKey* key = types->getObject(i)) {
788 0 : if (key->isSingleton())
789 0 : (void) key->singleton();
790 : else
791 0 : (void) key->group();
792 : }
793 : }
794 : }
795 :
796 : /* static */ bool
797 0 : TypeSet::IsTypeMarked(JSRuntime* rt, TypeSet::Type* v)
798 : {
799 : bool rv;
800 0 : if (v->isSingletonUnchecked()) {
801 0 : JSObject* obj = v->singletonNoBarrier();
802 0 : rv = IsMarkedUnbarriered(rt, &obj);
803 0 : *v = TypeSet::ObjectType(obj);
804 0 : } else if (v->isGroupUnchecked()) {
805 0 : ObjectGroup* group = v->groupNoBarrier();
806 0 : rv = IsMarkedUnbarriered(rt, &group);
807 0 : *v = TypeSet::ObjectType(group);
808 : } else {
809 0 : rv = true;
810 : }
811 0 : return rv;
812 : }
813 :
814 : static inline bool
815 0 : IsObjectKeyAboutToBeFinalized(TypeSet::ObjectKey** keyp)
816 : {
817 0 : TypeSet::ObjectKey* key = *keyp;
818 : bool isAboutToBeFinalized;
819 0 : if (key->isGroup()) {
820 0 : ObjectGroup* group = key->groupNoBarrier();
821 0 : isAboutToBeFinalized = IsAboutToBeFinalizedUnbarriered(&group);
822 0 : if (!isAboutToBeFinalized)
823 0 : *keyp = TypeSet::ObjectKey::get(group);
824 : } else {
825 0 : MOZ_ASSERT(key->isSingleton());
826 0 : JSObject* singleton = key->singletonNoBarrier();
827 0 : isAboutToBeFinalized = IsAboutToBeFinalizedUnbarriered(&singleton);
828 0 : if (!isAboutToBeFinalized)
829 0 : *keyp = TypeSet::ObjectKey::get(singleton);
830 : }
831 0 : return isAboutToBeFinalized;
832 : }
833 :
834 : bool
835 0 : TypeSet::IsTypeAboutToBeFinalized(TypeSet::Type* v)
836 : {
837 : bool isAboutToBeFinalized;
838 0 : if (v->isObjectUnchecked()) {
839 0 : TypeSet::ObjectKey* key = v->objectKey();
840 0 : isAboutToBeFinalized = IsObjectKeyAboutToBeFinalized(&key);
841 0 : if (!isAboutToBeFinalized)
842 0 : *v = TypeSet::ObjectType(key);
843 : } else {
844 0 : isAboutToBeFinalized = false;
845 : }
846 0 : return isAboutToBeFinalized;
847 : }
848 :
849 : bool
850 5158 : TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const
851 : {
852 5158 : MOZ_ASSERT(result->empty());
853 :
854 5158 : unsigned objectCount = baseObjectCount();
855 5158 : unsigned capacity = (objectCount >= 2) ? TypeHashSet::Capacity(objectCount) : 0;
856 :
857 : ObjectKey** newSet;
858 5158 : if (capacity) {
859 : // We allocate an extra word right before the array that stores the
860 : // capacity, so make sure we clone that as well.
861 1 : newSet = alloc->newArray<ObjectKey*>(capacity + 1);
862 1 : if (!newSet)
863 0 : return false;
864 1 : newSet++;
865 1 : PodCopy(newSet - 1, objectSet - 1, capacity + 1);
866 : }
867 :
868 5158 : new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
869 5158 : return true;
870 : }
871 :
872 : TemporaryTypeSet*
873 498 : TypeSet::clone(LifoAlloc* alloc) const
874 : {
875 498 : TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>();
876 498 : if (!res || !clone(alloc, res))
877 0 : return nullptr;
878 498 : return res;
879 : }
880 :
881 : TemporaryTypeSet*
882 34 : TypeSet::cloneObjectsOnly(LifoAlloc* alloc)
883 : {
884 34 : TemporaryTypeSet* res = clone(alloc);
885 34 : if (!res)
886 0 : return nullptr;
887 :
888 34 : res->flags &= ~TYPE_FLAG_BASE_MASK | TYPE_FLAG_ANYOBJECT;
889 :
890 34 : return res;
891 : }
892 :
893 : TemporaryTypeSet*
894 0 : TypeSet::cloneWithoutObjects(LifoAlloc* alloc)
895 : {
896 0 : TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>();
897 0 : if (!res)
898 0 : return nullptr;
899 :
900 0 : res->flags = flags & ~TYPE_FLAG_ANYOBJECT;
901 0 : res->setBaseObjectCount(0);
902 0 : return res;
903 : }
904 :
905 : /* static */ TemporaryTypeSet*
906 238 : TypeSet::unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc)
907 : {
908 476 : TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>(a->baseFlags() | b->baseFlags(),
909 238 : static_cast<ObjectKey**>(nullptr));
910 238 : if (!res)
911 0 : return nullptr;
912 :
913 238 : if (!res->unknownObject()) {
914 249 : for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
915 14 : if (ObjectKey* key = a->getObject(i))
916 14 : res->addType(ObjectType(key), alloc);
917 : }
918 263 : for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
919 28 : if (ObjectKey* key = b->getObject(i))
920 28 : res->addType(ObjectType(key), alloc);
921 : }
922 : }
923 :
924 238 : return res;
925 : }
926 :
927 : /* static */ TemporaryTypeSet*
928 684 : TypeSet::removeSet(TemporaryTypeSet* input, TemporaryTypeSet* removal, LifoAlloc* alloc)
929 : {
930 : // Only allow removal of primitives and the "AnyObject" flag.
931 684 : MOZ_ASSERT(!removal->unknown());
932 684 : MOZ_ASSERT_IF(!removal->unknownObject(), removal->getObjectCount() == 0);
933 :
934 684 : uint32_t flags = input->baseFlags() & ~removal->baseFlags();
935 : TemporaryTypeSet* res =
936 684 : alloc->new_<TemporaryTypeSet>(flags, static_cast<ObjectKey**>(nullptr));
937 684 : if (!res)
938 0 : return nullptr;
939 :
940 684 : res->setBaseObjectCount(0);
941 684 : if (removal->unknownObject() || input->unknownObject())
942 7 : return res;
943 :
944 677 : for (size_t i = 0; i < input->getObjectCount(); i++) {
945 0 : if (!input->getObject(i))
946 0 : continue;
947 :
948 0 : res->addType(TypeSet::ObjectType(input->getObject(i)), alloc);
949 : }
950 :
951 677 : return res;
952 : }
953 :
954 : /* static */ TemporaryTypeSet*
955 684 : TypeSet::intersectSets(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc)
956 : {
957 : TemporaryTypeSet* res;
958 1368 : res = alloc->new_<TemporaryTypeSet>(a->baseFlags() & b->baseFlags(),
959 684 : static_cast<ObjectKey**>(nullptr));
960 684 : if (!res)
961 0 : return nullptr;
962 :
963 684 : res->setBaseObjectCount(0);
964 684 : if (res->unknownObject())
965 0 : return res;
966 :
967 684 : MOZ_ASSERT(!a->unknownObject() || !b->unknownObject());
968 :
969 684 : if (a->unknownObject()) {
970 0 : for (size_t i = 0; i < b->getObjectCount(); i++) {
971 0 : if (b->getObject(i))
972 0 : res->addType(ObjectType(b->getObject(i)), alloc);
973 : }
974 0 : return res;
975 : }
976 :
977 684 : if (b->unknownObject()) {
978 7 : for (size_t i = 0; i < a->getObjectCount(); i++) {
979 0 : if (a->getObject(i))
980 0 : res->addType(ObjectType(a->getObject(i)), alloc);
981 : }
982 7 : return res;
983 : }
984 :
985 677 : MOZ_ASSERT(!a->unknownObject() && !b->unknownObject());
986 :
987 677 : for (size_t i = 0; i < a->getObjectCount(); i++) {
988 0 : for (size_t j = 0; j < b->getObjectCount(); j++) {
989 0 : if (b->getObject(j) != a->getObject(i))
990 0 : continue;
991 0 : if (!b->getObject(j))
992 0 : continue;
993 0 : res->addType(ObjectType(b->getObject(j)), alloc);
994 0 : break;
995 : }
996 : }
997 :
998 677 : return res;
999 : }
1000 :
1001 : /////////////////////////////////////////////////////////////////////
1002 : // Compiler constraints
1003 : /////////////////////////////////////////////////////////////////////
1004 :
1005 : // Compiler constraints overview
1006 : //
1007 : // Constraints generated during Ion compilation capture assumptions made about
1008 : // heap properties that will trigger invalidation of the resulting Ion code if
1009 : // the constraint is violated. Constraints can only be attached to type sets on
1010 : // the active thread, so to allow compilation to occur almost entirely off thread
1011 : // the generation is split into two phases.
1012 : //
1013 : // During compilation, CompilerConstraint values are constructed in a list,
1014 : // recording the heap property type set which was read from and its expected
1015 : // contents, along with the assumption made about those contents.
1016 : //
1017 : // At the end of compilation, when linking the result on the active thread, the
1018 : // list of compiler constraints are read and converted to type constraints and
1019 : // attached to the type sets. If the property type sets have changed so that the
1020 : // assumptions no longer hold then the compilation is aborted and its result
1021 : // discarded.
1022 :
1023 : // Superclass of all constraints generated during Ion compilation. These may
1024 : // be allocated off thread, using the current JIT context's allocator.
1025 : class CompilerConstraint
1026 : {
1027 : public:
1028 : // Property being queried by the compiler.
1029 : HeapTypeSetKey property;
1030 :
1031 : // Contents of the property at the point when the query was performed. This
1032 : // may differ from the actual property types later in compilation as the
1033 : // active thread performs side effects.
1034 : TemporaryTypeSet* expected;
1035 :
1036 1080 : CompilerConstraint(LifoAlloc* alloc, const HeapTypeSetKey& property)
1037 1080 : : property(property),
1038 1080 : expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr)
1039 1080 : {}
1040 :
1041 : // Generate the type constraint recording the assumption made by this
1042 : // compilation. Returns true if the assumption originally made still holds.
1043 : virtual bool generateTypeConstraint(JSContext* cx, RecompileInfo recompileInfo) = 0;
1044 : };
1045 :
1046 : class js::CompilerConstraintList
1047 : {
1048 : public:
1049 : struct FrozenScript
1050 : {
1051 : JSScript* script;
1052 : TemporaryTypeSet* thisTypes;
1053 : TemporaryTypeSet* argTypes;
1054 : TemporaryTypeSet* bytecodeTypes;
1055 : };
1056 :
1057 : private:
1058 :
1059 : // OOM during generation of some constraint.
1060 : bool failed_;
1061 :
1062 : // Allocator used for constraints.
1063 : LifoAlloc* alloc_;
1064 :
1065 : // Constraints generated on heap properties.
1066 : Vector<CompilerConstraint*, 0, jit::JitAllocPolicy> constraints;
1067 :
1068 : // Scripts whose stack type sets were frozen for the compilation.
1069 : Vector<FrozenScript, 1, jit::JitAllocPolicy> frozenScripts;
1070 :
1071 : public:
1072 146 : explicit CompilerConstraintList(jit::TempAllocator& alloc)
1073 146 : : failed_(false),
1074 146 : alloc_(alloc.lifoAlloc()),
1075 : constraints(alloc),
1076 292 : frozenScripts(alloc)
1077 146 : {}
1078 :
1079 1080 : void add(CompilerConstraint* constraint) {
1080 1080 : if (!constraint || !constraints.append(constraint))
1081 0 : setFailed();
1082 1080 : }
1083 :
1084 179 : void freezeScript(JSScript* script,
1085 : TemporaryTypeSet* thisTypes,
1086 : TemporaryTypeSet* argTypes,
1087 : TemporaryTypeSet* bytecodeTypes)
1088 : {
1089 : FrozenScript entry;
1090 179 : entry.script = script;
1091 179 : entry.thisTypes = thisTypes;
1092 179 : entry.argTypes = argTypes;
1093 179 : entry.bytecodeTypes = bytecodeTypes;
1094 179 : if (!frozenScripts.append(entry))
1095 0 : setFailed();
1096 179 : }
1097 :
1098 679 : size_t length() {
1099 679 : return constraints.length();
1100 : }
1101 :
1102 672 : CompilerConstraint* get(size_t i) {
1103 672 : return constraints[i];
1104 : }
1105 :
1106 42 : size_t numFrozenScripts() {
1107 42 : return frozenScripts.length();
1108 : }
1109 :
1110 33 : const FrozenScript& frozenScript(size_t i) {
1111 33 : return frozenScripts[i];
1112 : }
1113 :
1114 7 : bool failed() {
1115 7 : return failed_;
1116 : }
1117 0 : void setFailed() {
1118 0 : failed_ = true;
1119 0 : }
1120 1259 : LifoAlloc* alloc() const {
1121 1259 : return alloc_;
1122 : }
1123 : };
1124 :
1125 : CompilerConstraintList*
1126 146 : js::NewCompilerConstraintList(jit::TempAllocator& alloc)
1127 : {
1128 146 : return alloc.lifoAlloc()->new_<CompilerConstraintList>(alloc);
1129 : }
1130 :
1131 : /* static */ bool
1132 179 : TypeScript::FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script,
1133 : TemporaryTypeSet** pThisTypes,
1134 : TemporaryTypeSet** pArgTypes,
1135 : TemporaryTypeSet** pBytecodeTypes)
1136 : {
1137 179 : LifoAlloc* alloc = constraints->alloc();
1138 179 : StackTypeSet* existing = script->types()->typeArray();
1139 :
1140 179 : size_t count = NumTypeSets(script);
1141 179 : TemporaryTypeSet* types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
1142 179 : if (!types)
1143 0 : return false;
1144 179 : PodZero(types, count);
1145 :
1146 4839 : for (size_t i = 0; i < count; i++) {
1147 4660 : if (!existing[i].clone(alloc, &types[i]))
1148 0 : return false;
1149 : }
1150 :
1151 179 : *pThisTypes = types + (ThisTypes(script) - existing);
1152 537 : *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs())
1153 315 : ? (types + (ArgTypes(script, 0) - existing))
1154 : : nullptr;
1155 179 : *pBytecodeTypes = types;
1156 :
1157 179 : constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
1158 179 : return true;
1159 : }
1160 :
1161 : namespace {
1162 :
1163 : template <typename T>
1164 : class CompilerConstraintInstance : public CompilerConstraint
1165 : {
1166 : T data;
1167 :
1168 : public:
1169 1080 : CompilerConstraintInstance<T>(LifoAlloc* alloc, const HeapTypeSetKey& property, const T& data)
1170 1080 : : CompilerConstraint(alloc, property), data(data)
1171 1080 : {}
1172 :
1173 : bool generateTypeConstraint(JSContext* cx, RecompileInfo recompileInfo);
1174 : };
1175 :
1176 : // Constraint generated from a CompilerConstraint when linking the compilation.
1177 : template <typename T>
1178 : class TypeCompilerConstraint : public TypeConstraint
1179 : {
1180 : // Compilation which this constraint may invalidate.
1181 : RecompileInfo compilation;
1182 :
1183 : T data;
1184 :
1185 : public:
1186 669 : TypeCompilerConstraint<T>(RecompileInfo compilation, const T& data)
1187 669 : : compilation(compilation), data(data)
1188 669 : {}
1189 :
1190 0 : const char* kind() { return data.kind(); }
1191 :
1192 0 : void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) {
1193 0 : if (data.invalidateOnNewType(type))
1194 0 : cx->zone()->types.addPendingRecompile(cx, compilation);
1195 0 : }
1196 :
1197 0 : void newPropertyState(JSContext* cx, TypeSet* source) {
1198 0 : if (data.invalidateOnNewPropertyState(source))
1199 0 : cx->zone()->types.addPendingRecompile(cx, compilation);
1200 0 : }
1201 :
1202 0 : void newObjectState(JSContext* cx, ObjectGroup* group) {
1203 : // Note: Once the object has unknown properties, no more notifications
1204 : // will be sent on changes to its state, so always invalidate any
1205 : // associated compilations.
1206 0 : if (group->unknownProperties() || data.invalidateOnNewObjectState(group))
1207 0 : cx->zone()->types.addPendingRecompile(cx, compilation);
1208 0 : }
1209 :
1210 0 : bool sweep(TypeZone& zone, TypeConstraint** res) {
1211 0 : if (data.shouldSweep() || compilation.shouldSweep(zone))
1212 0 : return false;
1213 0 : *res = zone.typeLifoAlloc().new_<TypeCompilerConstraint<T> >(compilation, data);
1214 0 : return true;
1215 : }
1216 :
1217 669 : JSCompartment* maybeCompartment() {
1218 669 : return data.maybeCompartment();
1219 : }
1220 : };
1221 :
1222 : template <typename T>
1223 : bool
1224 672 : CompilerConstraintInstance<T>::generateTypeConstraint(JSContext* cx, RecompileInfo recompileInfo)
1225 : {
1226 672 : if (property.object()->unknownProperties())
1227 0 : return false;
1228 :
1229 672 : if (!property.instantiate(cx))
1230 0 : return false;
1231 :
1232 672 : if (!data.constraintHolds(cx, property, expected))
1233 3 : return false;
1234 :
1235 1338 : return property.maybeTypes()->addConstraint(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
1236 2007 : /* callExisting = */ false);
1237 : }
1238 :
1239 : } /* anonymous namespace */
1240 :
1241 : const Class*
1242 322 : TypeSet::ObjectKey::clasp()
1243 : {
1244 322 : return isGroup() ? group()->clasp() : singleton()->getClass();
1245 : }
1246 :
1247 : TaggedProto
1248 84 : TypeSet::ObjectKey::proto()
1249 : {
1250 84 : return isGroup() ? group()->proto() : singleton()->taggedProto();
1251 : }
1252 :
1253 : TypeNewScript*
1254 0 : TypeSet::ObjectKey::newScript()
1255 : {
1256 0 : if (isGroup() && group()->newScript())
1257 0 : return group()->newScript();
1258 0 : return nullptr;
1259 : }
1260 :
1261 : ObjectGroup*
1262 14388 : TypeSet::ObjectKey::maybeGroup()
1263 : {
1264 14388 : if (isGroup())
1265 9130 : return group();
1266 5258 : if (!singleton()->hasLazyGroup())
1267 2314 : return singleton()->group();
1268 2944 : return nullptr;
1269 : }
1270 :
1271 : bool
1272 11475 : TypeSet::ObjectKey::unknownProperties()
1273 : {
1274 11475 : if (ObjectGroup* group = maybeGroup())
1275 9175 : return group->unknownProperties();
1276 2300 : return false;
1277 : }
1278 :
1279 : HeapTypeSetKey
1280 1312 : TypeSet::ObjectKey::property(jsid id)
1281 : {
1282 1312 : MOZ_ASSERT(!unknownProperties());
1283 :
1284 1312 : HeapTypeSetKey property;
1285 1312 : property.object_ = this;
1286 1312 : property.id_ = id;
1287 1312 : if (ObjectGroup* group = maybeGroup())
1288 966 : property.maybeTypes_ = group->maybeGetProperty(id);
1289 :
1290 1312 : return property;
1291 : }
1292 :
1293 : void
1294 3 : TypeSet::ObjectKey::ensureTrackedProperty(JSContext* cx, jsid id)
1295 : {
1296 : // If we are accessing a lazily defined property which actually exists in
1297 : // the VM and has not been instantiated yet, instantiate it now if we are
1298 : // on the active thread and able to do so.
1299 3 : if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
1300 3 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1301 3 : if (isSingleton()) {
1302 3 : JSObject* obj = singleton();
1303 3 : if (obj->isNative() && obj->as<NativeObject>().containsPure(id))
1304 2 : EnsureTrackPropertyTypes(cx, obj, id);
1305 : }
1306 : }
1307 3 : }
1308 :
1309 : void
1310 5603 : js::EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id)
1311 : {
1312 5603 : id = IdToTypeId(id);
1313 :
1314 5603 : if (obj->isSingleton()) {
1315 5624 : AutoEnterAnalysis enter(cx);
1316 2812 : if (obj->hasLazyGroup()) {
1317 1094 : AutoEnterOOMUnsafeRegion oomUnsafe;
1318 1094 : RootedObject objRoot(cx, obj);
1319 547 : if (!JSObject::getGroup(cx, objRoot)) {
1320 0 : oomUnsafe.crash("Could not allocate ObjectGroup in EnsureTrackPropertyTypes");
1321 : return;
1322 : }
1323 : }
1324 2812 : if (!obj->group()->unknownProperties() && !obj->group()->getProperty(cx, obj, id)) {
1325 0 : MOZ_ASSERT(obj->group()->unknownProperties());
1326 0 : return;
1327 : }
1328 : }
1329 :
1330 5603 : MOZ_ASSERT(obj->group()->unknownProperties() || TrackPropertyTypes(obj, id));
1331 : }
1332 :
1333 : bool
1334 672 : HeapTypeSetKey::instantiate(JSContext* cx)
1335 : {
1336 672 : if (maybeTypes())
1337 404 : return true;
1338 268 : if (object()->isSingleton()) {
1339 324 : RootedObject obj(cx, object()->singleton());
1340 162 : if (!JSObject::getGroup(cx, obj)) {
1341 0 : cx->clearPendingException();
1342 0 : return false;
1343 : }
1344 : }
1345 268 : JSObject* obj = object()->isSingleton() ? object()->singleton() : nullptr;
1346 268 : maybeTypes_ = object()->maybeGroup()->getProperty(cx, obj, id());
1347 268 : return maybeTypes_ != nullptr;
1348 : }
1349 :
1350 : static bool
1351 498 : CheckFrozenTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual)
1352 : {
1353 : // Return whether the types frozen for a script during compilation are
1354 : // still valid. Also check for any new types added to the frozen set during
1355 : // compilation, and add them to the actual stack type sets. These new types
1356 : // indicate places where the compiler relaxed its possible inputs to be
1357 : // more tolerant of potential new types.
1358 :
1359 498 : if (!actual->isSubset(frozen))
1360 2 : return false;
1361 :
1362 496 : if (!frozen->isSubset(actual)) {
1363 0 : TypeSet::TypeList list;
1364 0 : frozen->enumerateTypes(&list);
1365 :
1366 0 : for (size_t i = 0; i < list.length(); i++)
1367 0 : actual->addType(cx, list[i]);
1368 : }
1369 :
1370 496 : return true;
1371 : }
1372 :
1373 : namespace {
1374 :
1375 : /*
1376 : * As for TypeConstraintFreeze, but describes an implicit freeze constraint
1377 : * added for stack types within a script. Applies to all compilations of the
1378 : * script, not just a single one.
1379 : */
1380 : class TypeConstraintFreezeStack : public TypeConstraint
1381 : {
1382 : JSScript* script_;
1383 :
1384 : public:
1385 197 : explicit TypeConstraintFreezeStack(JSScript* script)
1386 197 : : script_(script)
1387 197 : {}
1388 :
1389 0 : const char* kind() { return "freezeStack"; }
1390 :
1391 2 : void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) {
1392 : /*
1393 : * Unlike TypeConstraintFreeze, triggering this constraint once does
1394 : * not disable it on future changes to the type set.
1395 : */
1396 2 : cx->zone()->types.addPendingRecompile(cx, script_);
1397 2 : }
1398 :
1399 0 : bool sweep(TypeZone& zone, TypeConstraint** res) {
1400 0 : if (IsAboutToBeFinalizedUnbarriered(&script_))
1401 0 : return false;
1402 0 : *res = zone.typeLifoAlloc().new_<TypeConstraintFreezeStack>(script_);
1403 0 : return true;
1404 : }
1405 :
1406 197 : JSCompartment* maybeCompartment() {
1407 197 : return script_->compartment();
1408 : }
1409 : };
1410 :
1411 : } /* anonymous namespace */
1412 :
1413 : bool
1414 7 : js::FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList* constraints,
1415 : RecompileInfo* precompileInfo, bool* isValidOut)
1416 : {
1417 7 : if (constraints->failed())
1418 0 : return false;
1419 :
1420 7 : CompilerOutput co(script);
1421 :
1422 7 : TypeZone& types = cx->zone()->types;
1423 7 : if (!types.compilerOutputs) {
1424 1 : types.compilerOutputs = cx->new_<TypeZone::CompilerOutputVector>();
1425 1 : if (!types.compilerOutputs)
1426 0 : return false;
1427 : }
1428 :
1429 : #ifdef DEBUG
1430 28 : for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
1431 21 : const CompilerOutput& co = (*types.compilerOutputs)[i];
1432 21 : MOZ_ASSERT_IF(co.isValid(), co.script() != script);
1433 : }
1434 : #endif
1435 :
1436 7 : uint32_t index = types.compilerOutputs->length();
1437 7 : if (!types.compilerOutputs->append(co)) {
1438 0 : ReportOutOfMemory(cx);
1439 0 : return false;
1440 : }
1441 :
1442 7 : *precompileInfo = RecompileInfo(index, types.generation);
1443 :
1444 7 : bool succeeded = true;
1445 :
1446 679 : for (size_t i = 0; i < constraints->length(); i++) {
1447 672 : CompilerConstraint* constraint = constraints->get(i);
1448 672 : if (!constraint->generateTypeConstraint(cx, *precompileInfo))
1449 3 : succeeded = false;
1450 : }
1451 :
1452 38 : for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1453 31 : const CompilerConstraintList::FrozenScript& entry = constraints->frozenScript(i);
1454 31 : if (!entry.script->types()) {
1455 0 : succeeded = false;
1456 0 : break;
1457 : }
1458 :
1459 : // It could happen that one of the compiled scripts was made a
1460 : // debuggee mid-compilation (e.g., via setting a breakpoint). If so,
1461 : // throw away the compilation.
1462 31 : if (entry.script->isDebuggee()) {
1463 0 : succeeded = false;
1464 0 : break;
1465 : }
1466 :
1467 31 : if (!CheckFrozenTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(entry.script)))
1468 0 : succeeded = false;
1469 31 : unsigned nargs = entry.script->functionNonDelazifying()
1470 31 : ? entry.script->functionNonDelazifying()->nargs()
1471 62 : : 0;
1472 68 : for (size_t i = 0; i < nargs; i++) {
1473 37 : if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], TypeScript::ArgTypes(entry.script, i)))
1474 1 : succeeded = false;
1475 : }
1476 461 : for (size_t i = 0; i < entry.script->nTypeSets(); i++) {
1477 430 : if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types()->typeArray()[i]))
1478 1 : succeeded = false;
1479 : }
1480 :
1481 : // Add this compilation to the inlinedCompilations list of each inlined
1482 : // script, so we can invalidate it on changes to stack type sets.
1483 31 : if (entry.script != script) {
1484 24 : if (!entry.script->types()->addInlinedCompilation(*precompileInfo))
1485 0 : succeeded = false;
1486 : }
1487 :
1488 : // If necessary, add constraints to trigger invalidation on the script
1489 : // after any future changes to the stack type sets.
1490 31 : if (entry.script->hasFreezeConstraints())
1491 19 : continue;
1492 :
1493 12 : size_t count = TypeScript::NumTypeSets(entry.script);
1494 :
1495 12 : StackTypeSet* array = entry.script->types()->typeArray();
1496 209 : for (size_t i = 0; i < count; i++) {
1497 197 : if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
1498 0 : succeeded = false;
1499 : }
1500 :
1501 12 : if (succeeded)
1502 10 : entry.script->setHasFreezeConstraints();
1503 : }
1504 :
1505 7 : if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
1506 2 : types.compilerOutputs->back().invalidate();
1507 2 : script->resetWarmUpCounter();
1508 2 : *isValidOut = false;
1509 2 : return true;
1510 : }
1511 :
1512 5 : *isValidOut = true;
1513 5 : return true;
1514 : }
1515 :
1516 : void
1517 0 : js::InvalidateCompilerOutputsForScript(JSContext* cx, HandleScript script)
1518 : {
1519 0 : TypeZone& types = cx->zone()->types;
1520 0 : if (types.compilerOutputs) {
1521 0 : for (auto& co : *types.compilerOutputs) {
1522 0 : if (co.script() == script)
1523 0 : co.invalidate();
1524 : }
1525 : }
1526 0 : }
1527 :
1528 : static void
1529 6 : CheckDefinitePropertiesTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual)
1530 : {
1531 : // The definite properties analysis happens on the active thread, so no new
1532 : // types can have been added to actual. The analysis may have updated the
1533 : // contents of |frozen| though with new speculative types, and these need
1534 : // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
1535 : // to work.
1536 6 : if (!frozen->isSubset(actual)) {
1537 0 : TypeSet::TypeList list;
1538 0 : frozen->enumerateTypes(&list);
1539 :
1540 0 : for (size_t i = 0; i < list.length(); i++)
1541 0 : actual->addType(cx, list[i]);
1542 : }
1543 6 : }
1544 :
1545 : void
1546 1 : js::FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints)
1547 : {
1548 : #ifdef DEBUG
1549 : // Assert no new types have been added to the StackTypeSets. Do this before
1550 : // calling CheckDefinitePropertiesTypeSet, as it may add new types to the
1551 : // StackTypeSets and break these invariants if a script is inlined more
1552 : // than once. See also CheckDefinitePropertiesTypeSet.
1553 2 : for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1554 1 : const CompilerConstraintList::FrozenScript& entry = constraints->frozenScript(i);
1555 1 : JSScript* script = entry.script;
1556 1 : MOZ_ASSERT(script->types());
1557 :
1558 1 : MOZ_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes));
1559 :
1560 1 : unsigned nargs = entry.script->functionNonDelazifying()
1561 1 : ? entry.script->functionNonDelazifying()->nargs()
1562 2 : : 0;
1563 1 : for (size_t j = 0; j < nargs; j++)
1564 0 : MOZ_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
1565 :
1566 6 : for (size_t j = 0; j < script->nTypeSets(); j++)
1567 5 : MOZ_ASSERT(script->types()->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
1568 : }
1569 : #endif
1570 :
1571 2 : for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1572 1 : const CompilerConstraintList::FrozenScript& entry = constraints->frozenScript(i);
1573 1 : JSScript* script = entry.script;
1574 1 : if (!script->types())
1575 0 : MOZ_CRASH();
1576 :
1577 1 : CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
1578 :
1579 1 : unsigned nargs = script->functionNonDelazifying()
1580 1 : ? script->functionNonDelazifying()->nargs()
1581 2 : : 0;
1582 1 : for (size_t j = 0; j < nargs; j++)
1583 0 : CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
1584 :
1585 6 : for (size_t j = 0; j < script->nTypeSets(); j++)
1586 5 : CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types()->typeArray()[j]);
1587 : }
1588 1 : }
1589 :
1590 : namespace {
1591 :
1592 : // Constraint which triggers recompilation of a script if any type is added to a type set. */
1593 : class ConstraintDataFreeze
1594 : {
1595 : public:
1596 188 : ConstraintDataFreeze() {}
1597 :
1598 0 : const char* kind() { return "freeze"; }
1599 :
1600 0 : bool invalidateOnNewType(TypeSet::Type type) { return true; }
1601 0 : bool invalidateOnNewPropertyState(TypeSet* property) { return true; }
1602 0 : bool invalidateOnNewObjectState(ObjectGroup* group) { return false; }
1603 :
1604 144 : bool constraintHolds(JSContext* cx,
1605 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1606 : {
1607 : return expected
1608 215 : ? property.maybeTypes()->isSubset(expected)
1609 215 : : property.maybeTypes()->empty();
1610 : }
1611 :
1612 0 : bool shouldSweep() { return false; }
1613 :
1614 141 : JSCompartment* maybeCompartment() { return nullptr; }
1615 : };
1616 :
1617 : } /* anonymous namespace */
1618 :
1619 : void
1620 188 : HeapTypeSetKey::freeze(CompilerConstraintList* constraints)
1621 : {
1622 188 : LifoAlloc* alloc = constraints->alloc();
1623 :
1624 : typedef CompilerConstraintInstance<ConstraintDataFreeze> T;
1625 188 : constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataFreeze()));
1626 188 : }
1627 :
1628 : static inline jit::MIRType
1629 4703 : GetMIRTypeFromTypeFlags(TypeFlags flags)
1630 : {
1631 4703 : switch (flags) {
1632 : case TYPE_FLAG_UNDEFINED:
1633 102 : return jit::MIRType::Undefined;
1634 : case TYPE_FLAG_NULL:
1635 0 : return jit::MIRType::Null;
1636 : case TYPE_FLAG_BOOLEAN:
1637 154 : return jit::MIRType::Boolean;
1638 : case TYPE_FLAG_INT32:
1639 156 : return jit::MIRType::Int32;
1640 : case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
1641 2 : return jit::MIRType::Double;
1642 : case TYPE_FLAG_STRING:
1643 121 : return jit::MIRType::String;
1644 : case TYPE_FLAG_SYMBOL:
1645 0 : return jit::MIRType::Symbol;
1646 : case TYPE_FLAG_LAZYARGS:
1647 2 : return jit::MIRType::MagicOptimizedArguments;
1648 : case TYPE_FLAG_ANYOBJECT:
1649 82 : return jit::MIRType::Object;
1650 : default:
1651 4084 : return jit::MIRType::Value;
1652 : }
1653 : }
1654 :
1655 : jit::MIRType
1656 4945 : TemporaryTypeSet::getKnownMIRType()
1657 : {
1658 4945 : TypeFlags flags = baseFlags();
1659 : jit::MIRType type;
1660 :
1661 4945 : if (baseObjectCount())
1662 243 : type = flags ? jit::MIRType::Value : jit::MIRType::Object;
1663 : else
1664 4702 : type = GetMIRTypeFromTypeFlags(flags);
1665 :
1666 : /*
1667 : * If the type set is totally empty then it will be treated as unknown,
1668 : * but we still need to record the dependency as adding a new type can give
1669 : * it a definite type tag. This is not needed if there are enough types
1670 : * that the exact tag is unknown, as it will stay unknown as more types are
1671 : * added to the set.
1672 : */
1673 9890 : DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
1674 4945 : MOZ_ASSERT_IF(empty, type == jit::MIRType::Value);
1675 :
1676 9890 : return type;
1677 : }
1678 :
1679 : jit::MIRType
1680 1 : HeapTypeSetKey::knownMIRType(CompilerConstraintList* constraints)
1681 : {
1682 1 : TypeSet* types = maybeTypes();
1683 :
1684 1 : if (!types || types->unknown())
1685 0 : return jit::MIRType::Value;
1686 :
1687 1 : TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
1688 : jit::MIRType type;
1689 :
1690 1 : if (types->unknownObject() || types->getObjectCount())
1691 0 : type = flags ? jit::MIRType::Value : jit::MIRType::Object;
1692 : else
1693 1 : type = GetMIRTypeFromTypeFlags(flags);
1694 :
1695 1 : if (type != jit::MIRType::Value)
1696 1 : freeze(constraints);
1697 :
1698 : /*
1699 : * If the type set is totally empty then it will be treated as unknown,
1700 : * but we still need to record the dependency as adding a new type can give
1701 : * it a definite type tag. This is not needed if there are enough types
1702 : * that the exact tag is unknown, as it will stay unknown as more types are
1703 : * added to the set.
1704 : */
1705 1 : MOZ_ASSERT_IF(types->empty(), type == jit::MIRType::Value);
1706 :
1707 1 : return type;
1708 : }
1709 :
1710 : bool
1711 91 : HeapTypeSetKey::isOwnProperty(CompilerConstraintList* constraints,
1712 : bool allowEmptyTypesForGlobal/* = false*/)
1713 : {
1714 91 : if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
1715 32 : return true;
1716 59 : if (object()->isSingleton()) {
1717 36 : JSObject* obj = object()->singleton();
1718 36 : MOZ_ASSERT(CanHaveEmptyPropertyTypesForOwnProperty(obj) == obj->is<GlobalObject>());
1719 36 : if (!allowEmptyTypesForGlobal) {
1720 36 : if (CanHaveEmptyPropertyTypesForOwnProperty(obj))
1721 0 : return true;
1722 : }
1723 : }
1724 59 : freeze(constraints);
1725 59 : return false;
1726 : }
1727 :
1728 : bool
1729 0 : HeapTypeSetKey::knownSubset(CompilerConstraintList* constraints, const HeapTypeSetKey& other)
1730 : {
1731 0 : if (!maybeTypes() || maybeTypes()->empty()) {
1732 0 : freeze(constraints);
1733 0 : return true;
1734 : }
1735 0 : if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes()))
1736 0 : return false;
1737 0 : freeze(constraints);
1738 0 : return true;
1739 : }
1740 :
1741 : JSObject*
1742 355 : TemporaryTypeSet::maybeSingleton()
1743 : {
1744 355 : if (baseFlags() != 0 || baseObjectCount() != 1)
1745 210 : return nullptr;
1746 :
1747 145 : return getSingleton(0);
1748 : }
1749 :
1750 : TemporaryTypeSet::ObjectKey*
1751 46 : TemporaryTypeSet::maybeSingleObject()
1752 : {
1753 46 : if (baseFlags() != 0 || baseObjectCount() != 1)
1754 5 : return nullptr;
1755 :
1756 41 : return getObject(0);
1757 : }
1758 :
1759 : JSObject*
1760 21 : HeapTypeSetKey::singleton(CompilerConstraintList* constraints)
1761 : {
1762 21 : HeapTypeSet* types = maybeTypes();
1763 :
1764 21 : if (!types || types->nonDataProperty() || types->baseFlags() != 0 || types->getObjectCount() != 1)
1765 0 : return nullptr;
1766 :
1767 21 : JSObject* obj = types->getSingleton(0);
1768 :
1769 21 : if (obj)
1770 21 : freeze(constraints);
1771 :
1772 21 : return obj;
1773 : }
1774 :
1775 : bool
1776 52 : HeapTypeSetKey::needsBarrier(CompilerConstraintList* constraints)
1777 : {
1778 52 : TypeSet* types = maybeTypes();
1779 52 : if (!types)
1780 0 : return false;
1781 52 : bool result = types->unknownObject()
1782 24 : || types->getObjectCount() > 0
1783 76 : || types->hasAnyFlag(TYPE_FLAG_STRING | TYPE_FLAG_SYMBOL);
1784 52 : if (!result)
1785 23 : freeze(constraints);
1786 52 : return result;
1787 : }
1788 :
1789 : namespace {
1790 :
1791 : // Constraint which triggers recompilation if an object acquires particular flags.
1792 : class ConstraintDataFreezeObjectFlags
1793 : {
1794 : public:
1795 : // Flags we are watching for on this object.
1796 : ObjectGroupFlags flags;
1797 :
1798 740 : explicit ConstraintDataFreezeObjectFlags(ObjectGroupFlags flags)
1799 740 : : flags(flags)
1800 : {
1801 740 : MOZ_ASSERT(flags);
1802 740 : }
1803 :
1804 0 : const char* kind() { return "freezeObjectFlags"; }
1805 :
1806 0 : bool invalidateOnNewType(TypeSet::Type type) { return false; }
1807 0 : bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1808 387 : bool invalidateOnNewObjectState(ObjectGroup* group) {
1809 387 : return group->hasAnyFlags(flags);
1810 : }
1811 :
1812 387 : bool constraintHolds(JSContext* cx,
1813 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1814 : {
1815 387 : return !invalidateOnNewObjectState(property.object()->maybeGroup());
1816 : }
1817 :
1818 0 : bool shouldSweep() { return false; }
1819 :
1820 387 : JSCompartment* maybeCompartment() { return nullptr; }
1821 : };
1822 :
1823 : } /* anonymous namespace */
1824 :
1825 : bool
1826 724 : TypeSet::ObjectKey::hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags)
1827 : {
1828 724 : MOZ_ASSERT(flags);
1829 :
1830 724 : if (ObjectGroup* group = maybeGroup()) {
1831 426 : if (group->hasAnyFlags(flags))
1832 0 : return true;
1833 : }
1834 :
1835 724 : HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1836 724 : LifoAlloc* alloc = constraints->alloc();
1837 :
1838 : typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
1839 724 : constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(flags)));
1840 724 : return false;
1841 : }
1842 :
1843 : bool
1844 707 : TypeSet::ObjectKey::hasStableClassAndProto(CompilerConstraintList* constraints)
1845 : {
1846 707 : return !hasFlags(constraints, OBJECT_FLAG_UNKNOWN_PROPERTIES);
1847 : }
1848 :
1849 : bool
1850 17 : TemporaryTypeSet::hasObjectFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags)
1851 : {
1852 17 : if (unknownObject())
1853 0 : return true;
1854 :
1855 : /*
1856 : * Treat type sets containing no objects as having all object flags,
1857 : * to spare callers from having to check this.
1858 : */
1859 17 : if (baseObjectCount() == 0)
1860 0 : return true;
1861 :
1862 17 : unsigned count = getObjectCount();
1863 34 : for (unsigned i = 0; i < count; i++) {
1864 17 : ObjectKey* key = getObject(i);
1865 17 : if (key && key->hasFlags(constraints, flags))
1866 0 : return true;
1867 : }
1868 :
1869 17 : return false;
1870 : }
1871 :
1872 : gc::InitialHeap
1873 16 : ObjectGroup::initialHeap(CompilerConstraintList* constraints)
1874 : {
1875 : // If this object is not required to be pretenured but could be in the
1876 : // future, add a constraint to trigger recompilation if the requirement
1877 : // changes.
1878 :
1879 16 : if (shouldPreTenure())
1880 0 : return gc::TenuredHeap;
1881 :
1882 16 : if (!canPreTenure())
1883 0 : return gc::DefaultHeap;
1884 :
1885 16 : HeapTypeSetKey objectProperty = TypeSet::ObjectKey::get(this)->property(JSID_EMPTY);
1886 16 : LifoAlloc* alloc = constraints->alloc();
1887 :
1888 : typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
1889 16 : constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE)));
1890 :
1891 16 : return gc::DefaultHeap;
1892 : }
1893 :
1894 : namespace {
1895 :
1896 : // Constraint which triggers recompilation when a typed array's data becomes
1897 : // invalid.
1898 : class ConstraintDataFreezeObjectForTypedArrayData
1899 : {
1900 : NativeObject* obj;
1901 :
1902 : uintptr_t viewData;
1903 : uint32_t length;
1904 :
1905 : public:
1906 0 : explicit ConstraintDataFreezeObjectForTypedArrayData(TypedArrayObject& tarray)
1907 0 : : obj(&tarray),
1908 0 : viewData(tarray.viewDataEither().unwrapValue()),
1909 0 : length(tarray.length())
1910 : {
1911 0 : MOZ_ASSERT(tarray.isSingleton());
1912 0 : }
1913 :
1914 0 : const char* kind() { return "freezeObjectForTypedArrayData"; }
1915 :
1916 0 : bool invalidateOnNewType(TypeSet::Type type) { return false; }
1917 0 : bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1918 0 : bool invalidateOnNewObjectState(ObjectGroup* group) {
1919 0 : MOZ_ASSERT(obj->group() == group);
1920 0 : TypedArrayObject& tarr = obj->as<TypedArrayObject>();
1921 0 : return tarr.viewDataEither().unwrapValue() != viewData || tarr.length() != length;
1922 : }
1923 :
1924 0 : bool constraintHolds(JSContext* cx,
1925 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1926 : {
1927 0 : return !invalidateOnNewObjectState(property.object()->maybeGroup());
1928 : }
1929 :
1930 0 : bool shouldSweep() {
1931 : // Note: |viewData| is only used for equality testing.
1932 0 : return IsAboutToBeFinalizedUnbarriered(&obj);
1933 : }
1934 :
1935 0 : JSCompartment* maybeCompartment() {
1936 0 : return obj->compartment();
1937 : }
1938 : };
1939 :
1940 : // Constraint which triggers recompilation if an unboxed object in some group
1941 : // is converted to a native object.
1942 : class ConstraintDataFreezeObjectForUnboxedConvertedToNative
1943 : {
1944 : public:
1945 0 : ConstraintDataFreezeObjectForUnboxedConvertedToNative()
1946 0 : {}
1947 :
1948 0 : const char* kind() { return "freezeObjectForUnboxedConvertedToNative"; }
1949 :
1950 0 : bool invalidateOnNewType(TypeSet::Type type) { return false; }
1951 0 : bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1952 0 : bool invalidateOnNewObjectState(ObjectGroup* group) {
1953 0 : return group->unboxedLayout().nativeGroup() != nullptr;
1954 : }
1955 :
1956 0 : bool constraintHolds(JSContext* cx,
1957 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1958 : {
1959 0 : return !invalidateOnNewObjectState(property.object()->maybeGroup());
1960 : }
1961 :
1962 0 : bool shouldSweep() { return false; }
1963 :
1964 0 : JSCompartment* maybeCompartment() { return nullptr; }
1965 : };
1966 :
1967 : } /* anonymous namespace */
1968 :
1969 : void
1970 0 : TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* constraints)
1971 : {
1972 0 : TypedArrayObject& tarray = singleton()->as<TypedArrayObject>();
1973 0 : HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1974 0 : LifoAlloc* alloc = constraints->alloc();
1975 :
1976 : typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayData> T;
1977 0 : constraints->add(alloc->new_<T>(alloc, objectProperty,
1978 0 : ConstraintDataFreezeObjectForTypedArrayData(tarray)));
1979 0 : }
1980 :
1981 : void
1982 0 : TypeSet::ObjectKey::watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints)
1983 : {
1984 0 : HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1985 0 : LifoAlloc* alloc = constraints->alloc();
1986 :
1987 : typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForUnboxedConvertedToNative> T;
1988 0 : constraints->add(alloc->new_<T>(alloc, objectProperty,
1989 0 : ConstraintDataFreezeObjectForUnboxedConvertedToNative()));
1990 0 : }
1991 :
1992 : static void
1993 236 : ObjectStateChange(JSContext* cx, ObjectGroup* group, bool markingUnknown)
1994 : {
1995 236 : if (group->unknownProperties())
1996 64 : return;
1997 :
1998 : /* All constraints listening to state changes are on the empty id. */
1999 172 : HeapTypeSet* types = group->maybeGetProperty(JSID_EMPTY);
2000 :
2001 : /* Mark as unknown after getting the types, to avoid assertion. */
2002 172 : if (markingUnknown)
2003 128 : group->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
2004 :
2005 172 : if (types) {
2006 0 : if (!cx->helperThread()) {
2007 0 : TypeConstraint* constraint = types->constraintList();
2008 0 : while (constraint) {
2009 0 : constraint->newObjectState(cx, group);
2010 0 : constraint = constraint->next();
2011 : }
2012 : } else {
2013 0 : MOZ_ASSERT(!types->constraintList());
2014 : }
2015 : }
2016 : }
2017 :
2018 : namespace {
2019 :
2020 : class ConstraintDataFreezePropertyState
2021 : {
2022 : public:
2023 : enum Which {
2024 : NON_DATA,
2025 : NON_WRITABLE
2026 : } which;
2027 :
2028 146 : explicit ConstraintDataFreezePropertyState(Which which)
2029 146 : : which(which)
2030 146 : {}
2031 :
2032 0 : const char* kind() { return (which == NON_DATA) ? "freezeNonDataProperty" : "freezeNonWritableProperty"; }
2033 :
2034 0 : bool invalidateOnNewType(TypeSet::Type type) { return false; }
2035 137 : bool invalidateOnNewPropertyState(TypeSet* property) {
2036 137 : return (which == NON_DATA)
2037 137 : ? property->nonDataProperty()
2038 137 : : property->nonWritableProperty();
2039 : }
2040 0 : bool invalidateOnNewObjectState(ObjectGroup* group) { return false; }
2041 :
2042 137 : bool constraintHolds(JSContext* cx,
2043 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
2044 : {
2045 137 : return !invalidateOnNewPropertyState(property.maybeTypes());
2046 : }
2047 :
2048 0 : bool shouldSweep() { return false; }
2049 :
2050 137 : JSCompartment* maybeCompartment() { return nullptr; }
2051 : };
2052 :
2053 : } /* anonymous namespace */
2054 :
2055 : bool
2056 98 : HeapTypeSetKey::nonData(CompilerConstraintList* constraints)
2057 : {
2058 98 : if (maybeTypes() && maybeTypes()->nonDataProperty())
2059 3 : return true;
2060 :
2061 95 : LifoAlloc* alloc = constraints->alloc();
2062 :
2063 : typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
2064 95 : constraints->add(alloc->new_<T>(alloc, *this,
2065 190 : ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA)));
2066 95 : return false;
2067 : }
2068 :
2069 : bool
2070 51 : HeapTypeSetKey::nonWritable(CompilerConstraintList* constraints)
2071 : {
2072 51 : if (maybeTypes() && maybeTypes()->nonWritableProperty())
2073 0 : return true;
2074 :
2075 51 : LifoAlloc* alloc = constraints->alloc();
2076 :
2077 : typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
2078 51 : constraints->add(alloc->new_<T>(alloc, *this,
2079 102 : ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE)));
2080 51 : return false;
2081 : }
2082 :
2083 : namespace {
2084 :
2085 : class ConstraintDataConstantProperty
2086 : {
2087 : public:
2088 6 : explicit ConstraintDataConstantProperty() {}
2089 :
2090 0 : const char* kind() { return "constantProperty"; }
2091 :
2092 0 : bool invalidateOnNewType(TypeSet::Type type) { return false; }
2093 4 : bool invalidateOnNewPropertyState(TypeSet* property) {
2094 4 : return property->nonConstantProperty();
2095 : }
2096 0 : bool invalidateOnNewObjectState(ObjectGroup* group) { return false; }
2097 :
2098 4 : bool constraintHolds(JSContext* cx,
2099 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
2100 : {
2101 4 : return !invalidateOnNewPropertyState(property.maybeTypes());
2102 : }
2103 :
2104 0 : bool shouldSweep() { return false; }
2105 :
2106 4 : JSCompartment* maybeCompartment() { return nullptr; }
2107 : };
2108 :
2109 : } /* anonymous namespace */
2110 :
2111 : bool
2112 6 : HeapTypeSetKey::constant(CompilerConstraintList* constraints, Value* valOut)
2113 : {
2114 6 : if (nonData(constraints))
2115 0 : return false;
2116 :
2117 : // Only singleton object properties can be marked as constants.
2118 6 : JSObject* obj = object()->singleton();
2119 6 : if (!obj || !obj->isNative())
2120 0 : return false;
2121 :
2122 6 : if (maybeTypes() && maybeTypes()->nonConstantProperty())
2123 0 : return false;
2124 :
2125 : // Get the current value of the property.
2126 6 : Shape* shape = obj->as<NativeObject>().lookupPure(id());
2127 6 : if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot() || shape->hadOverwrite())
2128 0 : return false;
2129 :
2130 6 : Value val = obj->as<NativeObject>().getSlot(shape->slot());
2131 :
2132 : // If the value is a pointer to an object in the nursery, don't optimize.
2133 6 : if (val.isGCThing() && IsInsideNursery(val.toGCThing()))
2134 0 : return false;
2135 :
2136 : // If the value is a string that's not atomic, don't optimize.
2137 6 : if (val.isString() && !val.toString()->isAtom())
2138 0 : return false;
2139 :
2140 6 : *valOut = val;
2141 :
2142 6 : LifoAlloc* alloc = constraints->alloc();
2143 : typedef CompilerConstraintInstance<ConstraintDataConstantProperty> T;
2144 6 : constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataConstantProperty()));
2145 6 : return true;
2146 : }
2147 :
2148 : // A constraint that never triggers recompilation.
2149 : class ConstraintDataInert
2150 : {
2151 : public:
2152 0 : explicit ConstraintDataInert() {}
2153 :
2154 0 : const char* kind() { return "inert"; }
2155 :
2156 0 : bool invalidateOnNewType(TypeSet::Type type) { return false; }
2157 0 : bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
2158 0 : bool invalidateOnNewObjectState(ObjectGroup* group) { return false; }
2159 :
2160 0 : bool constraintHolds(JSContext* cx,
2161 : const HeapTypeSetKey& property, TemporaryTypeSet* expected)
2162 : {
2163 0 : return true;
2164 : }
2165 :
2166 0 : bool shouldSweep() { return false; }
2167 :
2168 0 : JSCompartment* maybeCompartment() { return nullptr; }
2169 : };
2170 :
2171 : bool
2172 64 : HeapTypeSetKey::couldBeConstant(CompilerConstraintList* constraints)
2173 : {
2174 : // Only singleton object properties can be marked as constants.
2175 64 : if (!object()->isSingleton())
2176 64 : return false;
2177 :
2178 0 : if (!maybeTypes() || !maybeTypes()->nonConstantProperty())
2179 0 : return true;
2180 :
2181 : // It is possible for a property that was not marked as constant to
2182 : // 'become' one, if we throw away the type property during a GC and
2183 : // regenerate it with the constant flag set. ObjectGroup::sweep only removes
2184 : // type properties if they have no constraints attached to them, so add
2185 : // inert constraints to pin these properties in place.
2186 :
2187 0 : LifoAlloc* alloc = constraints->alloc();
2188 : typedef CompilerConstraintInstance<ConstraintDataInert> T;
2189 0 : constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataInert()));
2190 :
2191 0 : return false;
2192 : }
2193 :
2194 : bool
2195 2 : TemporaryTypeSet::filtersType(const TemporaryTypeSet* other, Type filteredType) const
2196 : {
2197 2 : if (other->unknown())
2198 0 : return unknown();
2199 :
2200 18 : for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
2201 16 : Type type = PrimitiveType(TypeFlagPrimitive(flag));
2202 16 : if (type != filteredType && other->hasType(type) && !hasType(type))
2203 0 : return false;
2204 : }
2205 :
2206 2 : if (other->unknownObject())
2207 0 : return unknownObject();
2208 :
2209 4 : for (size_t i = 0; i < other->getObjectCount(); i++) {
2210 2 : ObjectKey* key = other->getObject(i);
2211 2 : if (key) {
2212 2 : Type type = ObjectType(key);
2213 2 : if (type != filteredType && !hasType(type))
2214 0 : return false;
2215 : }
2216 : }
2217 :
2218 2 : return true;
2219 : }
2220 :
2221 : TemporaryTypeSet::DoubleConversion
2222 12 : TemporaryTypeSet::convertDoubleElements(CompilerConstraintList* constraints)
2223 : {
2224 12 : if (unknownObject() || !getObjectCount())
2225 0 : return AmbiguousDoubleConversion;
2226 :
2227 12 : bool alwaysConvert = true;
2228 12 : bool maybeConvert = false;
2229 12 : bool dontConvert = false;
2230 :
2231 24 : for (unsigned i = 0; i < getObjectCount(); i++) {
2232 12 : ObjectKey* key = getObject(i);
2233 12 : if (!key)
2234 12 : continue;
2235 :
2236 12 : if (key->unknownProperties()) {
2237 0 : alwaysConvert = false;
2238 0 : continue;
2239 : }
2240 :
2241 12 : HeapTypeSetKey property = key->property(JSID_VOID);
2242 12 : property.freeze(constraints);
2243 :
2244 : // We can't convert to double elements for objects which do not have
2245 : // double in their element types (as the conversion may render the type
2246 : // information incorrect), nor for non-array objects (as their elements
2247 : // may point to emptyObjectElements or emptyObjectElementsShared, which
2248 : // cannot be converted).
2249 36 : if (!property.maybeTypes() ||
2250 12 : !property.maybeTypes()->hasType(DoubleType()) ||
2251 0 : key->clasp() != &ArrayObject::class_)
2252 : {
2253 12 : dontConvert = true;
2254 12 : alwaysConvert = false;
2255 12 : continue;
2256 : }
2257 :
2258 : // Only bother with converting known packed arrays whose possible
2259 : // element types are int or double. Other arrays require type tests
2260 : // when elements are accessed regardless of the conversion.
2261 0 : if (property.knownMIRType(constraints) == jit::MIRType::Double &&
2262 0 : !key->hasFlags(constraints, OBJECT_FLAG_NON_PACKED))
2263 : {
2264 0 : maybeConvert = true;
2265 : } else {
2266 0 : alwaysConvert = false;
2267 : }
2268 : }
2269 :
2270 12 : MOZ_ASSERT_IF(alwaysConvert, maybeConvert);
2271 :
2272 12 : if (maybeConvert && dontConvert)
2273 0 : return AmbiguousDoubleConversion;
2274 12 : if (alwaysConvert)
2275 0 : return AlwaysConvertToDoubles;
2276 12 : if (maybeConvert)
2277 0 : return MaybeConvertToDoubles;
2278 12 : return DontConvertToDoubles;
2279 : }
2280 :
2281 : const Class*
2282 214 : TemporaryTypeSet::getKnownClass(CompilerConstraintList* constraints)
2283 : {
2284 214 : if (unknownObject())
2285 54 : return nullptr;
2286 :
2287 160 : const Class* clasp = nullptr;
2288 160 : unsigned count = getObjectCount();
2289 :
2290 178 : for (unsigned i = 0; i < count; i++) {
2291 18 : const Class* nclasp = getObjectClass(i);
2292 18 : if (!nclasp)
2293 0 : continue;
2294 :
2295 18 : if (getObject(i)->unknownProperties())
2296 0 : return nullptr;
2297 :
2298 18 : if (clasp && clasp != nclasp)
2299 0 : return nullptr;
2300 18 : clasp = nclasp;
2301 : }
2302 :
2303 160 : if (clasp) {
2304 36 : for (unsigned i = 0; i < count; i++) {
2305 18 : ObjectKey* key = getObject(i);
2306 18 : if (key && !key->hasStableClassAndProto(constraints))
2307 0 : return nullptr;
2308 : }
2309 : }
2310 :
2311 160 : return clasp;
2312 : }
2313 :
2314 : void
2315 0 : TemporaryTypeSet::getTypedArraySharedness(CompilerConstraintList* constraints,
2316 : TypedArraySharedness* sharedness)
2317 : {
2318 : // In the future this will inspect the object set.
2319 0 : *sharedness = UnknownSharedness;
2320 0 : }
2321 :
2322 : TemporaryTypeSet::ForAllResult
2323 13 : TemporaryTypeSet::forAllClasses(CompilerConstraintList* constraints,
2324 : bool (*func)(const Class* clasp))
2325 : {
2326 13 : if (unknownObject())
2327 7 : return ForAllResult::MIXED;
2328 :
2329 6 : unsigned count = getObjectCount();
2330 6 : if (count == 0)
2331 0 : return ForAllResult::EMPTY;
2332 :
2333 6 : bool true_results = false;
2334 6 : bool false_results = false;
2335 12 : for (unsigned i = 0; i < count; i++) {
2336 6 : const Class* clasp = getObjectClass(i);
2337 6 : if (!clasp)
2338 0 : continue;
2339 6 : if (!getObject(i)->hasStableClassAndProto(constraints))
2340 0 : return ForAllResult::MIXED;
2341 6 : if (func(clasp)) {
2342 0 : true_results = true;
2343 0 : if (false_results)
2344 0 : return ForAllResult::MIXED;
2345 : } else {
2346 6 : false_results = true;
2347 6 : if (true_results)
2348 0 : return ForAllResult::MIXED;
2349 : }
2350 : }
2351 :
2352 6 : MOZ_ASSERT(true_results != false_results);
2353 :
2354 6 : return true_results ? ForAllResult::ALL_TRUE : ForAllResult::ALL_FALSE;
2355 : }
2356 :
2357 : Scalar::Type
2358 30 : TemporaryTypeSet::getTypedArrayType(CompilerConstraintList* constraints,
2359 : TypedArraySharedness* sharedness)
2360 : {
2361 30 : const Class* clasp = getKnownClass(constraints);
2362 :
2363 30 : if (clasp && IsTypedArrayClass(clasp)) {
2364 0 : if (sharedness)
2365 0 : getTypedArraySharedness(constraints, sharedness);
2366 0 : return (Scalar::Type) (clasp - &TypedArrayObject::classes[0]);
2367 : }
2368 30 : return Scalar::MaxTypedArrayViewType;
2369 : }
2370 :
2371 : bool
2372 8 : TemporaryTypeSet::isDOMClass(CompilerConstraintList* constraints)
2373 : {
2374 8 : if (unknownObject())
2375 0 : return false;
2376 :
2377 8 : unsigned count = getObjectCount();
2378 8 : for (unsigned i = 0; i < count; i++) {
2379 8 : const Class* clasp = getObjectClass(i);
2380 8 : if (!clasp)
2381 0 : continue;
2382 8 : if (!clasp->isDOMClass() || !getObject(i)->hasStableClassAndProto(constraints))
2383 8 : return false;
2384 : }
2385 :
2386 0 : return count > 0;
2387 : }
2388 :
2389 : bool
2390 0 : TemporaryTypeSet::maybeCallable(CompilerConstraintList* constraints)
2391 : {
2392 0 : if (!maybeObject())
2393 0 : return false;
2394 :
2395 0 : if (unknownObject())
2396 0 : return true;
2397 :
2398 0 : unsigned count = getObjectCount();
2399 0 : for (unsigned i = 0; i < count; i++) {
2400 0 : const Class* clasp = getObjectClass(i);
2401 0 : if (!clasp)
2402 0 : continue;
2403 0 : if (clasp->isProxy() || clasp->nonProxyCallable())
2404 0 : return true;
2405 0 : if (!getObject(i)->hasStableClassAndProto(constraints))
2406 0 : return true;
2407 : }
2408 :
2409 0 : return false;
2410 : }
2411 :
2412 : bool
2413 0 : TemporaryTypeSet::maybeProxy(CompilerConstraintList* constraints)
2414 : {
2415 0 : if (!maybeObject())
2416 0 : return false;
2417 :
2418 0 : if (unknownObject())
2419 0 : return true;
2420 :
2421 0 : unsigned count = getObjectCount();
2422 0 : for (unsigned i = 0; i < count; i++) {
2423 0 : const Class* clasp = getObjectClass(i);
2424 0 : if (!clasp)
2425 0 : continue;
2426 0 : if (clasp->isProxy() || !getObject(i)->hasStableClassAndProto(constraints))
2427 0 : return true;
2428 : }
2429 :
2430 0 : return false;
2431 : }
2432 :
2433 : bool
2434 678 : TemporaryTypeSet::maybeEmulatesUndefined(CompilerConstraintList* constraints)
2435 : {
2436 678 : if (!maybeObject())
2437 671 : return false;
2438 :
2439 7 : if (unknownObject())
2440 7 : return true;
2441 :
2442 0 : unsigned count = getObjectCount();
2443 0 : for (unsigned i = 0; i < count; i++) {
2444 : // The object emulates undefined if clasp->emulatesUndefined() or if
2445 : // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
2446 : // proxies, we can just check for that.
2447 0 : const Class* clasp = getObjectClass(i);
2448 0 : if (!clasp)
2449 0 : continue;
2450 0 : if (clasp->emulatesUndefined() || clasp->isProxy())
2451 0 : return true;
2452 0 : if (!getObject(i)->hasStableClassAndProto(constraints))
2453 0 : return true;
2454 : }
2455 :
2456 0 : return false;
2457 : }
2458 :
2459 : bool
2460 3 : TemporaryTypeSet::getCommonPrototype(CompilerConstraintList* constraints, JSObject** proto)
2461 : {
2462 3 : if (unknownObject())
2463 0 : return false;
2464 :
2465 3 : *proto = nullptr;
2466 3 : bool isFirst = true;
2467 3 : unsigned count = getObjectCount();
2468 :
2469 6 : for (unsigned i = 0; i < count; i++) {
2470 3 : ObjectKey* key = getObject(i);
2471 3 : if (!key)
2472 0 : continue;
2473 :
2474 3 : if (key->unknownProperties())
2475 0 : return false;
2476 :
2477 3 : TaggedProto nproto = key->proto();
2478 3 : if (isFirst) {
2479 3 : if (nproto.isDynamic())
2480 0 : return false;
2481 3 : *proto = nproto.toObjectOrNull();
2482 3 : isFirst = false;
2483 : } else {
2484 0 : if (nproto != TaggedProto(*proto))
2485 0 : return false;
2486 : }
2487 : }
2488 :
2489 : // Guard against mutating __proto__.
2490 6 : for (unsigned i = 0; i < count; i++) {
2491 3 : if (ObjectKey* key = getObject(i))
2492 3 : JS_ALWAYS_TRUE(key->hasStableClassAndProto(constraints));
2493 : }
2494 :
2495 3 : return true;
2496 : }
2497 :
2498 : bool
2499 1 : TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid id)
2500 : {
2501 1 : if (unknownObject())
2502 0 : return true;
2503 :
2504 1 : for (unsigned i = 0; i < getObjectCount(); i++) {
2505 1 : ObjectKey* key = getObject(i);
2506 1 : if (!key)
2507 0 : continue;
2508 :
2509 1 : if (key->unknownProperties())
2510 1 : return true;
2511 :
2512 1 : HeapTypeSetKey property = key->property(id);
2513 1 : if (property.needsBarrier(constraints))
2514 1 : return true;
2515 : }
2516 :
2517 0 : return false;
2518 : }
2519 :
2520 : bool
2521 17 : js::ClassCanHaveExtraProperties(const Class* clasp)
2522 : {
2523 17 : if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
2524 0 : return false;
2525 17 : return clasp->getResolve()
2526 17 : || clasp->getGetProperty()
2527 17 : || clasp->getOpsLookupProperty()
2528 17 : || clasp->getOpsGetProperty()
2529 34 : || IsTypedArrayClass(clasp);
2530 : }
2531 :
2532 : void
2533 0 : TypeZone::processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles)
2534 : {
2535 0 : MOZ_ASSERT(!recompiles.empty());
2536 :
2537 : /*
2538 : * Steal the list of scripts to recompile, to make sure we don't try to
2539 : * recursively recompile them.
2540 : */
2541 0 : RecompileInfoVector pending;
2542 0 : for (size_t i = 0; i < recompiles.length(); i++) {
2543 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
2544 0 : if (!pending.append(recompiles[i]))
2545 0 : oomUnsafe.crash("processPendingRecompiles");
2546 : }
2547 0 : recompiles.clear();
2548 :
2549 0 : jit::Invalidate(*this, fop, pending);
2550 :
2551 0 : MOZ_ASSERT(recompiles.empty());
2552 0 : }
2553 :
2554 : void
2555 0 : TypeZone::addPendingRecompile(JSContext* cx, const RecompileInfo& info)
2556 : {
2557 0 : CompilerOutput* co = info.compilerOutput(cx);
2558 0 : if (!co || !co->isValid() || co->pendingInvalidation())
2559 0 : return;
2560 :
2561 0 : InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%" PRIuSIZE,
2562 : co->script(), co->script()->filename(), co->script()->lineno());
2563 :
2564 0 : co->setPendingInvalidation();
2565 :
2566 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
2567 0 : if (!cx->zone()->types.activeAnalysis->pendingRecompiles.append(info))
2568 0 : oomUnsafe.crash("Could not update pendingRecompiles");
2569 : }
2570 :
2571 : void
2572 2 : TypeZone::addPendingRecompile(JSContext* cx, JSScript* script)
2573 : {
2574 2 : MOZ_ASSERT(script);
2575 :
2576 2 : CancelOffThreadIonCompile(script);
2577 :
2578 : // Let the script warm up again before attempting another compile.
2579 2 : if (jit::IsBaselineEnabled(cx))
2580 2 : script->resetWarmUpCounter();
2581 :
2582 2 : if (script->hasIonScript())
2583 0 : addPendingRecompile(cx, script->ionScript()->recompileInfo());
2584 :
2585 : // Trigger recompilation of any callers inlining this script.
2586 2 : if (TypeScript* types = script->types()) {
2587 2 : for (RecompileInfo info : types->inlinedCompilations())
2588 0 : addPendingRecompile(cx, info);
2589 2 : types->inlinedCompilations().clearAndFree();
2590 : }
2591 2 : }
2592 :
2593 : #ifdef JS_CRASH_DIAGNOSTICS
2594 : void
2595 0 : js::ReportMagicWordFailure(uintptr_t actual, uintptr_t expected)
2596 : {
2597 0 : MOZ_CRASH_UNSAFE_PRINTF("Got 0x%" PRIxPTR " expected magic word 0x%" PRIxPTR,
2598 : actual, expected);
2599 : }
2600 :
2601 : void
2602 0 : js::ReportMagicWordFailure(uintptr_t actual, uintptr_t expected, uintptr_t flags, uintptr_t objectSet)
2603 : {
2604 0 : MOZ_CRASH_UNSAFE_PRINTF("Got 0x%" PRIxPTR " expected magic word 0x%" PRIxPTR
2605 : " flags 0x%" PRIxPTR " objectSet 0x%" PRIxPTR,
2606 : actual, expected, flags, objectSet);
2607 : }
2608 : #endif
2609 :
2610 : void
2611 0 : js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force)
2612 : {
2613 : #ifdef DEBUG
2614 0 : gc::AutoSuppressGC suppressGC(cx);
2615 0 : JSAutoRequest request(cx);
2616 :
2617 0 : Zone* zone = comp->zone();
2618 0 : AutoEnterAnalysis enter(nullptr, zone);
2619 :
2620 0 : if (!force && !InferSpewActive(ISpewResult))
2621 0 : return;
2622 :
2623 0 : RootedScript script(cx);
2624 0 : for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
2625 0 : script = iter;
2626 0 : if (script->types())
2627 0 : script->types()->printTypes(cx, script);
2628 : }
2629 :
2630 0 : for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next())
2631 0 : group->print();
2632 : #endif
2633 : }
2634 :
2635 : /////////////////////////////////////////////////////////////////////
2636 : // ObjectGroup
2637 : /////////////////////////////////////////////////////////////////////
2638 :
2639 : static inline void
2640 955 : UpdatePropertyType(JSContext* cx, HeapTypeSet* types, NativeObject* obj, Shape* shape,
2641 : bool indexed)
2642 : {
2643 955 : MOZ_ASSERT(obj->isSingleton() && !obj->hasLazyGroup());
2644 :
2645 955 : if (!shape->writable())
2646 151 : types->setNonWritableProperty(cx);
2647 :
2648 955 : if (shape->hasGetterValue() || shape->hasSetterValue()) {
2649 1 : types->setNonDataProperty(cx);
2650 1 : types->TypeSet::addType(TypeSet::UnknownType(), &cx->typeLifoAlloc());
2651 954 : } else if (shape->hasDefaultGetter() && shape->hasSlot()) {
2652 954 : if (!indexed && types->canSetDefinite(shape->slot()))
2653 954 : types->setDefinite(shape->slot());
2654 :
2655 954 : const Value& value = obj->getSlot(shape->slot());
2656 :
2657 : /*
2658 : * Don't add initial undefined types for properties of global objects
2659 : * that are not collated into the JSID_VOID property (see propertySet
2660 : * comment).
2661 : *
2662 : * Also don't add untracked values (initial uninitialized lexical magic
2663 : * values and optimized out values) as appearing in CallObjects, module
2664 : * environments or the global lexical scope.
2665 : */
2666 954 : MOZ_ASSERT_IF(TypeSet::IsUntrackedValue(value),
2667 : obj->is<CallObject>() ||
2668 : obj->is<ModuleEnvironmentObject>() ||
2669 : IsExtensibleLexicalEnvironment(obj));
2670 1908 : if ((indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) &&
2671 954 : !TypeSet::IsUntrackedValue(value))
2672 : {
2673 954 : TypeSet::Type type = TypeSet::GetValueType(value);
2674 954 : types->TypeSet::addType(type, &cx->typeLifoAlloc());
2675 954 : types->postWriteBarrier(cx, type);
2676 : }
2677 :
2678 954 : if (indexed || shape->hadOverwrite()) {
2679 50 : types->setNonConstantProperty(cx);
2680 : } else {
2681 904 : InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant",
2682 : InferSpewColor(types), types, InferSpewColorReset(),
2683 : TypeSet::ObjectGroupString(obj->group()), TypeIdString(shape->propid()));
2684 : }
2685 : }
2686 955 : }
2687 :
2688 : void
2689 24801 : ObjectGroup::updateNewPropertyTypes(JSContext* cx, JSObject* objArg, jsid id, HeapTypeSet* types)
2690 : {
2691 24801 : InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
2692 : InferSpewColor(types), types, InferSpewColorReset(),
2693 : TypeSet::ObjectGroupString(this), TypeIdString(id));
2694 :
2695 24801 : MOZ_ASSERT_IF(objArg, objArg->group() == this);
2696 24801 : MOZ_ASSERT_IF(singleton(), objArg);
2697 :
2698 24801 : if (!singleton() || !objArg->isNative()) {
2699 23575 : types->setNonConstantProperty(cx);
2700 23575 : return;
2701 : }
2702 :
2703 1226 : NativeObject* obj = &objArg->as<NativeObject>();
2704 :
2705 : /*
2706 : * Fill the property in with any type the object already has in an own
2707 : * property. We are only interested in plain native properties and
2708 : * dense elements which don't go through a barrier when read by the VM
2709 : * or jitcode.
2710 : */
2711 :
2712 1226 : if (JSID_IS_VOID(id)) {
2713 : /* Go through all shapes on the object to get integer-valued properties. */
2714 12 : RootedShape shape(cx, obj->lastProperty());
2715 300 : while (!shape->isEmptyShape()) {
2716 147 : if (JSID_IS_VOID(IdToTypeId(shape->propid())))
2717 0 : UpdatePropertyType(cx, types, obj, shape, true);
2718 147 : shape = shape->previous();
2719 : }
2720 :
2721 : /* Also get values of any dense elements in the object. */
2722 6 : for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
2723 0 : const Value& value = obj->getDenseElement(i);
2724 0 : if (!value.isMagic(JS_ELEMENTS_HOLE)) {
2725 0 : TypeSet::Type type = TypeSet::GetValueType(value);
2726 0 : types->TypeSet::addType(type, &cx->typeLifoAlloc());
2727 0 : types->postWriteBarrier(cx, type);
2728 : }
2729 : }
2730 1220 : } else if (!JSID_IS_EMPTY(id)) {
2731 2366 : RootedId rootedId(cx, id);
2732 1183 : Shape* shape = obj->lookup(cx, rootedId);
2733 1183 : if (shape)
2734 955 : UpdatePropertyType(cx, types, obj, shape, false);
2735 : }
2736 :
2737 1226 : if (obj->watched()) {
2738 : /*
2739 : * Mark the property as non-data, to inhibit optimizations on it
2740 : * and avoid bypassing the watchpoint handler.
2741 : */
2742 0 : types->setNonDataProperty(cx);
2743 : }
2744 : }
2745 :
2746 : void
2747 44 : ObjectGroup::addDefiniteProperties(JSContext* cx, Shape* shape)
2748 : {
2749 44 : if (unknownProperties())
2750 0 : return;
2751 :
2752 : // Mark all properties of shape as definite properties of this group.
2753 88 : AutoEnterAnalysis enter(cx);
2754 :
2755 246 : while (!shape->isEmptyShape()) {
2756 101 : jsid id = IdToTypeId(shape->propid());
2757 101 : if (!JSID_IS_VOID(id)) {
2758 101 : MOZ_ASSERT_IF(shape->slot() >= shape->numFixedSlots(),
2759 : shape->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS);
2760 101 : TypeSet* types = getProperty(cx, nullptr, id);
2761 101 : if (!types) {
2762 0 : MOZ_ASSERT(unknownProperties());
2763 0 : return;
2764 : }
2765 101 : if (types->canSetDefinite(shape->slot()))
2766 101 : types->setDefinite(shape->slot());
2767 : }
2768 :
2769 101 : shape = shape->previous();
2770 : }
2771 : }
2772 :
2773 : bool
2774 0 : ObjectGroup::matchDefiniteProperties(HandleObject obj)
2775 : {
2776 0 : unsigned count = getPropertyCount();
2777 0 : for (unsigned i = 0; i < count; i++) {
2778 0 : Property* prop = getProperty(i);
2779 0 : if (!prop)
2780 0 : continue;
2781 0 : if (prop->types.definiteProperty()) {
2782 0 : unsigned slot = prop->types.definiteSlot();
2783 :
2784 0 : bool found = false;
2785 0 : Shape* shape = obj->as<NativeObject>().lastProperty();
2786 0 : while (!shape->isEmptyShape()) {
2787 0 : if (shape->slot() == slot && shape->propid() == prop->id) {
2788 0 : found = true;
2789 0 : break;
2790 : }
2791 0 : shape = shape->previous();
2792 : }
2793 0 : if (!found)
2794 0 : return false;
2795 : }
2796 : }
2797 :
2798 0 : return true;
2799 : }
2800 :
2801 : void
2802 37792 : js::AddTypePropertyId(JSContext* cx, ObjectGroup* group, JSObject* obj, jsid id, TypeSet::Type type)
2803 : {
2804 37792 : MOZ_ASSERT(id == IdToTypeId(id));
2805 :
2806 37792 : if (group->unknownProperties())
2807 13944 : return;
2808 :
2809 61643 : AutoEnterAnalysis enter(cx);
2810 :
2811 37733 : HeapTypeSet* types = group->getProperty(cx, obj, id);
2812 37732 : if (!types)
2813 0 : return;
2814 :
2815 : // Clear any constant flag if it exists.
2816 37732 : if (!types->empty() && !types->nonConstantProperty()) {
2817 33 : InferSpew(ISpewOps, "constantMutated: %sT%p%s %s",
2818 : InferSpewColor(types), types, InferSpewColorReset(), TypeSet::TypeString(type));
2819 33 : types->setNonConstantProperty(cx);
2820 : }
2821 :
2822 37732 : if (types->hasType(type))
2823 13823 : return;
2824 :
2825 23910 : InferSpew(ISpewOps, "externalType: property %s %s: %s",
2826 : TypeSet::ObjectGroupString(group), TypeIdString(id), TypeSet::TypeString(type));
2827 23910 : types->addType(cx, type);
2828 :
2829 : // If this addType caused the type set to be marked as containing any
2830 : // object, make sure that is reflected in other type sets the addType is
2831 : // propagated to below.
2832 23910 : if (type.isObjectUnchecked() && types->unknownObject())
2833 667 : type = TypeSet::AnyObjectType();
2834 :
2835 : // Propagate new types from partially initialized groups to fully
2836 : // initialized groups for the acquired properties analysis. Note that we
2837 : // don't need to do this for other property changes, as these will also be
2838 : // reflected via shape changes on the object that will prevent the object
2839 : // from acquiring the fully initialized group.
2840 23910 : if (group->newScript() && group->newScript()->initializedGroup())
2841 5 : AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type);
2842 :
2843 : // Maintain equivalent type information for unboxed object groups and their
2844 : // corresponding native group. Since type sets might contain the unboxed
2845 : // group but not the native group, this ensures optimizations based on the
2846 : // unboxed group are valid for the native group.
2847 23910 : if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup())
2848 1 : AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type);
2849 23910 : if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
2850 1 : AddTypePropertyId(cx, unboxedGroup, nullptr, id, type);
2851 : }
2852 :
2853 : void
2854 3068 : js::AddTypePropertyId(JSContext* cx, ObjectGroup* group, JSObject* obj, jsid id, const Value& value)
2855 : {
2856 3068 : AddTypePropertyId(cx, group, obj, id, TypeSet::GetValueType(value));
2857 3068 : }
2858 :
2859 : void
2860 2229 : ObjectGroup::markPropertyNonData(JSContext* cx, JSObject* obj, jsid id)
2861 : {
2862 4458 : AutoEnterAnalysis enter(cx);
2863 :
2864 2229 : id = IdToTypeId(id);
2865 :
2866 2229 : HeapTypeSet* types = getProperty(cx, obj, id);
2867 2229 : if (types)
2868 2229 : types->setNonDataProperty(cx);
2869 2229 : }
2870 :
2871 : void
2872 3776 : ObjectGroup::markPropertyNonWritable(JSContext* cx, JSObject* obj, jsid id)
2873 : {
2874 7552 : AutoEnterAnalysis enter(cx);
2875 :
2876 3776 : id = IdToTypeId(id);
2877 :
2878 3776 : HeapTypeSet* types = getProperty(cx, obj, id);
2879 3776 : if (types)
2880 3776 : types->setNonWritableProperty(cx);
2881 3776 : }
2882 :
2883 : void
2884 12 : ObjectGroup::markStateChange(JSContext* cx)
2885 : {
2886 12 : if (unknownProperties())
2887 0 : return;
2888 :
2889 24 : AutoEnterAnalysis enter(cx);
2890 12 : HeapTypeSet* types = maybeGetProperty(JSID_EMPTY);
2891 12 : if (types) {
2892 0 : if (!cx->helperThread()) {
2893 0 : TypeConstraint* constraint = types->constraintList();
2894 0 : while (constraint) {
2895 0 : constraint->newObjectState(cx, this);
2896 0 : constraint = constraint->next();
2897 : }
2898 : } else {
2899 0 : MOZ_ASSERT(!types->constraintList());
2900 : }
2901 : }
2902 : }
2903 :
2904 : void
2905 108 : ObjectGroup::setFlags(JSContext* cx, ObjectGroupFlags flags)
2906 : {
2907 108 : if (hasAllFlags(flags))
2908 0 : return;
2909 :
2910 216 : AutoEnterAnalysis enter(cx);
2911 :
2912 108 : addFlags(flags);
2913 :
2914 108 : InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeSet::ObjectGroupString(this), flags);
2915 :
2916 108 : ObjectStateChange(cx, this, false);
2917 :
2918 : // Propagate flag changes from partially to fully initialized groups for the
2919 : // acquired properties analysis.
2920 108 : if (newScript() && newScript()->initializedGroup())
2921 0 : newScript()->initializedGroup()->setFlags(cx, flags);
2922 :
2923 : // Propagate flag changes between unboxed and corresponding native groups.
2924 108 : if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
2925 0 : maybeUnboxedLayout()->nativeGroup()->setFlags(cx, flags);
2926 108 : if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
2927 0 : unboxedGroup->setFlags(cx, flags);
2928 : }
2929 :
2930 : void
2931 128 : ObjectGroup::markUnknown(JSContext* cx)
2932 : {
2933 256 : AutoEnterAnalysis enter(cx);
2934 :
2935 128 : MOZ_ASSERT(cx->zone()->types.activeAnalysis);
2936 128 : MOZ_ASSERT(!unknownProperties());
2937 :
2938 128 : InferSpew(ISpewOps, "UnknownProperties: %s", TypeSet::ObjectGroupString(this));
2939 :
2940 128 : clearNewScript(cx);
2941 128 : ObjectStateChange(cx, this, true);
2942 :
2943 : /*
2944 : * Existing constraints may have already been added to this object, which we need
2945 : * to do the right thing for. We can't ensure that we will mark all unknown
2946 : * objects before they have been accessed, as the __proto__ of a known object
2947 : * could be dynamically set to an unknown object, and we can decide to ignore
2948 : * properties of an object during analysis (i.e. hashmaps). Adding unknown for
2949 : * any properties accessed already accounts for possible values read from them.
2950 : */
2951 :
2952 128 : unsigned count = getPropertyCount();
2953 307 : for (unsigned i = 0; i < count; i++) {
2954 179 : Property* prop = getProperty(i);
2955 179 : if (prop) {
2956 161 : prop->types.addType(cx, TypeSet::UnknownType());
2957 161 : prop->types.setNonDataProperty(cx);
2958 : }
2959 : }
2960 :
2961 128 : if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
2962 0 : MarkObjectGroupUnknownProperties(cx, unboxedGroup);
2963 128 : if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
2964 0 : MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup());
2965 128 : if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
2966 0 : MarkObjectGroupUnknownProperties(cx, unboxedGroup);
2967 128 : }
2968 :
2969 : TypeNewScript*
2970 136 : ObjectGroup::anyNewScript()
2971 : {
2972 136 : if (newScript())
2973 16 : return newScript();
2974 120 : if (maybeUnboxedLayout())
2975 0 : return unboxedLayout().newScript();
2976 120 : return nullptr;
2977 : }
2978 :
2979 : void
2980 8 : ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
2981 : {
2982 : // Clear the TypeNewScript from this ObjectGroup and, if it has been
2983 : // analyzed, remove it from the newObjectGroups table so that it will not be
2984 : // produced by calling 'new' on the associated function anymore.
2985 : // The TypeNewScript is not actually destroyed.
2986 8 : TypeNewScript* newScript = anyNewScript();
2987 8 : MOZ_ASSERT(newScript);
2988 :
2989 8 : if (newScript->analyzed()) {
2990 0 : ObjectGroupCompartment& objectGroups = newScript->function()->compartment()->objectGroups;
2991 0 : TaggedProto proto = this->proto();
2992 0 : if (proto.isObject() && IsForwarded(proto.toObject()))
2993 0 : proto = TaggedProto(Forwarded(proto.toObject()));
2994 0 : JSObject* associated = MaybeForwarded(newScript->function());
2995 0 : if (replacement) {
2996 0 : MOZ_ASSERT(replacement->newScript()->function() == newScript->function());
2997 0 : objectGroups.replaceDefaultNewGroup(nullptr, proto, associated, replacement);
2998 : } else {
2999 0 : objectGroups.removeDefaultNewGroup(nullptr, proto, associated);
3000 : }
3001 : } else {
3002 8 : MOZ_ASSERT(!replacement);
3003 : }
3004 :
3005 8 : if (this->newScript())
3006 8 : setAddendum(Addendum_None, nullptr, writeBarrier);
3007 : else
3008 0 : unboxedLayout().setNewScript(nullptr, writeBarrier);
3009 8 : }
3010 :
3011 : void
3012 0 : ObjectGroup::maybeClearNewScriptOnOOM()
3013 : {
3014 0 : MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
3015 :
3016 0 : if (!isMarkedAny())
3017 0 : return;
3018 :
3019 0 : TypeNewScript* newScript = anyNewScript();
3020 0 : if (!newScript)
3021 0 : return;
3022 :
3023 0 : addFlags(OBJECT_FLAG_NEW_SCRIPT_CLEARED);
3024 :
3025 : // This method is called during GC sweeping, so don't trigger pre barriers.
3026 0 : detachNewScript(/* writeBarrier = */ false, nullptr);
3027 :
3028 0 : js_delete(newScript);
3029 : }
3030 :
3031 : void
3032 128 : ObjectGroup::clearNewScript(JSContext* cx, ObjectGroup* replacement /* = nullptr*/)
3033 : {
3034 128 : TypeNewScript* newScript = anyNewScript();
3035 128 : if (!newScript)
3036 120 : return;
3037 :
3038 16 : AutoEnterAnalysis enter(cx);
3039 :
3040 8 : if (!replacement) {
3041 : // Invalidate any Ion code constructing objects of this type.
3042 8 : setFlags(cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED);
3043 :
3044 : // Mark the constructing function as having its 'new' script cleared, so we
3045 : // will not try to construct another one later.
3046 16 : RootedFunction fun(cx, newScript->function());
3047 8 : if (!JSObject::setNewScriptCleared(cx, fun))
3048 0 : cx->recoverFromOutOfMemory();
3049 : }
3050 :
3051 8 : detachNewScript(/* writeBarrier = */ true, replacement);
3052 :
3053 8 : if (!cx->helperThread()) {
3054 8 : bool found = newScript->rollbackPartiallyInitializedObjects(cx, this);
3055 :
3056 : // If we managed to rollback any partially initialized objects, then
3057 : // any definite properties we added due to analysis of the new script
3058 : // are now invalid, so remove them. If there weren't any partially
3059 : // initialized objects then we don't need to change type information,
3060 : // as no more objects of this type will be created and the 'new' script
3061 : // analysis was still valid when older objects were created.
3062 8 : if (found) {
3063 0 : for (unsigned i = 0; i < getPropertyCount(); i++) {
3064 0 : Property* prop = getProperty(i);
3065 0 : if (!prop)
3066 0 : continue;
3067 0 : if (prop->types.definiteProperty())
3068 0 : prop->types.setNonDataProperty(cx);
3069 : }
3070 : }
3071 : } else {
3072 : // Helper threads are not allowed to run scripts.
3073 0 : MOZ_ASSERT(!cx->activation());
3074 : }
3075 :
3076 8 : js_delete(newScript);
3077 8 : markStateChange(cx);
3078 : }
3079 :
3080 : void
3081 0 : ObjectGroup::print()
3082 : {
3083 0 : TaggedProto tagged(proto());
3084 0 : fprintf(stderr, "%s : %s",
3085 : TypeSet::ObjectGroupString(this),
3086 0 : tagged.isObject()
3087 0 : ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject()))
3088 0 : : tagged.isDynamic()
3089 0 : ? "(dynamic)"
3090 0 : : "(null)");
3091 :
3092 0 : if (unknownProperties()) {
3093 0 : fprintf(stderr, " unknown");
3094 : } else {
3095 0 : if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES))
3096 0 : fprintf(stderr, " dense");
3097 0 : if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED))
3098 0 : fprintf(stderr, " packed");
3099 0 : if (!hasAnyFlags(OBJECT_FLAG_LENGTH_OVERFLOW))
3100 0 : fprintf(stderr, " noLengthOverflow");
3101 0 : if (hasAnyFlags(OBJECT_FLAG_ITERATED))
3102 0 : fprintf(stderr, " iterated");
3103 0 : if (maybeInterpretedFunction())
3104 0 : fprintf(stderr, " ifun");
3105 : }
3106 :
3107 0 : unsigned count = getPropertyCount();
3108 :
3109 0 : if (count == 0) {
3110 0 : fprintf(stderr, " {}\n");
3111 0 : return;
3112 : }
3113 :
3114 0 : fprintf(stderr, " {");
3115 :
3116 0 : if (newScript()) {
3117 0 : if (newScript()->analyzed()) {
3118 0 : fprintf(stderr, "\n newScript %d properties",
3119 0 : (int) newScript()->templateObject()->slotSpan());
3120 0 : if (newScript()->initializedGroup()) {
3121 0 : fprintf(stderr, " initializedGroup %#" PRIxPTR " with %d properties",
3122 0 : uintptr_t(newScript()->initializedGroup()), int(newScript()->initializedShape()->slotSpan()));
3123 : }
3124 : } else {
3125 0 : fprintf(stderr, "\n newScript unanalyzed");
3126 : }
3127 : }
3128 :
3129 0 : for (unsigned i = 0; i < count; i++) {
3130 0 : Property* prop = getProperty(i);
3131 0 : if (prop) {
3132 0 : fprintf(stderr, "\n %s:", TypeIdString(prop->id));
3133 0 : prop->types.print();
3134 : }
3135 : }
3136 :
3137 0 : fprintf(stderr, "\n}\n");
3138 : }
3139 :
3140 : /////////////////////////////////////////////////////////////////////
3141 : // Type Analysis
3142 : /////////////////////////////////////////////////////////////////////
3143 :
3144 : /*
3145 : * Persistent constraint clearing out newScript and definite properties from
3146 : * an object should a property on another object get a getter or setter.
3147 : */
3148 : class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
3149 : {
3150 : public:
3151 : ObjectGroup* group;
3152 :
3153 0 : explicit TypeConstraintClearDefiniteGetterSetter(ObjectGroup* group)
3154 0 : : group(group)
3155 0 : {}
3156 :
3157 0 : const char* kind() { return "clearDefiniteGetterSetter"; }
3158 :
3159 0 : void newPropertyState(JSContext* cx, TypeSet* source) {
3160 : /*
3161 : * Clear out the newScript shape and definite property information from
3162 : * an object if the source type set could be a setter or could be
3163 : * non-writable.
3164 : */
3165 0 : if (source->nonDataProperty() || source->nonWritableProperty())
3166 0 : group->clearNewScript(cx);
3167 0 : }
3168 :
3169 0 : void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) {}
3170 :
3171 0 : bool sweep(TypeZone& zone, TypeConstraint** res) {
3172 0 : if (IsAboutToBeFinalizedUnbarriered(&group))
3173 0 : return false;
3174 0 : *res = zone.typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group);
3175 0 : return true;
3176 : }
3177 :
3178 0 : JSCompartment* maybeCompartment() {
3179 0 : return group->compartment();
3180 : }
3181 : };
3182 :
3183 : bool
3184 0 : js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id)
3185 : {
3186 : /*
3187 : * Ensure that if the properties named here could have a getter, setter or
3188 : * a permanent property in any transitive prototype, the definite
3189 : * properties get cleared from the group.
3190 : */
3191 0 : RootedObject proto(cx, group->proto().toObjectOrNull());
3192 0 : while (proto) {
3193 0 : ObjectGroup* protoGroup = JSObject::getGroup(cx, proto);
3194 0 : if (!protoGroup) {
3195 0 : cx->recoverFromOutOfMemory();
3196 0 : return false;
3197 : }
3198 0 : if (protoGroup->unknownProperties())
3199 0 : return false;
3200 0 : HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id);
3201 0 : if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
3202 0 : return false;
3203 0 : if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group)))
3204 0 : return false;
3205 0 : proto = proto->staticPrototype();
3206 : }
3207 0 : return true;
3208 : }
3209 :
3210 : /*
3211 : * Constraint which clears definite properties on a group should a type set
3212 : * contain any types other than a single object.
3213 : */
3214 : class TypeConstraintClearDefiniteSingle : public TypeConstraint
3215 : {
3216 : public:
3217 : ObjectGroup* group;
3218 :
3219 0 : explicit TypeConstraintClearDefiniteSingle(ObjectGroup* group)
3220 0 : : group(group)
3221 0 : {}
3222 :
3223 0 : const char* kind() { return "clearDefiniteSingle"; }
3224 :
3225 0 : void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) {
3226 0 : if (source->baseFlags() || source->getObjectCount() > 1)
3227 0 : group->clearNewScript(cx);
3228 0 : }
3229 :
3230 0 : bool sweep(TypeZone& zone, TypeConstraint** res) {
3231 0 : if (IsAboutToBeFinalizedUnbarriered(&group))
3232 0 : return false;
3233 0 : *res = zone.typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(group);
3234 0 : return true;
3235 : }
3236 :
3237 0 : JSCompartment* maybeCompartment() {
3238 0 : return group->compartment();
3239 : }
3240 : };
3241 :
3242 : bool
3243 0 : js::AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
3244 : JSScript* script, JSScript* calleeScript)
3245 : {
3246 : // Look for any uses of the specified calleeScript in type sets for
3247 : // |script|, and add constraints to ensure that if the type sets' contents
3248 : // change then the definite properties are cleared from the type.
3249 : // This ensures that the inlining performed when the definite properties
3250 : // analysis was done is stable. We only need to look at type sets which
3251 : // contain a single object, as IonBuilder does not inline polymorphic sites
3252 : // during the definite properties analysis.
3253 :
3254 : TypeSet::ObjectKey* calleeKey =
3255 0 : TypeSet::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
3256 :
3257 0 : unsigned count = TypeScript::NumTypeSets(script);
3258 0 : StackTypeSet* typeArray = script->types()->typeArray();
3259 :
3260 0 : for (unsigned i = 0; i < count; i++) {
3261 0 : StackTypeSet* types = &typeArray[i];
3262 0 : if (!types->unknownObject() && types->getObjectCount() == 1) {
3263 0 : if (calleeKey != types->getObject(0)) {
3264 : // Also check if the object is the Function.call or
3265 : // Function.apply native. IonBuilder uses the presence of these
3266 : // functions during inlining.
3267 0 : JSObject* singleton = types->getSingleton(0);
3268 0 : if (!singleton || !singleton->is<JSFunction>())
3269 0 : continue;
3270 0 : JSFunction* fun = &singleton->as<JSFunction>();
3271 0 : if (!fun->isNative())
3272 0 : continue;
3273 0 : if (fun->native() != fun_call && fun->native() != fun_apply)
3274 0 : continue;
3275 : }
3276 : // This is a type set that might have been used when inlining
3277 : // |calleeScript| into |script|.
3278 0 : if (!types->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(group)))
3279 0 : return false;
3280 : }
3281 : }
3282 :
3283 0 : return true;
3284 : }
3285 :
3286 : /////////////////////////////////////////////////////////////////////
3287 : // Interface functions
3288 : /////////////////////////////////////////////////////////////////////
3289 :
3290 : void
3291 1053 : js::TypeMonitorCallSlow(JSContext* cx, JSObject* callee, const CallArgs& args, bool constructing)
3292 : {
3293 1053 : unsigned nargs = callee->as<JSFunction>().nargs();
3294 1053 : JSScript* script = callee->as<JSFunction>().nonLazyScript();
3295 :
3296 1053 : if (!constructing)
3297 1037 : TypeScript::SetThis(cx, script, args.thisv());
3298 :
3299 : /*
3300 : * Add constraints going up to the minimum of the actual and formal count.
3301 : * If there are more actuals than formals the later values can only be
3302 : * accessed through the arguments object, which is monitored.
3303 : */
3304 1053 : unsigned arg = 0;
3305 4039 : for (; arg < args.length() && arg < nargs; arg++)
3306 1493 : TypeScript::SetArgument(cx, script, arg, args[arg]);
3307 :
3308 : /* Watch for fewer actuals than formals to the call. */
3309 1289 : for (; arg < nargs; arg++)
3310 118 : TypeScript::SetArgument(cx, script, arg, UndefinedValue());
3311 1053 : }
3312 :
3313 : void
3314 762 : js::FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap)
3315 : {
3316 762 : uint32_t added = 0;
3317 72959 : for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
3318 72925 : JSOp op = JSOp(*pc);
3319 72925 : if (CodeSpec[op].format & JOF_TYPESET) {
3320 15729 : bytecodeMap[added++] = script->pcToOffset(pc);
3321 15729 : if (added == script->nTypeSets())
3322 728 : break;
3323 : }
3324 : }
3325 762 : MOZ_ASSERT(added == script->nTypeSets());
3326 762 : }
3327 :
3328 : void
3329 1153 : js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type)
3330 : {
3331 1153 : assertSameCompartment(cx, script, type);
3332 :
3333 2199 : AutoEnterAnalysis enter(cx);
3334 :
3335 1153 : StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
3336 1153 : if (types->hasType(type))
3337 107 : return;
3338 :
3339 1046 : InferSpew(ISpewOps, "bytecodeType: %p %05" PRIuSIZE ": %s",
3340 : script, script->pcToOffset(pc), TypeSet::TypeString(type));
3341 1046 : types->addType(cx, type);
3342 : }
3343 :
3344 : void
3345 6400 : js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, StackTypeSet* types,
3346 : TypeSet::Type type)
3347 : {
3348 6400 : assertSameCompartment(cx, script, type);
3349 :
3350 12800 : AutoEnterAnalysis enter(cx);
3351 :
3352 6400 : MOZ_ASSERT(types == TypeScript::BytecodeTypes(script, pc));
3353 6400 : MOZ_ASSERT(!types->hasType(type));
3354 :
3355 6400 : InferSpew(ISpewOps, "bytecodeType: %p %05" PRIuSIZE ": %s",
3356 : script, script->pcToOffset(pc), TypeSet::TypeString(type));
3357 6400 : types->addType(cx, type);
3358 6400 : }
3359 :
3360 : void
3361 108759 : js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const js::Value& rval)
3362 : {
3363 : /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
3364 108759 : if (!(CodeSpec[*pc].format & JOF_TYPESET))
3365 198 : return;
3366 :
3367 108561 : if (!script->hasBaselineScript())
3368 107408 : return;
3369 :
3370 1153 : TypeMonitorResult(cx, script, pc, TypeSet::GetValueType(rval));
3371 : }
3372 :
3373 : /////////////////////////////////////////////////////////////////////
3374 : // TypeScript
3375 : /////////////////////////////////////////////////////////////////////
3376 :
3377 : bool
3378 705 : JSScript::makeTypes(JSContext* cx)
3379 : {
3380 705 : MOZ_ASSERT(!types_);
3381 :
3382 1410 : AutoEnterAnalysis enter(cx);
3383 :
3384 705 : unsigned count = TypeScript::NumTypeSets(this);
3385 :
3386 : TypeScript* typeScript = (TypeScript*)
3387 705 : zone()->pod_calloc<uint8_t>(TypeScript::SizeIncludingTypeArray(count));
3388 705 : if (!typeScript) {
3389 0 : ReportOutOfMemory(cx);
3390 0 : return false;
3391 : }
3392 :
3393 : #ifdef JS_CRASH_DIAGNOSTICS
3394 : {
3395 705 : StackTypeSet* typeArray = typeScript->typeArray();
3396 16990 : for (unsigned i = 0; i < count; i++)
3397 16285 : typeArray[i].initMagic();
3398 : }
3399 : #endif
3400 :
3401 705 : types_ = typeScript;
3402 705 : setTypesGeneration(cx->zone()->types.generation);
3403 :
3404 : #ifdef DEBUG
3405 705 : StackTypeSet* typeArray = typeScript->typeArray();
3406 15355 : for (unsigned i = 0; i < nTypeSets(); i++) {
3407 14650 : InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p",
3408 : InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
3409 : i, this);
3410 : }
3411 705 : TypeSet* thisTypes = TypeScript::ThisTypes(this);
3412 705 : InferSpew(ISpewOps, "typeSet: %sT%p%s this %p",
3413 : InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
3414 : this);
3415 705 : unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
3416 1635 : for (unsigned i = 0; i < nargs; i++) {
3417 930 : TypeSet* types = TypeScript::ArgTypes(this, i);
3418 930 : InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u %p",
3419 : InferSpewColor(types), types, InferSpewColorReset(),
3420 : i, this);
3421 : }
3422 : #endif
3423 :
3424 705 : return true;
3425 : }
3426 :
3427 : /* static */ bool
3428 23370 : JSFunction::setTypeForScriptedFunction(JSContext* cx, HandleFunction fun,
3429 : bool singleton /* = false */)
3430 : {
3431 23370 : if (singleton) {
3432 1467 : if (!setSingleton(cx, fun))
3433 0 : return false;
3434 : } else {
3435 43806 : RootedObject funProto(cx, fun->staticPrototype());
3436 43806 : Rooted<TaggedProto> taggedProto(cx, TaggedProto(funProto));
3437 43806 : ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_,
3438 21903 : taggedProto);
3439 21903 : if (!group)
3440 0 : return false;
3441 :
3442 21903 : fun->setGroup(group);
3443 21903 : group->setInterpretedFunction(fun);
3444 : }
3445 :
3446 23370 : return true;
3447 : }
3448 :
3449 : /////////////////////////////////////////////////////////////////////
3450 : // PreliminaryObjectArray
3451 : /////////////////////////////////////////////////////////////////////
3452 :
3453 : void
3454 7000 : PreliminaryObjectArray::registerNewObject(JSObject* res)
3455 : {
3456 : // The preliminary object pointers are weak, and won't be swept properly
3457 : // during nursery collections, so the preliminary objects need to be
3458 : // initially tenured.
3459 7000 : MOZ_ASSERT(!IsInsideNursery(res));
3460 :
3461 28902 : for (size_t i = 0; i < COUNT; i++) {
3462 28902 : if (!objects[i]) {
3463 7000 : objects[i] = res;
3464 14000 : return;
3465 : }
3466 : }
3467 :
3468 0 : MOZ_CRASH("There should be room for registering the new object");
3469 : }
3470 :
3471 : void
3472 0 : PreliminaryObjectArray::unregisterObject(JSObject* obj)
3473 : {
3474 0 : for (size_t i = 0; i < COUNT; i++) {
3475 0 : if (objects[i] == obj) {
3476 0 : objects[i] = nullptr;
3477 0 : return;
3478 : }
3479 : }
3480 :
3481 0 : MOZ_CRASH("The object should be in the array");
3482 : }
3483 :
3484 : bool
3485 4219 : PreliminaryObjectArray::full() const
3486 : {
3487 28617 : for (size_t i = 0; i < COUNT; i++) {
3488 28560 : if (!objects[i])
3489 4162 : return false;
3490 : }
3491 57 : return true;
3492 : }
3493 :
3494 : bool
3495 0 : PreliminaryObjectArray::empty() const
3496 : {
3497 0 : for (size_t i = 0; i < COUNT; i++) {
3498 0 : if (objects[i])
3499 0 : return false;
3500 : }
3501 0 : return true;
3502 : }
3503 :
3504 : void
3505 0 : PreliminaryObjectArray::sweep()
3506 : {
3507 : // All objects in the array are weak, so clear any that are about to be
3508 : // destroyed.
3509 0 : for (size_t i = 0; i < COUNT; i++) {
3510 0 : JSObject** ptr = &objects[i];
3511 0 : if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
3512 : // Before we clear this reference, change the object's group to the
3513 : // Object.prototype group. This is done to ensure JSObject::finalize
3514 : // sees a NativeObject Class even if we change the current group's
3515 : // Class to one of the unboxed object classes in the meantime. If
3516 : // the compartment's global is dead, we don't do anything as the
3517 : // group's Class is not going to change in that case.
3518 0 : JSObject* obj = *ptr;
3519 0 : GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal();
3520 0 : if (global && !obj->isSingleton()) {
3521 0 : JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object);
3522 0 : obj->setGroup(objectProto->groupRaw());
3523 0 : MOZ_ASSERT(obj->is<NativeObject>());
3524 0 : MOZ_ASSERT(obj->getClass() == objectProto->getClass());
3525 0 : MOZ_ASSERT(!obj->getClass()->hasFinalize());
3526 : }
3527 :
3528 0 : *ptr = nullptr;
3529 : }
3530 : }
3531 0 : }
3532 :
3533 : void
3534 41 : PreliminaryObjectArrayWithTemplate::trace(JSTracer* trc)
3535 : {
3536 41 : TraceNullableEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape");
3537 41 : }
3538 :
3539 : /* static */ void
3540 58 : PreliminaryObjectArrayWithTemplate::writeBarrierPre(PreliminaryObjectArrayWithTemplate* objects)
3541 : {
3542 58 : Shape* shape = objects->shape();
3543 :
3544 58 : if (!shape)
3545 1 : return;
3546 :
3547 57 : JS::Zone* zone = shape->zoneFromAnyThread();
3548 57 : if (zone->needsIncrementalBarrier())
3549 1 : objects->trace(zone->barrierTracer());
3550 : }
3551 :
3552 : // Return whether shape consists entirely of plain data properties.
3553 : static bool
3554 3537 : OnlyHasDataProperties(Shape* shape)
3555 : {
3556 3537 : MOZ_ASSERT(!shape->inDictionary());
3557 :
3558 21085 : while (!shape->isEmptyShape()) {
3559 26322 : if (!shape->isDataDescriptor() ||
3560 17548 : !shape->configurable() ||
3561 17548 : !shape->enumerable() ||
3562 26322 : !shape->writable() ||
3563 8774 : !shape->hasSlot())
3564 : {
3565 0 : return false;
3566 : }
3567 8774 : shape = shape->previous();
3568 : }
3569 :
3570 3537 : return true;
3571 : }
3572 :
3573 : // Find the most recent common ancestor of two shapes, or an empty shape if
3574 : // the two shapes have no common ancestor.
3575 : static Shape*
3576 1159 : CommonPrefix(Shape* first, Shape* second)
3577 : {
3578 1159 : MOZ_ASSERT(OnlyHasDataProperties(first));
3579 1159 : MOZ_ASSERT(OnlyHasDataProperties(second));
3580 :
3581 1525 : while (first->slotSpan() > second->slotSpan())
3582 183 : first = first->previous();
3583 1159 : while (second->slotSpan() > first->slotSpan())
3584 0 : second = second->previous();
3585 :
3586 1159 : while (first != second && !first->isEmptyShape()) {
3587 0 : first = first->previous();
3588 0 : second = second->previous();
3589 : }
3590 :
3591 1159 : return first;
3592 : }
3593 :
3594 : void
3595 3774 : PreliminaryObjectArrayWithTemplate::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool force)
3596 : {
3597 : // Don't perform the analyses until sufficient preliminary objects have
3598 : // been allocated.
3599 3774 : if (!force && !full())
3600 7451 : return;
3601 :
3602 97 : AutoEnterAnalysis enter(cx);
3603 :
3604 97 : ScopedJSDeletePtr<PreliminaryObjectArrayWithTemplate> preliminaryObjects(this);
3605 58 : group->detachPreliminaryObjects();
3606 :
3607 58 : if (shape()) {
3608 57 : MOZ_ASSERT(shape()->slotSpan() != 0);
3609 57 : MOZ_ASSERT(OnlyHasDataProperties(shape()));
3610 :
3611 : // Make sure all the preliminary objects reflect the properties originally
3612 : // in the template object.
3613 1197 : for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
3614 1140 : JSObject* objBase = preliminaryObjects->get(i);
3615 1140 : if (!objBase)
3616 19 : continue;
3617 1121 : PlainObject* obj = &objBase->as<PlainObject>();
3618 :
3619 1121 : if (obj->inDictionaryMode() || !OnlyHasDataProperties(obj->lastProperty()))
3620 0 : return;
3621 :
3622 1121 : if (CommonPrefix(obj->lastProperty(), shape()) != shape())
3623 0 : return;
3624 : }
3625 : }
3626 :
3627 58 : TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects);
3628 58 : if (group->maybeUnboxedLayout())
3629 19 : return;
3630 :
3631 39 : if (shape()) {
3632 : // We weren't able to use an unboxed layout, but since the preliminary
3633 : // objects still reflect the template object's properties, and all
3634 : // objects in the future will be created with those properties, the
3635 : // properties can be marked as definite for objects in the group.
3636 38 : group->addDefiniteProperties(cx, shape());
3637 : }
3638 : }
3639 :
3640 : /////////////////////////////////////////////////////////////////////
3641 : // TypeNewScript
3642 : /////////////////////////////////////////////////////////////////////
3643 :
3644 : // Make a TypeNewScript for |group|, and set it up to hold the preliminary
3645 : // objects created with the group.
3646 : /* static */ bool
3647 195 : TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun)
3648 : {
3649 195 : MOZ_ASSERT(cx->zone()->types.activeAnalysis);
3650 195 : MOZ_ASSERT(!group->newScript());
3651 195 : MOZ_ASSERT(!group->maybeUnboxedLayout());
3652 :
3653 195 : if (group->unknownProperties())
3654 0 : return true;
3655 :
3656 390 : ScopedJSDeletePtr<TypeNewScript> newScript(cx->new_<TypeNewScript>());
3657 195 : if (!newScript)
3658 0 : return false;
3659 :
3660 195 : newScript->function_ = fun;
3661 :
3662 195 : newScript->preliminaryObjects = group->zone()->new_<PreliminaryObjectArray>();
3663 195 : if (!newScript->preliminaryObjects)
3664 0 : return true;
3665 :
3666 195 : group->setNewScript(newScript.forget());
3667 :
3668 195 : gc::TraceTypeNewScript(group);
3669 195 : return true;
3670 : }
3671 :
3672 : // Make a TypeNewScript with the same initializer list as |newScript| but with
3673 : // a new template object.
3674 : /* static */ TypeNewScript*
3675 0 : TypeNewScript::makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
3676 : PlainObject* templateObject)
3677 : {
3678 0 : MOZ_RELEASE_ASSERT(cx->zone()->types.activeAnalysis);
3679 :
3680 0 : ScopedJSDeletePtr<TypeNewScript> nativeNewScript(cx->new_<TypeNewScript>());
3681 0 : if (!nativeNewScript)
3682 0 : return nullptr;
3683 :
3684 0 : nativeNewScript->function_ = newScript->function();
3685 0 : nativeNewScript->templateObject_ = templateObject;
3686 :
3687 0 : Initializer* cursor = newScript->initializerList;
3688 0 : while (cursor->kind != Initializer::DONE) { cursor++; }
3689 0 : size_t initializerLength = cursor - newScript->initializerList + 1;
3690 :
3691 0 : nativeNewScript->initializerList = cx->zone()->pod_calloc<Initializer>(initializerLength);
3692 0 : if (!nativeNewScript->initializerList) {
3693 0 : ReportOutOfMemory(cx);
3694 0 : return nullptr;
3695 : }
3696 0 : PodCopy(nativeNewScript->initializerList, newScript->initializerList, initializerLength);
3697 :
3698 0 : return nativeNewScript.forget();
3699 : }
3700 :
3701 : size_t
3702 0 : TypeNewScript::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
3703 : {
3704 0 : size_t n = mallocSizeOf(this);
3705 0 : n += mallocSizeOf(preliminaryObjects);
3706 0 : n += mallocSizeOf(initializerList);
3707 0 : return n;
3708 : }
3709 :
3710 : void
3711 446 : TypeNewScript::registerNewObject(PlainObject* res)
3712 : {
3713 446 : MOZ_ASSERT(!analyzed());
3714 :
3715 : // New script objects must have the maximum number of fixed slots, so that
3716 : // we can adjust their shape later to match the number of fixed slots used
3717 : // by the template object we eventually create.
3718 446 : MOZ_ASSERT(res->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS);
3719 :
3720 446 : preliminaryObjects->registerNewObject(res);
3721 446 : }
3722 :
3723 : static bool
3724 20 : ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocKind)
3725 : {
3726 20 : MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty()));
3727 :
3728 20 : Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), obj->taggedProto(), allocKind);
3729 20 : if (!newShape)
3730 0 : return false;
3731 :
3732 20 : obj->setLastPropertyShrinkFixedSlots(newShape);
3733 20 : return true;
3734 : }
3735 :
3736 : namespace {
3737 :
3738 : struct DestroyTypeNewScript
3739 : {
3740 : JSContext* cx;
3741 : ObjectGroup* group;
3742 :
3743 1 : DestroyTypeNewScript(JSContext* cx, ObjectGroup* group)
3744 1 : : cx(cx), group(group)
3745 1 : {}
3746 :
3747 2 : ~DestroyTypeNewScript() {
3748 1 : if (group)
3749 0 : group->clearNewScript(cx);
3750 1 : }
3751 : };
3752 :
3753 : } // namespace
3754 :
3755 : bool
3756 447 : TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force)
3757 : {
3758 : // Perform the new script properties analysis if necessary, returning
3759 : // whether the new group table was updated and group needs to be refreshed.
3760 447 : MOZ_ASSERT(this == group->newScript());
3761 :
3762 : // Make sure there aren't dead references in preliminaryObjects. This can
3763 : // clear out the new script information on OOM.
3764 447 : group->maybeSweep(nullptr);
3765 447 : if (!group->newScript())
3766 0 : return true;
3767 :
3768 447 : if (regenerate)
3769 447 : *regenerate = false;
3770 :
3771 447 : if (analyzed()) {
3772 : // The analyses have already been performed.
3773 0 : return true;
3774 : }
3775 :
3776 : // Don't perform the analyses until sufficient preliminary objects have
3777 : // been allocated.
3778 447 : if (!force && !preliminaryObjects->full())
3779 446 : return true;
3780 :
3781 2 : AutoEnterAnalysis enter(cx);
3782 :
3783 : // Any failures after this point will clear out this TypeNewScript.
3784 2 : DestroyTypeNewScript destroyNewScript(cx, group);
3785 :
3786 : // Compute the greatest common shape prefix and the largest slot span of
3787 : // the preliminary objects.
3788 1 : Shape* prefixShape = nullptr;
3789 1 : size_t maxSlotSpan = 0;
3790 21 : for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
3791 20 : JSObject* objBase = preliminaryObjects->get(i);
3792 20 : if (!objBase)
3793 0 : continue;
3794 20 : PlainObject* obj = &objBase->as<PlainObject>();
3795 :
3796 : // For now, we require all preliminary objects to have only simple
3797 : // lineages of plain data properties.
3798 20 : Shape* shape = obj->lastProperty();
3799 60 : if (shape->inDictionary() ||
3800 40 : !OnlyHasDataProperties(shape) ||
3801 20 : shape->getObjectFlags() != 0)
3802 : {
3803 0 : return true;
3804 : }
3805 :
3806 20 : maxSlotSpan = Max<size_t>(maxSlotSpan, obj->slotSpan());
3807 :
3808 20 : if (prefixShape) {
3809 19 : MOZ_ASSERT(shape->numFixedSlots() == prefixShape->numFixedSlots());
3810 19 : prefixShape = CommonPrefix(prefixShape, shape);
3811 : } else {
3812 1 : prefixShape = shape;
3813 : }
3814 20 : if (prefixShape->isEmptyShape()) {
3815 : // The preliminary objects don't have any common properties.
3816 0 : return true;
3817 : }
3818 : }
3819 1 : if (!prefixShape)
3820 0 : return true;
3821 :
3822 1 : gc::AllocKind kind = gc::GetGCObjectKind(maxSlotSpan);
3823 :
3824 1 : if (kind != gc::GetGCObjectKind(NativeObject::MAX_FIXED_SLOTS)) {
3825 : // The template object will have a different allocation kind from the
3826 : // preliminary objects that have already been constructed. Optimizing
3827 : // definite property accesses requires both that the property is
3828 : // definitely in a particular slot and that the object has a specific
3829 : // number of fixed slots. So, adjust the shape and slot layout of all
3830 : // the preliminary objects so that their structure matches that of the
3831 : // template object. Also recompute the prefix shape, as it reflects the
3832 : // old number of fixed slots.
3833 1 : Shape* newPrefixShape = nullptr;
3834 21 : for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
3835 20 : JSObject* objBase = preliminaryObjects->get(i);
3836 20 : if (!objBase)
3837 0 : continue;
3838 20 : PlainObject* obj = &objBase->as<PlainObject>();
3839 20 : if (!ChangeObjectFixedSlotCount(cx, obj, kind))
3840 0 : return false;
3841 20 : if (newPrefixShape) {
3842 19 : MOZ_ASSERT(CommonPrefix(obj->lastProperty(), newPrefixShape) == newPrefixShape);
3843 : } else {
3844 1 : newPrefixShape = obj->lastProperty();
3845 1 : while (newPrefixShape->slotSpan() > prefixShape->slotSpan())
3846 0 : newPrefixShape = newPrefixShape->previous();
3847 : }
3848 : }
3849 1 : prefixShape = newPrefixShape;
3850 : }
3851 :
3852 2 : RootedObjectGroup groupRoot(cx, group);
3853 1 : templateObject_ = NewObjectWithGroup<PlainObject>(cx, groupRoot, kind, TenuredObject);
3854 1 : if (!templateObject_)
3855 0 : return false;
3856 :
3857 2 : Vector<Initializer> initializerVector(cx);
3858 :
3859 2 : RootedPlainObject templateRoot(cx, templateObject());
3860 2 : RootedFunction fun(cx, function());
3861 1 : if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, group, templateRoot, &initializerVector))
3862 0 : return false;
3863 :
3864 1 : if (!group->newScript())
3865 0 : return true;
3866 :
3867 1 : MOZ_ASSERT(OnlyHasDataProperties(templateObject()->lastProperty()));
3868 :
3869 1 : if (templateObject()->slotSpan() != 0) {
3870 : // Make sure that all definite properties found are reflected in the
3871 : // prefix shape. Otherwise, the constructor behaved differently before
3872 : // we baseline compiled it and started observing types. Compare
3873 : // property names rather than looking at the shapes directly, as the
3874 : // allocation kind and other non-property parts of the template and
3875 : // existing objects may differ.
3876 0 : if (templateObject()->slotSpan() > prefixShape->slotSpan())
3877 0 : return true;
3878 : {
3879 0 : Shape* shape = prefixShape;
3880 0 : while (shape->slotSpan() != templateObject()->slotSpan())
3881 0 : shape = shape->previous();
3882 0 : Shape* templateShape = templateObject()->lastProperty();
3883 0 : while (!shape->isEmptyShape()) {
3884 0 : if (shape->slot() != templateShape->slot())
3885 0 : return true;
3886 0 : if (shape->propid() != templateShape->propid())
3887 0 : return true;
3888 0 : shape = shape->previous();
3889 0 : templateShape = templateShape->previous();
3890 : }
3891 0 : if (!templateShape->isEmptyShape())
3892 0 : return true;
3893 : }
3894 :
3895 0 : Initializer done(Initializer::DONE, 0);
3896 :
3897 0 : if (!initializerVector.append(done))
3898 0 : return false;
3899 :
3900 0 : initializerList = group->zone()->pod_calloc<Initializer>(initializerVector.length());
3901 0 : if (!initializerList) {
3902 0 : ReportOutOfMemory(cx);
3903 0 : return false;
3904 : }
3905 0 : PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
3906 : }
3907 :
3908 : // Try to use an unboxed representation for the group.
3909 1 : if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(), group, preliminaryObjects))
3910 0 : return false;
3911 :
3912 1 : js_delete(preliminaryObjects);
3913 1 : preliminaryObjects = nullptr;
3914 :
3915 1 : if (group->maybeUnboxedLayout()) {
3916 : // An unboxed layout was constructed for the group, and this has already
3917 : // been hooked into it.
3918 0 : MOZ_ASSERT(group->unboxedLayout().newScript() == this);
3919 0 : destroyNewScript.group = nullptr;
3920 :
3921 : // Clear out the template object, which is not used for TypeNewScripts
3922 : // with an unboxed layout. Currently it is a mutant object with a
3923 : // non-native group and native shape, so make it safe for GC by changing
3924 : // its group to the default for its prototype.
3925 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
3926 0 : ObjectGroup* plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
3927 0 : group->proto());
3928 0 : if (!plainGroup)
3929 0 : oomUnsafe.crash("TypeNewScript::maybeAnalyze");
3930 0 : templateObject_->setGroup(plainGroup);
3931 0 : templateObject_ = nullptr;
3932 :
3933 0 : return true;
3934 : }
3935 :
3936 1 : if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
3937 : // The definite properties analysis found exactly the properties that
3938 : // are held in common by the preliminary objects. No further analysis
3939 : // is needed.
3940 0 : group->addDefiniteProperties(cx, templateObject()->lastProperty());
3941 :
3942 0 : destroyNewScript.group = nullptr;
3943 0 : return true;
3944 : }
3945 :
3946 : // There are more properties consistently added to objects of this group
3947 : // than were discovered by the definite properties analysis. Use the
3948 : // existing group to represent fully initialized objects with all
3949 : // definite properties in the prefix shape, and make a new group to
3950 : // represent partially initialized objects.
3951 1 : MOZ_ASSERT(prefixShape->slotSpan() > templateObject()->slotSpan());
3952 :
3953 1 : ObjectGroupFlags initialFlags = group->flags() & OBJECT_FLAG_DYNAMIC_MASK;
3954 :
3955 2 : Rooted<TaggedProto> protoRoot(cx, group->proto());
3956 2 : ObjectGroup* initialGroup = ObjectGroupCompartment::makeGroup(cx, group->clasp(), protoRoot,
3957 1 : initialFlags);
3958 1 : if (!initialGroup)
3959 0 : return false;
3960 :
3961 1 : initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty());
3962 1 : group->addDefiniteProperties(cx, prefixShape);
3963 :
3964 2 : cx->compartment()->objectGroups.replaceDefaultNewGroup(nullptr, group->proto(), function(),
3965 1 : initialGroup);
3966 :
3967 1 : templateObject()->setGroup(initialGroup);
3968 :
3969 : // Transfer this TypeNewScript from the fully initialized group to the
3970 : // partially initialized group.
3971 1 : group->detachNewScript();
3972 1 : initialGroup->setNewScript(this);
3973 :
3974 1 : initializedShape_ = prefixShape;
3975 1 : initializedGroup_ = group;
3976 :
3977 1 : destroyNewScript.group = nullptr;
3978 :
3979 1 : if (regenerate)
3980 1 : *regenerate = true;
3981 1 : return true;
3982 : }
3983 :
3984 : bool
3985 8 : TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group)
3986 : {
3987 : // If we cleared this new script while in the middle of initializing an
3988 : // object, it will still have the new script's shape and reflect the no
3989 : // longer correct state of the object once its initialization is completed.
3990 : // We can't detect the possibility of this statically while remaining
3991 : // robust, but the new script keeps track of where each property is
3992 : // initialized so we can walk the stack and fix up any such objects.
3993 : // Return whether any objects were modified.
3994 :
3995 8 : if (!initializerList)
3996 8 : return false;
3997 :
3998 0 : bool found = false;
3999 :
4000 0 : RootedFunction function(cx, this->function());
4001 0 : Vector<uint32_t, 32> pcOffsets(cx);
4002 0 : for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
4003 : {
4004 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
4005 0 : if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc())))
4006 0 : oomUnsafe.crash("rollbackPartiallyInitializedObjects");
4007 : }
4008 :
4009 0 : if (!iter.isConstructing() || !iter.matchCallee(cx, function))
4010 0 : continue;
4011 :
4012 : // Derived class constructors initialize their this-binding later and
4013 : // we shouldn't run the definite properties analysis on them.
4014 0 : MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
4015 :
4016 0 : Value thisv = iter.thisArgument(cx);
4017 0 : if (!thisv.isObject() ||
4018 0 : thisv.toObject().hasLazyGroup() ||
4019 0 : thisv.toObject().group() != group)
4020 : {
4021 0 : continue;
4022 : }
4023 :
4024 0 : if (thisv.toObject().is<UnboxedPlainObject>()) {
4025 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
4026 0 : if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
4027 0 : oomUnsafe.crash("rollbackPartiallyInitializedObjects");
4028 : }
4029 :
4030 : // Found a matching frame.
4031 0 : RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
4032 :
4033 : // Whether all identified 'new' properties have been initialized.
4034 0 : bool finished = false;
4035 :
4036 : // If not finished, number of properties that have been added.
4037 0 : uint32_t numProperties = 0;
4038 :
4039 : // Whether the current SETPROP is within an inner frame which has
4040 : // finished entirely.
4041 0 : bool pastProperty = false;
4042 :
4043 : // Index in pcOffsets of the outermost frame.
4044 0 : int callDepth = pcOffsets.length() - 1;
4045 :
4046 : // Index in pcOffsets of the frame currently being checked for a SETPROP.
4047 0 : int setpropDepth = callDepth;
4048 :
4049 0 : for (Initializer* init = initializerList;; init++) {
4050 0 : if (init->kind == Initializer::SETPROP) {
4051 0 : if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
4052 : // Have not yet reached this setprop.
4053 0 : break;
4054 : }
4055 : // This setprop has executed, reset state for the next one.
4056 0 : numProperties++;
4057 0 : pastProperty = false;
4058 0 : setpropDepth = callDepth;
4059 0 : } else if (init->kind == Initializer::SETPROP_FRAME) {
4060 0 : if (!pastProperty) {
4061 0 : if (pcOffsets[setpropDepth] < init->offset) {
4062 : // Have not yet reached this inner call.
4063 0 : break;
4064 0 : } else if (pcOffsets[setpropDepth] > init->offset) {
4065 : // Have advanced past this inner call.
4066 0 : pastProperty = true;
4067 0 : } else if (setpropDepth == 0) {
4068 : // Have reached this call but not yet in it.
4069 0 : break;
4070 : } else {
4071 : // Somewhere inside this inner call.
4072 0 : setpropDepth--;
4073 : }
4074 : }
4075 : } else {
4076 0 : MOZ_ASSERT(init->kind == Initializer::DONE);
4077 0 : finished = true;
4078 0 : break;
4079 : }
4080 : }
4081 :
4082 0 : if (!finished) {
4083 0 : (void) NativeObject::rollbackProperties(cx, obj, numProperties);
4084 0 : found = true;
4085 : }
4086 : }
4087 :
4088 0 : return found;
4089 : }
4090 :
4091 : void
4092 2 : TypeNewScript::trace(JSTracer* trc)
4093 : {
4094 2 : TraceEdge(trc, &function_, "TypeNewScript_function");
4095 2 : TraceNullableEdge(trc, &templateObject_, "TypeNewScript_templateObject");
4096 2 : TraceNullableEdge(trc, &initializedShape_, "TypeNewScript_initializedShape");
4097 2 : TraceNullableEdge(trc, &initializedGroup_, "TypeNewScript_initializedGroup");
4098 2 : }
4099 :
4100 : /* static */ void
4101 9 : TypeNewScript::writeBarrierPre(TypeNewScript* newScript)
4102 : {
4103 9 : if (JS::CurrentThreadIsHeapCollecting())
4104 0 : return;
4105 :
4106 9 : JS::Zone* zone = newScript->function()->zoneFromAnyThread();
4107 9 : if (zone->needsIncrementalBarrier())
4108 0 : newScript->trace(zone->barrierTracer());
4109 : }
4110 :
4111 : void
4112 0 : TypeNewScript::sweep()
4113 : {
4114 0 : if (preliminaryObjects)
4115 0 : preliminaryObjects->sweep();
4116 0 : }
4117 :
4118 : /////////////////////////////////////////////////////////////////////
4119 : // Tracing
4120 : /////////////////////////////////////////////////////////////////////
4121 :
4122 : static inline void
4123 98 : TraceObjectKey(JSTracer* trc, TypeSet::ObjectKey** keyp)
4124 : {
4125 98 : TypeSet::ObjectKey* key = *keyp;
4126 98 : if (key->isGroup()) {
4127 36 : ObjectGroup* group = key->groupNoBarrier();
4128 36 : TraceManuallyBarrieredEdge(trc, &group, "objectKey_group");
4129 36 : *keyp = TypeSet::ObjectKey::get(group);
4130 : } else {
4131 62 : JSObject* singleton = key->singletonNoBarrier();
4132 62 : TraceManuallyBarrieredEdge(trc, &singleton, "objectKey_singleton");
4133 62 : *keyp = TypeSet::ObjectKey::get(singleton);
4134 : }
4135 98 : }
4136 :
4137 : void
4138 31 : ConstraintTypeSet::trace(Zone* zone, JSTracer* trc)
4139 : {
4140 31 : checkMagic();
4141 :
4142 : // ConstraintTypeSets only hold strong references during minor collections.
4143 31 : MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
4144 :
4145 31 : unsigned objectCount = baseObjectCount();
4146 31 : if (objectCount >= 2) {
4147 26 : unsigned oldCapacity = TypeHashSet::Capacity(objectCount);
4148 26 : ObjectKey** oldArray = objectSet;
4149 :
4150 26 : MOZ_RELEASE_ASSERT(uintptr_t(oldArray[-1]) == oldCapacity);
4151 :
4152 26 : unsigned oldObjectCount = objectCount;
4153 26 : unsigned oldObjectsFound = 0;
4154 :
4155 26 : clearObjects();
4156 26 : objectCount = 0;
4157 234 : for (unsigned i = 0; i < oldCapacity; i++) {
4158 208 : ObjectKey* key = oldArray[i];
4159 208 : if (!key)
4160 114 : continue;
4161 94 : TraceObjectKey(trc, &key);
4162 94 : oldObjectsFound++;
4163 :
4164 188 : AutoEnterOOMUnsafeRegion oomUnsafe;
4165 : ObjectKey** pentry =
4166 : TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey>
4167 94 : (zone->types.typeLifoAlloc(), objectSet, objectCount, key);
4168 94 : if (!pentry)
4169 0 : oomUnsafe.crash("ConstraintTypeSet::trace");
4170 :
4171 94 : *pentry = key;
4172 : }
4173 26 : MOZ_RELEASE_ASSERT(oldObjectCount == oldObjectsFound);
4174 26 : setBaseObjectCount(objectCount);
4175 5 : } else if (objectCount == 1) {
4176 4 : ObjectKey* key = (ObjectKey*) objectSet;
4177 4 : TraceObjectKey(trc, &key);
4178 4 : objectSet = reinterpret_cast<ObjectKey**>(key);
4179 : } else {
4180 1 : MOZ_RELEASE_ASSERT(!objectSet);
4181 : }
4182 31 : }
4183 :
4184 : static inline void
4185 0 : AssertGCStateForSweep(Zone* zone)
4186 : {
4187 0 : MOZ_ASSERT(zone->isGCSweepingOrCompacting());
4188 :
4189 : // IsAboutToBeFinalized doesn't work right on tenured objects when called
4190 : // during a minor collection.
4191 0 : MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
4192 0 : }
4193 :
4194 : void
4195 0 : ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
4196 : {
4197 0 : AssertGCStateForSweep(zone);
4198 :
4199 0 : checkMagic();
4200 :
4201 : /*
4202 : * Purge references to objects that are no longer live. Type sets hold
4203 : * only weak references. For type sets containing more than one object,
4204 : * live entries in the object hash need to be copied to the zone's
4205 : * new arena.
4206 : */
4207 0 : unsigned objectCount = baseObjectCount();
4208 0 : if (objectCount >= 2) {
4209 0 : unsigned oldCapacity = TypeHashSet::Capacity(objectCount);
4210 0 : ObjectKey** oldArray = objectSet;
4211 :
4212 0 : clearObjects();
4213 0 : objectCount = 0;
4214 0 : for (unsigned i = 0; i < oldCapacity; i++) {
4215 0 : ObjectKey* key = oldArray[i];
4216 0 : if (!key)
4217 0 : continue;
4218 0 : if (!IsObjectKeyAboutToBeFinalized(&key)) {
4219 : ObjectKey** pentry =
4220 : TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey>
4221 0 : (zone->types.typeLifoAlloc(), objectSet, objectCount, key);
4222 0 : if (pentry) {
4223 0 : *pentry = key;
4224 : } else {
4225 0 : oom.setOOM();
4226 0 : flags |= TYPE_FLAG_ANYOBJECT;
4227 0 : clearObjects();
4228 0 : objectCount = 0;
4229 0 : break;
4230 : }
4231 0 : } else if (key->isGroup() &&
4232 0 : key->groupNoBarrier()->unknownPropertiesDontCheckGeneration()) {
4233 : // Object sets containing objects with unknown properties might
4234 : // not be complete. Mark the type set as unknown, which it will
4235 : // be treated as during Ion compilation.
4236 : //
4237 : // Note that we don't have to do this when the type set might
4238 : // be missing the native group corresponding to an unboxed
4239 : // object group. In this case, the native group points to the
4240 : // unboxed object group via its addendum, so as long as objects
4241 : // with either group exist, neither group will be finalized.
4242 0 : flags |= TYPE_FLAG_ANYOBJECT;
4243 0 : clearObjects();
4244 0 : objectCount = 0;
4245 0 : break;
4246 : }
4247 : }
4248 0 : setBaseObjectCount(objectCount);
4249 0 : } else if (objectCount == 1) {
4250 0 : ObjectKey* key = (ObjectKey*) objectSet;
4251 0 : if (!IsObjectKeyAboutToBeFinalized(&key)) {
4252 0 : objectSet = reinterpret_cast<ObjectKey**>(key);
4253 : } else {
4254 : // As above, mark type sets containing objects with unknown
4255 : // properties as unknown.
4256 0 : if (key->isGroup() && key->groupNoBarrier()->unknownPropertiesDontCheckGeneration())
4257 0 : flags |= TYPE_FLAG_ANYOBJECT;
4258 0 : objectSet = nullptr;
4259 0 : setBaseObjectCount(0);
4260 : }
4261 : }
4262 :
4263 : /*
4264 : * Type constraints only hold weak references. Copy constraints referring
4265 : * to data that is still live into the zone's new arena.
4266 : */
4267 0 : TypeConstraint* constraint = constraintList();
4268 0 : constraintList_ = nullptr;
4269 0 : while (constraint) {
4270 0 : MOZ_ASSERT(zone->types.sweepTypeLifoAlloc.ref().contains(constraint));
4271 : TypeConstraint* copy;
4272 0 : if (constraint->sweep(zone->types, ©)) {
4273 0 : if (copy) {
4274 0 : MOZ_ASSERT(zone->types.typeLifoAlloc().contains(copy));
4275 0 : copy->setNext(constraintList_);
4276 0 : constraintList_ = copy;
4277 : } else {
4278 0 : oom.setOOM();
4279 : }
4280 : }
4281 0 : constraint = constraint->next();
4282 : }
4283 0 : }
4284 :
4285 : inline void
4286 0 : ObjectGroup::clearProperties()
4287 : {
4288 0 : setBasePropertyCount(0);
4289 0 : propertySet = nullptr;
4290 0 : }
4291 :
4292 : static void
4293 0 : EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom, Zone* zone,
4294 : Maybe<AutoClearTypeInferenceStateOnOOM>& fallback)
4295 : {
4296 0 : if (!oom) {
4297 0 : if (AutoEnterAnalysis* analysis = zone->types.activeAnalysis) {
4298 0 : if (analysis->oom.isNothing())
4299 0 : analysis->oom.emplace(zone);
4300 0 : oom = analysis->oom.ptr();
4301 : } else {
4302 0 : fallback.emplace(zone);
4303 0 : oom = &fallback.ref();
4304 : }
4305 : }
4306 0 : }
4307 :
4308 : /*
4309 : * Before sweeping the arenas themselves, scan all groups in a compartment to
4310 : * fixup weak references: property type sets referencing dead JS and type
4311 : * objects, and singleton JS objects whose type is not referenced elsewhere.
4312 : * This is done either incrementally as part of the sweep, or on demand as type
4313 : * objects are accessed before their contents have been swept.
4314 : */
4315 : void
4316 0 : ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom)
4317 : {
4318 0 : MOZ_ASSERT(generation() != zoneFromAnyThread()->types.generation);
4319 :
4320 0 : setGeneration(zone()->types.generation);
4321 :
4322 0 : AssertGCStateForSweep(zone());
4323 :
4324 0 : Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
4325 0 : EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
4326 :
4327 0 : if (maybeUnboxedLayout()) {
4328 : // Remove unboxed layouts that are about to be finalized from the
4329 : // compartment wide list while we are still on the active thread.
4330 0 : ObjectGroup* group = this;
4331 0 : if (IsAboutToBeFinalizedUnbarriered(&group))
4332 0 : unboxedLayout().detachFromCompartment();
4333 :
4334 0 : if (unboxedLayout().newScript())
4335 0 : unboxedLayout().newScript()->sweep();
4336 :
4337 : // Discard constructor code to avoid holding onto ExecutablePools.
4338 0 : if (zone()->isGCCompacting())
4339 0 : unboxedLayout().setConstructorCode(nullptr);
4340 : }
4341 :
4342 0 : if (maybePreliminaryObjects())
4343 0 : maybePreliminaryObjects()->sweep();
4344 :
4345 0 : if (newScript())
4346 0 : newScript()->sweep();
4347 :
4348 0 : LifoAlloc& typeLifoAlloc = zone()->types.typeLifoAlloc();
4349 :
4350 : /*
4351 : * Properties were allocated from the old arena, and need to be copied over
4352 : * to the new one.
4353 : */
4354 0 : unsigned propertyCount = basePropertyCount();
4355 0 : if (propertyCount >= 2) {
4356 0 : unsigned oldCapacity = TypeHashSet::Capacity(propertyCount);
4357 0 : Property** oldArray = propertySet;
4358 :
4359 0 : MOZ_RELEASE_ASSERT(uintptr_t(oldArray[-1]) == oldCapacity);
4360 :
4361 0 : unsigned oldPropertyCount = propertyCount;
4362 0 : unsigned oldPropertiesFound = 0;
4363 :
4364 0 : clearProperties();
4365 0 : propertyCount = 0;
4366 0 : for (unsigned i = 0; i < oldCapacity; i++) {
4367 0 : Property* prop = oldArray[i];
4368 0 : if (prop) {
4369 0 : oldPropertiesFound++;
4370 0 : prop->types.checkMagic();
4371 0 : if (singleton() && !prop->types.constraintList() && !zone()->isPreservingCode()) {
4372 : /*
4373 : * Don't copy over properties of singleton objects when their
4374 : * presence will not be required by jitcode or type constraints
4375 : * (i.e. for the definite properties analysis). The contents of
4376 : * these type sets will be regenerated as necessary.
4377 : */
4378 0 : continue;
4379 : }
4380 :
4381 0 : Property* newProp = typeLifoAlloc.new_<Property>(*prop);
4382 0 : if (newProp) {
4383 : Property** pentry = TypeHashSet::Insert<jsid, Property, Property>
4384 0 : (typeLifoAlloc, propertySet, propertyCount, prop->id);
4385 0 : if (pentry) {
4386 0 : *pentry = newProp;
4387 0 : newProp->types.sweep(zone(), *oom);
4388 0 : continue;
4389 : }
4390 : }
4391 :
4392 0 : oom->setOOM();
4393 0 : addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
4394 0 : clearProperties();
4395 0 : return;
4396 : }
4397 : }
4398 0 : MOZ_RELEASE_ASSERT(oldPropertyCount == oldPropertiesFound);
4399 0 : setBasePropertyCount(propertyCount);
4400 0 : } else if (propertyCount == 1) {
4401 0 : Property* prop = (Property*) propertySet;
4402 0 : prop->types.checkMagic();
4403 0 : if (singleton() && !prop->types.constraintList() && !zone()->isPreservingCode()) {
4404 : // Skip, as above.
4405 0 : clearProperties();
4406 : } else {
4407 0 : Property* newProp = typeLifoAlloc.new_<Property>(*prop);
4408 0 : if (newProp) {
4409 0 : propertySet = (Property**) newProp;
4410 0 : newProp->types.sweep(zone(), *oom);
4411 : } else {
4412 0 : oom->setOOM();
4413 0 : addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
4414 0 : clearProperties();
4415 0 : return;
4416 : }
4417 : }
4418 : } else {
4419 0 : MOZ_RELEASE_ASSERT(!propertySet);
4420 : }
4421 : }
4422 :
4423 : /* static */ void
4424 43879 : JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
4425 : {
4426 43879 : if (!types_ || typesGeneration() == zone()->types.generation)
4427 87758 : return;
4428 :
4429 0 : setTypesGeneration(zone()->types.generation);
4430 :
4431 0 : AssertGCStateForSweep(zone());
4432 :
4433 0 : Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
4434 0 : EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
4435 :
4436 0 : TypeZone& types = zone()->types;
4437 :
4438 : // Sweep the inlinedCompilations Vector.
4439 : {
4440 0 : RecompileInfoVector& inlinedCompilations = types_->inlinedCompilations();
4441 0 : size_t dest = 0;
4442 0 : for (size_t i = 0; i < inlinedCompilations.length(); i++) {
4443 0 : if (inlinedCompilations[i].shouldSweep(types))
4444 0 : continue;
4445 0 : inlinedCompilations[dest] = inlinedCompilations[i];
4446 0 : dest++;
4447 : }
4448 0 : inlinedCompilations.shrinkTo(dest);
4449 : }
4450 :
4451 : // Destroy all type information attached to the script if desired. We can
4452 : // only do this if nothing has been compiled for the script, which will be
4453 : // the case unless the script has been compiled since we started sweeping.
4454 0 : if (types.sweepReleaseTypes &&
4455 0 : !hasBaselineScript() &&
4456 0 : !hasIonScript())
4457 : {
4458 0 : types_->destroy();
4459 0 : types_ = nullptr;
4460 :
4461 : // Freeze constraints on stack type sets need to be regenerated the
4462 : // next time the script is analyzed.
4463 0 : hasFreezeConstraints_ = false;
4464 :
4465 0 : return;
4466 : }
4467 :
4468 0 : unsigned num = TypeScript::NumTypeSets(this);
4469 0 : StackTypeSet* typeArray = types_->typeArray();
4470 :
4471 : // Remove constraints and references to dead objects from stack type sets.
4472 0 : for (unsigned i = 0; i < num; i++)
4473 0 : typeArray[i].sweep(zone(), *oom);
4474 :
4475 0 : if (oom->hadOOM()) {
4476 : // It's possible we OOM'd while copying freeze constraints, so they
4477 : // need to be regenerated.
4478 0 : hasFreezeConstraints_ = false;
4479 : }
4480 :
4481 : // Update the recompile indexes in any IonScripts still on the script.
4482 0 : if (hasIonScript())
4483 0 : ionScript()->recompileInfoRef().shouldSweep(types);
4484 : }
4485 :
4486 : void
4487 0 : TypeScript::destroy()
4488 : {
4489 0 : js_delete(this);
4490 0 : }
4491 :
4492 : void
4493 0 : Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
4494 : size_t* typePool,
4495 : size_t* regexpZone,
4496 : size_t* jitZone,
4497 : size_t* baselineStubsOptimized,
4498 : size_t* cachedCFG,
4499 : size_t* uniqueIdMap,
4500 : size_t* shapeTables,
4501 : size_t* atomsMarkBitmaps)
4502 : {
4503 0 : *typePool += types.typeLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
4504 0 : *regexpZone += regExps.sizeOfExcludingThis(mallocSizeOf);
4505 0 : if (jitZone_)
4506 0 : jitZone_->addSizeOfIncludingThis(mallocSizeOf, jitZone, baselineStubsOptimized, cachedCFG);
4507 0 : *uniqueIdMap += uniqueIds().sizeOfExcludingThis(mallocSizeOf);
4508 0 : *shapeTables += baseShapes().sizeOfExcludingThis(mallocSizeOf)
4509 0 : + initialShapes().sizeOfExcludingThis(mallocSizeOf);
4510 0 : *atomsMarkBitmaps += markedAtoms().sizeOfExcludingThis(mallocSizeOf);
4511 0 : }
4512 :
4513 31 : TypeZone::TypeZone(Zone* zone)
4514 : : zone_(zone),
4515 : typeLifoAlloc_(zone->group(), (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
4516 : generation(zone->group(), 0),
4517 : compilerOutputs(zone->group(), nullptr),
4518 : sweepTypeLifoAlloc(zone->group(), (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
4519 : sweepCompilerOutputs(zone->group(), nullptr),
4520 : sweepReleaseTypes(zone->group(), false),
4521 : sweepingTypes(zone->group(), false),
4522 31 : activeAnalysis(zone->group(), nullptr)
4523 : {
4524 31 : }
4525 :
4526 0 : TypeZone::~TypeZone()
4527 : {
4528 0 : js_delete(compilerOutputs.ref());
4529 0 : js_delete(sweepCompilerOutputs.ref());
4530 0 : MOZ_RELEASE_ASSERT(!sweepingTypes);
4531 0 : }
4532 :
4533 : void
4534 0 : TypeZone::beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom)
4535 : {
4536 0 : MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
4537 0 : MOZ_ASSERT(!sweepCompilerOutputs);
4538 0 : MOZ_ASSERT(!sweepReleaseTypes);
4539 :
4540 0 : sweepReleaseTypes = releaseTypes;
4541 :
4542 : // Clear the analysis pool, but don't release its data yet. While sweeping
4543 : // types any live data will be allocated into the pool.
4544 0 : sweepTypeLifoAlloc.ref().steal(&typeLifoAlloc());
4545 :
4546 : // Sweep any invalid or dead compiler outputs, and keep track of the new
4547 : // index for remaining live outputs.
4548 0 : if (compilerOutputs) {
4549 0 : CompilerOutputVector* newCompilerOutputs = nullptr;
4550 0 : for (size_t i = 0; i < compilerOutputs->length(); i++) {
4551 0 : CompilerOutput& output = (*compilerOutputs)[i];
4552 0 : if (output.isValid()) {
4553 0 : JSScript* script = output.script();
4554 0 : if (IsAboutToBeFinalizedUnbarriered(&script)) {
4555 0 : if (script->hasIonScript())
4556 0 : script->ionScript()->recompileInfoRef() = RecompileInfo();
4557 0 : output.invalidate();
4558 : } else {
4559 0 : CompilerOutput newOutput(script);
4560 :
4561 0 : if (!newCompilerOutputs)
4562 0 : newCompilerOutputs = js_new<CompilerOutputVector>();
4563 0 : if (newCompilerOutputs && newCompilerOutputs->append(newOutput)) {
4564 0 : output.setSweepIndex(newCompilerOutputs->length() - 1);
4565 : } else {
4566 0 : oom.setOOM();
4567 0 : script->ionScript()->recompileInfoRef() = RecompileInfo();
4568 0 : output.invalidate();
4569 : }
4570 : }
4571 : }
4572 : }
4573 0 : sweepCompilerOutputs = compilerOutputs;
4574 0 : compilerOutputs = newCompilerOutputs;
4575 : }
4576 :
4577 : // All existing RecompileInfos are stale and will be updated to the new
4578 : // compiler outputs list later during the sweep. Since stale indexes only
4579 : // persist until the sweep finishes, we only need two different generation
4580 : // values.
4581 0 : generation = !generation;
4582 0 : }
4583 :
4584 : void
4585 0 : TypeZone::endSweep(JSRuntime* rt)
4586 : {
4587 0 : js_delete(sweepCompilerOutputs.ref());
4588 0 : sweepCompilerOutputs = nullptr;
4589 :
4590 0 : sweepReleaseTypes = false;
4591 :
4592 0 : rt->gc.freeAllLifoBlocksAfterSweeping(&sweepTypeLifoAlloc.ref());
4593 0 : }
4594 :
4595 : void
4596 0 : TypeZone::clearAllNewScriptsOnOOM()
4597 : {
4598 0 : for (auto iter = zone()->cellIter<ObjectGroup>(); !iter.done(); iter.next()) {
4599 0 : ObjectGroup* group = iter;
4600 0 : if (!IsAboutToBeFinalizedUnbarriered(&group))
4601 0 : group->maybeClearNewScriptOnOOM();
4602 : }
4603 0 : }
4604 :
4605 0 : AutoClearTypeInferenceStateOnOOM::AutoClearTypeInferenceStateOnOOM(Zone* zone)
4606 0 : : zone(zone), oom(false)
4607 : {
4608 0 : MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));
4609 0 : zone->types.setSweepingTypes(true);
4610 0 : }
4611 :
4612 0 : AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
4613 : {
4614 0 : zone->types.setSweepingTypes(false);
4615 :
4616 0 : if (oom) {
4617 0 : JSRuntime* rt = zone->runtimeFromActiveCooperatingThread();
4618 0 : js::CancelOffThreadIonCompile(rt);
4619 0 : JSRuntime::AutoProhibitActiveContextChange apacc(rt);
4620 0 : zone->setPreservingCode(false);
4621 0 : zone->discardJitCode(rt->defaultFreeOp(), /* discardBaselineCode = */ false);
4622 0 : zone->types.clearAllNewScriptsOnOOM();
4623 : }
4624 0 : }
4625 :
4626 : #ifdef DEBUG
4627 : void
4628 0 : TypeScript::printTypes(JSContext* cx, HandleScript script) const
4629 : {
4630 0 : MOZ_ASSERT(script->types() == this);
4631 :
4632 0 : if (!script->hasBaselineScript())
4633 0 : return;
4634 :
4635 0 : AutoEnterAnalysis enter(nullptr, script->zone());
4636 :
4637 0 : if (script->functionNonDelazifying())
4638 0 : fprintf(stderr, "Function");
4639 0 : else if (script->isForEval())
4640 0 : fprintf(stderr, "Eval");
4641 : else
4642 0 : fprintf(stderr, "Main");
4643 0 : fprintf(stderr, " %#" PRIxPTR " %s:%" PRIuSIZE " ",
4644 0 : uintptr_t(script.get()), script->filename(), script->lineno());
4645 :
4646 0 : if (script->functionNonDelazifying()) {
4647 0 : if (JSAtom* name = script->functionNonDelazifying()->explicitName())
4648 0 : name->dumpCharsNoNewline();
4649 : }
4650 :
4651 0 : fprintf(stderr, "\n this:");
4652 0 : TypeScript::ThisTypes(script)->print();
4653 :
4654 0 : for (unsigned i = 0;
4655 0 : script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs();
4656 : i++)
4657 : {
4658 0 : fprintf(stderr, "\n arg%u:", i);
4659 0 : TypeScript::ArgTypes(script, i)->print();
4660 : }
4661 0 : fprintf(stderr, "\n");
4662 :
4663 0 : for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
4664 : {
4665 0 : fprintf(stderr, "%p:", script.get());
4666 0 : Sprinter sprinter(cx);
4667 0 : if (!sprinter.init())
4668 0 : return;
4669 0 : Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
4670 0 : fprintf(stderr, "%s", sprinter.string());
4671 : }
4672 :
4673 0 : if (CodeSpec[*pc].format & JOF_TYPESET) {
4674 0 : StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
4675 0 : fprintf(stderr, " typeset %u:", unsigned(types - typeArray()));
4676 0 : types->print();
4677 0 : fprintf(stderr, "\n");
4678 : }
4679 : }
4680 :
4681 0 : fprintf(stderr, "\n");
4682 : }
4683 : #endif /* DEBUG */
4684 :
4685 : JS::ubi::Node::Size
4686 0 : JS::ubi::Concrete<js::ObjectGroup>::size(mozilla::MallocSizeOf mallocSizeOf) const
4687 : {
4688 0 : Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
4689 0 : size += get().sizeOfExcludingThis(mallocSizeOf);
4690 0 : return size;
4691 : }
|