Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsinfer.cpp
blobf22fef9d63dba2106b32666f6815471547ec5049
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/. */
7 #include "jsinferinlines.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/PodOperations.h"
13 #include "jsapi.h"
14 #include "jscntxt.h"
15 #include "jsgc.h"
16 #include "jshashutil.h"
17 #include "jsobj.h"
18 #include "jsprf.h"
19 #include "jsscript.h"
20 #include "jsstr.h"
21 #include "prmjtime.h"
23 #include "gc/Marking.h"
24 #include "jit/BaselineJIT.h"
25 #include "jit/Ion.h"
26 #include "jit/IonAnalysis.h"
27 #include "jit/JitCompartment.h"
28 #include "js/MemoryMetrics.h"
29 #include "vm/HelperThreads.h"
30 #include "vm/Opcodes.h"
31 #include "vm/Shape.h"
33 #include "jsatominlines.h"
34 #include "jsgcinlines.h"
35 #include "jsobjinlines.h"
36 #include "jsscriptinlines.h"
38 #include "jit/ExecutionMode-inl.h"
39 #include "vm/NativeObject-inl.h"
41 using namespace js;
42 using namespace js::gc;
43 using namespace js::types;
45 using mozilla::DebugOnly;
46 using mozilla::Maybe;
47 using mozilla::PodArrayZero;
48 using mozilla::PodCopy;
49 using mozilla::PodZero;
51 static inline jsid
52 id_prototype(JSContext* cx)
54 return NameToId(cx->names().prototype);
57 #ifdef DEBUG
59 static inline jsid
60 id___proto__(JSContext* cx)
62 return NameToId(cx->names().proto);
65 static inline jsid
66 id_constructor(JSContext* cx)
68 return NameToId(cx->names().constructor);
71 static inline jsid
72 id_caller(JSContext* cx)
74 return NameToId(cx->names().caller);
77 const char*
78 types::TypeIdStringImpl(jsid id)
80 if (JSID_IS_VOID(id))
81 return "(index)";
82 if (JSID_IS_EMPTY(id))
83 return "(new)";
84 if (JSID_IS_SYMBOL(id))
85 return "(symbol)";
86 static char bufs[4][100];
87 static unsigned which = 0;
88 which = (which + 1) & 3;
89 PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
90 return bufs[which];
93 #endif
95 /////////////////////////////////////////////////////////////////////
96 // Logging
97 /////////////////////////////////////////////////////////////////////
99 #ifdef DEBUG
101 static bool InferSpewActive(SpewChannel channel)
103 static bool active[SPEW_COUNT];
104 static bool checked = false;
105 if (!checked) {
106 checked = true;
107 PodArrayZero(active);
108 const char* env = getenv("INFERFLAGS");
109 if (!env)
110 return false;
111 if (strstr(env, "ops"))
112 active[ISpewOps] = true;
113 if (strstr(env, "result"))
114 active[ISpewResult] = true;
115 if (strstr(env, "full")) {
116 for (unsigned i = 0; i < SPEW_COUNT; i++)
117 active[i] = true;
120 return active[channel];
123 static bool InferSpewColorable()
125 /* Only spew colors on xterm-color to not screw up emacs. */
126 static bool colorable = false;
127 static bool checked = false;
128 if (!checked) {
129 checked = true;
130 const char* env = getenv("TERM");
131 if (!env)
132 return false;
133 if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
134 colorable = true;
136 return colorable;
139 const char*
140 types::InferSpewColorReset()
142 if (!InferSpewColorable())
143 return "";
144 return "\x1b[0m";
147 const char*
148 types::InferSpewColor(TypeConstraint* constraint)
150 /* Type constraints are printed out using foreground colors. */
151 static const char * const colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m",
152 "\x1b[34m", "\x1b[35m", "\x1b[36m",
153 "\x1b[37m" };
154 if (!InferSpewColorable())
155 return "";
156 return colors[DefaultHasher<TypeConstraint*>::hash(constraint) % 7];
159 const char*
160 types::InferSpewColor(TypeSet* types)
162 /* Type sets are printed out using bold colors. */
163 static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
164 "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
165 "\x1b[1;37m" };
166 if (!InferSpewColorable())
167 return "";
168 return colors[DefaultHasher<TypeSet*>::hash(types) % 7];
171 const char*
172 types::TypeString(Type type)
174 if (type.isPrimitive()) {
175 switch (type.primitive()) {
176 case JSVAL_TYPE_UNDEFINED:
177 return "void";
178 case JSVAL_TYPE_NULL:
179 return "null";
180 case JSVAL_TYPE_BOOLEAN:
181 return "bool";
182 case JSVAL_TYPE_INT32:
183 return "int";
184 case JSVAL_TYPE_DOUBLE:
185 return "float";
186 case JSVAL_TYPE_STRING:
187 return "string";
188 case JSVAL_TYPE_SYMBOL:
189 return "symbol";
190 case JSVAL_TYPE_MAGIC:
191 return "lazyargs";
192 default:
193 MOZ_CRASH("Bad type");
196 if (type.isUnknown())
197 return "unknown";
198 if (type.isAnyObject())
199 return " object";
201 static char bufs[4][40];
202 static unsigned which = 0;
203 which = (which + 1) & 3;
205 if (type.isSingleObject())
206 JS_snprintf(bufs[which], 40, "<0x%p>", (void*) type.singleObject());
207 else
208 JS_snprintf(bufs[which], 40, "[0x%p]", (void*) type.typeObject());
210 return bufs[which];
213 const char*
214 types::TypeObjectString(TypeObject* type)
216 return TypeString(Type::ObjectType(type));
219 unsigned JSScript::id() {
220 if (!id_) {
221 id_ = ++compartment()->types.scriptCount;
222 InferSpew(ISpewOps, "script #%u: %p %s:%d",
223 id_, this, filename() ? filename() : "<null>", lineno());
225 return id_;
228 void
229 types::InferSpew(SpewChannel channel, const char* fmt, ...)
231 if (!InferSpewActive(channel))
232 return;
234 va_list ap;
235 va_start(ap, fmt);
236 fprintf(stderr, "[infer] ");
237 vfprintf(stderr, fmt, ap);
238 fprintf(stderr, "\n");
239 va_end(ap);
242 bool
243 types::TypeHasProperty(JSContext* cx, TypeObject* obj, jsid id, const Value& value)
246 * Check the correctness of the type information in the object's property
247 * against an actual value.
249 if (!obj->unknownProperties() && !value.isUndefined()) {
250 id = IdToTypeId(id);
252 /* Watch for properties which inference does not monitor. */
253 if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
254 return true;
256 Type type = GetValueType(value);
258 AutoEnterAnalysis enter(cx);
261 * We don't track types for properties inherited from prototypes which
262 * haven't yet been accessed during analysis of the inheriting object.
263 * Don't do the property instantiation now.
265 TypeSet* types = obj->maybeGetProperty(id);
266 if (!types)
267 return true;
269 if (!types->hasType(type)) {
270 TypeFailure(cx, "Missing type in object %s %s: %s",
271 TypeObjectString(obj), TypeIdString(id), TypeString(type));
274 return true;
277 #endif
279 void
280 types::TypeFailure(JSContext* cx, const char* fmt, ...)
282 char msgbuf[1024]; /* Larger error messages will be truncated */
283 char errbuf[1024];
285 va_list ap;
286 va_start(ap, fmt);
287 JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
288 va_end(ap);
290 JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf);
292 /* Dump type state, even if INFERFLAGS is unset. */
293 cx->compartment()->types.print(cx, true);
295 MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
296 MOZ_CRASH();
299 /////////////////////////////////////////////////////////////////////
300 // TypeSet
301 /////////////////////////////////////////////////////////////////////
303 TemporaryTypeSet::TemporaryTypeSet(LifoAlloc* alloc, Type type)
305 if (type.isUnknown()) {
306 flags |= TYPE_FLAG_BASE_MASK;
307 } else if (type.isPrimitive()) {
308 flags = PrimitiveTypeFlag(type.primitive());
309 if (flags == TYPE_FLAG_DOUBLE)
310 flags |= TYPE_FLAG_INT32;
311 } else if (type.isAnyObject()) {
312 flags |= TYPE_FLAG_ANYOBJECT;
313 } else if (type.isTypeObject() && type.typeObject()->unknownProperties()) {
314 flags |= TYPE_FLAG_ANYOBJECT;
315 } else {
316 setBaseObjectCount(1);
317 objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey());
319 if (type.isTypeObject()) {
320 TypeObject* nobject = type.typeObject();
321 if (nobject->newScript() && nobject->newScript()->initializedType())
322 addType(Type::ObjectType(nobject->newScript()->initializedType()), alloc);
327 bool
328 TypeSet::mightBeMIRType(jit::MIRType type)
330 if (unknown())
331 return true;
333 if (type == jit::MIRType_Object)
334 return unknownObject() || baseObjectCount() != 0;
336 switch (type) {
337 case jit::MIRType_Undefined:
338 return baseFlags() & TYPE_FLAG_UNDEFINED;
339 case jit::MIRType_Null:
340 return baseFlags() & TYPE_FLAG_NULL;
341 case jit::MIRType_Boolean:
342 return baseFlags() & TYPE_FLAG_BOOLEAN;
343 case jit::MIRType_Int32:
344 return baseFlags() & TYPE_FLAG_INT32;
345 case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32.
346 case jit::MIRType_Double:
347 return baseFlags() & TYPE_FLAG_DOUBLE;
348 case jit::MIRType_String:
349 return baseFlags() & TYPE_FLAG_STRING;
350 case jit::MIRType_Symbol:
351 return baseFlags() & TYPE_FLAG_SYMBOL;
352 case jit::MIRType_MagicOptimizedArguments:
353 return baseFlags() & TYPE_FLAG_LAZYARGS;
354 case jit::MIRType_MagicHole:
355 case jit::MIRType_MagicIsConstructing:
356 // These magic constants do not escape to script and are not observed
357 // in the type sets.
359 // The reason we can return false here is subtle: if Ion is asking the
360 // type set if it has seen such a magic constant, then the MIR in
361 // question is the most generic type, MIRType_Value. A magic constant
362 // could only be emitted by a MIR of MIRType_Value if that MIR is a
363 // phi, and we check that different magic constants do not flow to the
364 // same join point in GuessPhiType.
365 return false;
366 default:
367 MOZ_CRASH("Bad MIR type");
371 bool
372 TypeSet::objectsAreSubset(TypeSet* other)
374 if (other->unknownObject())
375 return true;
377 if (unknownObject())
378 return false;
380 for (unsigned i = 0; i < getObjectCount(); i++) {
381 TypeObjectKey* obj = getObject(i);
382 if (!obj)
383 continue;
384 if (!other->hasType(Type::ObjectType(obj)))
385 return false;
388 return true;
391 bool
392 TypeSet::isSubset(const TypeSet* other) const
394 if ((baseFlags() & other->baseFlags()) != baseFlags())
395 return false;
397 if (unknownObject()) {
398 MOZ_ASSERT(other->unknownObject());
399 } else {
400 for (unsigned i = 0; i < getObjectCount(); i++) {
401 TypeObjectKey* obj = getObject(i);
402 if (!obj)
403 continue;
404 if (!other->hasType(Type::ObjectType(obj)))
405 return false;
409 return true;
412 bool
413 TypeSet::enumerateTypes(TypeList* list)
415 /* If any type is possible, there's no need to worry about specifics. */
416 if (flags & TYPE_FLAG_UNKNOWN)
417 return list->append(Type::UnknownType());
419 /* Enqueue type set members stored as bits. */
420 for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
421 if (flags & flag) {
422 Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
423 if (!list->append(type))
424 return false;
428 /* If any object is possible, skip specifics. */
429 if (flags & TYPE_FLAG_ANYOBJECT)
430 return list->append(Type::AnyObjectType());
432 /* Enqueue specific object types. */
433 unsigned count = getObjectCount();
434 for (unsigned i = 0; i < count; i++) {
435 TypeObjectKey* object = getObject(i);
436 if (object) {
437 if (!list->append(Type::ObjectType(object)))
438 return false;
442 return true;
445 inline bool
446 TypeSet::addTypesToConstraint(JSContext* cx, TypeConstraint* constraint)
449 * Build all types in the set into a vector before triggering the
450 * constraint, as doing so may modify this type set.
452 TypeList types;
453 if (!enumerateTypes(&types))
454 return false;
456 for (unsigned i = 0; i < types.length(); i++)
457 constraint->newType(cx, this, types[i]);
459 return true;
462 bool
463 ConstraintTypeSet::addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting)
465 if (!constraint) {
466 /* OOM failure while constructing the constraint. */
467 return false;
470 MOZ_ASSERT(cx->zone()->types.activeAnalysis);
472 InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
473 InferSpewColor(this), this, InferSpewColorReset(),
474 InferSpewColor(constraint), constraint, InferSpewColorReset(),
475 constraint->kind());
477 MOZ_ASSERT(constraint->next == nullptr);
478 constraint->next = constraintList;
479 constraintList = constraint;
481 if (callExisting)
482 return addTypesToConstraint(cx, constraint);
483 return true;
486 void
487 TypeSet::clearObjects()
489 setBaseObjectCount(0);
490 objectSet = nullptr;
493 void
494 TypeSet::addType(Type type, LifoAlloc* alloc)
496 if (unknown())
497 return;
499 if (type.isUnknown()) {
500 flags |= TYPE_FLAG_BASE_MASK;
501 clearObjects();
502 MOZ_ASSERT(unknown());
503 return;
506 if (type.isPrimitive()) {
507 TypeFlags flag = PrimitiveTypeFlag(type.primitive());
508 if (flags & flag)
509 return;
511 /* If we add float to a type set it is also considered to contain int. */
512 if (flag == TYPE_FLAG_DOUBLE)
513 flag |= TYPE_FLAG_INT32;
515 flags |= flag;
516 return;
519 if (flags & TYPE_FLAG_ANYOBJECT)
520 return;
521 if (type.isAnyObject())
522 goto unknownObject;
525 uint32_t objectCount = baseObjectCount();
526 TypeObjectKey* object = type.objectKey();
527 TypeObjectKey** pentry = HashSetInsert<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
528 (*alloc, objectSet, objectCount, object);
529 if (!pentry)
530 goto unknownObject;
531 if (*pentry)
532 return;
533 *pentry = object;
535 setBaseObjectCount(objectCount);
537 // Limit the number of objects we track. There is a different limit
538 // depending on whether the set only contains DOM objects, which can
539 // have many different classes and prototypes but are still optimizable
540 // by IonMonkey.
541 if (objectCount >= TYPE_FLAG_OBJECT_COUNT_LIMIT) {
542 JS_STATIC_ASSERT(TYPE_FLAG_DOMOBJECT_COUNT_LIMIT >= TYPE_FLAG_OBJECT_COUNT_LIMIT);
543 // Examining the entire type set is only required when we first hit
544 // the normal object limit.
545 if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT && !isDOMClass())
546 goto unknownObject;
548 // Make sure the newly added object is also a DOM object.
549 if (!object->clasp()->isDOMClass())
550 goto unknownObject;
552 // Limit the number of DOM objects.
553 if (objectCount == TYPE_FLAG_DOMOBJECT_COUNT_LIMIT)
554 goto unknownObject;
558 if (type.isTypeObject()) {
559 TypeObject* nobject = type.typeObject();
560 MOZ_ASSERT(!nobject->singleton());
561 if (nobject->unknownProperties())
562 goto unknownObject;
564 // If we add a partially initialized type to a type set, add the
565 // corresponding fully initialized type, as an object's type may change
566 // from the former to the latter via the acquired properties analysis.
567 if (nobject->newScript() && nobject->newScript()->initializedType())
568 addType(Type::ObjectType(nobject->newScript()->initializedType()), alloc);
571 if (false) {
572 unknownObject:
573 flags |= TYPE_FLAG_ANYOBJECT;
574 clearObjects();
578 void
579 ConstraintTypeSet::addType(ExclusiveContext* cxArg, Type type)
581 MOZ_ASSERT(cxArg->zone()->types.activeAnalysis);
583 if (hasType(type))
584 return;
586 TypeSet::addType(type, &cxArg->typeLifoAlloc());
588 if (type.isObjectUnchecked() && unknownObject())
589 type = Type::AnyObjectType();
591 InferSpew(ISpewOps, "addType: %sT%p%s %s",
592 InferSpewColor(this), this, InferSpewColorReset(),
593 TypeString(type));
595 /* Propagate the type to all constraints. */
596 if (JSContext* cx = cxArg->maybeJSContext()) {
597 TypeConstraint* constraint = constraintList;
598 while (constraint) {
599 constraint->newType(cx, this, type);
600 constraint = constraint->next;
602 } else {
603 MOZ_ASSERT(!constraintList);
607 void
608 TypeSet::print()
610 if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
611 fprintf(stderr, " [non-data]");
613 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
614 fprintf(stderr, " [non-writable]");
616 if (definiteProperty())
617 fprintf(stderr, " [definite:%d]", definiteSlot());
619 if (baseFlags() == 0 && !baseObjectCount()) {
620 fprintf(stderr, " missing");
621 return;
624 if (flags & TYPE_FLAG_UNKNOWN)
625 fprintf(stderr, " unknown");
626 if (flags & TYPE_FLAG_ANYOBJECT)
627 fprintf(stderr, " object");
629 if (flags & TYPE_FLAG_UNDEFINED)
630 fprintf(stderr, " void");
631 if (flags & TYPE_FLAG_NULL)
632 fprintf(stderr, " null");
633 if (flags & TYPE_FLAG_BOOLEAN)
634 fprintf(stderr, " bool");
635 if (flags & TYPE_FLAG_INT32)
636 fprintf(stderr, " int");
637 if (flags & TYPE_FLAG_DOUBLE)
638 fprintf(stderr, " float");
639 if (flags & TYPE_FLAG_STRING)
640 fprintf(stderr, " string");
641 if (flags & TYPE_FLAG_SYMBOL)
642 fprintf(stderr, " symbol");
643 if (flags & TYPE_FLAG_LAZYARGS)
644 fprintf(stderr, " lazyargs");
646 uint32_t objectCount = baseObjectCount();
647 if (objectCount) {
648 fprintf(stderr, " object[%u]", objectCount);
650 unsigned count = getObjectCount();
651 for (unsigned i = 0; i < count; i++) {
652 TypeObjectKey* object = getObject(i);
653 if (object)
654 fprintf(stderr, " %s", TypeString(Type::ObjectType(object)));
659 /* static */ void
660 TypeSet::readBarrier(const TypeSet* types)
662 if (types->unknownObject())
663 return;
665 for (unsigned i = 0; i < types->getObjectCount(); i++) {
666 if (TypeObjectKey* object = types->getObject(i)) {
667 if (object->isSingleObject())
668 (void) object->asSingleObject();
669 else
670 (void) object->asTypeObject();
675 bool
676 TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const
678 MOZ_ASSERT(result->empty());
680 unsigned objectCount = baseObjectCount();
681 unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
683 TypeObjectKey** newSet;
684 if (capacity) {
685 newSet = alloc->newArray<TypeObjectKey*>(capacity);
686 if (!newSet)
687 return false;
688 PodCopy(newSet, objectSet, capacity);
691 new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
692 return true;
695 TemporaryTypeSet*
696 TypeSet::clone(LifoAlloc* alloc) const
698 TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>();
699 if (!res || !clone(alloc, res))
700 return nullptr;
701 return res;
704 TemporaryTypeSet*
705 TypeSet::filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const
707 TemporaryTypeSet* res = clone(alloc);
708 if (!res)
709 return nullptr;
711 if (filterUndefined)
712 res->flags = res->flags & ~TYPE_FLAG_UNDEFINED;
714 if (filterNull)
715 res->flags = res->flags & ~TYPE_FLAG_NULL;
717 return res;
720 TemporaryTypeSet*
721 TypeSet::cloneObjectsOnly(LifoAlloc* alloc)
723 TemporaryTypeSet* res = clone(alloc);
724 if (!res)
725 return nullptr;
727 res->flags &= ~TYPE_FLAG_BASE_MASK | TYPE_FLAG_ANYOBJECT;
729 return res;
732 TemporaryTypeSet*
733 TypeSet::cloneWithoutObjects(LifoAlloc* alloc)
735 TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>();
736 if (!res)
737 return nullptr;
739 res->flags = flags & ~TYPE_FLAG_ANYOBJECT;
740 res->setBaseObjectCount(0);
741 return res;
744 /* static */ TemporaryTypeSet*
745 TypeSet::unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc)
747 TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>(a->baseFlags() | b->baseFlags(),
748 static_cast<TypeObjectKey**>(nullptr));
749 if (!res)
750 return nullptr;
752 if (!res->unknownObject()) {
753 for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
754 if (TypeObjectKey* key = a->getObject(i))
755 res->addType(Type::ObjectType(key), alloc);
757 for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
758 if (TypeObjectKey* key = b->getObject(i))
759 res->addType(Type::ObjectType(key), alloc);
763 return res;
766 /* static */ TemporaryTypeSet*
767 TypeSet::intersectSets(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc)
769 TemporaryTypeSet* res;
770 res = alloc->new_<TemporaryTypeSet>(a->baseFlags() & b->baseFlags(),
771 static_cast<TypeObjectKey**>(nullptr));
772 if (!res)
773 return nullptr;
775 res->setBaseObjectCount(0);
776 if (res->unknownObject())
777 return res;
779 MOZ_ASSERT(!a->unknownObject() || !b->unknownObject());
781 if (a->unknownObject()) {
782 for (size_t i = 0; i < b->getObjectCount(); i++) {
783 if (b->getObject(i))
784 res->addType(Type::ObjectType(b->getObject(i)), alloc);
786 return res;
789 if (b->unknownObject()) {
790 for (size_t i = 0; i < a->getObjectCount(); i++) {
791 if (b->getObject(i))
792 res->addType(Type::ObjectType(a->getObject(i)), alloc);
794 return res;
797 MOZ_ASSERT(!a->unknownObject() && !b->unknownObject());
799 for (size_t i = 0; i < a->getObjectCount(); i++) {
800 for (size_t j = 0; j < b->getObjectCount(); j++) {
801 if (b->getObject(j) != a->getObject(i))
802 continue;
803 if (!b->getObject(j))
804 continue;
805 res->addType(Type::ObjectType(b->getObject(j)), alloc);
806 break;
810 return res;
813 /////////////////////////////////////////////////////////////////////
814 // Compiler constraints
815 /////////////////////////////////////////////////////////////////////
817 // Compiler constraints overview
819 // Constraints generated during Ion compilation capture assumptions made about
820 // heap properties that will trigger invalidation of the resulting Ion code if
821 // the constraint is violated. Constraints can only be attached to type sets on
822 // the main thread, so to allow compilation to occur almost entirely off thread
823 // the generation is split into two phases.
825 // During compilation, CompilerConstraint values are constructed in a list,
826 // recording the heap property type set which was read from and its expected
827 // contents, along with the assumption made about those contents.
829 // At the end of compilation, when linking the result on the main thread, the
830 // list of compiler constraints are read and converted to type constraints and
831 // attached to the type sets. If the property type sets have changed so that the
832 // assumptions no longer hold then the compilation is aborted and its result
833 // discarded.
835 // Superclass of all constraints generated during Ion compilation. These may
836 // be allocated off the main thread, using the current JIT context's allocator.
837 class CompilerConstraint
839 public:
840 // Property being queried by the compiler.
841 HeapTypeSetKey property;
843 // Contents of the property at the point when the query was performed. This
844 // may differ from the actual property types later in compilation as the
845 // main thread performs side effects.
846 TemporaryTypeSet* expected;
848 CompilerConstraint(LifoAlloc* alloc, const HeapTypeSetKey& property)
849 : property(property),
850 expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr)
853 // Generate the type constraint recording the assumption made by this
854 // compilation. Returns true if the assumption originally made still holds.
855 virtual bool generateTypeConstraint(JSContext* cx, RecompileInfo recompileInfo) = 0;
858 class types::CompilerConstraintList
860 public:
861 struct FrozenScript
863 JSScript* script;
864 TemporaryTypeSet* thisTypes;
865 TemporaryTypeSet* argTypes;
866 TemporaryTypeSet* bytecodeTypes;
869 private:
871 // OOM during generation of some constraint.
872 bool failed_;
874 // Allocator used for constraints.
875 LifoAlloc* alloc_;
877 // Constraints generated on heap properties.
878 Vector<CompilerConstraint*, 0, jit::JitAllocPolicy> constraints;
880 // Scripts whose stack type sets were frozen for the compilation.
881 Vector<FrozenScript, 1, jit::JitAllocPolicy> frozenScripts;
883 public:
884 explicit CompilerConstraintList(jit::TempAllocator& alloc)
885 : failed_(false),
886 alloc_(alloc.lifoAlloc()),
887 constraints(alloc),
888 frozenScripts(alloc)
891 void add(CompilerConstraint* constraint) {
892 if (!constraint || !constraints.append(constraint))
893 setFailed();
896 void freezeScript(JSScript* script,
897 TemporaryTypeSet* thisTypes,
898 TemporaryTypeSet* argTypes,
899 TemporaryTypeSet* bytecodeTypes)
901 FrozenScript entry;
902 entry.script = script;
903 entry.thisTypes = thisTypes;
904 entry.argTypes = argTypes;
905 entry.bytecodeTypes = bytecodeTypes;
906 if (!frozenScripts.append(entry))
907 setFailed();
910 size_t length() {
911 return constraints.length();
914 CompilerConstraint* get(size_t i) {
915 return constraints[i];
918 size_t numFrozenScripts() {
919 return frozenScripts.length();
922 const FrozenScript& frozenScript(size_t i) {
923 return frozenScripts[i];
926 bool failed() {
927 return failed_;
929 void setFailed() {
930 failed_ = true;
932 LifoAlloc* alloc() const {
933 return alloc_;
937 CompilerConstraintList*
938 types::NewCompilerConstraintList(jit::TempAllocator& alloc)
940 return alloc.lifoAlloc()->new_<CompilerConstraintList>(alloc);
943 /* static */ bool
944 TypeScript::FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script,
945 TemporaryTypeSet** pThisTypes,
946 TemporaryTypeSet** pArgTypes,
947 TemporaryTypeSet** pBytecodeTypes)
949 LifoAlloc* alloc = constraints->alloc();
950 StackTypeSet* existing = script->types()->typeArray();
952 size_t count = NumTypeSets(script);
953 TemporaryTypeSet* types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
954 if (!types)
955 return false;
956 PodZero(types, count);
958 for (size_t i = 0; i < count; i++) {
959 if (!existing[i].clone(alloc, &types[i]))
960 return false;
963 *pThisTypes = types + (ThisTypes(script) - existing);
964 *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs())
965 ? (types + (ArgTypes(script, 0) - existing))
966 : nullptr;
967 *pBytecodeTypes = types;
969 constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
970 return true;
973 namespace {
975 template <typename T>
976 class CompilerConstraintInstance : public CompilerConstraint
978 T data;
980 public:
981 CompilerConstraintInstance<T>(LifoAlloc* alloc, const HeapTypeSetKey& property, const T& data)
982 : CompilerConstraint(alloc, property), data(data)
985 bool generateTypeConstraint(JSContext* cx, RecompileInfo recompileInfo);
988 // Constraint generated from a CompilerConstraint when linking the compilation.
989 template <typename T>
990 class TypeCompilerConstraint : public TypeConstraint
992 // Compilation which this constraint may invalidate.
993 RecompileInfo compilation;
995 T data;
997 public:
998 TypeCompilerConstraint<T>(RecompileInfo compilation, const T& data)
999 : compilation(compilation), data(data)
1002 const char* kind() { return data.kind(); }
1004 void newType(JSContext* cx, TypeSet* source, Type type) {
1005 if (data.invalidateOnNewType(type))
1006 cx->zone()->types.addPendingRecompile(cx, compilation);
1009 void newPropertyState(JSContext* cx, TypeSet* source) {
1010 if (data.invalidateOnNewPropertyState(source))
1011 cx->zone()->types.addPendingRecompile(cx, compilation);
1014 void newObjectState(JSContext* cx, TypeObject* object) {
1015 // Note: Once the object has unknown properties, no more notifications
1016 // will be sent on changes to its state, so always invalidate any
1017 // associated compilations.
1018 if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
1019 cx->zone()->types.addPendingRecompile(cx, compilation);
1022 bool sweep(TypeZone& zone, TypeConstraint** res) {
1023 if (data.shouldSweep() || compilation.shouldSweep(zone))
1024 return false;
1025 *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
1026 return true;
1030 template <typename T>
1031 bool
1032 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext* cx, RecompileInfo recompileInfo)
1034 if (property.object()->unknownProperties())
1035 return false;
1037 if (!property.instantiate(cx))
1038 return false;
1040 if (!data.constraintHolds(cx, property, expected))
1041 return false;
1043 return property.maybeTypes()->addConstraint(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
1044 /* callExisting = */ false);
1047 } /* anonymous namespace */
1049 const Class*
1050 TypeObjectKey::clasp()
1052 return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass();
1055 TaggedProto
1056 TypeObjectKey::proto()
1058 MOZ_ASSERT(hasTenuredProto());
1059 return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
1062 bool
1063 JSObject::hasTenuredProto() const
1065 return type_->hasTenuredProto();
1068 bool
1069 TypeObjectKey::hasTenuredProto()
1071 return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
1074 JSObject*
1075 TypeObjectKey::singleton()
1077 return isTypeObject() ? asTypeObject()->singleton() : asSingleObject();
1080 TypeNewScript*
1081 TypeObjectKey::newScript()
1083 if (isTypeObject() && asTypeObject()->newScript())
1084 return asTypeObject()->newScript();
1085 return nullptr;
1088 TypeObject*
1089 TypeObjectKey::maybeType()
1091 if (isTypeObject())
1092 return asTypeObject();
1093 if (asSingleObject()->hasLazyType())
1094 return nullptr;
1095 return asSingleObject()->type();
1098 bool
1099 TypeObjectKey::unknownProperties()
1101 if (TypeObject* type = maybeType())
1102 return type->unknownProperties();
1103 return false;
1106 HeapTypeSetKey
1107 TypeObjectKey::property(jsid id)
1109 MOZ_ASSERT(!unknownProperties());
1111 HeapTypeSetKey property;
1112 property.object_ = this;
1113 property.id_ = id;
1114 if (TypeObject* type = maybeType())
1115 property.maybeTypes_ = type->maybeGetProperty(id);
1117 return property;
1120 void
1121 TypeObjectKey::ensureTrackedProperty(JSContext* cx, jsid id)
1123 // If we are accessing a lazily defined property which actually exists in
1124 // the VM and has not been instantiated yet, instantiate it now if we are
1125 // on the main thread and able to do so.
1126 if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
1127 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1128 if (JSObject* obj = singleton()) {
1129 if (obj->isNative() && obj->as<NativeObject>().containsPure(id))
1130 EnsureTrackPropertyTypes(cx, obj, id);
1135 bool
1136 HeapTypeSetKey::instantiate(JSContext* cx)
1138 if (maybeTypes())
1139 return true;
1140 if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx)) {
1141 cx->clearPendingException();
1142 return false;
1144 maybeTypes_ = object()->maybeType()->getProperty(cx, id());
1145 return maybeTypes_ != nullptr;
1148 static bool
1149 CheckFrozenTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual)
1151 // Return whether the types frozen for a script during compilation are
1152 // still valid. Also check for any new types added to the frozen set during
1153 // compilation, and add them to the actual stack type sets. These new types
1154 // indicate places where the compiler relaxed its possible inputs to be
1155 // more tolerant of potential new types.
1157 if (!actual->isSubset(frozen))
1158 return false;
1160 if (!frozen->isSubset(actual)) {
1161 TypeSet::TypeList list;
1162 frozen->enumerateTypes(&list);
1164 for (size_t i = 0; i < list.length(); i++)
1165 actual->addType(cx, list[i]);
1168 return true;
1171 namespace {
1174 * As for TypeConstraintFreeze, but describes an implicit freeze constraint
1175 * added for stack types within a script. Applies to all compilations of the
1176 * script, not just a single one.
1178 class TypeConstraintFreezeStack : public TypeConstraint
1180 JSScript* script_;
1182 public:
1183 explicit TypeConstraintFreezeStack(JSScript* script)
1184 : script_(script)
1187 const char* kind() { return "freezeStack"; }
1189 void newType(JSContext* cx, TypeSet* source, Type type) {
1191 * Unlike TypeConstraintFreeze, triggering this constraint once does
1192 * not disable it on future changes to the type set.
1194 cx->zone()->types.addPendingRecompile(cx, script_);
1197 bool sweep(TypeZone& zone, TypeConstraint** res) {
1198 if (IsScriptAboutToBeFinalized(&script_))
1199 return false;
1200 *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
1201 return true;
1205 } /* anonymous namespace */
1207 bool
1208 types::FinishCompilation(JSContext* cx, HandleScript script, ExecutionMode executionMode,
1209 CompilerConstraintList* constraints, RecompileInfo* precompileInfo)
1211 if (constraints->failed())
1212 return false;
1214 CompilerOutput co(script, executionMode);
1216 TypeZone& types = cx->zone()->types;
1217 if (!types.compilerOutputs) {
1218 types.compilerOutputs = cx->new_<TypeZone::CompilerOutputVector>();
1219 if (!types.compilerOutputs)
1220 return false;
1223 #ifdef DEBUG
1224 for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
1225 const CompilerOutput& co = (*types.compilerOutputs)[i];
1226 MOZ_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode);
1228 #endif
1230 uint32_t index = types.compilerOutputs->length();
1231 if (!types.compilerOutputs->append(co)) {
1232 js_ReportOutOfMemory(cx);
1233 return false;
1236 *precompileInfo = RecompileInfo(index, types.generation);
1238 bool succeeded = true;
1240 for (size_t i = 0; i < constraints->length(); i++) {
1241 CompilerConstraint* constraint = constraints->get(i);
1242 if (!constraint->generateTypeConstraint(cx, *precompileInfo))
1243 succeeded = false;
1246 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1247 const CompilerConstraintList::FrozenScript& entry = constraints->frozenScript(i);
1248 if (!entry.script->types()) {
1249 succeeded = false;
1250 break;
1253 // It could happen that one of the compiled scripts was made a
1254 // debuggee mid-compilation (e.g., via setting a breakpoint). If so,
1255 // throw away the compilation.
1256 if (entry.script->isDebuggee()) {
1257 succeeded = false;
1258 break;
1261 if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
1262 succeeded = false;
1263 unsigned nargs = entry.script->functionNonDelazifying()
1264 ? entry.script->functionNonDelazifying()->nargs()
1265 : 0;
1266 for (size_t i = 0; i < nargs; i++) {
1267 if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i)))
1268 succeeded = false;
1270 for (size_t i = 0; i < entry.script->nTypeSets(); i++) {
1271 if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types()->typeArray()[i]))
1272 succeeded = false;
1275 // If necessary, add constraints to trigger invalidation on the script
1276 // after any future changes to the stack type sets.
1277 if (entry.script->hasFreezeConstraints())
1278 continue;
1279 entry.script->setHasFreezeConstraints();
1281 size_t count = TypeScript::NumTypeSets(entry.script);
1283 StackTypeSet* array = entry.script->types()->typeArray();
1284 for (size_t i = 0; i < count; i++) {
1285 if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
1286 succeeded = false;
1290 if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
1291 types.compilerOutputs->back().invalidate();
1292 script->resetWarmUpCounter();
1293 return false;
1296 return true;
1299 static void
1300 CheckDefinitePropertiesTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual)
1302 // The definite properties analysis happens on the main thread, so no new
1303 // types can have been added to actual. The analysis may have updated the
1304 // contents of |frozen| though with new speculative types, and these need
1305 // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
1306 // to work.
1307 if (!frozen->isSubset(actual)) {
1308 TypeSet::TypeList list;
1309 frozen->enumerateTypes(&list);
1311 for (size_t i = 0; i < list.length(); i++)
1312 actual->addType(cx, list[i]);
1316 void
1317 types::FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints)
1319 #ifdef DEBUG
1320 // Assert no new types have been added to the StackTypeSets. Do this before
1321 // calling CheckDefinitePropertiesTypeSet, as it may add new types to the
1322 // StackTypeSets and break these invariants if a script is inlined more
1323 // than once. See also CheckDefinitePropertiesTypeSet.
1324 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1325 const CompilerConstraintList::FrozenScript& entry = constraints->frozenScript(i);
1326 JSScript* script = entry.script;
1327 MOZ_ASSERT(script->types());
1329 MOZ_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes));
1331 unsigned nargs = entry.script->functionNonDelazifying()
1332 ? entry.script->functionNonDelazifying()->nargs()
1333 : 0;
1334 for (size_t j = 0; j < nargs; j++)
1335 MOZ_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
1337 for (size_t j = 0; j < script->nTypeSets(); j++)
1338 MOZ_ASSERT(script->types()->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
1340 #endif
1342 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1343 const CompilerConstraintList::FrozenScript& entry = constraints->frozenScript(i);
1344 JSScript* script = entry.script;
1345 if (!script->types())
1346 MOZ_CRASH();
1348 CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
1350 unsigned nargs = script->functionNonDelazifying()
1351 ? script->functionNonDelazifying()->nargs()
1352 : 0;
1353 for (size_t j = 0; j < nargs; j++)
1354 CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
1356 for (size_t j = 0; j < script->nTypeSets(); j++)
1357 CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types()->typeArray()[j]);
1361 namespace {
1363 // Constraint which triggers recompilation of a script if any type is added to a type set. */
1364 class ConstraintDataFreeze
1366 public:
1367 ConstraintDataFreeze() {}
1369 const char* kind() { return "freeze"; }
1371 bool invalidateOnNewType(Type type) { return true; }
1372 bool invalidateOnNewPropertyState(TypeSet* property) { return true; }
1373 bool invalidateOnNewObjectState(TypeObject* object) { return false; }
1375 bool constraintHolds(JSContext* cx,
1376 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1378 return expected
1379 ? property.maybeTypes()->isSubset(expected)
1380 : property.maybeTypes()->empty();
1383 bool shouldSweep() { return false; }
1386 } /* anonymous namespace */
1388 void
1389 HeapTypeSetKey::freeze(CompilerConstraintList* constraints)
1391 LifoAlloc* alloc = constraints->alloc();
1393 typedef CompilerConstraintInstance<ConstraintDataFreeze> T;
1394 constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataFreeze()));
1397 static inline jit::MIRType
1398 GetMIRTypeFromTypeFlags(TypeFlags flags)
1400 switch (flags) {
1401 case TYPE_FLAG_UNDEFINED:
1402 return jit::MIRType_Undefined;
1403 case TYPE_FLAG_NULL:
1404 return jit::MIRType_Null;
1405 case TYPE_FLAG_BOOLEAN:
1406 return jit::MIRType_Boolean;
1407 case TYPE_FLAG_INT32:
1408 return jit::MIRType_Int32;
1409 case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
1410 return jit::MIRType_Double;
1411 case TYPE_FLAG_STRING:
1412 return jit::MIRType_String;
1413 case TYPE_FLAG_SYMBOL:
1414 return jit::MIRType_Symbol;
1415 case TYPE_FLAG_LAZYARGS:
1416 return jit::MIRType_MagicOptimizedArguments;
1417 case TYPE_FLAG_ANYOBJECT:
1418 return jit::MIRType_Object;
1419 default:
1420 return jit::MIRType_Value;
1424 jit::MIRType
1425 TemporaryTypeSet::getKnownMIRType()
1427 TypeFlags flags = baseFlags();
1428 jit::MIRType type;
1430 if (baseObjectCount())
1431 type = flags ? jit::MIRType_Value : jit::MIRType_Object;
1432 else
1433 type = GetMIRTypeFromTypeFlags(flags);
1436 * If the type set is totally empty then it will be treated as unknown,
1437 * but we still need to record the dependency as adding a new type can give
1438 * it a definite type tag. This is not needed if there are enough types
1439 * that the exact tag is unknown, as it will stay unknown as more types are
1440 * added to the set.
1442 DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
1443 MOZ_ASSERT_IF(empty, type == jit::MIRType_Value);
1445 return type;
1448 jit::MIRType
1449 HeapTypeSetKey::knownMIRType(CompilerConstraintList* constraints)
1451 TypeSet* types = maybeTypes();
1453 if (!types || types->unknown())
1454 return jit::MIRType_Value;
1456 TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
1457 jit::MIRType type;
1459 if (types->unknownObject() || types->getObjectCount())
1460 type = flags ? jit::MIRType_Value : jit::MIRType_Object;
1461 else
1462 type = GetMIRTypeFromTypeFlags(flags);
1464 if (type != jit::MIRType_Value)
1465 freeze(constraints);
1468 * If the type set is totally empty then it will be treated as unknown,
1469 * but we still need to record the dependency as adding a new type can give
1470 * it a definite type tag. This is not needed if there are enough types
1471 * that the exact tag is unknown, as it will stay unknown as more types are
1472 * added to the set.
1474 MOZ_ASSERT_IF(types->empty(), type == jit::MIRType_Value);
1476 return type;
1479 bool
1480 HeapTypeSetKey::isOwnProperty(CompilerConstraintList* constraints,
1481 bool allowEmptyTypesForGlobal/* = false*/)
1483 if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
1484 return true;
1485 JSObject* obj = object()->singleton();
1486 MOZ_ASSERT_IF(obj, CanHaveEmptyPropertyTypesForOwnProperty(obj) == obj->is<GlobalObject>());
1487 if (obj && !allowEmptyTypesForGlobal) {
1488 if (CanHaveEmptyPropertyTypesForOwnProperty(obj))
1489 return true;
1491 freeze(constraints);
1492 return false;
1495 bool
1496 HeapTypeSetKey::knownSubset(CompilerConstraintList* constraints, const HeapTypeSetKey& other)
1498 if (!maybeTypes() || maybeTypes()->empty()) {
1499 freeze(constraints);
1500 return true;
1502 if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes()))
1503 return false;
1504 freeze(constraints);
1505 return true;
1508 JSObject*
1509 TemporaryTypeSet::getSingleton()
1511 if (baseFlags() != 0 || baseObjectCount() != 1)
1512 return nullptr;
1514 return getSingleObject(0);
1517 JSObject*
1518 HeapTypeSetKey::singleton(CompilerConstraintList* constraints)
1520 HeapTypeSet* types = maybeTypes();
1522 if (!types || types->nonDataProperty() || types->baseFlags() != 0 || types->getObjectCount() != 1)
1523 return nullptr;
1525 JSObject* obj = types->getSingleObject(0);
1527 if (obj)
1528 freeze(constraints);
1530 return obj;
1533 bool
1534 HeapTypeSetKey::needsBarrier(CompilerConstraintList* constraints)
1536 TypeSet* types = maybeTypes();
1537 if (!types)
1538 return false;
1539 bool result = types->unknownObject()
1540 || types->getObjectCount() > 0
1541 || types->hasAnyFlag(TYPE_FLAG_STRING | TYPE_FLAG_SYMBOL);
1542 if (!result)
1543 freeze(constraints);
1544 return result;
1547 namespace {
1549 // Constraint which triggers recompilation if an object acquires particular flags.
1550 class ConstraintDataFreezeObjectFlags
1552 public:
1553 // Flags we are watching for on this object.
1554 TypeObjectFlags flags;
1556 explicit ConstraintDataFreezeObjectFlags(TypeObjectFlags flags)
1557 : flags(flags)
1559 MOZ_ASSERT(flags);
1562 const char* kind() { return "freezeObjectFlags"; }
1564 bool invalidateOnNewType(Type type) { return false; }
1565 bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1566 bool invalidateOnNewObjectState(TypeObject* object) {
1567 return object->hasAnyFlags(flags);
1570 bool constraintHolds(JSContext* cx,
1571 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1573 return !invalidateOnNewObjectState(property.object()->maybeType());
1576 bool shouldSweep() { return false; }
1579 } /* anonymous namespace */
1581 bool
1582 TypeObjectKey::hasFlags(CompilerConstraintList* constraints, TypeObjectFlags flags)
1584 MOZ_ASSERT(flags);
1586 if (TypeObject* type = maybeType()) {
1587 if (type->hasAnyFlags(flags))
1588 return true;
1591 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1592 LifoAlloc* alloc = constraints->alloc();
1594 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
1595 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(flags)));
1596 return false;
1599 bool
1600 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList* constraints, TypeObjectFlags flags)
1602 if (unknownObject())
1603 return true;
1606 * Treat type sets containing no objects as having all object flags,
1607 * to spare callers from having to check this.
1609 if (baseObjectCount() == 0)
1610 return true;
1612 unsigned count = getObjectCount();
1613 for (unsigned i = 0; i < count; i++) {
1614 TypeObjectKey* object = getObject(i);
1615 if (object && object->hasFlags(constraints, flags))
1616 return true;
1619 return false;
1622 gc::InitialHeap
1623 TypeObject::initialHeap(CompilerConstraintList* constraints)
1625 // If this object is not required to be pretenured but could be in the
1626 // future, add a constraint to trigger recompilation if the requirement
1627 // changes.
1629 if (shouldPreTenure())
1630 return gc::TenuredHeap;
1632 if (!canPreTenure())
1633 return gc::DefaultHeap;
1635 HeapTypeSetKey objectProperty = TypeObjectKey::get(this)->property(JSID_EMPTY);
1636 LifoAlloc* alloc = constraints->alloc();
1638 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
1639 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE)));
1641 return gc::DefaultHeap;
1644 namespace {
1646 // Constraint which triggers recompilation on any type change in an inlined
1647 // script. The freeze constraints added to stack type sets will only directly
1648 // invalidate the script containing those stack type sets. To invalidate code
1649 // for scripts into which the base script was inlined, ObjectStateChange is used.
1650 class ConstraintDataFreezeObjectForInlinedCall
1652 public:
1653 ConstraintDataFreezeObjectForInlinedCall()
1656 const char* kind() { return "freezeObjectForInlinedCall"; }
1658 bool invalidateOnNewType(Type type) { return false; }
1659 bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1660 bool invalidateOnNewObjectState(TypeObject* object) {
1661 // We don't keep track of the exact dependencies the caller has on its
1662 // inlined scripts' type sets, so always invalidate the caller.
1663 return true;
1666 bool constraintHolds(JSContext* cx,
1667 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1669 return true;
1672 bool shouldSweep() { return false; }
1675 // Constraint which triggers recompilation when a typed array's data becomes
1676 // invalid.
1677 class ConstraintDataFreezeObjectForTypedArrayData
1679 void* viewData;
1680 uint32_t length;
1682 public:
1683 explicit ConstraintDataFreezeObjectForTypedArrayData(TypedArrayObject& tarray)
1684 : viewData(tarray.viewData()),
1685 length(tarray.length())
1688 const char* kind() { return "freezeObjectForTypedArrayData"; }
1690 bool invalidateOnNewType(Type type) { return false; }
1691 bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1692 bool invalidateOnNewObjectState(TypeObject* object) {
1693 TypedArrayObject& tarray = object->singleton()->as<TypedArrayObject>();
1694 return tarray.viewData() != viewData || tarray.length() != length;
1697 bool constraintHolds(JSContext* cx,
1698 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1700 return !invalidateOnNewObjectState(property.object()->maybeType());
1703 bool shouldSweep() {
1704 // Note: |viewData| is only used for equality testing.
1705 return false;
1709 } /* anonymous namespace */
1711 void
1712 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList* constraints)
1714 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1715 LifoAlloc* alloc = constraints->alloc();
1717 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> T;
1718 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectForInlinedCall()));
1721 void
1722 TypeObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* constraints)
1724 TypedArrayObject& tarray = asSingleObject()->as<TypedArrayObject>();
1725 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1726 LifoAlloc* alloc = constraints->alloc();
1728 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayData> T;
1729 constraints->add(alloc->new_<T>(alloc, objectProperty,
1730 ConstraintDataFreezeObjectForTypedArrayData(tarray)));
1733 static void
1734 ObjectStateChange(ExclusiveContext* cxArg, TypeObject* object, bool markingUnknown)
1736 if (object->unknownProperties())
1737 return;
1739 /* All constraints listening to state changes are on the empty id. */
1740 HeapTypeSet* types = object->maybeGetProperty(JSID_EMPTY);
1742 /* Mark as unknown after getting the types, to avoid assertion. */
1743 if (markingUnknown)
1744 object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
1746 if (types) {
1747 if (JSContext* cx = cxArg->maybeJSContext()) {
1748 TypeConstraint* constraint = types->constraintList;
1749 while (constraint) {
1750 constraint->newObjectState(cx, object);
1751 constraint = constraint->next;
1753 } else {
1754 MOZ_ASSERT(!types->constraintList);
1759 namespace {
1761 class ConstraintDataFreezePropertyState
1763 public:
1764 enum Which {
1765 NON_DATA,
1766 NON_WRITABLE
1767 } which;
1769 explicit ConstraintDataFreezePropertyState(Which which)
1770 : which(which)
1773 const char* kind() { return (which == NON_DATA) ? "freezeNonDataProperty" : "freezeNonWritableProperty"; }
1775 bool invalidateOnNewType(Type type) { return false; }
1776 bool invalidateOnNewPropertyState(TypeSet* property) {
1777 return (which == NON_DATA)
1778 ? property->nonDataProperty()
1779 : property->nonWritableProperty();
1781 bool invalidateOnNewObjectState(TypeObject* object) { return false; }
1783 bool constraintHolds(JSContext* cx,
1784 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1786 return !invalidateOnNewPropertyState(property.maybeTypes());
1789 bool shouldSweep() { return false; }
1792 } /* anonymous namespace */
1794 bool
1795 HeapTypeSetKey::nonData(CompilerConstraintList* constraints)
1797 if (maybeTypes() && maybeTypes()->nonDataProperty())
1798 return true;
1800 LifoAlloc* alloc = constraints->alloc();
1802 typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
1803 constraints->add(alloc->new_<T>(alloc, *this,
1804 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA)));
1805 return false;
1808 bool
1809 HeapTypeSetKey::nonWritable(CompilerConstraintList* constraints)
1811 if (maybeTypes() && maybeTypes()->nonWritableProperty())
1812 return true;
1814 LifoAlloc* alloc = constraints->alloc();
1816 typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
1817 constraints->add(alloc->new_<T>(alloc, *this,
1818 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE)));
1819 return false;
1822 namespace {
1824 class ConstraintDataConstantProperty
1826 public:
1827 explicit ConstraintDataConstantProperty() {}
1829 const char* kind() { return "constantProperty"; }
1831 bool invalidateOnNewType(Type type) { return false; }
1832 bool invalidateOnNewPropertyState(TypeSet* property) {
1833 return property->nonConstantProperty();
1835 bool invalidateOnNewObjectState(TypeObject* object) { return false; }
1837 bool constraintHolds(JSContext* cx,
1838 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1840 return !invalidateOnNewPropertyState(property.maybeTypes());
1843 bool shouldSweep() { return false; }
1846 } /* anonymous namespace */
1848 bool
1849 HeapTypeSetKey::constant(CompilerConstraintList* constraints, Value* valOut)
1851 if (nonData(constraints))
1852 return false;
1854 // Only singleton object properties can be marked as constants.
1855 JSObject* obj = object()->singleton();
1856 if (!obj || !obj->isNative())
1857 return false;
1859 if (maybeTypes() && maybeTypes()->nonConstantProperty())
1860 return false;
1862 // Get the current value of the property.
1863 Shape* shape = obj->as<NativeObject>().lookupPure(id());
1864 if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot() || shape->hadOverwrite())
1865 return false;
1867 Value val = obj->as<NativeObject>().getSlot(shape->slot());
1869 // If the value is a pointer to an object in the nursery, don't optimize.
1870 if (val.isGCThing() && IsInsideNursery(val.toGCThing()))
1871 return false;
1873 // If the value is a string that's not atomic, don't optimize.
1874 if (val.isString() && !val.toString()->isAtom())
1875 return false;
1877 *valOut = val;
1879 LifoAlloc* alloc = constraints->alloc();
1880 typedef CompilerConstraintInstance<ConstraintDataConstantProperty> T;
1881 constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataConstantProperty()));
1882 return true;
1885 // A constraint that never triggers recompilation.
1886 class ConstraintDataInert
1888 public:
1889 explicit ConstraintDataInert() {}
1891 const char* kind() { return "inert"; }
1893 bool invalidateOnNewType(Type type) { return false; }
1894 bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
1895 bool invalidateOnNewObjectState(TypeObject* object) { return false; }
1897 bool constraintHolds(JSContext* cx,
1898 const HeapTypeSetKey& property, TemporaryTypeSet* expected)
1900 return true;
1903 bool shouldSweep() { return false; }
1906 bool
1907 HeapTypeSetKey::couldBeConstant(CompilerConstraintList* constraints)
1909 // Only singleton object properties can be marked as constants.
1910 if (!object()->singleton())
1911 return false;
1913 if (!maybeTypes() || !maybeTypes()->nonConstantProperty())
1914 return true;
1916 // It is possible for a property that was not marked as constant to
1917 // 'become' one, if we throw away the type property during a GC and
1918 // regenerate it with the constant flag set. TypeObject::sweep only removes
1919 // type properties if they have no constraints attached to them, so add
1920 // inert constraints to pin these properties in place.
1922 LifoAlloc* alloc = constraints->alloc();
1923 typedef CompilerConstraintInstance<ConstraintDataInert> T;
1924 constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataInert()));
1926 return false;
1929 bool
1930 TemporaryTypeSet::filtersType(const TemporaryTypeSet* other, Type filteredType) const
1932 if (other->unknown())
1933 return unknown();
1935 for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
1936 Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
1937 if (type != filteredType && other->hasType(type) && !hasType(type))
1938 return false;
1941 if (other->unknownObject())
1942 return unknownObject();
1944 for (size_t i = 0; i < other->getObjectCount(); i++) {
1945 TypeObjectKey* key = other->getObject(i);
1946 if (key) {
1947 Type type = Type::ObjectType(key);
1948 if (type != filteredType && !hasType(type))
1949 return false;
1953 return true;
1956 TemporaryTypeSet::DoubleConversion
1957 TemporaryTypeSet::convertDoubleElements(CompilerConstraintList* constraints)
1959 if (unknownObject() || !getObjectCount())
1960 return AmbiguousDoubleConversion;
1962 bool alwaysConvert = true;
1963 bool maybeConvert = false;
1964 bool dontConvert = false;
1966 for (unsigned i = 0; i < getObjectCount(); i++) {
1967 TypeObjectKey* type = getObject(i);
1968 if (!type)
1969 continue;
1971 if (type->unknownProperties()) {
1972 alwaysConvert = false;
1973 continue;
1976 HeapTypeSetKey property = type->property(JSID_VOID);
1977 property.freeze(constraints);
1979 // We can't convert to double elements for objects which do not have
1980 // double in their element types (as the conversion may render the type
1981 // information incorrect), nor for non-array objects (as their elements
1982 // may point to emptyObjectElements, which cannot be converted).
1983 if (!property.maybeTypes() ||
1984 !property.maybeTypes()->hasType(Type::DoubleType()) ||
1985 type->clasp() != &ArrayObject::class_)
1987 dontConvert = true;
1988 alwaysConvert = false;
1989 continue;
1992 // Only bother with converting known packed arrays whose possible
1993 // element types are int or double. Other arrays require type tests
1994 // when elements are accessed regardless of the conversion.
1995 if (property.knownMIRType(constraints) == jit::MIRType_Double &&
1996 !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED))
1998 maybeConvert = true;
1999 } else {
2000 alwaysConvert = false;
2004 MOZ_ASSERT_IF(alwaysConvert, maybeConvert);
2006 if (maybeConvert && dontConvert)
2007 return AmbiguousDoubleConversion;
2008 if (alwaysConvert)
2009 return AlwaysConvertToDoubles;
2010 if (maybeConvert)
2011 return MaybeConvertToDoubles;
2012 return DontConvertToDoubles;
2015 const Class*
2016 TemporaryTypeSet::getKnownClass()
2018 if (unknownObject())
2019 return nullptr;
2021 const Class* clasp = nullptr;
2022 unsigned count = getObjectCount();
2024 for (unsigned i = 0; i < count; i++) {
2025 const Class* nclasp = getObjectClass(i);
2026 if (!nclasp)
2027 continue;
2029 if (clasp && clasp != nclasp)
2030 return nullptr;
2031 clasp = nclasp;
2034 return clasp;
2037 TemporaryTypeSet::ForAllResult
2038 TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp))
2040 if (unknownObject())
2041 return ForAllResult::MIXED;
2043 unsigned count = getObjectCount();
2044 if (count == 0)
2045 return ForAllResult::EMPTY;
2047 bool true_results = false;
2048 bool false_results = false;
2049 for (unsigned i = 0; i < count; i++) {
2050 const Class* clasp = getObjectClass(i);
2051 if (!clasp)
2052 return ForAllResult::MIXED;
2053 if (func(clasp)) {
2054 true_results = true;
2055 if (false_results) return ForAllResult::MIXED;
2057 else {
2058 false_results = true;
2059 if (true_results) return ForAllResult::MIXED;
2063 MOZ_ASSERT(true_results != false_results);
2065 return true_results ? ForAllResult::ALL_TRUE : ForAllResult::ALL_FALSE;
2068 Scalar::Type
2069 TemporaryTypeSet::getTypedArrayType()
2071 const Class* clasp = getKnownClass();
2073 if (clasp && IsTypedArrayClass(clasp))
2074 return (Scalar::Type) (clasp - &TypedArrayObject::classes[0]);
2075 return Scalar::MaxTypedArrayViewType;
2078 Scalar::Type
2079 TemporaryTypeSet::getSharedTypedArrayType()
2081 const Class* clasp = getKnownClass();
2083 if (clasp && IsSharedTypedArrayClass(clasp))
2084 return (Scalar::Type) (clasp - &SharedTypedArrayObject::classes[0]);
2085 return Scalar::MaxTypedArrayViewType;
2088 bool
2089 TypeSet::isDOMClass()
2091 if (unknownObject())
2092 return false;
2094 unsigned count = getObjectCount();
2095 for (unsigned i = 0; i < count; i++) {
2096 const Class* clasp = getObjectClass(i);
2097 if (clasp && !clasp->isDOMClass())
2098 return false;
2101 return count > 0;
2104 bool
2105 TemporaryTypeSet::maybeCallable()
2107 if (!maybeObject())
2108 return false;
2110 if (unknownObject())
2111 return true;
2113 unsigned count = getObjectCount();
2114 for (unsigned i = 0; i < count; i++) {
2115 const Class* clasp = getObjectClass(i);
2116 if (clasp && (clasp->isProxy() || clasp->nonProxyCallable()))
2117 return true;
2120 return false;
2123 bool
2124 TemporaryTypeSet::maybeEmulatesUndefined()
2126 if (!maybeObject())
2127 return false;
2129 if (unknownObject())
2130 return true;
2132 unsigned count = getObjectCount();
2133 for (unsigned i = 0; i < count; i++) {
2134 // The object emulates undefined if clasp->emulatesUndefined() or if
2135 // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
2136 // proxies, we can just check for that.
2137 const Class* clasp = getObjectClass(i);
2138 if (clasp && (clasp->emulatesUndefined() || clasp->isProxy()))
2139 return true;
2142 return false;
2145 JSObject*
2146 TemporaryTypeSet::getCommonPrototype()
2148 if (unknownObject())
2149 return nullptr;
2151 JSObject* proto = nullptr;
2152 unsigned count = getObjectCount();
2154 for (unsigned i = 0; i < count; i++) {
2155 TypeObjectKey* object = getObject(i);
2156 if (!object)
2157 continue;
2159 if (!object->hasTenuredProto())
2160 return nullptr;
2162 TaggedProto nproto = object->proto();
2163 if (proto) {
2164 if (nproto != TaggedProto(proto))
2165 return nullptr;
2166 } else {
2167 if (!nproto.isObject())
2168 return nullptr;
2169 proto = nproto.toObject();
2173 return proto;
2176 bool
2177 TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid id)
2179 if (unknownObject())
2180 return true;
2182 for (unsigned i = 0; i < getObjectCount(); i++) {
2183 TypeObjectKey* type = getObject(i);
2184 if (!type)
2185 continue;
2187 if (type->unknownProperties())
2188 return true;
2190 HeapTypeSetKey property = type->property(id);
2191 if (property.needsBarrier(constraints))
2192 return true;
2195 return false;
2198 /////////////////////////////////////////////////////////////////////
2199 // TypeCompartment
2200 /////////////////////////////////////////////////////////////////////
2202 TypeCompartment::TypeCompartment()
2204 PodZero(this);
2207 TypeObject*
2208 TypeCompartment::newTypeObject(ExclusiveContext* cx, const Class* clasp, Handle<TaggedProto> proto,
2209 TypeObjectFlags initialFlags)
2211 MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
2213 if (cx->isJSContext()) {
2214 if (proto.isObject() && IsInsideNursery(proto.toObject()))
2215 initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
2218 TypeObject* object = js::NewTypeObject(cx);
2219 if (!object)
2220 return nullptr;
2221 new(object) TypeObject(clasp, proto, initialFlags);
2223 return object;
2226 TypeObject*
2227 TypeCompartment::addAllocationSiteTypeObject(JSContext* cx, AllocationSiteKey key)
2229 AutoEnterAnalysis enter(cx);
2231 if (!allocationSiteTable) {
2232 allocationSiteTable = cx->new_<AllocationSiteTable>();
2233 if (!allocationSiteTable || !allocationSiteTable->init()) {
2234 js_delete(allocationSiteTable);
2235 allocationSiteTable = nullptr;
2236 return nullptr;
2240 AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
2241 MOZ_ASSERT(!p);
2243 TypeObject* res = nullptr;
2245 jsbytecode* pc = key.script->offsetToPC(key.offset);
2246 RootedScript keyScript(cx, key.script);
2248 if (!res) {
2249 RootedObject proto(cx);
2250 if (!GetBuiltinPrototype(cx, key.kind, &proto))
2251 return nullptr;
2253 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
2254 res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
2255 if (!res)
2256 return nullptr;
2257 key.script = keyScript;
2260 if (JSOp(*pc) == JSOP_NEWOBJECT) {
2262 * This object is always constructed the same way and will not be
2263 * observed by other code before all properties have been added. Mark
2264 * all the properties as definite properties of the object.
2266 RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc)));
2268 if (!res->addDefiniteProperties(cx, baseobj->lastProperty()))
2269 return nullptr;
2272 if (!allocationSiteTable->add(p, key, res))
2273 return nullptr;
2275 return res;
2278 static inline jsid
2279 GetAtomId(JSContext* cx, JSScript* script, const jsbytecode* pc, unsigned offset)
2281 PropertyName* name = script->getName(GET_UINT32_INDEX(pc + offset));
2282 return IdToTypeId(NameToId(name));
2285 bool
2286 types::UseNewType(JSContext* cx, JSScript* script, jsbytecode* pc)
2289 * Make a heuristic guess at a use of JSOP_NEW that the constructed object
2290 * should have a fresh type object. We do this when the NEW is immediately
2291 * followed by a simple assignment to an object's .prototype field.
2292 * This is designed to catch common patterns for subclassing in JS:
2294 * function Super() { ... }
2295 * function Sub1() { ... }
2296 * function Sub2() { ... }
2298 * Sub1.prototype = new Super();
2299 * Sub2.prototype = new Super();
2301 * Using distinct type objects for the particular prototypes of Sub1 and
2302 * Sub2 lets us continue to distinguish the two subclasses and any extra
2303 * properties added to those prototype objects.
2305 if (script->isGenerator())
2306 return false;
2307 if (JSOp(*pc) != JSOP_NEW)
2308 return false;
2309 pc += JSOP_NEW_LENGTH;
2310 if (JSOp(*pc) == JSOP_SETPROP) {
2311 jsid id = GetAtomId(cx, script, pc, 0);
2312 if (id == id_prototype(cx))
2313 return true;
2316 return false;
2319 NewObjectKind
2320 types::UseNewTypeForInitializer(JSScript* script, jsbytecode* pc, JSProtoKey key)
2323 * Objects created outside loops in global and eval scripts should have
2324 * singleton types. For now this is only done for plain objects and typed
2325 * arrays, but not normal arrays.
2328 if (script->functionNonDelazifying() && !script->treatAsRunOnce())
2329 return GenericObject;
2331 if (key != JSProto_Object &&
2332 !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) &&
2333 !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray))
2335 return GenericObject;
2338 // All loops in the script will have a try note indicating their boundary.
2340 if (!script->hasTrynotes())
2341 return SingletonObject;
2343 unsigned offset = script->pcToOffset(pc);
2345 JSTryNote* tn = script->trynotes()->vector;
2346 JSTryNote* tnlimit = tn + script->trynotes()->length;
2347 for (; tn < tnlimit; tn++) {
2348 if (tn->kind != JSTRY_FOR_IN && tn->kind != JSTRY_FOR_OF && tn->kind != JSTRY_LOOP)
2349 continue;
2351 unsigned startOffset = script->mainOffset() + tn->start;
2352 unsigned endOffset = startOffset + tn->length;
2354 if (offset >= startOffset && offset < endOffset)
2355 return GenericObject;
2358 return SingletonObject;
2361 NewObjectKind
2362 types::UseNewTypeForInitializer(JSScript* script, jsbytecode* pc, const Class* clasp)
2364 return UseNewTypeForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
2367 static inline bool
2368 ClassCanHaveExtraProperties(const Class* clasp)
2370 return clasp->resolve
2371 || clasp->ops.lookupGeneric
2372 || clasp->ops.getGeneric
2373 || IsAnyTypedArrayClass(clasp);
2376 static inline bool
2377 PrototypeHasIndexedProperty(CompilerConstraintList* constraints, JSObject* obj)
2379 do {
2380 TypeObjectKey* type = TypeObjectKey::get(obj);
2381 if (ClassCanHaveExtraProperties(type->clasp()))
2382 return true;
2383 if (type->unknownProperties())
2384 return true;
2385 HeapTypeSetKey index = type->property(JSID_VOID);
2386 if (index.nonData(constraints) || index.isOwnProperty(constraints))
2387 return true;
2388 if (!obj->hasTenuredProto())
2389 return true;
2390 obj = obj->getProto();
2391 } while (obj);
2393 return false;
2396 bool
2397 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList* constraints, JSScript* script)
2399 if (JSObject* proto = script->global().maybeGetArrayPrototype())
2400 return PrototypeHasIndexedProperty(constraints, proto);
2401 return true;
2404 bool
2405 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList* constraints,
2406 TemporaryTypeSet* types)
2408 const Class* clasp = types->getKnownClass();
2410 // Note: typed arrays have indexed properties not accounted for by type
2411 // information, though these are all in bounds and will be accounted for
2412 // by JIT paths.
2413 if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp)))
2414 return true;
2416 if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
2417 return true;
2419 JSObject* proto = types->getCommonPrototype();
2420 if (!proto)
2421 return true;
2423 return PrototypeHasIndexedProperty(constraints, proto);
2426 void
2427 TypeZone::processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles)
2429 MOZ_ASSERT(!recompiles.empty());
2432 * Steal the list of scripts to recompile, to make sure we don't try to
2433 * recursively recompile them.
2435 RecompileInfoVector pending;
2436 for (size_t i = 0; i < recompiles.length(); i++) {
2437 if (!pending.append(recompiles[i]))
2438 CrashAtUnhandlableOOM("processPendingRecompiles");
2440 recompiles.clear();
2442 jit::Invalidate(*this, fop, pending);
2444 MOZ_ASSERT(recompiles.empty());
2447 void
2448 TypeZone::addPendingRecompile(JSContext* cx, const RecompileInfo& info)
2450 CompilerOutput* co = info.compilerOutput(cx);
2451 if (!co || !co->isValid() || co->pendingInvalidation())
2452 return;
2454 InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
2455 co->script(), co->script()->filename(), co->script()->lineno());
2457 co->setPendingInvalidation();
2459 if (!cx->zone()->types.activeAnalysis->pendingRecompiles.append(info))
2460 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
2463 void
2464 TypeZone::addPendingRecompile(JSContext* cx, JSScript* script)
2466 MOZ_ASSERT(script);
2468 CancelOffThreadIonCompile(cx->compartment(), script);
2470 // Let the script warm up again before attempting another compile.
2471 if (jit::IsBaselineEnabled(cx))
2472 script->resetWarmUpCounter();
2474 if (script->hasIonScript())
2475 addPendingRecompile(cx, script->ionScript()->recompileInfo());
2477 if (script->hasParallelIonScript())
2478 addPendingRecompile(cx, script->parallelIonScript()->recompileInfo());
2480 // When one script is inlined into another the caller listens to state
2481 // changes on the callee's script, so trigger these to force recompilation
2482 // of any such callers.
2483 if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType())
2484 ObjectStateChange(cx, script->functionNonDelazifying()->type(), false);
2487 void
2488 TypeCompartment::markSetsUnknown(JSContext* cx, TypeObject* target)
2490 MOZ_ASSERT(this == &cx->compartment()->types);
2491 MOZ_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
2492 MOZ_ASSERT(!target->singleton());
2493 MOZ_ASSERT(target->unknownProperties());
2495 AutoEnterAnalysis enter(cx);
2497 /* Mark type sets which contain obj as having a generic object types. */
2499 for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
2500 TypeObject* object = i.get<TypeObject>();
2501 unsigned count = object->getPropertyCount();
2502 for (unsigned i = 0; i < count; i++) {
2503 Property* prop = object->getProperty(i);
2504 if (prop && prop->types.hasType(Type::ObjectType(target)))
2505 prop->types.addType(cx, Type::AnyObjectType());
2509 for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2510 JSScript* script = i.get<JSScript>();
2511 if (script->types()) {
2512 unsigned count = TypeScript::NumTypeSets(script);
2513 StackTypeSet* typeArray = script->types()->typeArray();
2514 for (unsigned i = 0; i < count; i++) {
2515 if (typeArray[i].hasType(Type::ObjectType(target)))
2516 typeArray[i].addType(cx, Type::AnyObjectType());
2521 target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN);
2524 void
2525 TypeCompartment::print(JSContext* cx, bool force)
2527 #ifdef DEBUG
2528 gc::AutoSuppressGC suppressGC(cx);
2529 JSAutoRequest request(cx);
2531 Zone* zone = compartment()->zone();
2532 AutoEnterAnalysis enter(nullptr, zone);
2534 if (!force && !InferSpewActive(ISpewResult))
2535 return;
2537 for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2538 RootedScript script(cx, i.get<JSScript>());
2539 if (script->types())
2540 script->types()->printTypes(cx, script);
2543 for (gc::ZoneCellIter i(zone, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
2544 TypeObject* object = i.get<TypeObject>();
2545 object->print();
2547 #endif
2550 /////////////////////////////////////////////////////////////////////
2551 // TypeCompartment tables
2552 /////////////////////////////////////////////////////////////////////
2555 * The arrayTypeTable and objectTypeTable are per-compartment tables for making
2556 * common type objects to model the contents of large script singletons and
2557 * JSON objects. These are vanilla Arrays and native Objects, so we distinguish
2558 * the types of different ones by looking at the types of their properties.
2560 * All singleton/JSON arrays which have the same prototype, are homogenous and
2561 * of the same element type will share a type object. All singleton/JSON
2562 * objects which have the same shape and property types will also share a type
2563 * object. We don't try to collate arrays or objects that have type mismatches.
2566 static inline bool
2567 NumberTypes(Type a, Type b)
2569 return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
2570 && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
2574 * As for GetValueType, but requires object types to be non-singletons with
2575 * their default prototype. These are the only values that should appear in
2576 * arrays and objects whose type can be fixed.
2578 static inline Type
2579 GetValueTypeForTable(const Value& v)
2581 Type type = GetValueType(v);
2582 MOZ_ASSERT(!type.isSingleObject());
2583 return type;
2586 struct types::ArrayTableKey : public DefaultHasher<types::ArrayTableKey>
2588 Type type;
2589 JSObject* proto;
2591 ArrayTableKey()
2592 : type(Type::UndefinedType()), proto(nullptr)
2595 ArrayTableKey(Type type, JSObject* proto)
2596 : type(type), proto(proto)
2599 static inline uint32_t hash(const ArrayTableKey& v) {
2600 return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
2603 static inline bool match(const ArrayTableKey& v1, const ArrayTableKey& v2) {
2604 return v1.type == v2.type && v1.proto == v2.proto;
2607 bool operator==(const ArrayTableKey& other) {
2608 return type == other.type && proto == other.proto;
2611 bool operator!=(const ArrayTableKey& other) {
2612 return !(*this == other);
2616 void
2617 TypeCompartment::setTypeToHomogenousArray(ExclusiveContext* cx,
2618 JSObject* obj, Type elementType)
2620 MOZ_ASSERT(cx->zone()->types.activeAnalysis);
2622 if (!arrayTypeTable) {
2623 arrayTypeTable = cx->new_<ArrayTypeTable>();
2624 if (!arrayTypeTable || !arrayTypeTable->init()) {
2625 arrayTypeTable = nullptr;
2626 return;
2630 ArrayTableKey key(elementType, obj->getProto());
2631 DependentAddPtr<ArrayTypeTable> p(cx, *arrayTypeTable, key);
2632 if (p) {
2633 obj->setType(p->value());
2634 } else {
2635 /* Make a new type to use for future arrays with the same elements. */
2636 RootedObject objProto(cx, obj->getProto());
2637 Rooted<TaggedProto> taggedProto(cx, TaggedProto(objProto));
2638 TypeObject* objType = newTypeObject(cx, &ArrayObject::class_, taggedProto);
2639 if (!objType)
2640 return;
2641 obj->setType(objType);
2643 AddTypePropertyId(cx, objType, JSID_VOID, elementType);
2645 key.proto = objProto;
2646 (void) p.add(cx, *arrayTypeTable, key, objType);
2650 void
2651 TypeCompartment::fixArrayType(ExclusiveContext* cx, ArrayObject* obj)
2653 AutoEnterAnalysis enter(cx);
2656 * If the array is of homogenous type, pick a type object which will be
2657 * shared with all other singleton/JSON arrays of the same type.
2658 * If the array is heterogenous, keep the existing type object, which has
2659 * unknown properties.
2662 unsigned len = obj->getDenseInitializedLength();
2663 if (len == 0)
2664 return;
2666 Type type = GetValueTypeForTable(obj->getDenseElement(0));
2668 for (unsigned i = 1; i < len; i++) {
2669 Type ntype = GetValueTypeForTable(obj->getDenseElement(i));
2670 if (ntype != type) {
2671 if (NumberTypes(type, ntype))
2672 type = Type::DoubleType();
2673 else
2674 return;
2678 setTypeToHomogenousArray(cx, obj, type);
2681 void
2682 types::FixRestArgumentsType(ExclusiveContext* cx, ArrayObject* obj)
2684 cx->compartment()->types.fixRestArgumentsType(cx, obj);
2687 void
2688 TypeCompartment::fixRestArgumentsType(ExclusiveContext* cx, ArrayObject* obj)
2690 AutoEnterAnalysis enter(cx);
2693 * Tracking element types for rest argument arrays is not worth it, but we
2694 * still want it to be known that it's a dense array.
2696 setTypeToHomogenousArray(cx, obj, Type::UnknownType());
2700 * N.B. We could also use the initial shape of the object (before its type is
2701 * fixed) as the key in the object table, but since all references in the table
2702 * are weak the hash entries would usually be collected on GC even if objects
2703 * with the new type/shape are still live.
2705 struct types::ObjectTableKey
2707 jsid* properties;
2708 uint32_t nproperties;
2709 uint32_t nfixed;
2711 struct Lookup {
2712 IdValuePair* properties;
2713 uint32_t nproperties;
2714 uint32_t nfixed;
2716 Lookup(IdValuePair* properties, uint32_t nproperties, uint32_t nfixed)
2717 : properties(properties), nproperties(nproperties), nfixed(nfixed)
2721 static inline HashNumber hash(const Lookup& lookup) {
2722 return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
2723 lookup.nproperties ^
2724 lookup.nfixed);
2727 static inline bool match(const ObjectTableKey& v, const Lookup& lookup) {
2728 if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
2729 return false;
2730 for (size_t i = 0; i < lookup.nproperties; i++) {
2731 if (lookup.properties[i].id != v.properties[i])
2732 return false;
2734 return true;
2738 struct types::ObjectTableEntry
2740 ReadBarrieredTypeObject object;
2741 ReadBarrieredShape shape;
2742 Type* types;
2745 static inline void
2746 UpdateObjectTableEntryTypes(ExclusiveContext* cx, ObjectTableEntry& entry,
2747 IdValuePair* properties, size_t nproperties)
2749 if (entry.object->unknownProperties())
2750 return;
2751 for (size_t i = 0; i < nproperties; i++) {
2752 Type type = entry.types[i];
2753 Type ntype = GetValueTypeForTable(properties[i].value);
2754 if (ntype == type)
2755 continue;
2756 if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
2757 type.isPrimitive(JSVAL_TYPE_DOUBLE))
2759 /* The property types already reflect 'int32'. */
2760 } else {
2761 if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
2762 type.isPrimitive(JSVAL_TYPE_INT32))
2764 /* Include 'double' in the property types to avoid the update below later. */
2765 entry.types[i] = Type::DoubleType();
2767 AddTypePropertyId(cx, entry.object, IdToTypeId(properties[i].id), ntype);
2772 void
2773 TypeCompartment::fixObjectType(ExclusiveContext* cx, PlainObject* obj)
2775 AutoEnterAnalysis enter(cx);
2777 if (!objectTypeTable) {
2778 objectTypeTable = cx->new_<ObjectTypeTable>();
2779 if (!objectTypeTable || !objectTypeTable->init()) {
2780 js_delete(objectTypeTable);
2781 objectTypeTable = nullptr;
2782 return;
2787 * Use the same type object for all singleton/JSON objects with the same
2788 * base shape, i.e. the same fields written in the same order.
2790 * Exclude some objects we can't readily associate common types for based on their
2791 * shape. Objects with metadata are excluded so that the metadata does not need to
2792 * be included in the table lookup (the metadata object might be in the nursery).
2794 if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata())
2795 return;
2797 Vector<IdValuePair> properties(cx);
2798 if (!properties.resize(obj->slotSpan()))
2799 return;
2801 Shape* shape = obj->lastProperty();
2802 while (!shape->isEmptyShape()) {
2803 IdValuePair& entry = properties[shape->slot()];
2804 entry.id = shape->propid();
2805 entry.value = obj->getSlot(shape->slot());
2806 shape = shape->previous();
2809 ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots());
2810 ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
2812 if (p) {
2813 MOZ_ASSERT(obj->getProto() == p->value().object->proto().toObject());
2814 MOZ_ASSERT(obj->lastProperty() == p->value().shape);
2816 UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length());
2817 obj->setType(p->value().object);
2818 return;
2821 /* Make a new type to use for the object and similar future ones. */
2822 Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
2823 TypeObject* objType = newTypeObject(cx, &PlainObject::class_, objProto);
2824 if (!objType || !objType->addDefiniteProperties(cx, obj->lastProperty()))
2825 return;
2827 if (obj->isIndexed())
2828 objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
2830 ScopedJSFreePtr<jsid> ids(objType->zone()->pod_calloc<jsid>(properties.length()));
2831 if (!ids)
2832 return;
2834 ScopedJSFreePtr<Type> types(objType->zone()->pod_calloc<Type>(properties.length()));
2835 if (!types)
2836 return;
2838 for (size_t i = 0; i < properties.length(); i++) {
2839 ids[i] = properties[i].id;
2840 types[i] = GetValueTypeForTable(obj->getSlot(i));
2841 AddTypePropertyId(cx, objType, IdToTypeId(ids[i]), types[i]);
2844 ObjectTableKey key;
2845 key.properties = ids;
2846 key.nproperties = properties.length();
2847 key.nfixed = obj->numFixedSlots();
2848 MOZ_ASSERT(ObjectTableKey::match(key, lookup));
2850 ObjectTableEntry entry;
2851 entry.object.set(objType);
2852 entry.shape.set(obj->lastProperty());
2853 entry.types = types;
2855 obj->setType(objType);
2857 p = objectTypeTable->lookupForAdd(lookup);
2858 if (objectTypeTable->add(p, key, entry)) {
2859 ids.forget();
2860 types.forget();
2864 JSObject*
2865 TypeCompartment::newTypedObject(JSContext* cx, IdValuePair* properties, size_t nproperties)
2867 AutoEnterAnalysis enter(cx);
2869 if (!objectTypeTable) {
2870 objectTypeTable = cx->new_<ObjectTypeTable>();
2871 if (!objectTypeTable || !objectTypeTable->init()) {
2872 js_delete(objectTypeTable);
2873 objectTypeTable = nullptr;
2874 return nullptr;
2879 * Use the object type table to allocate an object with the specified
2880 * properties, filling in its final type and shape and failing if no cache
2881 * entry could be found for the properties.
2885 * Filter out a few cases where we don't want to use the object type table.
2886 * Note that if the properties contain any duplicates or dense indexes,
2887 * the lookup below will fail as such arrays of properties cannot be stored
2888 * in the object type table --- fixObjectType populates the table with
2889 * properties read off its input object, which cannot be duplicates, and
2890 * ignores objects with dense indexes.
2892 if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT)
2893 return nullptr;
2895 gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
2896 size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_);
2898 ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
2899 ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
2901 if (!p)
2902 return nullptr;
2904 RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
2905 if (!obj) {
2906 cx->clearPendingException();
2907 return nullptr;
2909 MOZ_ASSERT(obj->getProto() == p->value().object->proto().toObject());
2911 RootedShape shape(cx, p->value().shape);
2912 if (!NativeObject::setLastProperty(cx, obj, shape)) {
2913 cx->clearPendingException();
2914 return nullptr;
2917 UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties);
2919 for (size_t i = 0; i < nproperties; i++)
2920 obj->setSlot(i, properties[i].value);
2922 obj->setType(p->value().object);
2923 return obj;
2926 /////////////////////////////////////////////////////////////////////
2927 // TypeObject
2928 /////////////////////////////////////////////////////////////////////
2930 void
2931 TypeObject::setProto(JSContext* cx, TaggedProto proto)
2933 MOZ_ASSERT(singleton());
2935 if (proto.isObject() && IsInsideNursery(proto.toObject()))
2936 addFlags(OBJECT_FLAG_NURSERY_PROTO);
2938 setProtoUnchecked(proto);
2941 static inline void
2942 UpdatePropertyType(ExclusiveContext* cx, HeapTypeSet* types, NativeObject* obj, Shape* shape,
2943 bool indexed)
2945 MOZ_ASSERT(obj->hasSingletonType() && !obj->hasLazyType());
2947 if (!shape->writable())
2948 types->setNonWritableProperty(cx);
2950 if (shape->hasGetterValue() || shape->hasSetterValue()) {
2951 types->setNonDataProperty(cx);
2952 types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc());
2953 } else if (shape->hasDefaultGetter() && shape->hasSlot()) {
2954 if (!indexed && types->canSetDefinite(shape->slot()))
2955 types->setDefinite(shape->slot());
2957 const Value& value = obj->getSlot(shape->slot());
2960 * Don't add initial undefined types for properties of global objects
2961 * that are not collated into the JSID_VOID property (see propertySet
2962 * comment).
2964 * Also don't add untracked values (initial uninitialized lexical
2965 * magic values and optimized out values) as appearing in CallObjects.
2967 MOZ_ASSERT_IF(IsUntrackedValue(value), obj->is<CallObject>());
2968 if ((indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) &&
2969 !IsUntrackedValue(value))
2971 Type type = GetValueType(value);
2972 types->TypeSet::addType(type, &cx->typeLifoAlloc());
2975 if (indexed || shape->hadOverwrite()) {
2976 types->setNonConstantProperty(cx);
2977 } else {
2978 InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant",
2979 InferSpewColor(types), types, InferSpewColorReset(),
2980 TypeObjectString(obj->type()), TypeIdString(shape->propid()));
2985 void
2986 TypeObject::updateNewPropertyTypes(ExclusiveContext* cx, jsid id, HeapTypeSet* types)
2988 InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
2989 InferSpewColor(types), types, InferSpewColorReset(),
2990 TypeObjectString(this), TypeIdString(id));
2992 if (!singleton() || !singleton()->isNative()) {
2993 types->setNonConstantProperty(cx);
2994 return;
2997 NativeObject* obj = &singleton()->as<NativeObject>();
3000 * Fill the property in with any type the object already has in an own
3001 * property. We are only interested in plain native properties and
3002 * dense elements which don't go through a barrier when read by the VM
3003 * or jitcode.
3006 if (JSID_IS_VOID(id)) {
3007 /* Go through all shapes on the object to get integer-valued properties. */
3008 RootedShape shape(cx, obj->lastProperty());
3009 while (!shape->isEmptyShape()) {
3010 if (JSID_IS_VOID(IdToTypeId(shape->propid())))
3011 UpdatePropertyType(cx, types, obj, shape, true);
3012 shape = shape->previous();
3015 /* Also get values of any dense elements in the object. */
3016 for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
3017 const Value& value = obj->getDenseElement(i);
3018 if (!value.isMagic(JS_ELEMENTS_HOLE)) {
3019 Type type = GetValueType(value);
3020 types->TypeSet::addType(type, &cx->typeLifoAlloc());
3023 } else if (!JSID_IS_EMPTY(id)) {
3024 RootedId rootedId(cx, id);
3025 Shape* shape = obj->lookup(cx, rootedId);
3026 if (shape)
3027 UpdatePropertyType(cx, types, obj, shape, false);
3030 if (obj->watched()) {
3032 * Mark the property as non-data, to inhibit optimizations on it
3033 * and avoid bypassing the watchpoint handler.
3035 types->setNonDataProperty(cx);
3039 bool
3040 TypeObject::addDefiniteProperties(ExclusiveContext* cx, Shape* shape)
3042 if (unknownProperties())
3043 return true;
3045 /* Mark all properties of shape as definite properties of this type. */
3046 AutoEnterAnalysis enter(cx);
3048 while (!shape->isEmptyShape()) {
3049 jsid id = IdToTypeId(shape->propid());
3050 if (!JSID_IS_VOID(id)) {
3051 MOZ_ASSERT_IF(shape->slot() >= shape->numFixedSlots(),
3052 shape->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS);
3053 TypeSet* types = getProperty(cx, id);
3054 if (!types)
3055 return false;
3056 if (types->canSetDefinite(shape->slot()))
3057 types->setDefinite(shape->slot());
3060 shape = shape->previous();
3063 return true;
3066 bool
3067 TypeObject::matchDefiniteProperties(HandleObject obj)
3069 unsigned count = getPropertyCount();
3070 for (unsigned i = 0; i < count; i++) {
3071 Property* prop = getProperty(i);
3072 if (!prop)
3073 continue;
3074 if (prop->types.definiteProperty()) {
3075 unsigned slot = prop->types.definiteSlot();
3077 bool found = false;
3078 Shape* shape = obj->lastProperty();
3079 while (!shape->isEmptyShape()) {
3080 if (shape->slot() == slot && shape->propid() == prop->id) {
3081 found = true;
3082 break;
3084 shape = shape->previous();
3086 if (!found)
3087 return false;
3091 return true;
3094 void
3095 types::AddTypePropertyId(ExclusiveContext* cx, TypeObject* obj, jsid id, Type type)
3097 MOZ_ASSERT(id == IdToTypeId(id));
3099 if (obj->unknownProperties())
3100 return;
3102 AutoEnterAnalysis enter(cx);
3104 HeapTypeSet* types = obj->getProperty(cx, id);
3105 if (!types)
3106 return;
3108 // Clear any constant flag if it exists.
3109 if (!types->empty() && !types->nonConstantProperty()) {
3110 InferSpew(ISpewOps, "constantMutated: %sT%p%s %s",
3111 InferSpewColor(types), types, InferSpewColorReset(), TypeString(type));
3112 types->setNonConstantProperty(cx);
3115 if (types->hasType(type))
3116 return;
3118 InferSpew(ISpewOps, "externalType: property %s %s: %s",
3119 TypeObjectString(obj), TypeIdString(id), TypeString(type));
3120 types->addType(cx, type);
3122 // Propagate new types from partially initialized types to fully
3123 // initialized types for the acquired properties analysis. Note that we
3124 // don't need to do this for other property changes, as these will also be
3125 // reflected via shape changes on the object that will prevent the object
3126 // from acquiring the fully initialized type.
3127 if (obj->newScript() && obj->newScript()->initializedType()) {
3128 if (type.isObjectUnchecked() && types->unknownObject())
3129 type = Type::AnyObjectType();
3130 AddTypePropertyId(cx, obj->newScript()->initializedType(), id, type);
3134 void
3135 types::AddTypePropertyId(ExclusiveContext* cx, TypeObject* obj, jsid id, const Value& value)
3137 AddTypePropertyId(cx, obj, id, GetValueType(value));
3140 void
3141 TypeObject::markPropertyNonData(ExclusiveContext* cx, jsid id)
3143 AutoEnterAnalysis enter(cx);
3145 id = IdToTypeId(id);
3147 HeapTypeSet* types = getProperty(cx, id);
3148 if (types)
3149 types->setNonDataProperty(cx);
3152 void
3153 TypeObject::markPropertyNonWritable(ExclusiveContext* cx, jsid id)
3155 AutoEnterAnalysis enter(cx);
3157 id = IdToTypeId(id);
3159 HeapTypeSet* types = getProperty(cx, id);
3160 if (types)
3161 types->setNonWritableProperty(cx);
3164 bool
3165 TypeObject::isPropertyNonData(jsid id)
3167 TypeSet* types = maybeGetProperty(id);
3168 if (types)
3169 return types->nonDataProperty();
3170 return false;
3173 bool
3174 TypeObject::isPropertyNonWritable(jsid id)
3176 TypeSet* types = maybeGetProperty(id);
3177 if (types)
3178 return types->nonWritableProperty();
3179 return false;
3182 void
3183 TypeObject::markStateChange(ExclusiveContext* cxArg)
3185 if (unknownProperties())
3186 return;
3188 AutoEnterAnalysis enter(cxArg);
3189 HeapTypeSet* types = maybeGetProperty(JSID_EMPTY);
3190 if (types) {
3191 if (JSContext* cx = cxArg->maybeJSContext()) {
3192 TypeConstraint* constraint = types->constraintList;
3193 while (constraint) {
3194 constraint->newObjectState(cx, this);
3195 constraint = constraint->next;
3197 } else {
3198 MOZ_ASSERT(!types->constraintList);
3203 void
3204 TypeObject::setFlags(ExclusiveContext* cx, TypeObjectFlags flags)
3206 if (hasAllFlags(flags))
3207 return;
3209 AutoEnterAnalysis enter(cx);
3211 if (singleton()) {
3212 /* Make sure flags are consistent with persistent object state. */
3213 MOZ_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
3214 singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
3217 addFlags(flags);
3219 InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
3221 ObjectStateChange(cx, this, false);
3223 // Propagate flag changes from partially to fully initialized types for the
3224 // acquired properties analysis.
3225 if (newScript() && newScript()->initializedType())
3226 newScript()->initializedType()->setFlags(cx, flags);
3229 void
3230 TypeObject::markUnknown(ExclusiveContext* cx)
3232 AutoEnterAnalysis enter(cx);
3234 MOZ_ASSERT(cx->zone()->types.activeAnalysis);
3235 MOZ_ASSERT(!unknownProperties());
3237 InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
3239 clearNewScript(cx);
3240 ObjectStateChange(cx, this, true);
3243 * Existing constraints may have already been added to this object, which we need
3244 * to do the right thing for. We can't ensure that we will mark all unknown
3245 * objects before they have been accessed, as the __proto__ of a known object
3246 * could be dynamically set to an unknown object, and we can decide to ignore
3247 * properties of an object during analysis (i.e. hashmaps). Adding unknown for
3248 * any properties accessed already accounts for possible values read from them.
3251 unsigned count = getPropertyCount();
3252 for (unsigned i = 0; i < count; i++) {
3253 Property* prop = getProperty(i);
3254 if (prop) {
3255 prop->types.addType(cx, Type::UnknownType());
3256 prop->types.setNonDataProperty(cx);
3261 void
3262 TypeObject::maybeClearNewScriptOnOOM()
3264 MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
3266 if (!isMarked())
3267 return;
3269 if (!newScript())
3270 return;
3272 for (unsigned i = 0; i < getPropertyCount(); i++) {
3273 Property* prop = getProperty(i);
3274 if (!prop)
3275 continue;
3276 if (prop->types.definiteProperty())
3277 prop->types.setNonDataPropertyIgnoringConstraints();
3280 // This method is called during GC sweeping, so there is no write barrier
3281 // that needs to be triggered.
3282 js_delete(newScript());
3283 addendum_ = nullptr;
3286 void
3287 TypeObject::clearNewScript(ExclusiveContext* cx)
3289 if (!newScript())
3290 return;
3292 TypeNewScript* newScript = this->newScript();
3293 setNewScript(nullptr);
3295 AutoEnterAnalysis enter(cx);
3298 * Any definite properties we added due to analysis of the new script when
3299 * the type object was created are now invalid: objects with the same type
3300 * can be created by using 'new' on a different script or through some
3301 * other mechanism (e.g. Object.create). Rather than clear out the definite
3302 * bits on the object's properties, just mark such properties as having
3303 * been deleted/reconfigured, which will have the same effect on JITs
3304 * wanting to use the definite bits to optimize property accesses.
3306 for (unsigned i = 0; i < getPropertyCount(); i++) {
3307 Property* prop = getProperty(i);
3308 if (!prop)
3309 continue;
3310 if (prop->types.definiteProperty())
3311 prop->types.setNonDataProperty(cx);
3314 if (cx->isJSContext()) {
3315 newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this);
3316 } else {
3317 // Threads with an ExclusiveContext are not allowed to run scripts.
3318 MOZ_ASSERT(!cx->perThreadData->activation());
3321 js_delete(newScript);
3322 markStateChange(cx);
3325 void
3326 TypeObject::print()
3328 TaggedProto tagged(proto());
3329 fprintf(stderr, "%s : %s",
3330 TypeObjectString(this),
3331 tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject()))
3332 : (tagged.isLazy() ? "(lazy)" : "(null)"));
3334 if (unknownProperties()) {
3335 fprintf(stderr, " unknown");
3336 } else {
3337 if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES))
3338 fprintf(stderr, " dense");
3339 if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED))
3340 fprintf(stderr, " packed");
3341 if (!hasAnyFlags(OBJECT_FLAG_LENGTH_OVERFLOW))
3342 fprintf(stderr, " noLengthOverflow");
3343 if (hasAnyFlags(OBJECT_FLAG_ITERATED))
3344 fprintf(stderr, " iterated");
3345 if (interpretedFunction)
3346 fprintf(stderr, " ifun");
3349 unsigned count = getPropertyCount();
3351 if (count == 0) {
3352 fprintf(stderr, " {}\n");
3353 return;
3356 fprintf(stderr, " {");
3358 if (newScript()) {
3359 if (newScript()->analyzed()) {
3360 fprintf(stderr, "\n newScript %d properties",
3361 (int) newScript()->templateObject()->slotSpan());
3362 if (newScript()->initializedType()) {
3363 fprintf(stderr, " initializedType %p with %d properties",
3364 newScript()->initializedType(), (int) newScript()->initializedShape()->slotSpan());
3366 } else {
3367 fprintf(stderr, "\n newScript unanalyzed");
3371 for (unsigned i = 0; i < count; i++) {
3372 Property* prop = getProperty(i);
3373 if (prop) {
3374 fprintf(stderr, "\n %s:", TypeIdString(prop->id));
3375 prop->types.print();
3379 fprintf(stderr, "\n}\n");
3382 /////////////////////////////////////////////////////////////////////
3383 // Type Analysis
3384 /////////////////////////////////////////////////////////////////////
3387 * Persistent constraint clearing out newScript and definite properties from
3388 * an object should a property on another object get a getter or setter.
3390 class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
3392 public:
3393 TypeObject* object;
3395 explicit TypeConstraintClearDefiniteGetterSetter(TypeObject* object)
3396 : object(object)
3399 const char* kind() { return "clearDefiniteGetterSetter"; }
3401 void newPropertyState(JSContext* cx, TypeSet* source) {
3403 * Clear out the newScript shape and definite property information from
3404 * an object if the source type set could be a setter or could be
3405 * non-writable.
3407 if (source->nonDataProperty() || source->nonWritableProperty())
3408 object->clearNewScript(cx);
3411 void newType(JSContext* cx, TypeSet* source, Type type) {}
3413 bool sweep(TypeZone& zone, TypeConstraint** res) {
3414 if (IsTypeObjectAboutToBeFinalized(&object))
3415 return false;
3416 *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
3417 return true;
3421 bool
3422 types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, TypeObject* type, HandleId id)
3425 * Ensure that if the properties named here could have a getter, setter or
3426 * a permanent property in any transitive prototype, the definite
3427 * properties get cleared from the type.
3429 RootedObject parent(cx, type->proto().toObjectOrNull());
3430 while (parent) {
3431 TypeObject* parentObject = parent->getType(cx);
3432 if (!parentObject || parentObject->unknownProperties())
3433 return false;
3434 HeapTypeSet* parentTypes = parentObject->getProperty(cx, id);
3435 if (!parentTypes || parentTypes->nonDataProperty() || parentTypes->nonWritableProperty())
3436 return false;
3437 if (!parentTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type)))
3438 return false;
3439 parent = parent->getProto();
3441 return true;
3445 * Constraint which clears definite properties on an object should a type set
3446 * contain any types other than a single object.
3448 class TypeConstraintClearDefiniteSingle : public TypeConstraint
3450 public:
3451 TypeObject* object;
3453 explicit TypeConstraintClearDefiniteSingle(TypeObject* object)
3454 : object(object)
3457 const char* kind() { return "clearDefiniteSingle"; }
3459 void newType(JSContext* cx, TypeSet* source, Type type) {
3460 if (source->baseFlags() || source->getObjectCount() > 1)
3461 object->clearNewScript(cx);
3464 bool sweep(TypeZone& zone, TypeConstraint** res) {
3465 if (IsTypeObjectAboutToBeFinalized(&object))
3466 return false;
3467 *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
3468 return true;
3472 bool
3473 types::AddClearDefiniteFunctionUsesInScript(JSContext* cx, TypeObject* type,
3474 JSScript* script, JSScript* calleeScript)
3476 // Look for any uses of the specified calleeScript in type sets for
3477 // |script|, and add constraints to ensure that if the type sets' contents
3478 // change then the definite properties are cleared from the type.
3479 // This ensures that the inlining performed when the definite properties
3480 // analysis was done is stable. We only need to look at type sets which
3481 // contain a single object, as IonBuilder does not inline polymorphic sites
3482 // during the definite properties analysis.
3484 TypeObjectKey* calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
3486 unsigned count = TypeScript::NumTypeSets(script);
3487 StackTypeSet* typeArray = script->types()->typeArray();
3489 for (unsigned i = 0; i < count; i++) {
3490 StackTypeSet* types = &typeArray[i];
3491 if (!types->unknownObject() && types->getObjectCount() == 1) {
3492 if (calleeKey != types->getObject(0)) {
3493 // Also check if the object is the Function.call or
3494 // Function.apply native. IonBuilder uses the presence of these
3495 // functions during inlining.
3496 JSObject* singleton = types->getSingleObject(0);
3497 if (!singleton || !singleton->is<JSFunction>())
3498 continue;
3499 JSFunction* fun = &singleton->as<JSFunction>();
3500 if (!fun->isNative())
3501 continue;
3502 if (fun->native() != js_fun_call && fun->native() != js_fun_apply)
3503 continue;
3505 // This is a type set that might have been used when inlining
3506 // |calleeScript| into |script|.
3507 if (!types->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type)))
3508 return false;
3512 return true;
3515 /////////////////////////////////////////////////////////////////////
3516 // Interface functions
3517 /////////////////////////////////////////////////////////////////////
3519 void
3520 types::TypeMonitorCallSlow(JSContext* cx, JSObject* callee, const CallArgs& args,
3521 bool constructing)
3523 unsigned nargs = callee->as<JSFunction>().nargs();
3524 JSScript* script = callee->as<JSFunction>().nonLazyScript();
3526 if (!constructing)
3527 TypeScript::SetThis(cx, script, args.thisv());
3530 * Add constraints going up to the minimum of the actual and formal count.
3531 * If there are more actuals than formals the later values can only be
3532 * accessed through the arguments object, which is monitored.
3534 unsigned arg = 0;
3535 for (; arg < args.length() && arg < nargs; arg++)
3536 TypeScript::SetArgument(cx, script, arg, args[arg]);
3538 /* Watch for fewer actuals than formals to the call. */
3539 for (; arg < nargs; arg++)
3540 TypeScript::SetArgument(cx, script, arg, UndefinedValue());
3543 static inline bool
3544 IsAboutToBeFinalized(TypeObjectKey** keyp)
3546 /* Mask out the low bit indicating whether this is a type or JS object. */
3547 uintptr_t flagBit = uintptr_t(*keyp) & 1;
3548 gc::Cell* tmp = reinterpret_cast<gc::Cell*>(uintptr_t(*keyp) & ~1);
3549 bool isAboutToBeFinalized = IsCellAboutToBeFinalized(&tmp);
3550 *keyp = reinterpret_cast<TypeObjectKey*>(uintptr_t(tmp) | flagBit);
3551 return isAboutToBeFinalized;
3554 void
3555 types::FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap)
3557 uint32_t added = 0;
3558 for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
3559 JSOp op = JSOp(*pc);
3560 if (js_CodeSpec[op].format & JOF_TYPESET) {
3561 bytecodeMap[added++] = script->pcToOffset(pc);
3562 if (added == script->nTypeSets())
3563 break;
3566 MOZ_ASSERT(added == script->nTypeSets());
3569 ArrayObject*
3570 types::GetOrFixupCopyOnWriteObject(JSContext* cx, HandleScript script, jsbytecode* pc)
3572 // Make sure that the template object for script/pc has a type indicating
3573 // that the object and its copies have copy on write elements.
3574 RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>());
3575 MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
3577 if (obj->type()->fromAllocationSite()) {
3578 MOZ_ASSERT(obj->type()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
3579 return obj;
3582 RootedTypeObject type(cx, TypeScript::InitObject(cx, script, pc, JSProto_Array));
3583 if (!type)
3584 return nullptr;
3586 type->addFlags(OBJECT_FLAG_COPY_ON_WRITE);
3588 // Update type information in the initializer object type.
3589 MOZ_ASSERT(obj->slotSpan() == 0);
3590 for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
3591 const Value& v = obj->getDenseElement(i);
3592 AddTypePropertyId(cx, type, JSID_VOID, v);
3595 obj->setType(type);
3596 return obj;
3599 ArrayObject*
3600 types::GetCopyOnWriteObject(JSScript* script, jsbytecode* pc)
3602 // GetOrFixupCopyOnWriteObject should already have been called for
3603 // script/pc, ensuring that the template object has a type with the
3604 // COPY_ON_WRITE flag. We don't assert this here, due to a corner case
3605 // where this property doesn't hold. See jsop_newarray_copyonwrite in
3606 // IonBuilder.
3607 ArrayObject* obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>();
3608 MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
3610 return obj;
3613 void
3614 types::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const js::Value& rval)
3616 /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
3617 if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
3618 return;
3620 if (!script->hasBaselineScript())
3621 return;
3623 AutoEnterAnalysis enter(cx);
3625 Type type = GetValueType(rval);
3626 StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
3627 if (types->hasType(type))
3628 return;
3630 InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
3631 script->id(), script->pcToOffset(pc), TypeString(type));
3632 types->addType(cx, type);
3635 bool
3636 types::UseNewTypeForClone(JSFunction* fun)
3638 if (!fun->isInterpreted())
3639 return false;
3641 if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite())
3642 return true;
3644 if (fun->isArrow())
3645 return false;
3647 if (fun->hasSingletonType())
3648 return false;
3651 * When a function is being used as a wrapper for another function, it
3652 * improves precision greatly to distinguish between different instances of
3653 * the wrapper; otherwise we will conflate much of the information about
3654 * the wrapped functions.
3656 * An important example is the Class.create function at the core of the
3657 * Prototype.js library, which looks like:
3659 * var Class = {
3660 * create: function() {
3661 * return function() {
3662 * this.initialize.apply(this, arguments);
3665 * };
3667 * Each instance of the innermost function will have a different wrapped
3668 * initialize method. We capture this, along with similar cases, by looking
3669 * for short scripts which use both .apply and arguments. For such scripts,
3670 * whenever creating a new instance of the function we both give that
3671 * instance a singleton type and clone the underlying script.
3674 uint32_t begin, end;
3675 if (fun->hasScript()) {
3676 if (!fun->nonLazyScript()->usesArgumentsApplyAndThis())
3677 return false;
3678 begin = fun->nonLazyScript()->sourceStart();
3679 end = fun->nonLazyScript()->sourceEnd();
3680 } else {
3681 if (!fun->lazyScript()->usesArgumentsApplyAndThis())
3682 return false;
3683 begin = fun->lazyScript()->begin();
3684 end = fun->lazyScript()->end();
3687 return end - begin <= 100;
3690 /////////////////////////////////////////////////////////////////////
3691 // TypeScript
3692 /////////////////////////////////////////////////////////////////////
3694 bool
3695 JSScript::makeTypes(JSContext* cx)
3697 MOZ_ASSERT(!types_);
3699 AutoEnterAnalysis enter(cx);
3701 unsigned count = TypeScript::NumTypeSets(this);
3703 TypeScript* typeScript = (TypeScript*)
3704 zone()->pod_calloc<uint8_t>(TypeScript::SizeIncludingTypeArray(count));
3705 if (!typeScript)
3706 return false;
3708 types_ = typeScript;
3709 setTypesGeneration(cx->zone()->types.generation);
3711 #ifdef DEBUG
3712 StackTypeSet* typeArray = typeScript->typeArray();
3713 for (unsigned i = 0; i < nTypeSets(); i++) {
3714 InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
3715 InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
3716 i, id());
3718 TypeSet* thisTypes = TypeScript::ThisTypes(this);
3719 InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
3720 InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
3721 id());
3722 unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
3723 for (unsigned i = 0; i < nargs; i++) {
3724 TypeSet* types = TypeScript::ArgTypes(this, i);
3725 InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
3726 InferSpewColor(types), types, InferSpewColorReset(),
3727 i, id());
3729 #endif
3731 return true;
3734 /* static */ bool
3735 JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun,
3736 bool singleton /* = false */)
3738 if (singleton) {
3739 if (!setSingletonType(cx, fun))
3740 return false;
3741 } else {
3742 RootedObject funProto(cx, fun->getProto());
3743 Rooted<TaggedProto> taggedProto(cx, TaggedProto(funProto));
3744 TypeObject* type =
3745 cx->compartment()->types.newTypeObject(cx, &JSFunction::class_, taggedProto);
3746 if (!type)
3747 return false;
3749 fun->setType(type);
3750 type->interpretedFunction = fun;
3753 return true;
3756 /////////////////////////////////////////////////////////////////////
3757 // TypeNewScript
3758 /////////////////////////////////////////////////////////////////////
3760 // Make a TypeNewScript for |type|, and set it up to hold the initial
3761 // PRELIMINARY_OBJECT_COUNT objects created with the type.
3762 /* static */ void
3763 TypeNewScript::make(JSContext* cx, TypeObject* type, JSFunction* fun)
3765 MOZ_ASSERT(cx->zone()->types.activeAnalysis);
3766 MOZ_ASSERT(!type->newScript());
3768 if (type->unknownProperties())
3769 return;
3771 ScopedJSDeletePtr<TypeNewScript> newScript(cx->new_<TypeNewScript>());
3772 if (!newScript)
3773 return;
3775 newScript->fun = fun;
3777 PlainObject** preliminaryObjects =
3778 type->zone()->pod_calloc<PlainObject*>(PRELIMINARY_OBJECT_COUNT);
3779 if (!preliminaryObjects)
3780 return;
3782 newScript->preliminaryObjects = preliminaryObjects;
3783 type->setNewScript(newScript.forget());
3785 gc::TraceTypeNewScript(type);
3788 void
3789 TypeNewScript::registerNewObject(PlainObject* res)
3791 MOZ_ASSERT(!analyzed());
3793 // The preliminary object pointers are weak, and won't be swept properly
3794 // during nursery collections, so the preliminary objects need to be
3795 // initially tenured.
3796 MOZ_ASSERT(!IsInsideNursery(res));
3798 // New script objects must have the maximum number of fixed slots, so that
3799 // we can adjust their shape later to match the number of fixed slots used
3800 // by the template object we eventually create.
3801 MOZ_ASSERT(res->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS);
3803 for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
3804 if (!preliminaryObjects[i]) {
3805 preliminaryObjects[i] = res;
3806 return;
3810 MOZ_CRASH("There should be room for registering the new object");
3813 void
3814 TypeNewScript::unregisterNewObject(PlainObject* res)
3816 MOZ_ASSERT(!analyzed());
3818 for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
3819 if (preliminaryObjects[i] == res) {
3820 preliminaryObjects[i] = nullptr;
3821 return;
3825 // The object should be one of the preliminary objects.
3826 MOZ_CRASH();
3829 // Return whether shape consists entirely of plain data properties.
3830 static bool
3831 OnlyHasDataProperties(Shape* shape)
3833 MOZ_ASSERT(!shape->inDictionary());
3835 while (!shape->isEmptyShape()) {
3836 if (!shape->isDataDescriptor() ||
3837 !shape->configurable() ||
3838 !shape->enumerable() ||
3839 !shape->writable() ||
3840 !shape->hasSlot())
3842 return false;
3844 shape = shape->previous();
3847 return true;
3850 // Find the most recent common ancestor of two shapes, or an empty shape if
3851 // the two shapes have no common ancestor.
3852 static Shape*
3853 CommonPrefix(Shape* first, Shape* second)
3855 MOZ_ASSERT(OnlyHasDataProperties(first));
3856 MOZ_ASSERT(OnlyHasDataProperties(second));
3858 while (first->slotSpan() > second->slotSpan())
3859 first = first->previous();
3860 while (second->slotSpan() > first->slotSpan())
3861 second = second->previous();
3863 while (first != second && !first->isEmptyShape()) {
3864 first = first->previous();
3865 second = second->previous();
3868 return first;
3871 static bool
3872 ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocKind)
3874 MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty()));
3876 // Make a clone of the object, with the new allocation kind.
3877 RootedShape oldShape(cx, obj->lastProperty());
3878 RootedTypeObject type(cx, obj->type());
3879 JSObject* clone = NewReshapedObject(cx, type, obj->getParent(), allocKind, oldShape);
3880 if (!clone)
3881 return false;
3883 obj->setLastPropertyShrinkFixedSlots(clone->lastProperty());
3884 return true;
3887 namespace {
3889 struct DestroyTypeNewScript
3891 JSContext* cx;
3892 TypeObject* type;
3894 DestroyTypeNewScript(JSContext* cx, TypeObject* type)
3895 : cx(cx), type(type)
3898 ~DestroyTypeNewScript() {
3899 if (type)
3900 type->clearNewScript(cx);
3904 } // anonymous namespace
3906 bool
3907 TypeNewScript::maybeAnalyze(JSContext* cx, TypeObject* type, bool* regenerate, bool force)
3909 // Perform the new script properties analysis if necessary, returning
3910 // whether the new type table was updated and type needs to be refreshed.
3911 MOZ_ASSERT(this == type->newScript());
3913 // Make sure there aren't dead references in preliminaryObjects. This can
3914 // clear out the new script information on OOM.
3915 type->maybeSweep(nullptr);
3916 if (!type->newScript())
3917 return true;
3919 if (regenerate)
3920 *regenerate = false;
3922 if (analyzed()) {
3923 // The analyses have already been performed.
3924 return true;
3927 if (!force) {
3928 // Don't perform the analyses until sufficient preliminary objects have
3929 // been allocated.
3930 for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
3931 if (!preliminaryObjects[i])
3932 return true;
3936 AutoEnterAnalysis enter(cx);
3938 // Any failures after this point will clear out this TypeNewScript.
3939 DestroyTypeNewScript destroyNewScript(cx, type);
3941 // Compute the greatest common shape prefix and the largest slot span of
3942 // the preliminary objects.
3943 Shape* prefixShape = nullptr;
3944 size_t maxSlotSpan = 0;
3945 for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
3946 PlainObject* obj = preliminaryObjects[i];
3947 if (!obj)
3948 continue;
3950 // For now, we require all preliminary objects to have only simple
3951 // lineages of plain data properties.
3952 Shape* shape = obj->lastProperty();
3953 if (shape->inDictionary() ||
3954 !OnlyHasDataProperties(shape) ||
3955 shape->getObjectFlags() != 0 ||
3956 shape->getObjectMetadata() != nullptr)
3958 return true;
3961 maxSlotSpan = Max<size_t>(maxSlotSpan, obj->slotSpan());
3963 if (prefixShape) {
3964 MOZ_ASSERT(shape->numFixedSlots() == prefixShape->numFixedSlots());
3965 prefixShape = CommonPrefix(prefixShape, shape);
3966 } else {
3967 prefixShape = shape;
3969 if (prefixShape->isEmptyShape()) {
3970 // The preliminary objects don't have any common properties.
3971 return true;
3974 if (!prefixShape)
3975 return true;
3977 gc::AllocKind kind = gc::GetGCObjectKind(maxSlotSpan);
3979 if (kind != gc::GetGCObjectKind(NativeObject::MAX_FIXED_SLOTS)) {
3980 // The template object will have a different allocation kind from the
3981 // preliminary objects that have already been constructed. Optimizing
3982 // definite property accesses requires both that the property is
3983 // definitely in a particular slot and that the object has a specific
3984 // number of fixed slots. So, adjust the shape and slot layout of all
3985 // the preliminary objects so that their structure matches that of the
3986 // template object. Also recompute the prefix shape, as it reflects the
3987 // old number of fixed slots.
3988 Shape* newPrefixShape = nullptr;
3989 for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
3990 PlainObject* obj = preliminaryObjects[i];
3991 if (!obj)
3992 continue;
3993 if (!ChangeObjectFixedSlotCount(cx, obj, kind))
3994 return false;
3995 if (newPrefixShape) {
3996 MOZ_ASSERT(CommonPrefix(obj->lastProperty(), newPrefixShape) == newPrefixShape);
3997 } else {
3998 newPrefixShape = obj->lastProperty();
3999 while (newPrefixShape->slotSpan() > prefixShape->slotSpan())
4000 newPrefixShape = newPrefixShape->previous();
4003 prefixShape = newPrefixShape;
4006 RootedTypeObject typeRoot(cx, type);
4007 templateObject_ = NewObjectWithType<PlainObject>(cx, typeRoot, cx->global(), kind, MaybeSingletonObject);
4008 if (!templateObject_)
4009 return false;
4011 Vector<Initializer> initializerVector(cx);
4013 RootedPlainObject templateRoot(cx, templateObject());
4014 if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, type, templateRoot, &initializerVector))
4015 return false;
4017 if (!type->newScript())
4018 return true;
4020 MOZ_ASSERT(OnlyHasDataProperties(templateObject()->lastProperty()));
4022 if (templateObject()->slotSpan() != 0) {
4023 // Make sure that all definite properties found are reflected in the
4024 // prefix shape. Otherwise, the constructor behaved differently before
4025 // we baseline compiled it and started observing types. Compare
4026 // property names rather than looking at the shapes directly, as the
4027 // allocation kind and other non-property parts of the template and
4028 // existing objects may differ.
4029 if (templateObject()->slotSpan() > prefixShape->slotSpan())
4030 return true;
4032 Shape* shape = prefixShape;
4033 while (shape->slotSpan() != templateObject()->slotSpan())
4034 shape = shape->previous();
4035 Shape* templateShape = templateObject()->lastProperty();
4036 while (!shape->isEmptyShape()) {
4037 if (shape->slot() != templateShape->slot())
4038 return true;
4039 if (shape->propid() != templateShape->propid())
4040 return true;
4041 shape = shape->previous();
4042 templateShape = templateShape->previous();
4044 if (!templateShape->isEmptyShape())
4045 return true;
4048 Initializer done(Initializer::DONE, 0);
4050 if (!initializerVector.append(done))
4051 return false;
4053 initializerList = type->zone()->pod_calloc<Initializer>(initializerVector.length());
4054 if (!initializerList)
4055 return false;
4056 PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
4059 js_free(preliminaryObjects);
4060 preliminaryObjects = nullptr;
4062 if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
4063 // The definite properties analysis found exactly the properties that
4064 // are held in common by the preliminary objects. No further analysis
4065 // is needed.
4066 if (!type->addDefiniteProperties(cx, templateObject()->lastProperty()))
4067 return false;
4069 destroyNewScript.type = nullptr;
4070 return true;
4073 // There are more properties consistently added to objects of this type
4074 // than were discovered by the definite properties analysis. Use the
4075 // existing type to represent fully initialized objects with all
4076 // definite properties in the prefix shape, and make a new type object
4077 // to represent partially initialized objects.
4078 MOZ_ASSERT(prefixShape->slotSpan() > templateObject()->slotSpan());
4080 TypeObjectFlags initialFlags = type->flags() & OBJECT_FLAG_UNKNOWN_MASK;
4082 Rooted<TaggedProto> protoRoot(cx, type->proto());
4083 TypeObject* initialType =
4084 cx->compartment()->types.newTypeObject(cx, type->clasp(), protoRoot, initialFlags);
4085 if (!initialType)
4086 return false;
4088 if (!initialType->addDefiniteProperties(cx, templateObject()->lastProperty()))
4089 return false;
4090 if (!type->addDefiniteProperties(cx, prefixShape))
4091 return false;
4093 NewTypeObjectTable& table = cx->compartment()->newTypeObjects;
4094 NewTypeObjectTable::Lookup lookup(type->clasp(), type->proto(), fun);
4096 MOZ_ASSERT(table.lookup(lookup)->object == type);
4097 table.remove(lookup);
4098 table.putNew(lookup, NewTypeObjectEntry(initialType, fun));
4100 templateObject()->setType(initialType);
4102 // Transfer this TypeNewScript from the fully initialized type to the
4103 // partially initialized type.
4104 type->setNewScript(nullptr);
4105 initialType->setNewScript(this);
4107 initializedShape_ = prefixShape;
4108 initializedType_ = type;
4110 destroyNewScript.type = nullptr;
4112 if (regenerate)
4113 *regenerate = true;
4114 return true;
4117 void
4118 TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, TypeObject* type)
4120 // If we cleared this new script while in the middle of initializing an
4121 // object, it will still have the new script's shape and reflect the no
4122 // longer correct state of the object once its initialization is completed.
4123 // We can't detect the possibility of this statically while remaining
4124 // robust, but the new script keeps track of where each property is
4125 // initialized so we can walk the stack and fix up any such objects.
4127 if (!initializerList)
4128 return;
4130 RootedFunction function(cx, fun);
4131 Vector<uint32_t, 32> pcOffsets(cx);
4132 for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
4133 pcOffsets.append(iter.script()->pcToOffset(iter.pc()));
4135 // This frame has no this.
4136 if (!iter.isConstructing() || iter.matchCallee(cx, function))
4137 continue;
4139 Value thisv = iter.thisv(cx);
4140 if (!thisv.isObject() ||
4141 thisv.toObject().hasLazyType() ||
4142 thisv.toObject().type() != type)
4144 continue;
4147 // Found a matching frame.
4148 RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
4150 // Whether all identified 'new' properties have been initialized.
4151 bool finished = false;
4153 // If not finished, number of properties that have been added.
4154 uint32_t numProperties = 0;
4156 // Whether the current SETPROP is within an inner frame which has
4157 // finished entirely.
4158 bool pastProperty = false;
4160 // Index in pcOffsets of the outermost frame.
4161 int callDepth = pcOffsets.length() - 1;
4163 // Index in pcOffsets of the frame currently being checked for a SETPROP.
4164 int setpropDepth = callDepth;
4166 for (Initializer* init = initializerList;; init++) {
4167 if (init->kind == Initializer::SETPROP) {
4168 if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
4169 // Have not yet reached this setprop.
4170 break;
4172 // This setprop has executed, reset state for the next one.
4173 numProperties++;
4174 pastProperty = false;
4175 setpropDepth = callDepth;
4176 } else if (init->kind == Initializer::SETPROP_FRAME) {
4177 if (!pastProperty) {
4178 if (pcOffsets[setpropDepth] < init->offset) {
4179 // Have not yet reached this inner call.
4180 break;
4181 } else if (pcOffsets[setpropDepth] > init->offset) {
4182 // Have advanced past this inner call.
4183 pastProperty = true;
4184 } else if (setpropDepth == 0) {
4185 // Have reached this call but not yet in it.
4186 break;
4187 } else {
4188 // Somewhere inside this inner call.
4189 setpropDepth--;
4192 } else {
4193 MOZ_ASSERT(init->kind == Initializer::DONE);
4194 finished = true;
4195 break;
4199 if (!finished)
4200 (void) NativeObject::rollbackProperties(cx, obj, numProperties);
4204 void
4205 TypeNewScript::trace(JSTracer* trc)
4207 MarkObject(trc, &fun, "TypeNewScript_function");
4209 if (templateObject_)
4210 MarkObject(trc, &templateObject_, "TypeNewScript_templateObject");
4212 if (initializedShape_)
4213 MarkShape(trc, &initializedShape_, "TypeNewScript_initializedShape");
4215 if (initializedType_)
4216 MarkTypeObject(trc, &initializedType_, "TypeNewScript_initializedType");
4219 void
4220 TypeNewScript::sweep()
4222 // preliminaryObjects only holds weak pointers, so clear any objects that
4223 // are about to be destroyed.
4224 if (preliminaryObjects) {
4225 for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
4226 PlainObject** ptr = &preliminaryObjects[i];
4227 if (*ptr && IsObjectAboutToBeFinalized(ptr))
4228 *ptr = nullptr;
4233 /////////////////////////////////////////////////////////////////////
4234 // JSObject
4235 /////////////////////////////////////////////////////////////////////
4237 bool
4238 JSObject::shouldSplicePrototype(JSContext* cx)
4241 * During bootstrapping, if inference is enabled we need to make sure not
4242 * to splice a new prototype in for Function.prototype or the global
4243 * object if their __proto__ had previously been set to null, as this
4244 * will change the prototype for all other objects with the same type.
4246 if (getProto() != nullptr)
4247 return false;
4248 return hasSingletonType();
4251 bool
4252 JSObject::splicePrototype(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto)
4254 MOZ_ASSERT(cx->compartment() == compartment());
4256 RootedObject self(cx, this);
4259 * For singleton types representing only a single JSObject, the proto
4260 * can be rearranged as needed without destroying type information for
4261 * the old or new types.
4263 MOZ_ASSERT(self->hasSingletonType());
4265 /* Inner objects may not appear on prototype chains. */
4266 MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
4269 * Force type instantiation when splicing lazy types. This may fail,
4270 * in which case inference will be disabled for the compartment.
4272 Rooted<TypeObject*> type(cx, self->getType(cx));
4273 if (!type)
4274 return false;
4275 Rooted<TypeObject*> protoType(cx, nullptr);
4276 if (proto.isObject()) {
4277 protoType = proto.toObject()->getType(cx);
4278 if (!protoType)
4279 return false;
4282 type->setClasp(clasp);
4283 type->setProto(cx, proto);
4284 return true;
4287 /* static */ TypeObject*
4288 JSObject::makeLazyType(JSContext* cx, HandleObject obj)
4290 MOZ_ASSERT(obj->hasLazyType());
4291 MOZ_ASSERT(cx->compartment() == obj->compartment());
4293 /* De-lazification of functions can GC, so we need to do it up here. */
4294 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
4295 RootedFunction fun(cx, &obj->as<JSFunction>());
4296 if (!fun->getOrCreateScript(cx))
4297 return nullptr;
4300 // Find flags which need to be specified immediately on the object.
4301 // Don't track whether singletons are packed.
4302 TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED;
4304 if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
4305 initialFlags |= OBJECT_FLAG_ITERATED;
4307 if (obj->isIndexed())
4308 initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
4310 if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
4311 initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
4313 Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
4314 TypeObject* type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags);
4315 if (!type)
4316 return nullptr;
4318 AutoEnterAnalysis enter(cx);
4320 /* Fill in the type according to the state of this object. */
4322 type->initSingleton(obj);
4324 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
4325 type->interpretedFunction = &obj->as<JSFunction>();
4327 obj->type_ = type;
4329 return type;
4332 /* static */ inline HashNumber
4333 NewTypeObjectEntry::hash(const Lookup& lookup)
4335 return PointerHasher<JSObject*, 3>::hash(lookup.hashProto.raw()) ^
4336 PointerHasher<const Class*, 3>::hash(lookup.clasp) ^
4337 PointerHasher<JSObject*, 3>::hash(lookup.associated);
4340 /* static */ inline bool
4341 NewTypeObjectEntry::match(const NewTypeObjectEntry& key, const Lookup& lookup)
4343 return key.object->proto() == lookup.matchProto &&
4344 key.object->clasp() == lookup.clasp &&
4345 key.associated == lookup.associated;
4348 #ifdef DEBUG
4349 bool
4350 JSObject::hasNewType(const Class* clasp, TypeObject* type)
4352 NewTypeObjectTable& table = compartment()->newTypeObjects;
4354 if (!table.initialized())
4355 return false;
4357 NewTypeObjectTable::Ptr p =
4358 table.lookup(NewTypeObjectTable::Lookup(clasp, TaggedProto(this), nullptr));
4359 return p && p->object == type;
4361 #endif /* DEBUG */
4363 /* static */ bool
4364 JSObject::setNewTypeUnknown(JSContext* cx, const Class* clasp, HandleObject obj)
4366 if (!obj->setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN))
4367 return false;
4370 * If the object already has a new type, mark that type as unknown. It will
4371 * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
4372 * crawl if prototypes of the object change dynamically in the future.
4374 NewTypeObjectTable& table = cx->compartment()->newTypeObjects;
4375 if (table.initialized()) {
4376 Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj));
4377 NewTypeObjectTable::Ptr p =
4378 table.lookup(NewTypeObjectTable::Lookup(clasp, taggedProto, nullptr));
4379 if (p)
4380 MarkTypeObjectUnknownProperties(cx, p->object);
4383 return true;
4387 * This class is used to add a post barrier on the newTypeObjects set, as the
4388 * key is calculated from a prototype object which may be moved by generational
4389 * GC.
4391 class NewTypeObjectsSetRef : public BufferableRef
4393 NewTypeObjectTable* set;
4394 const Class* clasp;
4395 JSObject* proto;
4396 JSObject* associated;
4398 public:
4399 NewTypeObjectsSetRef(NewTypeObjectTable* s, const Class* clasp, JSObject* proto, JSObject* associated)
4400 : set(s), clasp(clasp), proto(proto), associated(associated)
4403 void mark(JSTracer* trc) {
4404 JSObject* prior = proto;
4405 trc->setTracingLocation(&*prior);
4406 Mark(trc, &proto, "newTypeObjects set prototype");
4407 if (prior == proto)
4408 return;
4410 NewTypeObjectTable::Ptr p =
4411 set->lookup(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto),
4412 associated));
4413 MOZ_ASSERT(p); // newTypeObjects set must still contain original entry.
4415 set->rekeyAs(NewTypeObjectTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated),
4416 NewTypeObjectTable::Lookup(clasp, TaggedProto(proto), associated), *p);
4420 static void
4421 TypeObjectTablePostBarrier(ExclusiveContext* cx, NewTypeObjectTable* table,
4422 const Class* clasp, TaggedProto proto, JSObject* associated)
4424 MOZ_ASSERT_IF(associated, !IsInsideNursery(associated));
4426 if (!proto.isObject())
4427 return;
4429 if (!cx->isJSContext()) {
4430 MOZ_ASSERT(!IsInsideNursery(proto.toObject()));
4431 return;
4434 if (IsInsideNursery(proto.toObject())) {
4435 StoreBuffer& sb = cx->asJSContext()->runtime()->gc.storeBuffer;
4436 sb.putGeneric(NewTypeObjectsSetRef(table, clasp, proto.toObject(), associated));
4440 TypeObject*
4441 ExclusiveContext::getNewType(const Class* clasp, TaggedProto proto, JSObject* associated)
4443 MOZ_ASSERT_IF(associated, proto.isObject());
4444 MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>());
4445 MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject()));
4447 NewTypeObjectTable& newTypeObjects = compartment()->newTypeObjects;
4449 if (!newTypeObjects.initialized() && !newTypeObjects.init())
4450 return nullptr;
4452 // Canonicalize new functions to use the original one associated with its script.
4453 if (associated && associated->is<JSFunction>()) {
4454 JSFunction* fun = &associated->as<JSFunction>();
4455 if (fun->hasScript())
4456 associated = fun->nonLazyScript()->functionNonDelazifying();
4457 else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
4458 associated = fun->lazyScript()->functionNonDelazifying();
4459 else
4460 associated = nullptr;
4463 NewTypeObjectTable::AddPtr p =
4464 newTypeObjects.lookupForAdd(NewTypeObjectTable::Lookup(clasp, proto, associated));
4465 if (p) {
4466 TypeObject* type = p->object;
4467 MOZ_ASSERT(type->clasp() == clasp);
4468 MOZ_ASSERT(type->proto() == proto);
4469 return type;
4472 AutoEnterAnalysis enter(this);
4474 if (proto.isObject() && !proto.toObject()->setDelegate(this))
4475 return nullptr;
4477 TypeObjectFlags initialFlags = 0;
4478 if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) {
4479 // The new type is not present in any type sets, so mark the object as
4480 // unknown in all type sets it appears in. This allows the prototype of
4481 // such objects to mutate freely without triggering an expensive walk of
4482 // the compartment's type sets. (While scripts normally don't mutate
4483 // __proto__, the browser will for proxies and such, and we need to
4484 // accommodate this behavior).
4485 initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN;
4488 Rooted<TaggedProto> protoRoot(this, proto);
4489 TypeObject* type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags);
4490 if (!type)
4491 return nullptr;
4493 if (!newTypeObjects.add(p, NewTypeObjectEntry(type, associated)))
4494 return nullptr;
4496 TypeObjectTablePostBarrier(this, &newTypeObjects, clasp, proto, associated);
4498 if (proto.isObject()) {
4499 RootedObject obj(this, proto.toObject());
4501 if (associated) {
4502 if (associated->is<JSFunction>())
4503 TypeNewScript::make(asJSContext(), type, &associated->as<JSFunction>());
4504 else
4505 type->setTypeDescr(&associated->as<TypeDescr>());
4509 * Some builtin objects have slotful native properties baked in at
4510 * creation via the Shape::{insert,get}initialShape mechanism. Since
4511 * these properties are never explicitly defined on new objects, update
4512 * the type information for them here.
4515 if (obj->is<RegExpObject>()) {
4516 AddTypePropertyId(this, type, NameToId(names().source), Type::StringType());
4517 AddTypePropertyId(this, type, NameToId(names().global), Type::BooleanType());
4518 AddTypePropertyId(this, type, NameToId(names().ignoreCase), Type::BooleanType());
4519 AddTypePropertyId(this, type, NameToId(names().multiline), Type::BooleanType());
4520 AddTypePropertyId(this, type, NameToId(names().sticky), Type::BooleanType());
4521 AddTypePropertyId(this, type, NameToId(names().lastIndex), Type::Int32Type());
4524 if (obj->is<StringObject>())
4525 AddTypePropertyId(this, type, NameToId(names().length), Type::Int32Type());
4527 if (obj->is<ErrorObject>()) {
4528 AddTypePropertyId(this, type, NameToId(names().fileName), Type::StringType());
4529 AddTypePropertyId(this, type, NameToId(names().lineNumber), Type::Int32Type());
4530 AddTypePropertyId(this, type, NameToId(names().columnNumber), Type::Int32Type());
4531 AddTypePropertyId(this, type, NameToId(names().stack), Type::StringType());
4535 return type;
4538 TypeObject*
4539 ExclusiveContext::getSingletonType(const Class* clasp, TaggedProto proto)
4541 MOZ_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment());
4543 AutoEnterAnalysis enter(this);
4545 NewTypeObjectTable& table = compartment()->lazyTypeObjects;
4547 if (!table.initialized() && !table.init())
4548 return nullptr;
4550 NewTypeObjectTable::AddPtr p = table.lookupForAdd(NewTypeObjectTable::Lookup(clasp, proto, nullptr));
4551 if (p) {
4552 TypeObject* type = p->object;
4553 MOZ_ASSERT(type->lazy());
4555 return type;
4558 Rooted<TaggedProto> protoRoot(this, proto);
4559 TypeObject* type = compartment()->types.newTypeObject(this, clasp, protoRoot);
4560 if (!type)
4561 return nullptr;
4563 if (!table.add(p, NewTypeObjectEntry(type, nullptr)))
4564 return nullptr;
4566 TypeObjectTablePostBarrier(this, &table, clasp, proto, nullptr);
4568 type->initSingleton((JSObject*) TypeObject::LAZY_SINGLETON);
4569 MOZ_ASSERT(type->singleton(), "created type must be a proper singleton");
4571 return type;
4574 /////////////////////////////////////////////////////////////////////
4575 // Tracing
4576 /////////////////////////////////////////////////////////////////////
4578 void
4579 ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
4581 MOZ_ASSERT(zone->isGCSweepingOrCompacting());
4583 // IsAboutToBeFinalized doesn't work right on tenured objects when called
4584 // during a minor collection.
4585 MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMinorCollecting());
4588 * Purge references to type objects that are no longer live. Type sets hold
4589 * only weak references. For type sets containing more than one object,
4590 * live entries in the object hash need to be copied to the zone's
4591 * new arena.
4593 unsigned objectCount = baseObjectCount();
4594 if (objectCount >= 2) {
4595 unsigned oldCapacity = HashSetCapacity(objectCount);
4596 TypeObjectKey** oldArray = objectSet;
4598 clearObjects();
4599 objectCount = 0;
4600 for (unsigned i = 0; i < oldCapacity; i++) {
4601 TypeObjectKey* object = oldArray[i];
4602 if (object && !IsAboutToBeFinalized(&object)) {
4603 TypeObjectKey** pentry =
4604 HashSetInsert<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
4605 (zone->types.typeLifoAlloc, objectSet, objectCount, object);
4606 if (pentry) {
4607 *pentry = object;
4608 } else {
4609 oom.setOOM();
4610 flags |= TYPE_FLAG_ANYOBJECT;
4611 clearObjects();
4612 objectCount = 0;
4613 break;
4617 setBaseObjectCount(objectCount);
4618 } else if (objectCount == 1) {
4619 TypeObjectKey* object = (TypeObjectKey*) objectSet;
4620 if (IsAboutToBeFinalized(&object)) {
4621 objectSet = nullptr;
4622 setBaseObjectCount(0);
4623 } else {
4624 objectSet = reinterpret_cast<TypeObjectKey**>(object);
4629 * Type constraints only hold weak references. Copy constraints referring
4630 * to data that is still live into the zone's new arena.
4632 TypeConstraint* constraint = constraintList;
4633 constraintList = nullptr;
4634 while (constraint) {
4635 TypeConstraint* copy;
4636 if (constraint->sweep(zone->types, &copy)) {
4637 if (copy) {
4638 copy->next = constraintList;
4639 constraintList = copy;
4640 } else {
4641 oom.setOOM();
4644 constraint = constraint->next;
4648 inline void
4649 TypeObject::clearProperties()
4651 setBasePropertyCount(0);
4652 propertySet = nullptr;
4655 #ifdef DEBUG
4656 bool
4657 TypeObject::needsSweep()
4659 // Note: this can be called off thread during compacting GCs, in which case
4660 // nothing will be running on the main thread.
4661 return generation() != zoneFromAnyThread()->types.generation;
4663 #endif
4665 static void
4666 EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom, Zone* zone,
4667 Maybe<AutoClearTypeInferenceStateOnOOM>& fallback)
4669 if (!oom) {
4670 if (zone->types.activeAnalysis) {
4671 oom = &zone->types.activeAnalysis->oom;
4672 } else {
4673 fallback.emplace(zone);
4674 oom = &fallback.ref();
4680 * Before sweeping the arenas themselves, scan all type objects in a
4681 * compartment to fixup weak references: property type sets referencing dead
4682 * JS and type objects, and singleton JS objects whose type is not referenced
4683 * elsewhere. This is done either incrementally as part of the sweep, or on
4684 * demand as type objects are accessed before their contents have been swept.
4686 void
4687 TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom)
4689 if (generation() == zoneFromAnyThread()->types.generation) {
4690 // No sweeping required.
4691 return;
4694 setGeneration(zone()->types.generation);
4696 MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
4697 MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
4699 Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
4700 EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
4702 if (newScript())
4703 newScript()->sweep();
4705 LifoAlloc& typeLifoAlloc = zone()->types.typeLifoAlloc;
4708 * Properties were allocated from the old arena, and need to be copied over
4709 * to the new one.
4711 unsigned propertyCount = basePropertyCount();
4712 if (propertyCount >= 2) {
4713 unsigned oldCapacity = HashSetCapacity(propertyCount);
4714 Property** oldArray = propertySet;
4716 clearProperties();
4717 propertyCount = 0;
4718 for (unsigned i = 0; i < oldCapacity; i++) {
4719 Property* prop = oldArray[i];
4720 if (prop) {
4721 if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) {
4723 * Don't copy over properties of singleton objects when their
4724 * presence will not be required by jitcode or type constraints
4725 * (i.e. for the definite properties analysis). The contents of
4726 * these type sets will be regenerated as necessary.
4728 continue;
4731 Property* newProp = typeLifoAlloc.new_<Property>(*prop);
4732 if (newProp) {
4733 Property** pentry =
4734 HashSetInsert<jsid,Property,Property>
4735 (typeLifoAlloc, propertySet, propertyCount, prop->id);
4736 if (pentry) {
4737 *pentry = newProp;
4738 newProp->types.sweep(zone(), *oom);
4739 continue;
4743 oom->setOOM();
4744 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
4745 clearProperties();
4746 return;
4749 setBasePropertyCount(propertyCount);
4750 } else if (propertyCount == 1) {
4751 Property* prop = (Property*) propertySet;
4752 if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) {
4753 // Skip, as above.
4754 clearProperties();
4755 } else {
4756 Property* newProp = typeLifoAlloc.new_<Property>(*prop);
4757 if (newProp) {
4758 propertySet = (Property**) newProp;
4759 newProp->types.sweep(zone(), *oom);
4760 } else {
4761 oom->setOOM();
4762 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
4763 clearProperties();
4764 return;
4770 void
4771 TypeCompartment::clearTables()
4773 if (allocationSiteTable && allocationSiteTable->initialized())
4774 allocationSiteTable->clear();
4775 if (arrayTypeTable && arrayTypeTable->initialized())
4776 arrayTypeTable->clear();
4777 if (objectTypeTable && objectTypeTable->initialized())
4778 objectTypeTable->clear();
4781 void
4782 TypeCompartment::sweep(FreeOp* fop)
4785 * Iterate through the array/object type tables and remove all entries
4786 * referencing collected data. These tables only hold weak references.
4789 if (arrayTypeTable) {
4790 for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
4791 ArrayTableKey key = e.front().key();
4792 MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleObject());
4794 bool remove = false;
4795 if (!key.type.isUnknown() && key.type.isTypeObject()) {
4796 TypeObject* typeObject = key.type.typeObjectNoBarrier();
4797 if (IsTypeObjectAboutToBeFinalized(&typeObject))
4798 remove = true;
4799 else
4800 key.type = Type::ObjectType(typeObject);
4802 if (key.proto && key.proto != TaggedProto::LazyProto &&
4803 IsObjectAboutToBeFinalized(&key.proto))
4805 remove = true;
4807 if (IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet()))
4808 remove = true;
4810 if (remove)
4811 e.removeFront();
4812 else if (key != e.front().key())
4813 e.rekeyFront(key);
4817 if (objectTypeTable) {
4818 for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
4819 const ObjectTableKey& key = e.front().key();
4820 ObjectTableEntry& entry = e.front().value();
4822 bool remove = false;
4823 if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()))
4824 remove = true;
4825 if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet()))
4826 remove = true;
4827 for (unsigned i = 0; !remove && i < key.nproperties; i++) {
4828 if (JSID_IS_STRING(key.properties[i])) {
4829 JSString* str = JSID_TO_STRING(key.properties[i]);
4830 if (IsStringAboutToBeFinalized(&str))
4831 remove = true;
4832 MOZ_ASSERT(AtomToId((JSAtom*)str) == key.properties[i]);
4833 } else if (JSID_IS_SYMBOL(key.properties[i])) {
4834 JS::Symbol* sym = JSID_TO_SYMBOL(key.properties[i]);
4835 if (IsSymbolAboutToBeFinalized(&sym))
4836 remove = true;
4839 MOZ_ASSERT(!entry.types[i].isSingleObject());
4840 TypeObject* typeObject = nullptr;
4841 if (entry.types[i].isTypeObject()) {
4842 typeObject = entry.types[i].typeObjectNoBarrier();
4843 if (IsTypeObjectAboutToBeFinalized(&typeObject))
4844 remove = true;
4845 else if (typeObject != entry.types[i].typeObjectNoBarrier())
4846 entry.types[i] = Type::ObjectType(typeObject);
4850 if (remove) {
4851 js_free(key.properties);
4852 js_free(entry.types);
4853 e.removeFront();
4858 if (allocationSiteTable) {
4859 for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
4860 AllocationSiteKey key = e.front().key();
4861 bool keyDying = IsScriptAboutToBeFinalized(&key.script);
4862 bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet());
4863 if (keyDying || valDying)
4864 e.removeFront();
4865 else if (key.script != e.front().key().script)
4866 e.rekeyFront(key);
4871 void
4872 JSCompartment::sweepNewTypeObjectTable(NewTypeObjectTable& table)
4874 MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting());
4875 if (table.initialized()) {
4876 for (NewTypeObjectTable::Enum e(table); !e.empty(); e.popFront()) {
4877 NewTypeObjectEntry entry = e.front();
4878 if (IsTypeObjectAboutToBeFinalizedFromAnyThread(entry.object.unsafeGet()) ||
4879 (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated)))
4881 e.removeFront();
4882 } else {
4883 /* Any rekeying necessary is handled by fixupNewTypeObjectTable() below. */
4884 MOZ_ASSERT(entry.object.unbarrieredGet() == e.front().object.unbarrieredGet());
4885 MOZ_ASSERT(entry.associated == e.front().associated);
4891 #ifdef JSGC_COMPACTING
4893 void
4894 JSCompartment::fixupNewTypeObjectTable(NewTypeObjectTable& table)
4897 * Each entry's hash depends on the object's prototype and we can't tell
4898 * whether that has been moved or not in sweepNewTypeObjectTable().
4900 MOZ_ASSERT(zone()->isCollecting());
4901 if (table.initialized()) {
4902 for (NewTypeObjectTable::Enum e(table); !e.empty(); e.popFront()) {
4903 NewTypeObjectEntry entry = e.front();
4904 bool needRekey = false;
4905 if (IsForwarded(entry.object.get())) {
4906 entry.object.set(Forwarded(entry.object.get()));
4907 needRekey = true;
4909 TaggedProto proto = entry.object->proto();
4910 if (proto.isObject() && IsForwarded(proto.toObject())) {
4911 proto = TaggedProto(Forwarded(proto.toObject()));
4912 needRekey = true;
4914 if (entry.associated && IsForwarded(entry.associated)) {
4915 entry.associated = Forwarded(entry.associated);
4916 needRekey = true;
4918 if (needRekey) {
4919 NewTypeObjectTable::Lookup lookup(entry.object->clasp(),
4920 proto,
4921 entry.associated);
4922 e.rekeyFront(lookup, entry);
4928 void
4929 TypeNewScript::fixupAfterMovingGC()
4931 if (fun && IsForwarded(fun.get()))
4932 fun = Forwarded(fun.get());
4933 /* preliminaryObjects are handled by sweep(). */
4934 if (templateObject_ && IsForwarded(templateObject_.get()))
4935 templateObject_ = Forwarded(templateObject_.get());
4936 if (initializedShape_ && IsForwarded(initializedShape_.get()))
4937 initializedShape_ = Forwarded(initializedShape_.get());
4940 void
4941 TypeObject::fixupAfterMovingGC()
4943 if (proto().isObject() && IsForwarded(proto_.get()))
4944 proto_ = Forwarded(proto_.get());
4945 if (singleton_ && !lazy() && IsForwarded(singleton_.get()))
4946 singleton_ = Forwarded(singleton_.get());
4947 if (addendum_) {
4948 switch (addendumKind()) {
4949 case Addendum_NewScript:
4950 newScript()->fixupAfterMovingGC();
4951 break;
4952 case Addendum_TypeDescr:
4953 if (IsForwarded(&typeDescr()))
4954 addendum_ = Forwarded(&typeDescr());
4955 break;
4956 default:
4957 MOZ_CRASH();
4960 if (interpretedFunction && IsForwarded(interpretedFunction.get()))
4961 interpretedFunction = Forwarded(interpretedFunction.get());
4964 #endif // JSGC_COMPACTING
4966 #ifdef JSGC_HASH_TABLE_CHECKS
4968 void
4969 JSCompartment::checkTypeObjectTablesAfterMovingGC()
4971 checkTypeObjectTableAfterMovingGC(newTypeObjects);
4972 checkTypeObjectTableAfterMovingGC(lazyTypeObjects);
4975 void
4976 JSCompartment::checkTypeObjectTableAfterMovingGC(NewTypeObjectTable& table)
4979 * Assert that nothing points into the nursery or needs to be relocated, and
4980 * that the hash table entries are discoverable.
4982 if (!table.initialized())
4983 return;
4985 for (NewTypeObjectTable::Enum e(table); !e.empty(); e.popFront()) {
4986 NewTypeObjectEntry entry = e.front();
4987 CheckGCThingAfterMovingGC(entry.object.get());
4988 TaggedProto proto = entry.object->proto();
4989 if (proto.isObject())
4990 CheckGCThingAfterMovingGC(proto.toObject());
4991 CheckGCThingAfterMovingGC(entry.associated);
4993 NewTypeObjectTable::Lookup
4994 lookup(entry.object->clasp(), proto, entry.associated);
4995 NewTypeObjectTable::Ptr ptr = table.lookup(lookup);
4996 MOZ_ASSERT(ptr.found() && &*ptr == &e.front());
5000 #endif // JSGC_HASH_TABLE_CHECKS
5002 TypeCompartment::~TypeCompartment()
5004 js_delete(arrayTypeTable);
5005 js_delete(objectTypeTable);
5006 js_delete(allocationSiteTable);
5009 /* static */ void
5010 JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
5012 if (!types_ || typesGeneration() == zone()->types.generation)
5013 return;
5015 setTypesGeneration(zone()->types.generation);
5017 MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
5018 MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
5020 Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
5021 EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
5023 TypeZone& types = zone()->types;
5025 // Destroy all type information attached to the script if desired. We can
5026 // only do this if nothing has been compiled for the script, which will be
5027 // the case unless the script has been compiled since we started sweeping.
5028 if (types.sweepReleaseTypes &&
5029 !hasBaselineScript() &&
5030 !hasIonScript() &&
5031 !hasParallelIonScript())
5033 types_->destroy();
5034 types_ = nullptr;
5036 // Freeze constraints on stack type sets need to be regenerated the
5037 // next time the script is analyzed.
5038 hasFreezeConstraints_ = false;
5040 return;
5043 unsigned num = TypeScript::NumTypeSets(this);
5044 StackTypeSet* typeArray = types_->typeArray();
5046 // Remove constraints and references to dead objects from stack type sets.
5047 for (unsigned i = 0; i < num; i++)
5048 typeArray[i].sweep(zone(), *oom);
5050 // Update the recompile indexes in any IonScripts still on the script.
5051 if (hasIonScript())
5052 ionScript()->recompileInfoRef().shouldSweep(types);
5053 if (hasParallelIonScript())
5054 parallelIonScript()->recompileInfoRef().shouldSweep(types);
5057 void
5058 TypeScript::destroy()
5060 js_free(this);
5063 void
5064 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
5065 size_t* typePool,
5066 size_t* baselineStubsOptimized)
5068 *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
5069 if (jitZone()) {
5070 *baselineStubsOptimized +=
5071 jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf);
5075 void
5076 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
5077 size_t* allocationSiteTables,
5078 size_t* arrayTypeTables,
5079 size_t* objectTypeTables)
5081 if (allocationSiteTable)
5082 *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
5084 if (arrayTypeTable)
5085 *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
5087 if (objectTypeTable) {
5088 *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
5090 for (ObjectTypeTable::Enum e(*objectTypeTable);
5091 !e.empty();
5092 e.popFront())
5094 const ObjectTableKey& key = e.front().key();
5095 const ObjectTableEntry& value = e.front().value();
5097 /* key.ids and values.types have the same length. */
5098 *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
5103 size_t
5104 TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
5106 return mallocSizeOf(newScriptDontCheckGeneration());
5109 TypeZone::TypeZone(Zone* zone)
5110 : zone_(zone),
5111 typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
5112 generation(0),
5113 compilerOutputs(nullptr),
5114 sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
5115 sweepCompilerOutputs(nullptr),
5116 sweepReleaseTypes(false),
5117 activeAnalysis(nullptr)
5121 TypeZone::~TypeZone()
5123 js_delete(compilerOutputs);
5124 js_delete(sweepCompilerOutputs);
5127 void
5128 TypeZone::beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom)
5130 MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
5131 MOZ_ASSERT(!sweepCompilerOutputs);
5132 MOZ_ASSERT(!sweepReleaseTypes);
5134 sweepReleaseTypes = releaseTypes;
5136 // Clear the analysis pool, but don't release its data yet. While sweeping
5137 // types any live data will be allocated into the pool.
5138 sweepTypeLifoAlloc.steal(&typeLifoAlloc);
5140 // Sweep any invalid or dead compiler outputs, and keep track of the new
5141 // index for remaining live outputs.
5142 if (compilerOutputs) {
5143 CompilerOutputVector* newCompilerOutputs = nullptr;
5144 for (size_t i = 0; i < compilerOutputs->length(); i++) {
5145 CompilerOutput& output = (*compilerOutputs)[i];
5146 if (output.isValid()) {
5147 JSScript* script = output.script();
5148 ExecutionMode mode = output.mode();
5149 if (IsScriptAboutToBeFinalized(&script)) {
5150 jit::GetIonScript(script, mode)->recompileInfoRef() = RecompileInfo();
5151 output.invalidate();
5152 } else {
5153 CompilerOutput newOutput(script, output.mode());
5155 if (!newCompilerOutputs)
5156 newCompilerOutputs = js_new<CompilerOutputVector>();
5157 if (newCompilerOutputs && newCompilerOutputs->append(newOutput)) {
5158 output.setSweepIndex(newCompilerOutputs->length() - 1);
5159 } else {
5160 oom.setOOM();
5161 jit::GetIonScript(script, mode)->recompileInfoRef() = RecompileInfo();
5162 output.invalidate();
5167 sweepCompilerOutputs = compilerOutputs;
5168 compilerOutputs = newCompilerOutputs;
5171 // All existing RecompileInfos are stale and will be updated to the new
5172 // compiler outputs list later during the sweep. Don't worry about overflow
5173 // here, since stale indexes will persist only until the sweep finishes.
5174 generation++;
5176 for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
5177 comp->types.sweep(fop);
5180 void
5181 TypeZone::endSweep(JSRuntime* rt)
5183 js_delete(sweepCompilerOutputs);
5184 sweepCompilerOutputs = nullptr;
5186 sweepReleaseTypes = false;
5188 rt->gc.freeAllLifoBlocksAfterSweeping(&sweepTypeLifoAlloc);
5191 void
5192 TypeZone::clearAllNewScriptsOnOOM()
5194 for (gc::ZoneCellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
5195 !iter.done(); iter.next())
5197 TypeObject* object = iter.get<TypeObject>();
5198 if (!IsTypeObjectAboutToBeFinalized(&object))
5199 object->maybeClearNewScriptOnOOM();
5203 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
5205 if (oom) {
5206 zone->setPreservingCode(false);
5207 zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp());
5208 zone->types.clearAllNewScriptsOnOOM();
5212 #ifdef DEBUG
5213 void
5214 TypeScript::printTypes(JSContext* cx, HandleScript script) const
5216 MOZ_ASSERT(script->types() == this);
5218 if (!script->hasBaselineScript())
5219 return;
5221 AutoEnterAnalysis enter(nullptr, script->zone());
5223 if (script->functionNonDelazifying())
5224 fprintf(stderr, "Function");
5225 else if (script->isForEval())
5226 fprintf(stderr, "Eval");
5227 else
5228 fprintf(stderr, "Main");
5229 fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno());
5231 if (script->functionNonDelazifying()) {
5232 if (js::PropertyName* name = script->functionNonDelazifying()->name())
5233 name->dumpCharsNoNewline();
5236 fprintf(stderr, "\n this:");
5237 TypeScript::ThisTypes(script)->print();
5239 for (unsigned i = 0;
5240 script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs();
5241 i++)
5243 fprintf(stderr, "\n arg%u:", i);
5244 TypeScript::ArgTypes(script, i)->print();
5246 fprintf(stderr, "\n");
5248 for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
5250 fprintf(stderr, "#%u:", script->id());
5251 Sprinter sprinter(cx);
5252 if (!sprinter.init())
5253 return;
5254 js_Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
5255 fprintf(stderr, "%s", sprinter.string());
5258 if (js_CodeSpec[*pc].format & JOF_TYPESET) {
5259 StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
5260 fprintf(stderr, " typeset %u:", unsigned(types - typeArray()));
5261 types->print();
5262 fprintf(stderr, "\n");
5266 fprintf(stderr, "\n");
5268 #endif /* DEBUG */
5270 void
5271 TypeObject::setAddendum(AddendumKind kind, void* addendum)
5273 MOZ_ASSERT(!needsSweep());
5274 MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
5275 MOZ_ASSERT(addendumKind() == 0 || addendumKind() == kind);
5277 // Manually trigger barriers if we are clearing a TypeNewScript. Other
5278 // kinds of addendums are immutable.
5279 if (newScript()) {
5280 MOZ_ASSERT(kind == Addendum_NewScript);
5281 TypeNewScript::writeBarrierPre(newScript());
5284 flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
5285 addendum_ = addendum;