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, ©)) {
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