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"
16 #include "jshashutil.h"
23 #include "gc/Marking.h"
24 #include "jit/BaselineJIT.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"
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"
42 using namespace js::gc
;
43 using namespace js::types
;
45 using mozilla::DebugOnly
;
47 using mozilla::PodArrayZero
;
48 using mozilla::PodCopy
;
49 using mozilla::PodZero
;
52 id_prototype(JSContext
* cx
)
54 return NameToId(cx
->names().prototype
);
60 id___proto__(JSContext
* cx
)
62 return NameToId(cx
->names().proto
);
66 id_constructor(JSContext
* cx
)
68 return NameToId(cx
->names().constructor
);
72 id_caller(JSContext
* cx
)
74 return NameToId(cx
->names().caller
);
78 types::TypeIdStringImpl(jsid id
)
82 if (JSID_IS_EMPTY(id
))
84 if (JSID_IS_SYMBOL(id
))
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);
95 /////////////////////////////////////////////////////////////////////
97 /////////////////////////////////////////////////////////////////////
101 static bool InferSpewActive(SpewChannel channel
)
103 static bool active
[SPEW_COUNT
];
104 static bool checked
= false;
107 PodArrayZero(active
);
108 const char* env
= getenv("INFERFLAGS");
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
++)
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;
130 const char* env
= getenv("TERM");
133 if (strcmp(env
, "xterm-color") == 0 || strcmp(env
, "xterm-256color") == 0)
140 types::InferSpewColorReset()
142 if (!InferSpewColorable())
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",
154 if (!InferSpewColorable())
156 return colors
[DefaultHasher
<TypeConstraint
*>::hash(constraint
) % 7];
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",
166 if (!InferSpewColorable())
168 return colors
[DefaultHasher
<TypeSet
*>::hash(types
) % 7];
172 types::TypeString(Type type
)
174 if (type
.isPrimitive()) {
175 switch (type
.primitive()) {
176 case JSVAL_TYPE_UNDEFINED
:
178 case JSVAL_TYPE_NULL
:
180 case JSVAL_TYPE_BOOLEAN
:
182 case JSVAL_TYPE_INT32
:
184 case JSVAL_TYPE_DOUBLE
:
186 case JSVAL_TYPE_STRING
:
188 case JSVAL_TYPE_SYMBOL
:
190 case JSVAL_TYPE_MAGIC
:
193 MOZ_CRASH("Bad type");
196 if (type
.isUnknown())
198 if (type
.isAnyObject())
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());
208 JS_snprintf(bufs
[which
], 40, "[0x%p]", (void*) type
.typeObject());
214 types::TypeObjectString(TypeObject
* type
)
216 return TypeString(Type::ObjectType(type
));
219 unsigned JSScript::id() {
221 id_
= ++compartment()->types
.scriptCount
;
222 InferSpew(ISpewOps
, "script #%u: %p %s:%d",
223 id_
, this, filename() ? filename() : "<null>", lineno());
229 types::InferSpew(SpewChannel channel
, const char* fmt
, ...)
231 if (!InferSpewActive(channel
))
236 fprintf(stderr
, "[infer] ");
237 vfprintf(stderr
, fmt
, ap
);
238 fprintf(stderr
, "\n");
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()) {
252 /* Watch for properties which inference does not monitor. */
253 if (id
== id___proto__(cx
) || id
== id_constructor(cx
) || id
== id_caller(cx
))
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
);
269 if (!types
->hasType(type
)) {
270 TypeFailure(cx
, "Missing type in object %s %s: %s",
271 TypeObjectString(obj
), TypeIdString(id
), TypeString(type
));
280 types::TypeFailure(JSContext
* cx
, const char* fmt
, ...)
282 char msgbuf
[1024]; /* Larger error messages will be truncated */
287 JS_vsnprintf(errbuf
, sizeof(errbuf
), fmt
, 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__
);
299 /////////////////////////////////////////////////////////////////////
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
;
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
);
328 TypeSet::mightBeMIRType(jit::MIRType type
)
333 if (type
== jit::MIRType_Object
)
334 return unknownObject() || baseObjectCount() != 0;
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
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.
367 MOZ_CRASH("Bad MIR type");
372 TypeSet::objectsAreSubset(TypeSet
* other
)
374 if (other
->unknownObject())
380 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
381 TypeObjectKey
* obj
= getObject(i
);
384 if (!other
->hasType(Type::ObjectType(obj
)))
392 TypeSet::isSubset(const TypeSet
* other
) const
394 if ((baseFlags() & other
->baseFlags()) != baseFlags())
397 if (unknownObject()) {
398 MOZ_ASSERT(other
->unknownObject());
400 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
401 TypeObjectKey
* obj
= getObject(i
);
404 if (!other
->hasType(Type::ObjectType(obj
)))
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) {
422 Type type
= Type::PrimitiveType(TypeFlagPrimitive(flag
));
423 if (!list
->append(type
))
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
);
437 if (!list
->append(Type::ObjectType(object
)))
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.
453 if (!enumerateTypes(&types
))
456 for (unsigned i
= 0; i
< types
.length(); i
++)
457 constraint
->newType(cx
, this, types
[i
]);
463 ConstraintTypeSet::addConstraint(JSContext
* cx
, TypeConstraint
* constraint
, bool callExisting
)
466 /* OOM failure while constructing the constraint. */
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(),
477 MOZ_ASSERT(constraint
->next
== nullptr);
478 constraint
->next
= constraintList
;
479 constraintList
= constraint
;
482 return addTypesToConstraint(cx
, constraint
);
487 TypeSet::clearObjects()
489 setBaseObjectCount(0);
494 TypeSet::addType(Type type
, LifoAlloc
* alloc
)
499 if (type
.isUnknown()) {
500 flags
|= TYPE_FLAG_BASE_MASK
;
502 MOZ_ASSERT(unknown());
506 if (type
.isPrimitive()) {
507 TypeFlags flag
= PrimitiveTypeFlag(type
.primitive());
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
;
519 if (flags
& TYPE_FLAG_ANYOBJECT
)
521 if (type
.isAnyObject())
525 uint32_t objectCount
= baseObjectCount();
526 TypeObjectKey
* object
= type
.objectKey();
527 TypeObjectKey
** pentry
= HashSetInsert
<TypeObjectKey
*,TypeObjectKey
,TypeObjectKey
>
528 (*alloc
, objectSet
, objectCount
, 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
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())
548 // Make sure the newly added object is also a DOM object.
549 if (!object
->clasp()->isDOMClass())
552 // Limit the number of DOM objects.
553 if (objectCount
== TYPE_FLAG_DOMOBJECT_COUNT_LIMIT
)
558 if (type
.isTypeObject()) {
559 TypeObject
* nobject
= type
.typeObject();
560 MOZ_ASSERT(!nobject
->singleton());
561 if (nobject
->unknownProperties())
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
);
573 flags
|= TYPE_FLAG_ANYOBJECT
;
579 ConstraintTypeSet::addType(ExclusiveContext
* cxArg
, Type type
)
581 MOZ_ASSERT(cxArg
->zone()->types
.activeAnalysis
);
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(),
595 /* Propagate the type to all constraints. */
596 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
597 TypeConstraint
* constraint
= constraintList
;
599 constraint
->newType(cx
, this, type
);
600 constraint
= constraint
->next
;
603 MOZ_ASSERT(!constraintList
);
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");
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();
648 fprintf(stderr
, " object[%u]", objectCount
);
650 unsigned count
= getObjectCount();
651 for (unsigned i
= 0; i
< count
; i
++) {
652 TypeObjectKey
* object
= getObject(i
);
654 fprintf(stderr
, " %s", TypeString(Type::ObjectType(object
)));
660 TypeSet::readBarrier(const TypeSet
* types
)
662 if (types
->unknownObject())
665 for (unsigned i
= 0; i
< types
->getObjectCount(); i
++) {
666 if (TypeObjectKey
* object
= types
->getObject(i
)) {
667 if (object
->isSingleObject())
668 (void) object
->asSingleObject();
670 (void) object
->asTypeObject();
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
;
685 newSet
= alloc
->newArray
<TypeObjectKey
*>(capacity
);
688 PodCopy(newSet
, objectSet
, capacity
);
691 new(result
) TemporaryTypeSet(flags
, capacity
? newSet
: objectSet
);
696 TypeSet::clone(LifoAlloc
* alloc
) const
698 TemporaryTypeSet
* res
= alloc
->new_
<TemporaryTypeSet
>();
699 if (!res
|| !clone(alloc
, res
))
705 TypeSet::filter(LifoAlloc
* alloc
, bool filterUndefined
, bool filterNull
) const
707 TemporaryTypeSet
* res
= clone(alloc
);
712 res
->flags
= res
->flags
& ~TYPE_FLAG_UNDEFINED
;
715 res
->flags
= res
->flags
& ~TYPE_FLAG_NULL
;
721 TypeSet::cloneObjectsOnly(LifoAlloc
* alloc
)
723 TemporaryTypeSet
* res
= clone(alloc
);
727 res
->flags
&= ~TYPE_FLAG_BASE_MASK
| TYPE_FLAG_ANYOBJECT
;
733 TypeSet::cloneWithoutObjects(LifoAlloc
* alloc
)
735 TemporaryTypeSet
* res
= alloc
->new_
<TemporaryTypeSet
>();
739 res
->flags
= flags
& ~TYPE_FLAG_ANYOBJECT
;
740 res
->setBaseObjectCount(0);
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));
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
);
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));
775 res
->setBaseObjectCount(0);
776 if (res
->unknownObject())
779 MOZ_ASSERT(!a
->unknownObject() || !b
->unknownObject());
781 if (a
->unknownObject()) {
782 for (size_t i
= 0; i
< b
->getObjectCount(); i
++) {
784 res
->addType(Type::ObjectType(b
->getObject(i
)), alloc
);
789 if (b
->unknownObject()) {
790 for (size_t i
= 0; i
< a
->getObjectCount(); i
++) {
792 res
->addType(Type::ObjectType(a
->getObject(i
)), alloc
);
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
))
803 if (!b
->getObject(j
))
805 res
->addType(Type::ObjectType(b
->getObject(j
)), alloc
);
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
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
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
864 TemporaryTypeSet
* thisTypes
;
865 TemporaryTypeSet
* argTypes
;
866 TemporaryTypeSet
* bytecodeTypes
;
871 // OOM during generation of some constraint.
874 // Allocator used for constraints.
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
;
884 explicit CompilerConstraintList(jit::TempAllocator
& alloc
)
886 alloc_(alloc
.lifoAlloc()),
891 void add(CompilerConstraint
* constraint
) {
892 if (!constraint
|| !constraints
.append(constraint
))
896 void freezeScript(JSScript
* script
,
897 TemporaryTypeSet
* thisTypes
,
898 TemporaryTypeSet
* argTypes
,
899 TemporaryTypeSet
* bytecodeTypes
)
902 entry
.script
= script
;
903 entry
.thisTypes
= thisTypes
;
904 entry
.argTypes
= argTypes
;
905 entry
.bytecodeTypes
= bytecodeTypes
;
906 if (!frozenScripts
.append(entry
))
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
];
932 LifoAlloc
* alloc() const {
937 CompilerConstraintList
*
938 types::NewCompilerConstraintList(jit::TempAllocator
& alloc
)
940 return alloc
.lifoAlloc()->new_
<CompilerConstraintList
>(alloc
);
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
);
956 PodZero(types
, count
);
958 for (size_t i
= 0; i
< count
; i
++) {
959 if (!existing
[i
].clone(alloc
, &types
[i
]))
963 *pThisTypes
= types
+ (ThisTypes(script
) - existing
);
964 *pArgTypes
= (script
->functionNonDelazifying() && script
->functionNonDelazifying()->nargs())
965 ? (types
+ (ArgTypes(script
, 0) - existing
))
967 *pBytecodeTypes
= types
;
969 constraints
->freezeScript(script
, *pThisTypes
, *pArgTypes
, *pBytecodeTypes
);
975 template <typename T
>
976 class CompilerConstraintInstance
: public CompilerConstraint
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
;
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
))
1025 *res
= zone
.typeLifoAlloc
.new_
<TypeCompilerConstraint
<T
> >(compilation
, data
);
1030 template <typename T
>
1032 CompilerConstraintInstance
<T
>::generateTypeConstraint(JSContext
* cx
, RecompileInfo recompileInfo
)
1034 if (property
.object()->unknownProperties())
1037 if (!property
.instantiate(cx
))
1040 if (!data
.constraintHolds(cx
, property
, expected
))
1043 return property
.maybeTypes()->addConstraint(cx
, cx
->typeLifoAlloc().new_
<TypeCompilerConstraint
<T
> >(recompileInfo
, data
),
1044 /* callExisting = */ false);
1047 } /* anonymous namespace */
1050 TypeObjectKey::clasp()
1052 return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass();
1056 TypeObjectKey::proto()
1058 MOZ_ASSERT(hasTenuredProto());
1059 return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
1063 JSObject::hasTenuredProto() const
1065 return type_
->hasTenuredProto();
1069 TypeObjectKey::hasTenuredProto()
1071 return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
1075 TypeObjectKey::singleton()
1077 return isTypeObject() ? asTypeObject()->singleton() : asSingleObject();
1081 TypeObjectKey::newScript()
1083 if (isTypeObject() && asTypeObject()->newScript())
1084 return asTypeObject()->newScript();
1089 TypeObjectKey::maybeType()
1092 return asTypeObject();
1093 if (asSingleObject()->hasLazyType())
1095 return asSingleObject()->type();
1099 TypeObjectKey::unknownProperties()
1101 if (TypeObject
* type
= maybeType())
1102 return type
->unknownProperties();
1107 TypeObjectKey::property(jsid id
)
1109 MOZ_ASSERT(!unknownProperties());
1111 HeapTypeSetKey property
;
1112 property
.object_
= this;
1114 if (TypeObject
* type
= maybeType())
1115 property
.maybeTypes_
= type
->maybeGetProperty(id
);
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
);
1136 HeapTypeSetKey::instantiate(JSContext
* cx
)
1140 if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx
)) {
1141 cx
->clearPendingException();
1144 maybeTypes_
= object()->maybeType()->getProperty(cx
, id());
1145 return maybeTypes_
!= nullptr;
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
))
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
]);
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
1183 explicit TypeConstraintFreezeStack(JSScript
* 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_
))
1200 *res
= zone
.typeLifoAlloc
.new_
<TypeConstraintFreezeStack
>(script_
);
1205 } /* anonymous namespace */
1208 types::FinishCompilation(JSContext
* cx
, HandleScript script
, ExecutionMode executionMode
,
1209 CompilerConstraintList
* constraints
, RecompileInfo
* precompileInfo
)
1211 if (constraints
->failed())
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
)
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
);
1230 uint32_t index
= types
.compilerOutputs
->length();
1231 if (!types
.compilerOutputs
->append(co
)) {
1232 js_ReportOutOfMemory(cx
);
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
))
1246 for (size_t i
= 0; i
< constraints
->numFrozenScripts(); i
++) {
1247 const CompilerConstraintList::FrozenScript
& entry
= constraints
->frozenScript(i
);
1248 if (!entry
.script
->types()) {
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()) {
1261 if (!CheckFrozenTypeSet(cx
, entry
.thisTypes
, types::TypeScript::ThisTypes(entry
.script
)))
1263 unsigned nargs
= entry
.script
->functionNonDelazifying()
1264 ? entry
.script
->functionNonDelazifying()->nargs()
1266 for (size_t i
= 0; i
< nargs
; i
++) {
1267 if (!CheckFrozenTypeSet(cx
, &entry
.argTypes
[i
], types::TypeScript::ArgTypes(entry
.script
, i
)))
1270 for (size_t i
= 0; i
< entry
.script
->nTypeSets(); i
++) {
1271 if (!CheckFrozenTypeSet(cx
, &entry
.bytecodeTypes
[i
], &entry
.script
->types()->typeArray()[i
]))
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())
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))
1290 if (!succeeded
|| types
.compilerOutputs
->back().pendingInvalidation()) {
1291 types
.compilerOutputs
->back().invalidate();
1292 script
->resetWarmUpCounter();
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
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
]);
1317 types::FinishDefinitePropertiesAnalysis(JSContext
* cx
, CompilerConstraintList
* constraints
)
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()
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
]));
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())
1348 CheckDefinitePropertiesTypeSet(cx
, entry
.thisTypes
, TypeScript::ThisTypes(script
));
1350 unsigned nargs
= script
->functionNonDelazifying()
1351 ? script
->functionNonDelazifying()->nargs()
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
]);
1363 // Constraint which triggers recompilation of a script if any type is added to a type set. */
1364 class ConstraintDataFreeze
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
)
1379 ? property
.maybeTypes()->isSubset(expected
)
1380 : property
.maybeTypes()->empty();
1383 bool shouldSweep() { return false; }
1386 } /* anonymous namespace */
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
)
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
;
1420 return jit::MIRType_Value
;
1425 TemporaryTypeSet::getKnownMIRType()
1427 TypeFlags flags
= baseFlags();
1430 if (baseObjectCount())
1431 type
= flags
? jit::MIRType_Value
: jit::MIRType_Object
;
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
1442 DebugOnly
<bool> empty
= flags
== 0 && baseObjectCount() == 0;
1443 MOZ_ASSERT_IF(empty
, type
== jit::MIRType_Value
);
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
;
1459 if (types
->unknownObject() || types
->getObjectCount())
1460 type
= flags
? jit::MIRType_Value
: jit::MIRType_Object
;
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
1474 MOZ_ASSERT_IF(types
->empty(), type
== jit::MIRType_Value
);
1480 HeapTypeSetKey::isOwnProperty(CompilerConstraintList
* constraints
,
1481 bool allowEmptyTypesForGlobal
/* = false*/)
1483 if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
1485 JSObject
* obj
= object()->singleton();
1486 MOZ_ASSERT_IF(obj
, CanHaveEmptyPropertyTypesForOwnProperty(obj
) == obj
->is
<GlobalObject
>());
1487 if (obj
&& !allowEmptyTypesForGlobal
) {
1488 if (CanHaveEmptyPropertyTypesForOwnProperty(obj
))
1491 freeze(constraints
);
1496 HeapTypeSetKey::knownSubset(CompilerConstraintList
* constraints
, const HeapTypeSetKey
& other
)
1498 if (!maybeTypes() || maybeTypes()->empty()) {
1499 freeze(constraints
);
1502 if (!other
.maybeTypes() || !maybeTypes()->isSubset(other
.maybeTypes()))
1504 freeze(constraints
);
1509 TemporaryTypeSet::getSingleton()
1511 if (baseFlags() != 0 || baseObjectCount() != 1)
1514 return getSingleObject(0);
1518 HeapTypeSetKey::singleton(CompilerConstraintList
* constraints
)
1520 HeapTypeSet
* types
= maybeTypes();
1522 if (!types
|| types
->nonDataProperty() || types
->baseFlags() != 0 || types
->getObjectCount() != 1)
1525 JSObject
* obj
= types
->getSingleObject(0);
1528 freeze(constraints
);
1534 HeapTypeSetKey::needsBarrier(CompilerConstraintList
* constraints
)
1536 TypeSet
* types
= maybeTypes();
1539 bool result
= types
->unknownObject()
1540 || types
->getObjectCount() > 0
1541 || types
->hasAnyFlag(TYPE_FLAG_STRING
| TYPE_FLAG_SYMBOL
);
1543 freeze(constraints
);
1549 // Constraint which triggers recompilation if an object acquires particular flags.
1550 class ConstraintDataFreezeObjectFlags
1553 // Flags we are watching for on this object.
1554 TypeObjectFlags flags
;
1556 explicit ConstraintDataFreezeObjectFlags(TypeObjectFlags 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 */
1582 TypeObjectKey::hasFlags(CompilerConstraintList
* constraints
, TypeObjectFlags flags
)
1586 if (TypeObject
* type
= maybeType()) {
1587 if (type
->hasAnyFlags(flags
))
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
)));
1600 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList
* constraints
, TypeObjectFlags flags
)
1602 if (unknownObject())
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)
1612 unsigned count
= getObjectCount();
1613 for (unsigned i
= 0; i
< count
; i
++) {
1614 TypeObjectKey
* object
= getObject(i
);
1615 if (object
&& object
->hasFlags(constraints
, flags
))
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
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
;
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
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.
1666 bool constraintHolds(JSContext
* cx
,
1667 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1672 bool shouldSweep() { return false; }
1675 // Constraint which triggers recompilation when a typed array's data becomes
1677 class ConstraintDataFreezeObjectForTypedArrayData
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.
1709 } /* anonymous namespace */
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()));
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
)));
1734 ObjectStateChange(ExclusiveContext
* cxArg
, TypeObject
* object
, bool markingUnknown
)
1736 if (object
->unknownProperties())
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. */
1744 object
->addFlags(OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_UNKNOWN_PROPERTIES
);
1747 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
1748 TypeConstraint
* constraint
= types
->constraintList
;
1749 while (constraint
) {
1750 constraint
->newObjectState(cx
, object
);
1751 constraint
= constraint
->next
;
1754 MOZ_ASSERT(!types
->constraintList
);
1761 class ConstraintDataFreezePropertyState
1769 explicit ConstraintDataFreezePropertyState(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 */
1795 HeapTypeSetKey::nonData(CompilerConstraintList
* constraints
)
1797 if (maybeTypes() && maybeTypes()->nonDataProperty())
1800 LifoAlloc
* alloc
= constraints
->alloc();
1802 typedef CompilerConstraintInstance
<ConstraintDataFreezePropertyState
> T
;
1803 constraints
->add(alloc
->new_
<T
>(alloc
, *this,
1804 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA
)));
1809 HeapTypeSetKey::nonWritable(CompilerConstraintList
* constraints
)
1811 if (maybeTypes() && maybeTypes()->nonWritableProperty())
1814 LifoAlloc
* alloc
= constraints
->alloc();
1816 typedef CompilerConstraintInstance
<ConstraintDataFreezePropertyState
> T
;
1817 constraints
->add(alloc
->new_
<T
>(alloc
, *this,
1818 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE
)));
1824 class ConstraintDataConstantProperty
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 */
1849 HeapTypeSetKey::constant(CompilerConstraintList
* constraints
, Value
* valOut
)
1851 if (nonData(constraints
))
1854 // Only singleton object properties can be marked as constants.
1855 JSObject
* obj
= object()->singleton();
1856 if (!obj
|| !obj
->isNative())
1859 if (maybeTypes() && maybeTypes()->nonConstantProperty())
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())
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()))
1873 // If the value is a string that's not atomic, don't optimize.
1874 if (val
.isString() && !val
.toString()->isAtom())
1879 LifoAlloc
* alloc
= constraints
->alloc();
1880 typedef CompilerConstraintInstance
<ConstraintDataConstantProperty
> T
;
1881 constraints
->add(alloc
->new_
<T
>(alloc
, *this, ConstraintDataConstantProperty()));
1885 // A constraint that never triggers recompilation.
1886 class ConstraintDataInert
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
)
1903 bool shouldSweep() { return false; }
1907 HeapTypeSetKey::couldBeConstant(CompilerConstraintList
* constraints
)
1909 // Only singleton object properties can be marked as constants.
1910 if (!object()->singleton())
1913 if (!maybeTypes() || !maybeTypes()->nonConstantProperty())
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()));
1930 TemporaryTypeSet::filtersType(const TemporaryTypeSet
* other
, Type filteredType
) const
1932 if (other
->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
))
1941 if (other
->unknownObject())
1942 return unknownObject();
1944 for (size_t i
= 0; i
< other
->getObjectCount(); i
++) {
1945 TypeObjectKey
* key
= other
->getObject(i
);
1947 Type type
= Type::ObjectType(key
);
1948 if (type
!= filteredType
&& !hasType(type
))
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
);
1971 if (type
->unknownProperties()) {
1972 alwaysConvert
= false;
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_
)
1988 alwaysConvert
= false;
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;
2000 alwaysConvert
= false;
2004 MOZ_ASSERT_IF(alwaysConvert
, maybeConvert
);
2006 if (maybeConvert
&& dontConvert
)
2007 return AmbiguousDoubleConversion
;
2009 return AlwaysConvertToDoubles
;
2011 return MaybeConvertToDoubles
;
2012 return DontConvertToDoubles
;
2016 TemporaryTypeSet::getKnownClass()
2018 if (unknownObject())
2021 const Class
* clasp
= nullptr;
2022 unsigned count
= getObjectCount();
2024 for (unsigned i
= 0; i
< count
; i
++) {
2025 const Class
* nclasp
= getObjectClass(i
);
2029 if (clasp
&& clasp
!= nclasp
)
2037 TemporaryTypeSet::ForAllResult
2038 TemporaryTypeSet::forAllClasses(bool (*func
)(const Class
* clasp
))
2040 if (unknownObject())
2041 return ForAllResult::MIXED
;
2043 unsigned count
= getObjectCount();
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
);
2052 return ForAllResult::MIXED
;
2054 true_results
= true;
2055 if (false_results
) return ForAllResult::MIXED
;
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
;
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
;
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
;
2089 TypeSet::isDOMClass()
2091 if (unknownObject())
2094 unsigned count
= getObjectCount();
2095 for (unsigned i
= 0; i
< count
; i
++) {
2096 const Class
* clasp
= getObjectClass(i
);
2097 if (clasp
&& !clasp
->isDOMClass())
2105 TemporaryTypeSet::maybeCallable()
2110 if (unknownObject())
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()))
2124 TemporaryTypeSet::maybeEmulatesUndefined()
2129 if (unknownObject())
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()))
2146 TemporaryTypeSet::getCommonPrototype()
2148 if (unknownObject())
2151 JSObject
* proto
= nullptr;
2152 unsigned count
= getObjectCount();
2154 for (unsigned i
= 0; i
< count
; i
++) {
2155 TypeObjectKey
* object
= getObject(i
);
2159 if (!object
->hasTenuredProto())
2162 TaggedProto nproto
= object
->proto();
2164 if (nproto
!= TaggedProto(proto
))
2167 if (!nproto
.isObject())
2169 proto
= nproto
.toObject();
2177 TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList
* constraints
, jsid id
)
2179 if (unknownObject())
2182 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
2183 TypeObjectKey
* type
= getObject(i
);
2187 if (type
->unknownProperties())
2190 HeapTypeSetKey property
= type
->property(id
);
2191 if (property
.needsBarrier(constraints
))
2198 /////////////////////////////////////////////////////////////////////
2200 /////////////////////////////////////////////////////////////////////
2202 TypeCompartment::TypeCompartment()
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
);
2221 new(object
) TypeObject(clasp
, proto
, initialFlags
);
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;
2240 AllocationSiteTable::AddPtr p
= allocationSiteTable
->lookupForAdd(key
);
2243 TypeObject
* res
= nullptr;
2245 jsbytecode
* pc
= key
.script
->offsetToPC(key
.offset
);
2246 RootedScript
keyScript(cx
, key
.script
);
2249 RootedObject
proto(cx
);
2250 if (!GetBuiltinPrototype(cx
, key
.kind
, &proto
))
2253 Rooted
<TaggedProto
> tagged(cx
, TaggedProto(proto
));
2254 res
= newTypeObject(cx
, GetClassForProtoKey(key
.kind
), tagged
, OBJECT_FLAG_FROM_ALLOCATION_SITE
);
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()))
2272 if (!allocationSiteTable
->add(p
, key
, res
))
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
));
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())
2307 if (JSOp(*pc
) != JSOP_NEW
)
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
))
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
)
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
;
2362 types::UseNewTypeForInitializer(JSScript
* script
, jsbytecode
* pc
, const Class
* clasp
)
2364 return UseNewTypeForInitializer(script
, pc
, JSCLASS_CACHED_PROTO_KEY(clasp
));
2368 ClassCanHaveExtraProperties(const Class
* clasp
)
2370 return clasp
->resolve
2371 || clasp
->ops
.lookupGeneric
2372 || clasp
->ops
.getGeneric
2373 || IsAnyTypedArrayClass(clasp
);
2377 PrototypeHasIndexedProperty(CompilerConstraintList
* constraints
, JSObject
* obj
)
2380 TypeObjectKey
* type
= TypeObjectKey::get(obj
);
2381 if (ClassCanHaveExtraProperties(type
->clasp()))
2383 if (type
->unknownProperties())
2385 HeapTypeSetKey index
= type
->property(JSID_VOID
);
2386 if (index
.nonData(constraints
) || index
.isOwnProperty(constraints
))
2388 if (!obj
->hasTenuredProto())
2390 obj
= obj
->getProto();
2397 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList
* constraints
, JSScript
* script
)
2399 if (JSObject
* proto
= script
->global().maybeGetArrayPrototype())
2400 return PrototypeHasIndexedProperty(constraints
, proto
);
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
2413 if (!clasp
|| (ClassCanHaveExtraProperties(clasp
) && !IsAnyTypedArrayClass(clasp
)))
2416 if (types
->hasObjectFlags(constraints
, types::OBJECT_FLAG_SPARSE_INDEXES
))
2419 JSObject
* proto
= types
->getCommonPrototype();
2423 return PrototypeHasIndexedProperty(constraints
, proto
);
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");
2442 jit::Invalidate(*this, fop
, pending
);
2444 MOZ_ASSERT(recompiles
.empty());
2448 TypeZone::addPendingRecompile(JSContext
* cx
, const RecompileInfo
& info
)
2450 CompilerOutput
* co
= info
.compilerOutput(cx
);
2451 if (!co
|| !co
->isValid() || co
->pendingInvalidation())
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");
2464 TypeZone::addPendingRecompile(JSContext
* cx
, JSScript
* 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);
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
);
2525 TypeCompartment::print(JSContext
* cx
, bool force
)
2528 gc::AutoSuppressGC
suppressGC(cx
);
2529 JSAutoRequest
request(cx
);
2531 Zone
* zone
= compartment()->zone();
2532 AutoEnterAnalysis
enter(nullptr, zone
);
2534 if (!force
&& !InferSpewActive(ISpewResult
))
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
>();
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.
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.
2579 GetValueTypeForTable(const Value
& v
)
2581 Type type
= GetValueType(v
);
2582 MOZ_ASSERT(!type
.isSingleObject());
2586 struct types::ArrayTableKey
: public DefaultHasher
<types::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
);
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;
2630 ArrayTableKey
key(elementType
, obj
->getProto());
2631 DependentAddPtr
<ArrayTypeTable
> p(cx
, *arrayTypeTable
, key
);
2633 obj
->setType(p
->value());
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
);
2641 obj
->setType(objType
);
2643 AddTypePropertyId(cx
, objType
, JSID_VOID
, elementType
);
2645 key
.proto
= objProto
;
2646 (void) p
.add(cx
, *arrayTypeTable
, key
, objType
);
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();
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();
2678 setTypeToHomogenousArray(cx
, obj
, type
);
2682 types::FixRestArgumentsType(ExclusiveContext
* cx
, ArrayObject
* obj
)
2684 cx
->compartment()->types
.fixRestArgumentsType(cx
, obj
);
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
2708 uint32_t nproperties
;
2712 IdValuePair
* properties
;
2713 uint32_t nproperties
;
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
^
2727 static inline bool match(const ObjectTableKey
& v
, const Lookup
& lookup
) {
2728 if (lookup
.nproperties
!= v
.nproperties
|| lookup
.nfixed
!= v
.nfixed
)
2730 for (size_t i
= 0; i
< lookup
.nproperties
; i
++) {
2731 if (lookup
.properties
[i
].id
!= v
.properties
[i
])
2738 struct types::ObjectTableEntry
2740 ReadBarrieredTypeObject object
;
2741 ReadBarrieredShape shape
;
2746 UpdateObjectTableEntryTypes(ExclusiveContext
* cx
, ObjectTableEntry
& entry
,
2747 IdValuePair
* properties
, size_t nproperties
)
2749 if (entry
.object
->unknownProperties())
2751 for (size_t i
= 0; i
< nproperties
; i
++) {
2752 Type type
= entry
.types
[i
];
2753 Type ntype
= GetValueTypeForTable(properties
[i
].value
);
2756 if (ntype
.isPrimitive(JSVAL_TYPE_INT32
) &&
2757 type
.isPrimitive(JSVAL_TYPE_DOUBLE
))
2759 /* The property types already reflect 'int32'. */
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
);
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;
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())
2797 Vector
<IdValuePair
> properties(cx
);
2798 if (!properties
.resize(obj
->slotSpan()))
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
);
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
);
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()))
2827 if (obj
->isIndexed())
2828 objType
->setFlags(cx
, OBJECT_FLAG_SPARSE_INDEXES
);
2830 ScopedJSFreePtr
<jsid
> ids(objType
->zone()->pod_calloc
<jsid
>(properties
.length()));
2834 ScopedJSFreePtr
<Type
> types(objType
->zone()->pod_calloc
<Type
>(properties
.length()));
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
]);
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
)) {
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;
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
)
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
);
2904 RootedPlainObject
obj(cx
, NewBuiltinClassInstance
<PlainObject
>(cx
, allocKind
));
2906 cx
->clearPendingException();
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();
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
);
2926 /////////////////////////////////////////////////////////////////////
2928 /////////////////////////////////////////////////////////////////////
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
);
2942 UpdatePropertyType(ExclusiveContext
* cx
, HeapTypeSet
* types
, NativeObject
* obj
, Shape
* shape
,
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
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
);
2978 InferSpew(ISpewOps
, "typeSet: %sT%p%s property %s %s - setConstant",
2979 InferSpewColor(types
), types
, InferSpewColorReset(),
2980 TypeObjectString(obj
->type()), TypeIdString(shape
->propid()));
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
);
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
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
);
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
);
3040 TypeObject::addDefiniteProperties(ExclusiveContext
* cx
, Shape
* shape
)
3042 if (unknownProperties())
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
);
3056 if (types
->canSetDefinite(shape
->slot()))
3057 types
->setDefinite(shape
->slot());
3060 shape
= shape
->previous();
3067 TypeObject::matchDefiniteProperties(HandleObject obj
)
3069 unsigned count
= getPropertyCount();
3070 for (unsigned i
= 0; i
< count
; i
++) {
3071 Property
* prop
= getProperty(i
);
3074 if (prop
->types
.definiteProperty()) {
3075 unsigned slot
= prop
->types
.definiteSlot();
3078 Shape
* shape
= obj
->lastProperty();
3079 while (!shape
->isEmptyShape()) {
3080 if (shape
->slot() == slot
&& shape
->propid() == prop
->id
) {
3084 shape
= shape
->previous();
3095 types::AddTypePropertyId(ExclusiveContext
* cx
, TypeObject
* obj
, jsid id
, Type type
)
3097 MOZ_ASSERT(id
== IdToTypeId(id
));
3099 if (obj
->unknownProperties())
3102 AutoEnterAnalysis
enter(cx
);
3104 HeapTypeSet
* types
= obj
->getProperty(cx
, id
);
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
))
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
);
3135 types::AddTypePropertyId(ExclusiveContext
* cx
, TypeObject
* obj
, jsid id
, const Value
& value
)
3137 AddTypePropertyId(cx
, obj
, id
, GetValueType(value
));
3141 TypeObject::markPropertyNonData(ExclusiveContext
* cx
, jsid id
)
3143 AutoEnterAnalysis
enter(cx
);
3145 id
= IdToTypeId(id
);
3147 HeapTypeSet
* types
= getProperty(cx
, id
);
3149 types
->setNonDataProperty(cx
);
3153 TypeObject::markPropertyNonWritable(ExclusiveContext
* cx
, jsid id
)
3155 AutoEnterAnalysis
enter(cx
);
3157 id
= IdToTypeId(id
);
3159 HeapTypeSet
* types
= getProperty(cx
, id
);
3161 types
->setNonWritableProperty(cx
);
3165 TypeObject::isPropertyNonData(jsid id
)
3167 TypeSet
* types
= maybeGetProperty(id
);
3169 return types
->nonDataProperty();
3174 TypeObject::isPropertyNonWritable(jsid id
)
3176 TypeSet
* types
= maybeGetProperty(id
);
3178 return types
->nonWritableProperty();
3183 TypeObject::markStateChange(ExclusiveContext
* cxArg
)
3185 if (unknownProperties())
3188 AutoEnterAnalysis
enter(cxArg
);
3189 HeapTypeSet
* types
= maybeGetProperty(JSID_EMPTY
);
3191 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
3192 TypeConstraint
* constraint
= types
->constraintList
;
3193 while (constraint
) {
3194 constraint
->newObjectState(cx
, this);
3195 constraint
= constraint
->next
;
3198 MOZ_ASSERT(!types
->constraintList
);
3204 TypeObject::setFlags(ExclusiveContext
* cx
, TypeObjectFlags flags
)
3206 if (hasAllFlags(flags
))
3209 AutoEnterAnalysis
enter(cx
);
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
));
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
);
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));
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
);
3255 prop
->types
.addType(cx
, Type::UnknownType());
3256 prop
->types
.setNonDataProperty(cx
);
3262 TypeObject::maybeClearNewScriptOnOOM()
3264 MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
3272 for (unsigned i
= 0; i
< getPropertyCount(); i
++) {
3273 Property
* prop
= getProperty(i
);
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;
3287 TypeObject::clearNewScript(ExclusiveContext
* cx
)
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
);
3310 if (prop
->types
.definiteProperty())
3311 prop
->types
.setNonDataProperty(cx
);
3314 if (cx
->isJSContext()) {
3315 newScript
->rollbackPartiallyInitializedObjects(cx
->asJSContext(), this);
3317 // Threads with an ExclusiveContext are not allowed to run scripts.
3318 MOZ_ASSERT(!cx
->perThreadData
->activation());
3321 js_delete(newScript
);
3322 markStateChange(cx
);
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");
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();
3352 fprintf(stderr
, " {}\n");
3356 fprintf(stderr
, " {");
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());
3367 fprintf(stderr
, "\n newScript unanalyzed");
3371 for (unsigned i
= 0; i
< count
; i
++) {
3372 Property
* prop
= getProperty(i
);
3374 fprintf(stderr
, "\n %s:", TypeIdString(prop
->id
));
3375 prop
->types
.print();
3379 fprintf(stderr
, "\n}\n");
3382 /////////////////////////////////////////////////////////////////////
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
3395 explicit TypeConstraintClearDefiniteGetterSetter(TypeObject
* 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
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
))
3416 *res
= zone
.typeLifoAlloc
.new_
<TypeConstraintClearDefiniteGetterSetter
>(object
);
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());
3431 TypeObject
* parentObject
= parent
->getType(cx
);
3432 if (!parentObject
|| parentObject
->unknownProperties())
3434 HeapTypeSet
* parentTypes
= parentObject
->getProperty(cx
, id
);
3435 if (!parentTypes
|| parentTypes
->nonDataProperty() || parentTypes
->nonWritableProperty())
3437 if (!parentTypes
->addConstraint(cx
, cx
->typeLifoAlloc().new_
<TypeConstraintClearDefiniteGetterSetter
>(type
)))
3439 parent
= parent
->getProto();
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
3453 explicit TypeConstraintClearDefiniteSingle(TypeObject
* 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
))
3467 *res
= zone
.typeLifoAlloc
.new_
<TypeConstraintClearDefiniteSingle
>(object
);
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
>())
3499 JSFunction
* fun
= &singleton
->as
<JSFunction
>();
3500 if (!fun
->isNative())
3502 if (fun
->native() != js_fun_call
&& fun
->native() != js_fun_apply
)
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
)))
3515 /////////////////////////////////////////////////////////////////////
3516 // Interface functions
3517 /////////////////////////////////////////////////////////////////////
3520 types::TypeMonitorCallSlow(JSContext
* cx
, JSObject
* callee
, const CallArgs
& args
,
3523 unsigned nargs
= callee
->as
<JSFunction
>().nargs();
3524 JSScript
* script
= callee
->as
<JSFunction
>().nonLazyScript();
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.
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());
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
;
3555 types::FillBytecodeTypeMap(JSScript
* script
, uint32_t* bytecodeMap
)
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())
3566 MOZ_ASSERT(added
== script
->nTypeSets());
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
));
3582 RootedTypeObject
type(cx
, TypeScript::InitObject(cx
, script
, pc
, JSProto_Array
));
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
);
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
3607 ArrayObject
* obj
= &script
->getObject(GET_UINT32_INDEX(pc
))->as
<ArrayObject
>();
3608 MOZ_ASSERT(obj
->denseElementsAreCopyOnWrite());
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
))
3620 if (!script
->hasBaselineScript())
3623 AutoEnterAnalysis
enter(cx
);
3625 Type type
= GetValueType(rval
);
3626 StackTypeSet
* types
= TypeScript::BytecodeTypes(script
, pc
);
3627 if (types
->hasType(type
))
3630 InferSpew(ISpewOps
, "bytecodeType: #%u:%05u: %s",
3631 script
->id(), script
->pcToOffset(pc
), TypeString(type
));
3632 types
->addType(cx
, type
);
3636 types::UseNewTypeForClone(JSFunction
* fun
)
3638 if (!fun
->isInterpreted())
3641 if (fun
->hasScript() && fun
->nonLazyScript()->shouldCloneAtCallsite())
3647 if (fun
->hasSingletonType())
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:
3660 * create: function() {
3661 * return function() {
3662 * this.initialize.apply(this, arguments);
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())
3678 begin
= fun
->nonLazyScript()->sourceStart();
3679 end
= fun
->nonLazyScript()->sourceEnd();
3681 if (!fun
->lazyScript()->usesArgumentsApplyAndThis())
3683 begin
= fun
->lazyScript()->begin();
3684 end
= fun
->lazyScript()->end();
3687 return end
- begin
<= 100;
3690 /////////////////////////////////////////////////////////////////////
3692 /////////////////////////////////////////////////////////////////////
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
));
3708 types_
= typeScript
;
3709 setTypesGeneration(cx
->zone()->types
.generation
);
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(),
3718 TypeSet
* thisTypes
= TypeScript::ThisTypes(this);
3719 InferSpew(ISpewOps
, "typeSet: %sT%p%s this #%u",
3720 InferSpewColor(thisTypes
), thisTypes
, InferSpewColorReset(),
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(),
3735 JSFunction::setTypeForScriptedFunction(ExclusiveContext
* cx
, HandleFunction fun
,
3736 bool singleton
/* = false */)
3739 if (!setSingletonType(cx
, fun
))
3742 RootedObject
funProto(cx
, fun
->getProto());
3743 Rooted
<TaggedProto
> taggedProto(cx
, TaggedProto(funProto
));
3745 cx
->compartment()->types
.newTypeObject(cx
, &JSFunction::class_
, taggedProto
);
3750 type
->interpretedFunction
= fun
;
3756 /////////////////////////////////////////////////////////////////////
3758 /////////////////////////////////////////////////////////////////////
3760 // Make a TypeNewScript for |type|, and set it up to hold the initial
3761 // PRELIMINARY_OBJECT_COUNT objects created with the type.
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())
3771 ScopedJSDeletePtr
<TypeNewScript
> newScript(cx
->new_
<TypeNewScript
>());
3775 newScript
->fun
= fun
;
3777 PlainObject
** preliminaryObjects
=
3778 type
->zone()->pod_calloc
<PlainObject
*>(PRELIMINARY_OBJECT_COUNT
);
3779 if (!preliminaryObjects
)
3782 newScript
->preliminaryObjects
= preliminaryObjects
;
3783 type
->setNewScript(newScript
.forget());
3785 gc::TraceTypeNewScript(type
);
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
;
3810 MOZ_CRASH("There should be room for registering the new object");
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;
3825 // The object should be one of the preliminary objects.
3829 // Return whether shape consists entirely of plain data properties.
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() ||
3844 shape
= shape
->previous();
3850 // Find the most recent common ancestor of two shapes, or an empty shape if
3851 // the two shapes have no common ancestor.
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();
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
);
3883 obj
->setLastPropertyShrinkFixedSlots(clone
->lastProperty());
3889 struct DestroyTypeNewScript
3894 DestroyTypeNewScript(JSContext
* cx
, TypeObject
* type
)
3895 : cx(cx
), type(type
)
3898 ~DestroyTypeNewScript() {
3900 type
->clearNewScript(cx
);
3904 } // anonymous namespace
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())
3920 *regenerate
= false;
3923 // The analyses have already been performed.
3928 // Don't perform the analyses until sufficient preliminary objects have
3930 for (size_t i
= 0; i
< PRELIMINARY_OBJECT_COUNT
; i
++) {
3931 if (!preliminaryObjects
[i
])
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
];
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)
3961 maxSlotSpan
= Max
<size_t>(maxSlotSpan
, obj
->slotSpan());
3964 MOZ_ASSERT(shape
->numFixedSlots() == prefixShape
->numFixedSlots());
3965 prefixShape
= CommonPrefix(prefixShape
, shape
);
3967 prefixShape
= shape
;
3969 if (prefixShape
->isEmptyShape()) {
3970 // The preliminary objects don't have any common properties.
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
];
3993 if (!ChangeObjectFixedSlotCount(cx
, obj
, kind
))
3995 if (newPrefixShape
) {
3996 MOZ_ASSERT(CommonPrefix(obj
->lastProperty(), newPrefixShape
) == newPrefixShape
);
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_
)
4011 Vector
<Initializer
> initializerVector(cx
);
4013 RootedPlainObject
templateRoot(cx
, templateObject());
4014 if (!jit::AnalyzeNewScriptDefiniteProperties(cx
, fun
, type
, templateRoot
, &initializerVector
))
4017 if (!type
->newScript())
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())
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())
4039 if (shape
->propid() != templateShape
->propid())
4041 shape
= shape
->previous();
4042 templateShape
= templateShape
->previous();
4044 if (!templateShape
->isEmptyShape())
4048 Initializer
done(Initializer::DONE
, 0);
4050 if (!initializerVector
.append(done
))
4053 initializerList
= type
->zone()->pod_calloc
<Initializer
>(initializerVector
.length());
4054 if (!initializerList
)
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
4066 if (!type
->addDefiniteProperties(cx
, templateObject()->lastProperty()))
4069 destroyNewScript
.type
= nullptr;
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
);
4088 if (!initialType
->addDefiniteProperties(cx
, templateObject()->lastProperty()))
4090 if (!type
->addDefiniteProperties(cx
, prefixShape
))
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;
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
)
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
))
4139 Value thisv
= iter
.thisv(cx
);
4140 if (!thisv
.isObject() ||
4141 thisv
.toObject().hasLazyType() ||
4142 thisv
.toObject().type() != type
)
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.
4172 // This setprop has executed, reset state for the next one.
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.
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.
4188 // Somewhere inside this inner call.
4193 MOZ_ASSERT(init
->kind
== Initializer::DONE
);
4200 (void) NativeObject::rollbackProperties(cx
, obj
, numProperties
);
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");
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
))
4233 /////////////////////////////////////////////////////////////////////
4235 /////////////////////////////////////////////////////////////////////
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)
4248 return hasSingletonType();
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
));
4275 Rooted
<TypeObject
*> protoType(cx
, nullptr);
4276 if (proto
.isObject()) {
4277 protoType
= proto
.toObject()->getType(cx
);
4282 type
->setClasp(clasp
);
4283 type
->setProto(cx
, proto
);
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
))
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
);
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
>();
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
;
4350 JSObject::hasNewType(const Class
* clasp
, TypeObject
* type
)
4352 NewTypeObjectTable
& table
= compartment()->newTypeObjects
;
4354 if (!table
.initialized())
4357 NewTypeObjectTable::Ptr p
=
4358 table
.lookup(NewTypeObjectTable::Lookup(clasp
, TaggedProto(this), nullptr));
4359 return p
&& p
->object
== type
;
4364 JSObject::setNewTypeUnknown(JSContext
* cx
, const Class
* clasp
, HandleObject obj
)
4366 if (!obj
->setFlag(cx
, js::BaseShape::NEW_TYPE_UNKNOWN
))
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));
4380 MarkTypeObjectUnknownProperties(cx
, p
->object
);
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
4391 class NewTypeObjectsSetRef
: public BufferableRef
4393 NewTypeObjectTable
* set
;
4396 JSObject
* associated
;
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");
4410 NewTypeObjectTable::Ptr p
=
4411 set
->lookup(NewTypeObjectTable::Lookup(clasp
, TaggedProto(prior
), TaggedProto(proto
),
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
);
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())
4429 if (!cx
->isJSContext()) {
4430 MOZ_ASSERT(!IsInsideNursery(proto
.toObject()));
4434 if (IsInsideNursery(proto
.toObject())) {
4435 StoreBuffer
& sb
= cx
->asJSContext()->runtime()->gc
.storeBuffer
;
4436 sb
.putGeneric(NewTypeObjectsSetRef(table
, clasp
, proto
.toObject(), associated
));
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())
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();
4460 associated
= nullptr;
4463 NewTypeObjectTable::AddPtr p
=
4464 newTypeObjects
.lookupForAdd(NewTypeObjectTable::Lookup(clasp
, proto
, associated
));
4466 TypeObject
* type
= p
->object
;
4467 MOZ_ASSERT(type
->clasp() == clasp
);
4468 MOZ_ASSERT(type
->proto() == proto
);
4472 AutoEnterAnalysis
enter(this);
4474 if (proto
.isObject() && !proto
.toObject()->setDelegate(this))
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
);
4493 if (!newTypeObjects
.add(p
, NewTypeObjectEntry(type
, associated
)))
4496 TypeObjectTablePostBarrier(this, &newTypeObjects
, clasp
, proto
, associated
);
4498 if (proto
.isObject()) {
4499 RootedObject
obj(this, proto
.toObject());
4502 if (associated
->is
<JSFunction
>())
4503 TypeNewScript::make(asJSContext(), type
, &associated
->as
<JSFunction
>());
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());
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())
4550 NewTypeObjectTable::AddPtr p
= table
.lookupForAdd(NewTypeObjectTable::Lookup(clasp
, proto
, nullptr));
4552 TypeObject
* type
= p
->object
;
4553 MOZ_ASSERT(type
->lazy());
4558 Rooted
<TaggedProto
> protoRoot(this, proto
);
4559 TypeObject
* type
= compartment()->types
.newTypeObject(this, clasp
, protoRoot
);
4563 if (!table
.add(p
, NewTypeObjectEntry(type
, 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");
4574 /////////////////////////////////////////////////////////////////////
4576 /////////////////////////////////////////////////////////////////////
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
4593 unsigned objectCount
= baseObjectCount();
4594 if (objectCount
>= 2) {
4595 unsigned oldCapacity
= HashSetCapacity(objectCount
);
4596 TypeObjectKey
** oldArray
= objectSet
;
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
);
4610 flags
|= TYPE_FLAG_ANYOBJECT
;
4617 setBaseObjectCount(objectCount
);
4618 } else if (objectCount
== 1) {
4619 TypeObjectKey
* object
= (TypeObjectKey
*) objectSet
;
4620 if (IsAboutToBeFinalized(&object
)) {
4621 objectSet
= nullptr;
4622 setBaseObjectCount(0);
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
, ©
)) {
4638 copy
->next
= constraintList
;
4639 constraintList
= copy
;
4644 constraint
= constraint
->next
;
4649 TypeObject::clearProperties()
4651 setBasePropertyCount(0);
4652 propertySet
= nullptr;
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
;
4666 EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM
*& oom
, Zone
* zone
,
4667 Maybe
<AutoClearTypeInferenceStateOnOOM
>& fallback
)
4670 if (zone
->types
.activeAnalysis
) {
4671 oom
= &zone
->types
.activeAnalysis
->oom
;
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.
4687 TypeObject::maybeSweep(AutoClearTypeInferenceStateOnOOM
* oom
)
4689 if (generation() == zoneFromAnyThread()->types
.generation
) {
4690 // No sweeping required.
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
);
4703 newScript()->sweep();
4705 LifoAlloc
& typeLifoAlloc
= zone()->types
.typeLifoAlloc
;
4708 * Properties were allocated from the old arena, and need to be copied over
4711 unsigned propertyCount
= basePropertyCount();
4712 if (propertyCount
>= 2) {
4713 unsigned oldCapacity
= HashSetCapacity(propertyCount
);
4714 Property
** oldArray
= propertySet
;
4718 for (unsigned i
= 0; i
< oldCapacity
; i
++) {
4719 Property
* prop
= oldArray
[i
];
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.
4731 Property
* newProp
= typeLifoAlloc
.new_
<Property
>(*prop
);
4734 HashSetInsert
<jsid
,Property
,Property
>
4735 (typeLifoAlloc
, propertySet
, propertyCount
, prop
->id
);
4738 newProp
->types
.sweep(zone(), *oom
);
4744 addFlags(OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_UNKNOWN_PROPERTIES
);
4749 setBasePropertyCount(propertyCount
);
4750 } else if (propertyCount
== 1) {
4751 Property
* prop
= (Property
*) propertySet
;
4752 if (singleton() && !prop
->types
.constraintList
&& !zone()->isPreservingCode()) {
4756 Property
* newProp
= typeLifoAlloc
.new_
<Property
>(*prop
);
4758 propertySet
= (Property
**) newProp
;
4759 newProp
->types
.sweep(zone(), *oom
);
4762 addFlags(OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_UNKNOWN_PROPERTIES
);
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();
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
))
4800 key
.type
= Type::ObjectType(typeObject
);
4802 if (key
.proto
&& key
.proto
!= TaggedProto::LazyProto
&&
4803 IsObjectAboutToBeFinalized(&key
.proto
))
4807 if (IsTypeObjectAboutToBeFinalized(e
.front().value().unsafeGet()))
4812 else if (key
!= e
.front().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()))
4825 if (IsShapeAboutToBeFinalized(entry
.shape
.unsafeGet()))
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
))
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
))
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
))
4845 else if (typeObject
!= entry
.types
[i
].typeObjectNoBarrier())
4846 entry
.types
[i
] = Type::ObjectType(typeObject
);
4851 js_free(key
.properties
);
4852 js_free(entry
.types
);
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
)
4865 else if (key
.script
!= e
.front().key().script
)
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
)))
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
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()));
4909 TaggedProto proto
= entry
.object
->proto();
4910 if (proto
.isObject() && IsForwarded(proto
.toObject())) {
4911 proto
= TaggedProto(Forwarded(proto
.toObject()));
4914 if (entry
.associated
&& IsForwarded(entry
.associated
)) {
4915 entry
.associated
= Forwarded(entry
.associated
);
4919 NewTypeObjectTable::Lookup
lookup(entry
.object
->clasp(),
4922 e
.rekeyFront(lookup
, entry
);
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());
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());
4948 switch (addendumKind()) {
4949 case Addendum_NewScript
:
4950 newScript()->fixupAfterMovingGC();
4952 case Addendum_TypeDescr
:
4953 if (IsForwarded(&typeDescr()))
4954 addendum_
= Forwarded(&typeDescr());
4960 if (interpretedFunction
&& IsForwarded(interpretedFunction
.get()))
4961 interpretedFunction
= Forwarded(interpretedFunction
.get());
4964 #endif // JSGC_COMPACTING
4966 #ifdef JSGC_HASH_TABLE_CHECKS
4969 JSCompartment::checkTypeObjectTablesAfterMovingGC()
4971 checkTypeObjectTableAfterMovingGC(newTypeObjects
);
4972 checkTypeObjectTableAfterMovingGC(lazyTypeObjects
);
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())
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
);
5010 JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM
* oom
)
5012 if (!types_
|| typesGeneration() == zone()->types
.generation
)
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() &&
5031 !hasParallelIonScript())
5036 // Freeze constraints on stack type sets need to be regenerated the
5037 // next time the script is analyzed.
5038 hasFreezeConstraints_
= false;
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.
5052 ionScript()->recompileInfoRef().shouldSweep(types
);
5053 if (hasParallelIonScript())
5054 parallelIonScript()->recompileInfoRef().shouldSweep(types
);
5058 TypeScript::destroy()
5064 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
,
5066 size_t* baselineStubsOptimized
)
5068 *typePool
+= types
.typeLifoAlloc
.sizeOfExcludingThis(mallocSizeOf
);
5070 *baselineStubsOptimized
+=
5071 jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf
);
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
);
5085 *arrayTypeTables
+= arrayTypeTable
->sizeOfIncludingThis(mallocSizeOf
);
5087 if (objectTypeTable
) {
5088 *objectTypeTables
+= objectTypeTable
->sizeOfIncludingThis(mallocSizeOf
);
5090 for (ObjectTypeTable::Enum
e(*objectTypeTable
);
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
);
5104 TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
5106 return mallocSizeOf(newScriptDontCheckGeneration());
5109 TypeZone::TypeZone(Zone
* zone
)
5111 typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE
),
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
);
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();
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);
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.
5176 for (CompartmentsInZoneIter
comp(zone()); !comp
.done(); comp
.next())
5177 comp
->types
.sweep(fop
);
5181 TypeZone::endSweep(JSRuntime
* rt
)
5183 js_delete(sweepCompilerOutputs
);
5184 sweepCompilerOutputs
= nullptr;
5186 sweepReleaseTypes
= false;
5188 rt
->gc
.freeAllLifoBlocksAfterSweeping(&sweepTypeLifoAlloc
);
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()
5206 zone
->setPreservingCode(false);
5207 zone
->discardJitCode(zone
->runtimeFromMainThread()->defaultFreeOp());
5208 zone
->types
.clearAllNewScriptsOnOOM();
5214 TypeScript::printTypes(JSContext
* cx
, HandleScript script
) const
5216 MOZ_ASSERT(script
->types() == this);
5218 if (!script
->hasBaselineScript())
5221 AutoEnterAnalysis
enter(nullptr, script
->zone());
5223 if (script
->functionNonDelazifying())
5224 fprintf(stderr
, "Function");
5225 else if (script
->isForEval())
5226 fprintf(stderr
, "Eval");
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();
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())
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()));
5262 fprintf(stderr
, "\n");
5266 fprintf(stderr
, "\n");
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.
5280 MOZ_ASSERT(kind
== Addendum_NewScript
);
5281 TypeNewScript::writeBarrierPre(newScript());
5284 flags_
|= kind
<< OBJECT_FLAG_ADDENDUM_SHIFT
;
5285 addendum_
= addendum
;