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