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"
41 using namespace js::gc
;
42 using namespace js::types
;
44 using mozilla::DebugOnly
;
46 using mozilla::PodArrayZero
;
47 using mozilla::PodCopy
;
48 using mozilla::PodZero
;
51 id_prototype(JSContext
* cx
) {
52 return NameToId(cx
->names().prototype
);
56 id___proto__(JSContext
* cx
) {
57 return NameToId(cx
->names().proto
);
61 id_constructor(JSContext
* cx
) {
62 return NameToId(cx
->names().constructor
);
66 id_caller(JSContext
* cx
) {
67 return NameToId(cx
->names().caller
);
72 types::TypeIdStringImpl(jsid id
)
76 if (JSID_IS_EMPTY(id
))
78 if (JSID_IS_SYMBOL(id
))
80 static char bufs
[4][100];
81 static unsigned which
= 0;
82 which
= (which
+ 1) & 3;
83 PutEscapedString(bufs
[which
], 100, JSID_TO_FLAT_STRING(id
), 0);
88 /////////////////////////////////////////////////////////////////////
90 /////////////////////////////////////////////////////////////////////
94 static bool InferSpewActive(SpewChannel channel
)
96 static bool active
[SPEW_COUNT
];
97 static bool checked
= false;
100 PodArrayZero(active
);
101 const char* env
= getenv("INFERFLAGS");
104 if (strstr(env
, "ops"))
105 active
[ISpewOps
] = true;
106 if (strstr(env
, "result"))
107 active
[ISpewResult
] = true;
108 if (strstr(env
, "full")) {
109 for (unsigned i
= 0; i
< SPEW_COUNT
; i
++)
113 return active
[channel
];
116 static bool InferSpewColorable()
118 /* Only spew colors on xterm-color to not screw up emacs. */
119 static bool colorable
= false;
120 static bool checked
= false;
123 const char* env
= getenv("TERM");
126 if (strcmp(env
, "xterm-color") == 0 || strcmp(env
, "xterm-256color") == 0)
133 types::InferSpewColorReset()
135 if (!InferSpewColorable())
141 types::InferSpewColor(TypeConstraint
* constraint
)
143 /* Type constraints are printed out using foreground colors. */
144 static const char * const colors
[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m",
145 "\x1b[34m", "\x1b[35m", "\x1b[36m",
147 if (!InferSpewColorable())
149 return colors
[DefaultHasher
<TypeConstraint
*>::hash(constraint
) % 7];
153 types::InferSpewColor(TypeSet
* types
)
155 /* Type sets are printed out using bold colors. */
156 static const char * const colors
[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
157 "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
159 if (!InferSpewColorable())
161 return colors
[DefaultHasher
<TypeSet
*>::hash(types
) % 7];
165 types::TypeString(Type type
)
167 if (type
.isPrimitive()) {
168 switch (type
.primitive()) {
169 case JSVAL_TYPE_UNDEFINED
:
171 case JSVAL_TYPE_NULL
:
173 case JSVAL_TYPE_BOOLEAN
:
175 case JSVAL_TYPE_INT32
:
177 case JSVAL_TYPE_DOUBLE
:
179 case JSVAL_TYPE_STRING
:
181 case JSVAL_TYPE_SYMBOL
:
183 case JSVAL_TYPE_MAGIC
:
186 MOZ_ASSUME_UNREACHABLE("Bad type");
189 if (type
.isUnknown())
191 if (type
.isAnyObject())
194 static char bufs
[4][40];
195 static unsigned which
= 0;
196 which
= (which
+ 1) & 3;
198 if (type
.isSingleObject())
199 JS_snprintf(bufs
[which
], 40, "<0x%p>", (void*) type
.singleObject());
201 JS_snprintf(bufs
[which
], 40, "[0x%p]", (void*) type
.typeObject());
207 types::TypeObjectString(TypeObject
* type
)
209 return TypeString(Type::ObjectType(type
));
212 unsigned JSScript::id() {
214 id_
= ++compartment()->types
.scriptCount
;
215 InferSpew(ISpewOps
, "script #%u: %p %s:%d",
216 id_
, this, filename() ? filename() : "<null>", lineno());
222 types::InferSpew(SpewChannel channel
, const char* fmt
, ...)
224 if (!InferSpewActive(channel
))
229 fprintf(stderr
, "[infer] ");
230 vfprintf(stderr
, fmt
, ap
);
231 fprintf(stderr
, "\n");
236 types::TypeHasProperty(JSContext
* cx
, TypeObject
* obj
, jsid id
, const Value
& value
)
239 * Check the correctness of the type information in the object's property
240 * against an actual value.
242 if (!obj
->unknownProperties() && !value
.isUndefined()) {
245 /* Watch for properties which inference does not monitor. */
246 if (id
== id___proto__(cx
) || id
== id_constructor(cx
) || id
== id_caller(cx
))
249 Type type
= GetValueType(value
);
251 AutoEnterAnalysis
enter(cx
);
254 * We don't track types for properties inherited from prototypes which
255 * haven't yet been accessed during analysis of the inheriting object.
256 * Don't do the property instantiation now.
258 TypeSet
* types
= obj
->maybeGetProperty(id
);
262 if (!types
->hasType(type
)) {
263 TypeFailure(cx
, "Missing type in object %s %s: %s",
264 TypeObjectString(obj
), TypeIdString(id
), TypeString(type
));
273 types::TypeFailure(JSContext
* cx
, const char* fmt
, ...)
275 char msgbuf
[1024]; /* Larger error messages will be truncated */
280 JS_vsnprintf(errbuf
, sizeof(errbuf
), fmt
, ap
);
283 JS_snprintf(msgbuf
, sizeof(msgbuf
), "[infer failure] %s", errbuf
);
285 /* Dump type state, even if INFERFLAGS is unset. */
286 cx
->compartment()->types
.print(cx
, true);
288 MOZ_ReportAssertionFailure(msgbuf
, __FILE__
, __LINE__
);
292 /////////////////////////////////////////////////////////////////////
294 /////////////////////////////////////////////////////////////////////
296 TemporaryTypeSet::TemporaryTypeSet(Type type
)
298 if (type
.isUnknown()) {
299 flags
|= TYPE_FLAG_BASE_MASK
;
300 } else if (type
.isPrimitive()) {
301 flags
= PrimitiveTypeFlag(type
.primitive());
302 if (flags
== TYPE_FLAG_DOUBLE
)
303 flags
|= TYPE_FLAG_INT32
;
304 } else if (type
.isAnyObject()) {
305 flags
|= TYPE_FLAG_ANYOBJECT
;
306 } else if (type
.isTypeObject() && type
.typeObject()->unknownProperties()) {
307 flags
|= TYPE_FLAG_ANYOBJECT
;
309 setBaseObjectCount(1);
310 objectSet
= reinterpret_cast<TypeObjectKey
**>(type
.objectKey());
315 TypeSet::mightBeMIRType(jit::MIRType type
)
320 if (type
== jit::MIRType_Object
)
321 return unknownObject() || baseObjectCount() != 0;
324 case jit::MIRType_Undefined
:
325 return baseFlags() & TYPE_FLAG_UNDEFINED
;
326 case jit::MIRType_Null
:
327 return baseFlags() & TYPE_FLAG_NULL
;
328 case jit::MIRType_Boolean
:
329 return baseFlags() & TYPE_FLAG_BOOLEAN
;
330 case jit::MIRType_Int32
:
331 return baseFlags() & TYPE_FLAG_INT32
;
332 case jit::MIRType_Float32
: // Fall through, there's no JSVAL for Float32.
333 case jit::MIRType_Double
:
334 return baseFlags() & TYPE_FLAG_DOUBLE
;
335 case jit::MIRType_String
:
336 return baseFlags() & TYPE_FLAG_STRING
;
337 case jit::MIRType_Symbol
:
338 return baseFlags() & TYPE_FLAG_SYMBOL
;
339 case jit::MIRType_MagicOptimizedArguments
:
340 return baseFlags() & TYPE_FLAG_LAZYARGS
;
341 case jit::MIRType_MagicHole
:
342 case jit::MIRType_MagicIsConstructing
:
343 // These magic constants do not escape to script and are not observed
346 // The reason we can return false here is subtle: if Ion is asking the
347 // type set if it has seen such a magic constant, then the MIR in
348 // question is the most generic type, MIRType_Value. A magic constant
349 // could only be emitted by a MIR of MIRType_Value if that MIR is a
350 // phi, and we check that different magic constants do not flow to the
351 // same join point in GuessPhiType.
354 MOZ_ASSUME_UNREACHABLE("Bad MIR type");
359 TypeSet::objectsAreSubset(TypeSet
* other
)
361 if (other
->unknownObject())
367 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
368 TypeObjectKey
* obj
= getObject(i
);
371 if (!other
->hasType(Type::ObjectType(obj
)))
379 TypeSet::isSubset(TypeSet
* other
)
381 if ((baseFlags() & other
->baseFlags()) != baseFlags())
384 if (unknownObject()) {
385 JS_ASSERT(other
->unknownObject());
387 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
388 TypeObjectKey
* obj
= getObject(i
);
391 if (!other
->hasType(Type::ObjectType(obj
)))
400 TypeSet::enumerateTypes(TypeList
* list
)
402 /* If any type is possible, there's no need to worry about specifics. */
403 if (flags
& TYPE_FLAG_UNKNOWN
)
404 return list
->append(Type::UnknownType());
406 /* Enqueue type set members stored as bits. */
407 for (TypeFlags flag
= 1; flag
< TYPE_FLAG_ANYOBJECT
; flag
<<= 1) {
409 Type type
= Type::PrimitiveType(TypeFlagPrimitive(flag
));
410 if (!list
->append(type
))
415 /* If any object is possible, skip specifics. */
416 if (flags
& TYPE_FLAG_ANYOBJECT
)
417 return list
->append(Type::AnyObjectType());
419 /* Enqueue specific object types. */
420 unsigned count
= getObjectCount();
421 for (unsigned i
= 0; i
< count
; i
++) {
422 TypeObjectKey
* object
= getObject(i
);
424 if (!list
->append(Type::ObjectType(object
)))
433 TypeSet::addTypesToConstraint(JSContext
* cx
, TypeConstraint
* constraint
)
436 * Build all types in the set into a vector before triggering the
437 * constraint, as doing so may modify this type set.
440 if (!enumerateTypes(&types
))
443 for (unsigned i
= 0; i
< types
.length(); i
++)
444 constraint
->newType(cx
, this, types
[i
]);
450 ConstraintTypeSet::addConstraint(JSContext
* cx
, TypeConstraint
* constraint
, bool callExisting
)
453 /* OOM failure while constructing the constraint. */
457 JS_ASSERT(cx
->compartment()->activeAnalysis
);
459 InferSpew(ISpewOps
, "addConstraint: %sT%p%s %sC%p%s %s",
460 InferSpewColor(this), this, InferSpewColorReset(),
461 InferSpewColor(constraint
), constraint
, InferSpewColorReset(),
464 JS_ASSERT(constraint
->next
== nullptr);
465 constraint
->next
= constraintList
;
466 constraintList
= constraint
;
469 return addTypesToConstraint(cx
, constraint
);
474 TypeSet::clearObjects()
476 setBaseObjectCount(0);
481 TypeSet::addType(Type type
, LifoAlloc
* alloc
)
486 if (type
.isUnknown()) {
487 flags
|= TYPE_FLAG_BASE_MASK
;
489 JS_ASSERT(unknown());
493 if (type
.isPrimitive()) {
494 TypeFlags flag
= PrimitiveTypeFlag(type
.primitive());
498 /* If we add float to a type set it is also considered to contain int. */
499 if (flag
== TYPE_FLAG_DOUBLE
)
500 flag
|= TYPE_FLAG_INT32
;
506 if (flags
& TYPE_FLAG_ANYOBJECT
)
508 if (type
.isAnyObject())
512 uint32_t objectCount
= baseObjectCount();
513 TypeObjectKey
* object
= type
.objectKey();
514 TypeObjectKey
** pentry
= HashSetInsert
<TypeObjectKey
*,TypeObjectKey
,TypeObjectKey
>
515 (*alloc
, objectSet
, objectCount
, object
);
522 setBaseObjectCount(objectCount
);
524 if (objectCount
== TYPE_FLAG_OBJECT_COUNT_LIMIT
)
528 if (type
.isTypeObject()) {
529 TypeObject
* nobject
= type
.typeObject();
530 JS_ASSERT(!nobject
->singleton());
531 if (nobject
->unknownProperties())
537 flags
|= TYPE_FLAG_ANYOBJECT
;
543 ConstraintTypeSet::addType(ExclusiveContext
* cxArg
, Type type
)
545 JS_ASSERT(cxArg
->compartment()->activeAnalysis
);
550 TypeSet::addType(type
, &cxArg
->typeLifoAlloc());
552 if (type
.isObjectUnchecked() && unknownObject())
553 type
= Type::AnyObjectType();
555 InferSpew(ISpewOps
, "addType: %sT%p%s %s",
556 InferSpewColor(this), this, InferSpewColorReset(),
559 /* Propagate the type to all constraints. */
560 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
561 TypeConstraint
* constraint
= constraintList
;
563 constraint
->newType(cx
, this, type
);
564 constraint
= constraint
->next
;
567 JS_ASSERT(!constraintList
);
574 if (flags
& TYPE_FLAG_NON_DATA_PROPERTY
)
575 fprintf(stderr
, " [non-data]");
577 if (flags
& TYPE_FLAG_NON_WRITABLE_PROPERTY
)
578 fprintf(stderr
, " [non-writable]");
580 if (definiteProperty())
581 fprintf(stderr
, " [definite:%d]", definiteSlot());
583 if (baseFlags() == 0 && !baseObjectCount()) {
584 fprintf(stderr
, " missing");
588 if (flags
& TYPE_FLAG_UNKNOWN
)
589 fprintf(stderr
, " unknown");
590 if (flags
& TYPE_FLAG_ANYOBJECT
)
591 fprintf(stderr
, " object");
593 if (flags
& TYPE_FLAG_UNDEFINED
)
594 fprintf(stderr
, " void");
595 if (flags
& TYPE_FLAG_NULL
)
596 fprintf(stderr
, " null");
597 if (flags
& TYPE_FLAG_BOOLEAN
)
598 fprintf(stderr
, " bool");
599 if (flags
& TYPE_FLAG_INT32
)
600 fprintf(stderr
, " int");
601 if (flags
& TYPE_FLAG_DOUBLE
)
602 fprintf(stderr
, " float");
603 if (flags
& TYPE_FLAG_STRING
)
604 fprintf(stderr
, " string");
605 if (flags
& TYPE_FLAG_LAZYARGS
)
606 fprintf(stderr
, " lazyargs");
608 uint32_t objectCount
= baseObjectCount();
610 fprintf(stderr
, " object[%u]", objectCount
);
612 unsigned count
= getObjectCount();
613 for (unsigned i
= 0; i
< count
; i
++) {
614 TypeObjectKey
* object
= getObject(i
);
616 fprintf(stderr
, " %s", TypeString(Type::ObjectType(object
)));
622 TypeSet::readBarrier(const TypeSet
* types
)
624 if (types
->unknownObject())
627 for (unsigned i
= 0; i
< types
->getObjectCount(); i
++) {
628 if (TypeObjectKey
* object
= types
->getObject(i
)) {
629 if (object
->isSingleObject())
630 (void) object
->asSingleObject();
632 (void) object
->asTypeObject();
638 TypeSet::clone(LifoAlloc
* alloc
, TemporaryTypeSet
* result
) const
640 JS_ASSERT(result
->empty());
642 unsigned objectCount
= baseObjectCount();
643 unsigned capacity
= (objectCount
>= 2) ? HashSetCapacity(objectCount
) : 0;
645 TypeObjectKey
** newSet
;
647 newSet
= alloc
->newArray
<TypeObjectKey
*>(capacity
);
650 PodCopy(newSet
, objectSet
, capacity
);
653 new(result
) TemporaryTypeSet(flags
, capacity
? newSet
: objectSet
);
658 TypeSet::clone(LifoAlloc
* alloc
) const
660 TemporaryTypeSet
* res
= alloc
->new_
<TemporaryTypeSet
>();
661 if (!res
|| !clone(alloc
, res
))
667 TypeSet::filter(LifoAlloc
* alloc
, bool filterUndefined
, bool filterNull
) const
669 TemporaryTypeSet
* res
= clone(alloc
);
674 res
->flags
= res
->flags
& ~TYPE_FLAG_UNDEFINED
;
677 res
->flags
= res
->flags
& ~TYPE_FLAG_NULL
;
683 TypeSet::cloneObjectsOnly(LifoAlloc
* alloc
)
685 TemporaryTypeSet
* res
= clone(alloc
);
689 res
->flags
&= TYPE_FLAG_ANYOBJECT
;
694 /* static */ TemporaryTypeSet
*
695 TypeSet::unionSets(TypeSet
* a
, TypeSet
* b
, LifoAlloc
* alloc
)
697 TemporaryTypeSet
* res
= alloc
->new_
<TemporaryTypeSet
>(a
->baseFlags() | b
->baseFlags(),
698 static_cast<TypeObjectKey
**>(nullptr));
702 if (!res
->unknownObject()) {
703 for (size_t i
= 0; i
< a
->getObjectCount() && !res
->unknownObject(); i
++) {
704 if (TypeObjectKey
* key
= a
->getObject(i
))
705 res
->addType(Type::ObjectType(key
), alloc
);
707 for (size_t i
= 0; i
< b
->getObjectCount() && !res
->unknownObject(); i
++) {
708 if (TypeObjectKey
* key
= b
->getObject(i
))
709 res
->addType(Type::ObjectType(key
), alloc
);
716 /////////////////////////////////////////////////////////////////////
717 // Compiler constraints
718 /////////////////////////////////////////////////////////////////////
720 // Compiler constraints overview
722 // Constraints generated during Ion compilation capture assumptions made about
723 // heap properties that will trigger invalidation of the resulting Ion code if
724 // the constraint is violated. Constraints can only be attached to type sets on
725 // the main thread, so to allow compilation to occur almost entirely off thread
726 // the generation is split into two phases.
728 // During compilation, CompilerConstraint values are constructed in a list,
729 // recording the heap property type set which was read from and its expected
730 // contents, along with the assumption made about those contents.
732 // At the end of compilation, when linking the result on the main thread, the
733 // list of compiler constraints are read and converted to type constraints and
734 // attached to the type sets. If the property type sets have changed so that the
735 // assumptions no longer hold then the compilation is aborted and its result
738 // Superclass of all constraints generated during Ion compilation. These may
739 // be allocated off the main thread, using the current Ion context's allocator.
740 class CompilerConstraint
743 // Property being queried by the compiler.
744 HeapTypeSetKey property
;
746 // Contents of the property at the point when the query was performed. This
747 // may differ from the actual property types later in compilation as the
748 // main thread performs side effects.
749 TemporaryTypeSet
* expected
;
751 CompilerConstraint(LifoAlloc
* alloc
, const HeapTypeSetKey
& property
)
752 : property(property
),
753 expected(property
.maybeTypes() ? property
.maybeTypes()->clone(alloc
) : nullptr)
756 // Generate the type constraint recording the assumption made by this
757 // compilation. Returns true if the assumption originally made still holds.
758 virtual bool generateTypeConstraint(JSContext
* cx
, RecompileInfo recompileInfo
) = 0;
761 class types::CompilerConstraintList
767 TemporaryTypeSet
* thisTypes
;
768 TemporaryTypeSet
* argTypes
;
769 TemporaryTypeSet
* bytecodeTypes
;
774 // OOM during generation of some constraint.
777 // Allocator used for constraints.
780 // Constraints generated on heap properties.
781 Vector
<CompilerConstraint
*, 0, jit::IonAllocPolicy
> constraints
;
783 // Scripts whose stack type sets were frozen for the compilation.
784 Vector
<FrozenScript
, 1, jit::IonAllocPolicy
> frozenScripts
;
787 explicit CompilerConstraintList(jit::TempAllocator
& alloc
)
789 alloc_(alloc
.lifoAlloc()),
794 void add(CompilerConstraint
* constraint
) {
795 if (!constraint
|| !constraints
.append(constraint
))
799 void freezeScript(JSScript
* script
,
800 TemporaryTypeSet
* thisTypes
,
801 TemporaryTypeSet
* argTypes
,
802 TemporaryTypeSet
* bytecodeTypes
)
805 entry
.script
= script
;
806 entry
.thisTypes
= thisTypes
;
807 entry
.argTypes
= argTypes
;
808 entry
.bytecodeTypes
= bytecodeTypes
;
809 if (!frozenScripts
.append(entry
))
814 return constraints
.length();
817 CompilerConstraint
* get(size_t i
) {
818 return constraints
[i
];
821 size_t numFrozenScripts() {
822 return frozenScripts
.length();
825 const FrozenScript
& frozenScript(size_t i
) {
826 return frozenScripts
[i
];
835 LifoAlloc
* alloc() const {
840 CompilerConstraintList
*
841 types::NewCompilerConstraintList(jit::TempAllocator
& alloc
)
843 return alloc
.lifoAlloc()->new_
<CompilerConstraintList
>(alloc
);
847 TypeScript::FreezeTypeSets(CompilerConstraintList
* constraints
, JSScript
* script
,
848 TemporaryTypeSet
** pThisTypes
,
849 TemporaryTypeSet
** pArgTypes
,
850 TemporaryTypeSet
** pBytecodeTypes
)
852 LifoAlloc
* alloc
= constraints
->alloc();
853 StackTypeSet
* existing
= script
->types
->typeArray();
855 size_t count
= NumTypeSets(script
);
856 TemporaryTypeSet
* types
= alloc
->newArrayUninitialized
<TemporaryTypeSet
>(count
);
859 PodZero(types
, count
);
861 for (size_t i
= 0; i
< count
; i
++) {
862 if (!existing
[i
].clone(alloc
, &types
[i
]))
866 *pThisTypes
= types
+ (ThisTypes(script
) - existing
);
867 *pArgTypes
= (script
->functionNonDelazifying() && script
->functionNonDelazifying()->nargs())
868 ? (types
+ (ArgTypes(script
, 0) - existing
))
870 *pBytecodeTypes
= types
;
872 constraints
->freezeScript(script
, *pThisTypes
, *pArgTypes
, *pBytecodeTypes
);
878 template <typename T
>
879 class CompilerConstraintInstance
: public CompilerConstraint
884 CompilerConstraintInstance
<T
>(LifoAlloc
* alloc
, const HeapTypeSetKey
& property
, const T
& data
)
885 : CompilerConstraint(alloc
, property
), data(data
)
888 bool generateTypeConstraint(JSContext
* cx
, RecompileInfo recompileInfo
);
891 // Constraint generated from a CompilerConstraint when linking the compilation.
892 template <typename T
>
893 class TypeCompilerConstraint
: public TypeConstraint
895 // Compilation which this constraint may invalidate.
896 RecompileInfo compilation
;
901 TypeCompilerConstraint
<T
>(RecompileInfo compilation
, const T
& data
)
902 : compilation(compilation
), data(data
)
905 const char* kind() { return data
.kind(); }
907 void newType(JSContext
* cx
, TypeSet
* source
, Type type
) {
908 if (data
.invalidateOnNewType(type
))
909 cx
->zone()->types
.addPendingRecompile(cx
, compilation
);
912 void newPropertyState(JSContext
* cx
, TypeSet
* source
) {
913 if (data
.invalidateOnNewPropertyState(source
))
914 cx
->zone()->types
.addPendingRecompile(cx
, compilation
);
917 void newObjectState(JSContext
* cx
, TypeObject
* object
) {
918 // Note: Once the object has unknown properties, no more notifications
919 // will be sent on changes to its state, so always invalidate any
920 // associated compilations.
921 if (object
->unknownProperties() || data
.invalidateOnNewObjectState(object
))
922 cx
->zone()->types
.addPendingRecompile(cx
, compilation
);
925 bool sweep(TypeZone
& zone
, TypeConstraint
** res
) {
926 if (data
.shouldSweep() || compilation
.shouldSweep(zone
))
928 *res
= zone
.typeLifoAlloc
.new_
<TypeCompilerConstraint
<T
> >(compilation
, data
);
933 template <typename T
>
935 CompilerConstraintInstance
<T
>::generateTypeConstraint(JSContext
* cx
, RecompileInfo recompileInfo
)
937 if (property
.object()->unknownProperties())
940 if (!property
.instantiate(cx
))
943 if (!data
.constraintHolds(cx
, property
, expected
))
946 return property
.maybeTypes()->addConstraint(cx
, cx
->typeLifoAlloc().new_
<TypeCompilerConstraint
<T
> >(recompileInfo
, data
),
947 /* callExisting = */ false);
950 } /* anonymous namespace */
953 TypeObjectKey::clasp()
955 return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass();
959 TypeObjectKey::proto()
961 JS_ASSERT(hasTenuredProto());
962 return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
966 ObjectImpl::hasTenuredProto() const
968 return type_
->hasTenuredProto();
972 TypeObjectKey::hasTenuredProto()
974 return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
978 TypeObjectKey::singleton()
980 return isTypeObject() ? asTypeObject()->singleton() : asSingleObject();
984 TypeObjectKey::newScript()
986 if (isTypeObject() && asTypeObject()->newScript())
987 return asTypeObject()->newScript();
992 TypeObjectKey::maybeType()
995 return asTypeObject();
996 if (asSingleObject()->hasLazyType())
998 return asSingleObject()->type();
1002 TypeObjectKey::unknownProperties()
1004 if (TypeObject
* type
= maybeType())
1005 return type
->unknownProperties();
1010 TypeObjectKey::property(jsid id
)
1012 JS_ASSERT(!unknownProperties());
1014 HeapTypeSetKey property
;
1015 property
.object_
= this;
1017 if (TypeObject
* type
= maybeType())
1018 property
.maybeTypes_
= type
->maybeGetProperty(id
);
1024 TypeObjectKey::ensureTrackedProperty(JSContext
* cx
, jsid id
)
1026 // If we are accessing a lazily defined property which actually exists in
1027 // the VM and has not been instantiated yet, instantiate it now if we are
1028 // on the main thread and able to do so.
1029 if (!JSID_IS_VOID(id
) && !JSID_IS_EMPTY(id
)) {
1030 JS_ASSERT(CurrentThreadCanAccessRuntime(cx
->runtime()));
1031 if (JSObject
* obj
= singleton()) {
1032 if (obj
->isNative() && obj
->nativeLookupPure(id
))
1033 EnsureTrackPropertyTypes(cx
, obj
, id
);
1039 HeapTypeSetKey::instantiate(JSContext
* cx
)
1043 if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx
)) {
1044 cx
->clearPendingException();
1047 maybeTypes_
= object()->maybeType()->getProperty(cx
, id());
1048 return maybeTypes_
!= nullptr;
1052 CheckFrozenTypeSet(JSContext
* cx
, TemporaryTypeSet
* frozen
, StackTypeSet
* actual
)
1054 // Return whether the types frozen for a script during compilation are
1055 // still valid. Also check for any new types added to the frozen set during
1056 // compilation, and add them to the actual stack type sets. These new types
1057 // indicate places where the compiler relaxed its possible inputs to be
1058 // more tolerant of potential new types.
1060 if (!actual
->isSubset(frozen
))
1063 if (!frozen
->isSubset(actual
)) {
1064 TypeSet::TypeList list
;
1065 frozen
->enumerateTypes(&list
);
1067 for (size_t i
= 0; i
< list
.length(); i
++)
1068 actual
->addType(cx
, list
[i
]);
1077 * As for TypeConstraintFreeze, but describes an implicit freeze constraint
1078 * added for stack types within a script. Applies to all compilations of the
1079 * script, not just a single one.
1081 class TypeConstraintFreezeStack
: public TypeConstraint
1086 explicit TypeConstraintFreezeStack(JSScript
* script
)
1090 const char* kind() { return "freezeStack"; }
1092 void newType(JSContext
* cx
, TypeSet
* source
, Type type
) {
1094 * Unlike TypeConstraintFreeze, triggering this constraint once does
1095 * not disable it on future changes to the type set.
1097 cx
->zone()->types
.addPendingRecompile(cx
, script_
);
1100 bool sweep(TypeZone
& zone
, TypeConstraint
** res
) {
1101 if (IsScriptAboutToBeFinalized(&script_
))
1103 *res
= zone
.typeLifoAlloc
.new_
<TypeConstraintFreezeStack
>(script_
);
1108 } /* anonymous namespace */
1111 types::FinishCompilation(JSContext
* cx
, HandleScript script
, ExecutionMode executionMode
,
1112 CompilerConstraintList
* constraints
, RecompileInfo
* precompileInfo
)
1114 if (constraints
->failed())
1117 CompilerOutput
co(script
, executionMode
);
1119 TypeZone
& types
= cx
->zone()->types
;
1120 if (!types
.compilerOutputs
) {
1121 types
.compilerOutputs
= cx
->new_
< Vector
<CompilerOutput
> >(cx
);
1122 if (!types
.compilerOutputs
)
1127 for (size_t i
= 0; i
< types
.compilerOutputs
->length(); i
++) {
1128 const CompilerOutput
& co
= (*types
.compilerOutputs
)[i
];
1129 JS_ASSERT_IF(co
.isValid(), co
.script() != script
|| co
.mode() != executionMode
);
1133 uint32_t index
= types
.compilerOutputs
->length();
1134 if (!types
.compilerOutputs
->append(co
))
1137 *precompileInfo
= RecompileInfo(index
);
1139 bool succeeded
= true;
1141 for (size_t i
= 0; i
< constraints
->length(); i
++) {
1142 CompilerConstraint
* constraint
= constraints
->get(i
);
1143 if (!constraint
->generateTypeConstraint(cx
, *precompileInfo
))
1147 for (size_t i
= 0; i
< constraints
->numFrozenScripts(); i
++) {
1148 const CompilerConstraintList::FrozenScript
& entry
= constraints
->frozenScript(i
);
1149 JS_ASSERT(entry
.script
->types
);
1151 if (!CheckFrozenTypeSet(cx
, entry
.thisTypes
, types::TypeScript::ThisTypes(entry
.script
)))
1153 unsigned nargs
= entry
.script
->functionNonDelazifying()
1154 ? entry
.script
->functionNonDelazifying()->nargs()
1156 for (size_t i
= 0; i
< nargs
; i
++) {
1157 if (!CheckFrozenTypeSet(cx
, &entry
.argTypes
[i
], types::TypeScript::ArgTypes(entry
.script
, i
)))
1160 for (size_t i
= 0; i
< entry
.script
->nTypeSets(); i
++) {
1161 if (!CheckFrozenTypeSet(cx
, &entry
.bytecodeTypes
[i
], &entry
.script
->types
->typeArray()[i
]))
1165 // If necessary, add constraints to trigger invalidation on the script
1166 // after any future changes to the stack type sets.
1167 if (entry
.script
->hasFreezeConstraints())
1169 entry
.script
->setHasFreezeConstraints();
1171 size_t count
= TypeScript::NumTypeSets(entry
.script
);
1173 StackTypeSet
* array
= entry
.script
->types
->typeArray();
1174 for (size_t i
= 0; i
< count
; i
++) {
1175 if (!array
[i
].addConstraint(cx
, cx
->typeLifoAlloc().new_
<TypeConstraintFreezeStack
>(entry
.script
), false))
1180 if (!succeeded
|| types
.compilerOutputs
->back().pendingInvalidation()) {
1181 types
.compilerOutputs
->back().invalidate();
1182 script
->resetUseCount();
1190 CheckDefinitePropertiesTypeSet(JSContext
* cx
, TemporaryTypeSet
* frozen
, StackTypeSet
* actual
)
1192 // The definite properties analysis happens on the main thread, so no new
1193 // types can have been added to actual. The analysis may have updated the
1194 // contents of |frozen| though with new speculative types, and these need
1195 // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
1197 if (!frozen
->isSubset(actual
)) {
1198 TypeSet::TypeList list
;
1199 frozen
->enumerateTypes(&list
);
1201 for (size_t i
= 0; i
< list
.length(); i
++)
1202 actual
->addType(cx
, list
[i
]);
1207 types::FinishDefinitePropertiesAnalysis(JSContext
* cx
, CompilerConstraintList
* constraints
)
1210 // Assert no new types have been added to the StackTypeSets. Do this before
1211 // calling CheckDefinitePropertiesTypeSet, as it may add new types to the
1212 // StackTypeSets and break these invariants if a script is inlined more
1213 // than once. See also CheckDefinitePropertiesTypeSet.
1214 for (size_t i
= 0; i
< constraints
->numFrozenScripts(); i
++) {
1215 const CompilerConstraintList::FrozenScript
& entry
= constraints
->frozenScript(i
);
1216 JSScript
* script
= entry
.script
;
1217 JS_ASSERT(script
->types
);
1219 JS_ASSERT(TypeScript::ThisTypes(script
)->isSubset(entry
.thisTypes
));
1221 unsigned nargs
= entry
.script
->functionNonDelazifying()
1222 ? entry
.script
->functionNonDelazifying()->nargs()
1224 for (size_t j
= 0; j
< nargs
; j
++)
1225 JS_ASSERT(TypeScript::ArgTypes(script
, j
)->isSubset(&entry
.argTypes
[j
]));
1227 for (size_t j
= 0; j
< script
->nTypeSets(); j
++)
1228 JS_ASSERT(script
->types
->typeArray()[j
].isSubset(&entry
.bytecodeTypes
[j
]));
1232 for (size_t i
= 0; i
< constraints
->numFrozenScripts(); i
++) {
1233 const CompilerConstraintList::FrozenScript
& entry
= constraints
->frozenScript(i
);
1234 JSScript
* script
= entry
.script
;
1235 JS_ASSERT(script
->types
);
1240 CheckDefinitePropertiesTypeSet(cx
, entry
.thisTypes
, TypeScript::ThisTypes(script
));
1242 unsigned nargs
= script
->functionNonDelazifying()
1243 ? script
->functionNonDelazifying()->nargs()
1245 for (size_t j
= 0; j
< nargs
; j
++)
1246 CheckDefinitePropertiesTypeSet(cx
, &entry
.argTypes
[j
], TypeScript::ArgTypes(script
, j
));
1248 for (size_t j
= 0; j
< script
->nTypeSets(); j
++)
1249 CheckDefinitePropertiesTypeSet(cx
, &entry
.bytecodeTypes
[j
], &script
->types
->typeArray()[j
]);
1255 // Constraint which triggers recompilation of a script if any type is added to a type set. */
1256 class ConstraintDataFreeze
1259 ConstraintDataFreeze() {}
1261 const char* kind() { return "freeze"; }
1263 bool invalidateOnNewType(Type type
) { return true; }
1264 bool invalidateOnNewPropertyState(TypeSet
* property
) { return true; }
1265 bool invalidateOnNewObjectState(TypeObject
* object
) { return false; }
1267 bool constraintHolds(JSContext
* cx
,
1268 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1271 ? property
.maybeTypes()->isSubset(expected
)
1272 : property
.maybeTypes()->empty();
1275 bool shouldSweep() { return false; }
1278 } /* anonymous namespace */
1281 HeapTypeSetKey::freeze(CompilerConstraintList
* constraints
)
1283 LifoAlloc
* alloc
= constraints
->alloc();
1285 typedef CompilerConstraintInstance
<ConstraintDataFreeze
> T
;
1286 constraints
->add(alloc
->new_
<T
>(alloc
, *this, ConstraintDataFreeze()));
1289 static inline jit::MIRType
1290 GetMIRTypeFromTypeFlags(TypeFlags flags
)
1293 case TYPE_FLAG_UNDEFINED
:
1294 return jit::MIRType_Undefined
;
1295 case TYPE_FLAG_NULL
:
1296 return jit::MIRType_Null
;
1297 case TYPE_FLAG_BOOLEAN
:
1298 return jit::MIRType_Boolean
;
1299 case TYPE_FLAG_INT32
:
1300 return jit::MIRType_Int32
;
1301 case (TYPE_FLAG_INT32
| TYPE_FLAG_DOUBLE
):
1302 return jit::MIRType_Double
;
1303 case TYPE_FLAG_STRING
:
1304 return jit::MIRType_String
;
1305 case TYPE_FLAG_SYMBOL
:
1306 return jit::MIRType_Symbol
;
1307 case TYPE_FLAG_LAZYARGS
:
1308 return jit::MIRType_MagicOptimizedArguments
;
1309 case TYPE_FLAG_ANYOBJECT
:
1310 return jit::MIRType_Object
;
1312 return jit::MIRType_Value
;
1317 TemporaryTypeSet::getKnownMIRType()
1319 TypeFlags flags
= baseFlags();
1322 if (baseObjectCount())
1323 type
= flags
? jit::MIRType_Value
: jit::MIRType_Object
;
1325 type
= GetMIRTypeFromTypeFlags(flags
);
1328 * If the type set is totally empty then it will be treated as unknown,
1329 * but we still need to record the dependency as adding a new type can give
1330 * it a definite type tag. This is not needed if there are enough types
1331 * that the exact tag is unknown, as it will stay unknown as more types are
1334 DebugOnly
<bool> empty
= flags
== 0 && baseObjectCount() == 0;
1335 JS_ASSERT_IF(empty
, type
== jit::MIRType_Value
);
1341 HeapTypeSetKey::knownMIRType(CompilerConstraintList
* constraints
)
1343 TypeSet
* types
= maybeTypes();
1345 if (!types
|| types
->unknown())
1346 return jit::MIRType_Value
;
1348 TypeFlags flags
= types
->baseFlags() & ~TYPE_FLAG_ANYOBJECT
;
1351 if (types
->unknownObject() || types
->getObjectCount())
1352 type
= flags
? jit::MIRType_Value
: jit::MIRType_Object
;
1354 type
= GetMIRTypeFromTypeFlags(flags
);
1356 if (type
!= jit::MIRType_Value
)
1357 freeze(constraints
);
1360 * If the type set is totally empty then it will be treated as unknown,
1361 * but we still need to record the dependency as adding a new type can give
1362 * it a definite type tag. This is not needed if there are enough types
1363 * that the exact tag is unknown, as it will stay unknown as more types are
1366 JS_ASSERT_IF(types
->empty(), type
== jit::MIRType_Value
);
1372 HeapTypeSetKey::isOwnProperty(CompilerConstraintList
* constraints
)
1374 if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
1376 if (JSObject
* obj
= object()->singleton()) {
1377 if (CanHaveEmptyPropertyTypesForOwnProperty(obj
))
1380 freeze(constraints
);
1385 HeapTypeSetKey::knownSubset(CompilerConstraintList
* constraints
, const HeapTypeSetKey
& other
)
1387 if (!maybeTypes() || maybeTypes()->empty()) {
1388 freeze(constraints
);
1391 if (!other
.maybeTypes() || !maybeTypes()->isSubset(other
.maybeTypes()))
1393 freeze(constraints
);
1398 TemporaryTypeSet::getSingleton()
1400 if (baseFlags() != 0 || baseObjectCount() != 1)
1403 return getSingleObject(0);
1407 HeapTypeSetKey::singleton(CompilerConstraintList
* constraints
)
1409 HeapTypeSet
* types
= maybeTypes();
1411 if (!types
|| types
->nonDataProperty() || types
->baseFlags() != 0 || types
->getObjectCount() != 1)
1414 JSObject
* obj
= types
->getSingleObject(0);
1417 freeze(constraints
);
1423 HeapTypeSetKey::needsBarrier(CompilerConstraintList
* constraints
)
1425 TypeSet
* types
= maybeTypes();
1428 bool result
= types
->unknownObject()
1429 || types
->getObjectCount() > 0
1430 || types
->hasAnyFlag(TYPE_FLAG_STRING
);
1432 freeze(constraints
);
1438 // Constraint which triggers recompilation if an object acquires particular flags.
1439 class ConstraintDataFreezeObjectFlags
1442 // Flags we are watching for on this object.
1443 TypeObjectFlags flags
;
1445 explicit ConstraintDataFreezeObjectFlags(TypeObjectFlags flags
)
1451 const char* kind() { return "freezeObjectFlags"; }
1453 bool invalidateOnNewType(Type type
) { return false; }
1454 bool invalidateOnNewPropertyState(TypeSet
* property
) { return false; }
1455 bool invalidateOnNewObjectState(TypeObject
* object
) {
1456 return object
->hasAnyFlags(flags
);
1459 bool constraintHolds(JSContext
* cx
,
1460 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1462 return !invalidateOnNewObjectState(property
.object()->maybeType());
1465 bool shouldSweep() { return false; }
1468 } /* anonymous namespace */
1471 TypeObjectKey::hasFlags(CompilerConstraintList
* constraints
, TypeObjectFlags flags
)
1475 if (TypeObject
* type
= maybeType()) {
1476 if (type
->hasAnyFlags(flags
))
1480 HeapTypeSetKey objectProperty
= property(JSID_EMPTY
);
1481 LifoAlloc
* alloc
= constraints
->alloc();
1483 typedef CompilerConstraintInstance
<ConstraintDataFreezeObjectFlags
> T
;
1484 constraints
->add(alloc
->new_
<T
>(alloc
, objectProperty
, ConstraintDataFreezeObjectFlags(flags
)));
1489 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList
* constraints
, TypeObjectFlags flags
)
1491 if (unknownObject())
1495 * Treat type sets containing no objects as having all object flags,
1496 * to spare callers from having to check this.
1498 if (baseObjectCount() == 0)
1501 unsigned count
= getObjectCount();
1502 for (unsigned i
= 0; i
< count
; i
++) {
1503 TypeObjectKey
* object
= getObject(i
);
1504 if (object
&& object
->hasFlags(constraints
, flags
))
1512 TypeObject::initialHeap(CompilerConstraintList
* constraints
)
1514 // If this object is not required to be pretenured but could be in the
1515 // future, add a constraint to trigger recompilation if the requirement
1518 if (shouldPreTenure())
1519 return gc::TenuredHeap
;
1521 if (!canPreTenure())
1522 return gc::DefaultHeap
;
1524 HeapTypeSetKey objectProperty
= TypeObjectKey::get(this)->property(JSID_EMPTY
);
1525 LifoAlloc
* alloc
= constraints
->alloc();
1527 typedef CompilerConstraintInstance
<ConstraintDataFreezeObjectFlags
> T
;
1528 constraints
->add(alloc
->new_
<T
>(alloc
, objectProperty
, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE
)));
1530 return gc::DefaultHeap
;
1535 // Constraint which triggers recompilation on any type change in an inlined
1536 // script. The freeze constraints added to stack type sets will only directly
1537 // invalidate the script containing those stack type sets. To invalidate code
1538 // for scripts into which the base script was inlined, ObjectStateChange is used.
1539 class ConstraintDataFreezeObjectForInlinedCall
1542 ConstraintDataFreezeObjectForInlinedCall()
1545 const char* kind() { return "freezeObjectForInlinedCall"; }
1547 bool invalidateOnNewType(Type type
) { return false; }
1548 bool invalidateOnNewPropertyState(TypeSet
* property
) { return false; }
1549 bool invalidateOnNewObjectState(TypeObject
* object
) {
1550 // We don't keep track of the exact dependencies the caller has on its
1551 // inlined scripts' type sets, so always invalidate the caller.
1555 bool constraintHolds(JSContext
* cx
,
1556 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1561 bool shouldSweep() { return false; }
1564 // Constraint which triggers recompilation when the template object for a
1565 // type's new script changes.
1566 class ConstraintDataFreezeObjectForNewScriptTemplate
1568 JSObject
* templateObject
;
1571 explicit ConstraintDataFreezeObjectForNewScriptTemplate(JSObject
* templateObject
)
1572 : templateObject(templateObject
)
1575 const char* kind() { return "freezeObjectForNewScriptTemplate"; }
1577 bool invalidateOnNewType(Type type
) { return false; }
1578 bool invalidateOnNewPropertyState(TypeSet
* property
) { return false; }
1579 bool invalidateOnNewObjectState(TypeObject
* object
) {
1580 return !object
->newScript() || object
->newScript()->templateObject
!= templateObject
;
1583 bool constraintHolds(JSContext
* cx
,
1584 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1586 return !invalidateOnNewObjectState(property
.object()->maybeType());
1589 bool shouldSweep() {
1590 // Note: |templateObject| is only used for equality testing.
1595 // Constraint which triggers recompilation when a typed array's data becomes
1597 class ConstraintDataFreezeObjectForTypedArrayData
1603 explicit ConstraintDataFreezeObjectForTypedArrayData(TypedArrayObject
& tarray
)
1604 : viewData(tarray
.viewData()),
1605 length(tarray
.length())
1608 const char* kind() { return "freezeObjectForTypedArrayData"; }
1610 bool invalidateOnNewType(Type type
) { return false; }
1611 bool invalidateOnNewPropertyState(TypeSet
* property
) { return false; }
1612 bool invalidateOnNewObjectState(TypeObject
* object
) {
1613 TypedArrayObject
& tarray
= object
->singleton()->as
<TypedArrayObject
>();
1614 return tarray
.viewData() != viewData
|| tarray
.length() != length
;
1617 bool constraintHolds(JSContext
* cx
,
1618 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1620 return !invalidateOnNewObjectState(property
.object()->maybeType());
1623 bool shouldSweep() {
1624 // Note: |viewData| is only used for equality testing.
1629 } /* anonymous namespace */
1632 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList
* constraints
)
1634 HeapTypeSetKey objectProperty
= property(JSID_EMPTY
);
1635 LifoAlloc
* alloc
= constraints
->alloc();
1637 typedef CompilerConstraintInstance
<ConstraintDataFreezeObjectForInlinedCall
> T
;
1638 constraints
->add(alloc
->new_
<T
>(alloc
, objectProperty
, ConstraintDataFreezeObjectForInlinedCall()));
1642 TypeObjectKey::watchStateChangeForNewScriptTemplate(CompilerConstraintList
* constraints
)
1644 JSObject
* templateObject
= asTypeObject()->newScript()->templateObject
;
1645 HeapTypeSetKey objectProperty
= property(JSID_EMPTY
);
1646 LifoAlloc
* alloc
= constraints
->alloc();
1648 typedef CompilerConstraintInstance
<ConstraintDataFreezeObjectForNewScriptTemplate
> T
;
1649 constraints
->add(alloc
->new_
<T
>(alloc
, objectProperty
,
1650 ConstraintDataFreezeObjectForNewScriptTemplate(templateObject
)));
1654 TypeObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList
* constraints
)
1656 TypedArrayObject
& tarray
= asSingleObject()->as
<TypedArrayObject
>();
1657 HeapTypeSetKey objectProperty
= property(JSID_EMPTY
);
1658 LifoAlloc
* alloc
= constraints
->alloc();
1660 typedef CompilerConstraintInstance
<ConstraintDataFreezeObjectForTypedArrayData
> T
;
1661 constraints
->add(alloc
->new_
<T
>(alloc
, objectProperty
,
1662 ConstraintDataFreezeObjectForTypedArrayData(tarray
)));
1666 ObjectStateChange(ExclusiveContext
* cxArg
, TypeObject
* object
, bool markingUnknown
)
1668 if (object
->unknownProperties())
1671 /* All constraints listening to state changes are on the empty id. */
1672 HeapTypeSet
* types
= object
->maybeGetProperty(JSID_EMPTY
);
1674 /* Mark as unknown after getting the types, to avoid assertion. */
1676 object
->addFlags(OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_UNKNOWN_PROPERTIES
);
1679 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
1680 TypeConstraint
* constraint
= types
->constraintList
;
1681 while (constraint
) {
1682 constraint
->newObjectState(cx
, object
);
1683 constraint
= constraint
->next
;
1686 JS_ASSERT(!types
->constraintList
);
1692 CheckNewScriptProperties(JSContext
* cx
, TypeObject
* type
, JSFunction
* fun
);
1696 class ConstraintDataFreezePropertyState
1704 explicit ConstraintDataFreezePropertyState(Which which
)
1708 const char* kind() { return (which
== NON_DATA
) ? "freezeNonDataProperty" : "freezeNonWritableProperty"; }
1710 bool invalidateOnNewType(Type type
) { return false; }
1711 bool invalidateOnNewPropertyState(TypeSet
* property
) {
1712 return (which
== NON_DATA
)
1713 ? property
->nonDataProperty()
1714 : property
->nonWritableProperty();
1716 bool invalidateOnNewObjectState(TypeObject
* object
) { return false; }
1718 bool constraintHolds(JSContext
* cx
,
1719 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1721 return !invalidateOnNewPropertyState(property
.maybeTypes());
1724 bool shouldSweep() { return false; }
1727 } /* anonymous namespace */
1730 HeapTypeSetKey::nonData(CompilerConstraintList
* constraints
)
1732 if (maybeTypes() && maybeTypes()->nonDataProperty())
1735 LifoAlloc
* alloc
= constraints
->alloc();
1737 typedef CompilerConstraintInstance
<ConstraintDataFreezePropertyState
> T
;
1738 constraints
->add(alloc
->new_
<T
>(alloc
, *this,
1739 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA
)));
1744 HeapTypeSetKey::nonWritable(CompilerConstraintList
* constraints
)
1746 if (maybeTypes() && maybeTypes()->nonWritableProperty())
1749 LifoAlloc
* alloc
= constraints
->alloc();
1751 typedef CompilerConstraintInstance
<ConstraintDataFreezePropertyState
> T
;
1752 constraints
->add(alloc
->new_
<T
>(alloc
, *this,
1753 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE
)));
1759 class ConstraintDataConstantProperty
1762 explicit ConstraintDataConstantProperty() {}
1764 const char* kind() { return "constantProperty"; }
1766 bool invalidateOnNewType(Type type
) { return false; }
1767 bool invalidateOnNewPropertyState(TypeSet
* property
) {
1768 return property
->nonConstantProperty();
1770 bool invalidateOnNewObjectState(TypeObject
* object
) { return false; }
1772 bool constraintHolds(JSContext
* cx
,
1773 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1775 return !invalidateOnNewPropertyState(property
.maybeTypes());
1778 bool shouldSweep() { return false; }
1781 } /* anonymous namespace */
1784 HeapTypeSetKey::constant(CompilerConstraintList
* constraints
, Value
* valOut
)
1786 if (nonData(constraints
))
1789 // Only singleton object properties can be marked as constants.
1790 JSObject
* obj
= object()->singleton();
1791 if (!obj
|| !obj
->isNative())
1794 if (maybeTypes() && maybeTypes()->nonConstantProperty())
1797 // Get the current value of the property.
1798 Shape
* shape
= obj
->nativeLookupPure(id());
1799 if (!shape
|| !shape
->hasDefaultGetter() || !shape
->hasSlot() || shape
->hadOverwrite())
1802 Value val
= obj
->nativeGetSlot(shape
->slot());
1804 // If the value is a pointer to an object in the nursery, don't optimize.
1805 if (val
.isGCThing() && IsInsideNursery(val
.toGCThing()))
1808 // If the value is a string that's not atomic, don't optimize.
1809 if (val
.isString() && !val
.toString()->isAtom())
1814 LifoAlloc
* alloc
= constraints
->alloc();
1815 typedef CompilerConstraintInstance
<ConstraintDataConstantProperty
> T
;
1816 constraints
->add(alloc
->new_
<T
>(alloc
, *this, ConstraintDataConstantProperty()));
1821 TemporaryTypeSet::filtersType(const TemporaryTypeSet
* other
, Type filteredType
) const
1823 if (other
->unknown())
1826 for (TypeFlags flag
= 1; flag
< TYPE_FLAG_ANYOBJECT
; flag
<<= 1) {
1827 Type type
= Type::PrimitiveType(TypeFlagPrimitive(flag
));
1828 if (type
!= filteredType
&& other
->hasType(type
) && !hasType(type
))
1832 if (other
->unknownObject())
1833 return unknownObject();
1835 for (size_t i
= 0; i
< other
->getObjectCount(); i
++) {
1836 TypeObjectKey
* key
= other
->getObject(i
);
1838 Type type
= Type::ObjectType(key
);
1839 if (type
!= filteredType
&& !hasType(type
))
1849 // A constraint that never triggers recompilation.
1850 class ConstraintDataInert
1853 explicit ConstraintDataInert() {}
1855 const char* kind() { return "inert"; }
1857 bool invalidateOnNewType(Type type
) { return false; }
1858 bool invalidateOnNewPropertyState(TypeSet
* property
) { return false; }
1859 bool invalidateOnNewObjectState(TypeObject
* object
) { return false; }
1861 bool constraintHolds(JSContext
* cx
,
1862 const HeapTypeSetKey
& property
, TemporaryTypeSet
* expected
)
1867 bool shouldSweep() { return false; }
1870 } /* anonymous namespace */
1873 TemporaryTypeSet::propertyMightBeConstant(CompilerConstraintList
* constraints
, jsid id
)
1875 if (unknownObject())
1878 for (size_t i
= 0; i
< getObjectCount(); i
++) {
1879 TypeObjectKey
* type
= getObject(i
);
1881 // Type sets are only marked as constants when they are lazily
1882 // constructed from the properties of singleton typed objects. So watch
1883 // for the cases when a property either already is or might be marked
1884 // as constant in the future.
1886 if (!type
|| !type
->isSingleObject())
1889 if (type
->unknownProperties())
1892 HeapTypeSetKey property
= type
->property(id
);
1893 if (!property
.maybeTypes() || !property
.maybeTypes()->nonConstantProperty())
1897 // It is possible for a property that was not marked as constant to
1898 // 'become' one, if we throw away the type property during a GC and
1899 // regenerate it with the constant flag set. TypeObject::sweep only removes
1900 // type properties if they have no constraints attached to them, so add
1901 // inert constraints to pin these properties in place.
1903 LifoAlloc
* alloc
= constraints
->alloc();
1904 for (size_t i
= 0; i
< getObjectCount(); i
++) {
1905 TypeObjectKey
* type
= getObject(i
);
1907 if (!type
|| !type
->isSingleObject())
1910 HeapTypeSetKey property
= type
->property(id
);
1912 typedef CompilerConstraintInstance
<ConstraintDataInert
> T
;
1913 constraints
->add(alloc
->new_
<T
>(alloc
, property
, ConstraintDataInert()));
1920 TemporaryTypeSet::propertyIsConstant(CompilerConstraintList
* constraints
, jsid id
, Value
* valOut
)
1924 JSObject
* singleton
= getSingleton();
1928 TypeObjectKey
* type
= TypeObjectKey::get(singleton
);
1929 if (type
->unknownProperties())
1932 HeapTypeSetKey property
= type
->property(id
);
1933 return property
.constant(constraints
, valOut
);
1936 TemporaryTypeSet::DoubleConversion
1937 TemporaryTypeSet::convertDoubleElements(CompilerConstraintList
* constraints
)
1939 if (unknownObject() || !getObjectCount())
1940 return AmbiguousDoubleConversion
;
1942 bool alwaysConvert
= true;
1943 bool maybeConvert
= false;
1944 bool dontConvert
= false;
1946 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
1947 TypeObjectKey
* type
= getObject(i
);
1951 if (type
->unknownProperties()) {
1952 alwaysConvert
= false;
1956 HeapTypeSetKey property
= type
->property(JSID_VOID
);
1957 property
.freeze(constraints
);
1959 // We can't convert to double elements for objects which do not have
1960 // double in their element types (as the conversion may render the type
1961 // information incorrect), nor for non-array objects (as their elements
1962 // may point to emptyObjectElements, which cannot be converted).
1963 if (!property
.maybeTypes() ||
1964 !property
.maybeTypes()->hasType(Type::DoubleType()) ||
1965 type
->clasp() != &ArrayObject::class_
)
1968 alwaysConvert
= false;
1972 // Only bother with converting known packed arrays whose possible
1973 // element types are int or double. Other arrays require type tests
1974 // when elements are accessed regardless of the conversion.
1975 if (property
.knownMIRType(constraints
) == jit::MIRType_Double
&&
1976 !type
->hasFlags(constraints
, OBJECT_FLAG_NON_PACKED
))
1978 maybeConvert
= true;
1980 alwaysConvert
= false;
1984 JS_ASSERT_IF(alwaysConvert
, maybeConvert
);
1986 if (maybeConvert
&& dontConvert
)
1987 return AmbiguousDoubleConversion
;
1989 return AlwaysConvertToDoubles
;
1991 return MaybeConvertToDoubles
;
1992 return DontConvertToDoubles
;
1996 TemporaryTypeSet::getKnownClass()
1998 if (unknownObject())
2001 const Class
* clasp
= nullptr;
2002 unsigned count
= getObjectCount();
2004 for (unsigned i
= 0; i
< count
; i
++) {
2005 const Class
* nclasp
= getObjectClass(i
);
2009 if (clasp
&& clasp
!= nclasp
)
2017 TemporaryTypeSet::ForAllResult
2018 TemporaryTypeSet::forAllClasses(bool (*func
)(const Class
* clasp
))
2020 if (unknownObject())
2021 return ForAllResult::MIXED
;
2023 unsigned count
= getObjectCount();
2025 return ForAllResult::EMPTY
;
2027 bool true_results
= false;
2028 bool false_results
= false;
2029 for (unsigned i
= 0; i
< count
; i
++) {
2030 const Class
* clasp
= getObjectClass(i
);
2032 return ForAllResult::MIXED
;
2034 true_results
= true;
2035 if (false_results
) return ForAllResult::MIXED
;
2038 false_results
= true;
2039 if (true_results
) return ForAllResult::MIXED
;
2043 JS_ASSERT(true_results
!= false_results
);
2045 return true_results
? ForAllResult::ALL_TRUE
: ForAllResult::ALL_FALSE
;
2049 TemporaryTypeSet::getTypedArrayType()
2051 const Class
* clasp
= getKnownClass();
2053 if (clasp
&& IsTypedArrayClass(clasp
))
2054 return (Scalar::Type
) (clasp
- &TypedArrayObject::classes
[0]);
2055 return Scalar::TypeMax
;
2059 TemporaryTypeSet::isDOMClass()
2061 if (unknownObject())
2064 unsigned count
= getObjectCount();
2065 for (unsigned i
= 0; i
< count
; i
++) {
2066 const Class
* clasp
= getObjectClass(i
);
2067 if (clasp
&& !clasp
->isDOMClass())
2075 TemporaryTypeSet::maybeCallable()
2080 if (unknownObject())
2083 unsigned count
= getObjectCount();
2084 for (unsigned i
= 0; i
< count
; i
++) {
2085 const Class
* clasp
= getObjectClass(i
);
2086 if (clasp
&& clasp
->isCallable())
2094 TemporaryTypeSet::maybeEmulatesUndefined()
2099 if (unknownObject())
2102 unsigned count
= getObjectCount();
2103 for (unsigned i
= 0; i
< count
; i
++) {
2104 // The object emulates undefined if clasp->emulatesUndefined() or if
2105 // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
2106 // proxies, we can just check for that.
2107 const Class
* clasp
= getObjectClass(i
);
2108 if (clasp
&& (clasp
->emulatesUndefined() || clasp
->isProxy()))
2116 TemporaryTypeSet::getCommonPrototype()
2118 if (unknownObject())
2121 JSObject
* proto
= nullptr;
2122 unsigned count
= getObjectCount();
2124 for (unsigned i
= 0; i
< count
; i
++) {
2125 TypeObjectKey
* object
= getObject(i
);
2129 if (!object
->hasTenuredProto())
2132 TaggedProto nproto
= object
->proto();
2134 if (nproto
!= TaggedProto(proto
))
2137 if (!nproto
.isObject())
2139 proto
= nproto
.toObject();
2147 TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList
* constraints
, jsid id
)
2149 if (unknownObject())
2152 for (unsigned i
= 0; i
< getObjectCount(); i
++) {
2153 TypeObjectKey
* type
= getObject(i
);
2157 if (type
->unknownProperties())
2160 HeapTypeSetKey property
= type
->property(id
);
2161 if (property
.needsBarrier(constraints
))
2168 /////////////////////////////////////////////////////////////////////
2170 /////////////////////////////////////////////////////////////////////
2172 TypeCompartment::TypeCompartment()
2178 TypeCompartment::newTypeObject(ExclusiveContext
* cx
, const Class
* clasp
, Handle
<TaggedProto
> proto
,
2179 TypeObjectFlags initialFlags
)
2181 JS_ASSERT_IF(proto
.isObject(), cx
->isInsideCurrentCompartment(proto
.toObject()));
2183 if (cx
->isJSContext()) {
2184 if (proto
.isObject() && IsInsideNursery(proto
.toObject()))
2185 initialFlags
|= OBJECT_FLAG_NURSERY_PROTO
;
2188 TypeObject
* object
= js::NewTypeObject(cx
);
2191 new(object
) TypeObject(clasp
, proto
, initialFlags
);
2197 TypeCompartment::addAllocationSiteTypeObject(JSContext
* cx
, AllocationSiteKey key
)
2199 AutoEnterAnalysis
enter(cx
);
2201 if (!allocationSiteTable
) {
2202 allocationSiteTable
= cx
->new_
<AllocationSiteTable
>();
2203 if (!allocationSiteTable
|| !allocationSiteTable
->init()) {
2204 js_delete(allocationSiteTable
);
2205 allocationSiteTable
= nullptr;
2210 AllocationSiteTable::AddPtr p
= allocationSiteTable
->lookupForAdd(key
);
2213 TypeObject
* res
= nullptr;
2215 jsbytecode
* pc
= key
.script
->offsetToPC(key
.offset
);
2216 RootedScript
keyScript(cx
, key
.script
);
2219 RootedObject
proto(cx
);
2220 if (!GetBuiltinPrototype(cx
, key
.kind
, &proto
))
2223 Rooted
<TaggedProto
> tagged(cx
, TaggedProto(proto
));
2224 res
= newTypeObject(cx
, GetClassForProtoKey(key
.kind
), tagged
, OBJECT_FLAG_FROM_ALLOCATION_SITE
);
2227 key
.script
= keyScript
;
2230 if (JSOp(*pc
) == JSOP_NEWOBJECT
) {
2232 * This object is always constructed the same way and will not be
2233 * observed by other code before all properties have been added. Mark
2234 * all the properties as definite properties of the object.
2236 RootedObject
baseobj(cx
, key
.script
->getObject(GET_UINT32_INDEX(pc
)));
2238 if (!res
->addDefiniteProperties(cx
, baseobj
))
2242 if (!allocationSiteTable
->add(p
, key
, res
))
2249 GetAtomId(JSContext
* cx
, JSScript
* script
, const jsbytecode
* pc
, unsigned offset
)
2251 PropertyName
* name
= script
->getName(GET_UINT32_INDEX(pc
+ offset
));
2252 return IdToTypeId(NameToId(name
));
2256 types::UseNewType(JSContext
* cx
, JSScript
* script
, jsbytecode
* pc
)
2259 * Make a heuristic guess at a use of JSOP_NEW that the constructed object
2260 * should have a fresh type object. We do this when the NEW is immediately
2261 * followed by a simple assignment to an object's .prototype field.
2262 * This is designed to catch common patterns for subclassing in JS:
2264 * function Super() { ... }
2265 * function Sub1() { ... }
2266 * function Sub2() { ... }
2268 * Sub1.prototype = new Super();
2269 * Sub2.prototype = new Super();
2271 * Using distinct type objects for the particular prototypes of Sub1 and
2272 * Sub2 lets us continue to distinguish the two subclasses and any extra
2273 * properties added to those prototype objects.
2275 if (JSOp(*pc
) != JSOP_NEW
)
2277 pc
+= JSOP_NEW_LENGTH
;
2278 if (JSOp(*pc
) == JSOP_SETPROP
) {
2279 jsid id
= GetAtomId(cx
, script
, pc
, 0);
2280 if (id
== id_prototype(cx
))
2288 types::UseNewTypeForInitializer(JSScript
* script
, jsbytecode
* pc
, JSProtoKey key
)
2291 * Objects created outside loops in global and eval scripts should have
2292 * singleton types. For now this is only done for plain objects and typed
2293 * arrays, but not normal arrays.
2296 if (script
->functionNonDelazifying() && !script
->treatAsRunOnce())
2297 return GenericObject
;
2299 if (key
!= JSProto_Object
&& !(key
>= JSProto_Int8Array
&& key
<= JSProto_Uint8ClampedArray
))
2300 return GenericObject
;
2303 * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
2304 * indicating their boundary.
2307 if (!script
->hasTrynotes())
2308 return SingletonObject
;
2310 unsigned offset
= script
->pcToOffset(pc
);
2312 JSTryNote
* tn
= script
->trynotes()->vector
;
2313 JSTryNote
* tnlimit
= tn
+ script
->trynotes()->length
;
2314 for (; tn
< tnlimit
; tn
++) {
2315 if (tn
->kind
!= JSTRY_ITER
&& tn
->kind
!= JSTRY_LOOP
)
2318 unsigned startOffset
= script
->mainOffset() + tn
->start
;
2319 unsigned endOffset
= startOffset
+ tn
->length
;
2321 if (offset
>= startOffset
&& offset
< endOffset
)
2322 return GenericObject
;
2325 return SingletonObject
;
2329 types::UseNewTypeForInitializer(JSScript
* script
, jsbytecode
* pc
, const Class
* clasp
)
2331 return UseNewTypeForInitializer(script
, pc
, JSCLASS_CACHED_PROTO_KEY(clasp
));
2335 ClassCanHaveExtraProperties(const Class
* clasp
)
2337 JS_ASSERT(clasp
->resolve
);
2338 return clasp
->resolve
!= JS_ResolveStub
2339 || clasp
->ops
.lookupGeneric
2340 || clasp
->ops
.getGeneric
2341 || IsTypedArrayClass(clasp
);
2345 PrototypeHasIndexedProperty(CompilerConstraintList
* constraints
, JSObject
* obj
)
2348 TypeObjectKey
* type
= TypeObjectKey::get(obj
);
2349 if (ClassCanHaveExtraProperties(type
->clasp()))
2351 if (type
->unknownProperties())
2353 HeapTypeSetKey index
= type
->property(JSID_VOID
);
2354 if (index
.nonData(constraints
) || index
.isOwnProperty(constraints
))
2356 if (!obj
->hasTenuredProto())
2358 obj
= obj
->getProto();
2365 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList
* constraints
, JSScript
* script
)
2367 if (JSObject
* proto
= script
->global().maybeGetArrayPrototype())
2368 return PrototypeHasIndexedProperty(constraints
, proto
);
2373 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList
* constraints
,
2374 TemporaryTypeSet
* types
)
2376 const Class
* clasp
= types
->getKnownClass();
2378 // Note: typed arrays have indexed properties not accounted for by type
2379 // information, though these are all in bounds and will be accounted for
2381 if (!clasp
|| (ClassCanHaveExtraProperties(clasp
) && !IsTypedArrayClass(clasp
)))
2384 if (types
->hasObjectFlags(constraints
, types::OBJECT_FLAG_SPARSE_INDEXES
))
2387 JSObject
* proto
= types
->getCommonPrototype();
2391 return PrototypeHasIndexedProperty(constraints
, proto
);
2395 TypeZone::processPendingRecompiles(FreeOp
* fop
)
2397 if (!pendingRecompiles
)
2400 /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
2401 Vector
<RecompileInfo
>* pending
= pendingRecompiles
;
2402 pendingRecompiles
= nullptr;
2404 JS_ASSERT(!pending
->empty());
2406 jit::Invalidate(*this, fop
, *pending
);
2408 fop
->delete_(pending
);
2412 TypeZone::addPendingRecompile(JSContext
* cx
, const RecompileInfo
& info
)
2414 CompilerOutput
* co
= info
.compilerOutput(cx
);
2415 if (!co
|| !co
->isValid() || co
->pendingInvalidation())
2418 InferSpew(ISpewOps
, "addPendingRecompile: %p:%s:%d",
2419 co
->script(), co
->script()->filename(), co
->script()->lineno());
2421 co
->setPendingInvalidation();
2423 if (!pendingRecompiles
) {
2424 pendingRecompiles
= cx
->new_
< Vector
<RecompileInfo
> >(cx
);
2425 if (!pendingRecompiles
)
2426 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
2429 if (!pendingRecompiles
->append(info
))
2430 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
2434 TypeZone::addPendingRecompile(JSContext
* cx
, JSScript
* script
)
2438 CancelOffThreadIonCompile(cx
->compartment(), script
);
2440 // Let the script warm up again before attempting another compile.
2441 if (jit::IsBaselineEnabled(cx
))
2442 script
->resetUseCount();
2444 if (script
->hasIonScript())
2445 addPendingRecompile(cx
, script
->ionScript()->recompileInfo());
2447 if (script
->hasParallelIonScript())
2448 addPendingRecompile(cx
, script
->parallelIonScript()->recompileInfo());
2450 // When one script is inlined into another the caller listens to state
2451 // changes on the callee's script, so trigger these to force recompilation
2452 // of any such callers.
2453 if (script
->functionNonDelazifying() && !script
->functionNonDelazifying()->hasLazyType())
2454 ObjectStateChange(cx
, script
->functionNonDelazifying()->type(), false);
2458 TypeCompartment::markSetsUnknown(JSContext
* cx
, TypeObject
* target
)
2460 JS_ASSERT(this == &cx
->compartment()->types
);
2461 JS_ASSERT(!(target
->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN
));
2462 JS_ASSERT(!target
->singleton());
2463 JS_ASSERT(target
->unknownProperties());
2465 AutoEnterAnalysis
enter(cx
);
2467 /* Mark type sets which contain obj as having a generic object types. */
2469 for (gc::ZoneCellIter
i(cx
->zone(), gc::FINALIZE_TYPE_OBJECT
); !i
.done(); i
.next()) {
2470 TypeObject
* object
= i
.get
<TypeObject
>();
2471 unsigned count
= object
->getPropertyCount();
2472 for (unsigned i
= 0; i
< count
; i
++) {
2473 Property
* prop
= object
->getProperty(i
);
2474 if (prop
&& prop
->types
.hasType(Type::ObjectType(target
)))
2475 prop
->types
.addType(cx
, Type::AnyObjectType());
2479 for (gc::ZoneCellIter
i(cx
->zone(), gc::FINALIZE_SCRIPT
); !i
.done(); i
.next()) {
2480 JSScript
* script
= i
.get
<JSScript
>();
2481 if (script
->types
) {
2482 unsigned count
= TypeScript::NumTypeSets(script
);
2483 StackTypeSet
* typeArray
= script
->types
->typeArray();
2484 for (unsigned i
= 0; i
< count
; i
++) {
2485 if (typeArray
[i
].hasType(Type::ObjectType(target
)))
2486 typeArray
[i
].addType(cx
, Type::AnyObjectType());
2491 target
->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN
);
2495 TypeCompartment::print(JSContext
* cx
, bool force
)
2498 gc::AutoSuppressGC
suppressGC(cx
);
2499 JSAutoRequest
request(cx
);
2501 JSCompartment
* compartment
= this->compartment();
2502 AutoEnterAnalysis
enter(nullptr, compartment
);
2504 if (!force
&& !InferSpewActive(ISpewResult
))
2507 for (gc::ZoneCellIter
i(compartment
->zone(), gc::FINALIZE_SCRIPT
); !i
.done(); i
.next()) {
2508 // Note: use cx->runtime() instead of cx to work around IsInRequest(cx)
2509 // assertion failures when we're called from DestroyContext.
2510 RootedScript
script(cx
->runtime(), i
.get
<JSScript
>());
2512 script
->types
->printTypes(cx
, script
);
2515 for (gc::ZoneCellIter
i(compartment
->zone(), gc::FINALIZE_TYPE_OBJECT
); !i
.done(); i
.next()) {
2516 TypeObject
* object
= i
.get
<TypeObject
>();
2522 /////////////////////////////////////////////////////////////////////
2523 // TypeCompartment tables
2524 /////////////////////////////////////////////////////////////////////
2527 * The arrayTypeTable and objectTypeTable are per-compartment tables for making
2528 * common type objects to model the contents of large script singletons and
2529 * JSON objects. These are vanilla Arrays and native Objects, so we distinguish
2530 * the types of different ones by looking at the types of their properties.
2532 * All singleton/JSON arrays which have the same prototype, are homogenous and
2533 * of the same element type will share a type object. All singleton/JSON
2534 * objects which have the same shape and property types will also share a type
2535 * object. We don't try to collate arrays or objects that have type mismatches.
2539 NumberTypes(Type a
, Type b
)
2541 return (a
.isPrimitive(JSVAL_TYPE_INT32
) || a
.isPrimitive(JSVAL_TYPE_DOUBLE
))
2542 && (b
.isPrimitive(JSVAL_TYPE_INT32
) || b
.isPrimitive(JSVAL_TYPE_DOUBLE
));
2546 * As for GetValueType, but requires object types to be non-singletons with
2547 * their default prototype. These are the only values that should appear in
2548 * arrays and objects whose type can be fixed.
2551 GetValueTypeForTable(const Value
& v
)
2553 Type type
= GetValueType(v
);
2554 JS_ASSERT(!type
.isSingleObject());
2558 struct types::ArrayTableKey
: public DefaultHasher
<types::ArrayTableKey
>
2564 : type(Type::UndefinedType()), proto(nullptr)
2567 ArrayTableKey(Type type
, JSObject
* proto
)
2568 : type(type
), proto(proto
)
2571 static inline uint32_t hash(const ArrayTableKey
& v
) {
2572 return (uint32_t) (v
.type
.raw() ^ ((uint32_t)(size_t)v
.proto
>> 2));
2575 static inline bool match(const ArrayTableKey
& v1
, const ArrayTableKey
& v2
) {
2576 return v1
.type
== v2
.type
&& v1
.proto
== v2
.proto
;
2581 TypeCompartment::setTypeToHomogenousArray(ExclusiveContext
* cx
,
2582 JSObject
* obj
, Type elementType
)
2584 JS_ASSERT(cx
->compartment()->activeAnalysis
);
2586 if (!arrayTypeTable
) {
2587 arrayTypeTable
= cx
->new_
<ArrayTypeTable
>();
2588 if (!arrayTypeTable
|| !arrayTypeTable
->init()) {
2589 arrayTypeTable
= nullptr;
2594 ArrayTableKey
key(elementType
, obj
->getProto());
2595 DependentAddPtr
<ArrayTypeTable
> p(cx
, *arrayTypeTable
, key
);
2597 obj
->setType(p
->value());
2599 /* Make a new type to use for future arrays with the same elements. */
2600 RootedObject
objProto(cx
, obj
->getProto());
2601 Rooted
<TaggedProto
> taggedProto(cx
, TaggedProto(objProto
));
2602 TypeObject
* objType
= newTypeObject(cx
, &ArrayObject::class_
, taggedProto
);
2605 obj
->setType(objType
);
2607 if (!objType
->unknownProperties())
2608 objType
->addPropertyType(cx
, JSID_VOID
, elementType
);
2610 key
.proto
= objProto
;
2611 (void) p
.add(cx
, *arrayTypeTable
, key
, objType
);
2616 TypeCompartment::fixArrayType(ExclusiveContext
* cx
, JSObject
* obj
)
2618 AutoEnterAnalysis
enter(cx
);
2621 * If the array is of homogenous type, pick a type object which will be
2622 * shared with all other singleton/JSON arrays of the same type.
2623 * If the array is heterogenous, keep the existing type object, which has
2624 * unknown properties.
2626 JS_ASSERT(obj
->is
<ArrayObject
>());
2628 unsigned len
= obj
->getDenseInitializedLength();
2632 Type type
= GetValueTypeForTable(obj
->getDenseElement(0));
2634 for (unsigned i
= 1; i
< len
; i
++) {
2635 Type ntype
= GetValueTypeForTable(obj
->getDenseElement(i
));
2636 if (ntype
!= type
) {
2637 if (NumberTypes(type
, ntype
))
2638 type
= Type::DoubleType();
2644 setTypeToHomogenousArray(cx
, obj
, type
);
2648 types::FixRestArgumentsType(ExclusiveContext
* cx
, JSObject
* obj
)
2650 cx
->compartment()->types
.fixRestArgumentsType(cx
, obj
);
2654 TypeCompartment::fixRestArgumentsType(ExclusiveContext
* cx
, JSObject
* obj
)
2656 AutoEnterAnalysis
enter(cx
);
2659 * Tracking element types for rest argument arrays is not worth it, but we
2660 * still want it to be known that it's a dense array.
2662 JS_ASSERT(obj
->is
<ArrayObject
>());
2664 setTypeToHomogenousArray(cx
, obj
, Type::UnknownType());
2668 * N.B. We could also use the initial shape of the object (before its type is
2669 * fixed) as the key in the object table, but since all references in the table
2670 * are weak the hash entries would usually be collected on GC even if objects
2671 * with the new type/shape are still live.
2673 struct types::ObjectTableKey
2676 uint32_t nproperties
;
2680 IdValuePair
* properties
;
2681 uint32_t nproperties
;
2684 Lookup(IdValuePair
* properties
, uint32_t nproperties
, uint32_t nfixed
)
2685 : properties(properties
), nproperties(nproperties
), nfixed(nfixed
)
2689 static inline HashNumber
hash(const Lookup
& lookup
) {
2690 return (HashNumber
) (JSID_BITS(lookup
.properties
[lookup
.nproperties
- 1].id
) ^
2691 lookup
.nproperties
^
2695 static inline bool match(const ObjectTableKey
& v
, const Lookup
& lookup
) {
2696 if (lookup
.nproperties
!= v
.nproperties
|| lookup
.nfixed
!= v
.nfixed
)
2698 for (size_t i
= 0; i
< lookup
.nproperties
; i
++) {
2699 if (lookup
.properties
[i
].id
!= v
.properties
[i
])
2706 struct types::ObjectTableEntry
2708 ReadBarrieredTypeObject object
;
2709 ReadBarrieredShape shape
;
2714 UpdateObjectTableEntryTypes(ExclusiveContext
* cx
, ObjectTableEntry
& entry
,
2715 IdValuePair
* properties
, size_t nproperties
)
2717 if (entry
.object
->unknownProperties())
2719 for (size_t i
= 0; i
< nproperties
; i
++) {
2720 Type type
= entry
.types
[i
];
2721 Type ntype
= GetValueTypeForTable(properties
[i
].value
);
2724 if (ntype
.isPrimitive(JSVAL_TYPE_INT32
) &&
2725 type
.isPrimitive(JSVAL_TYPE_DOUBLE
))
2727 /* The property types already reflect 'int32'. */
2729 if (ntype
.isPrimitive(JSVAL_TYPE_DOUBLE
) &&
2730 type
.isPrimitive(JSVAL_TYPE_INT32
))
2732 /* Include 'double' in the property types to avoid the update below later. */
2733 entry
.types
[i
] = Type::DoubleType();
2735 entry
.object
->addPropertyType(cx
, IdToTypeId(properties
[i
].id
), ntype
);
2741 TypeCompartment::fixObjectType(ExclusiveContext
* cx
, JSObject
* obj
)
2743 AutoEnterAnalysis
enter(cx
);
2745 if (!objectTypeTable
) {
2746 objectTypeTable
= cx
->new_
<ObjectTypeTable
>();
2747 if (!objectTypeTable
|| !objectTypeTable
->init()) {
2748 js_delete(objectTypeTable
);
2749 objectTypeTable
= nullptr;
2755 * Use the same type object for all singleton/JSON objects with the same
2756 * base shape, i.e. the same fields written in the same order.
2758 JS_ASSERT(obj
->is
<JSObject
>());
2761 * Exclude some objects we can't readily associate common types for based on their
2762 * shape. Objects with metadata are excluded so that the metadata does not need to
2763 * be included in the table lookup (the metadata object might be in the nursery).
2765 if (obj
->slotSpan() == 0 || obj
->inDictionaryMode() || !obj
->hasEmptyElements() || obj
->getMetadata())
2768 Vector
<IdValuePair
> properties(cx
);
2769 if (!properties
.resize(obj
->slotSpan()))
2772 Shape
* shape
= obj
->lastProperty();
2773 while (!shape
->isEmptyShape()) {
2774 IdValuePair
& entry
= properties
[shape
->slot()];
2775 entry
.id
= shape
->propid();
2776 entry
.value
= obj
->getSlot(shape
->slot());
2777 shape
= shape
->previous();
2780 ObjectTableKey::Lookup
lookup(properties
.begin(), properties
.length(), obj
->numFixedSlots());
2781 ObjectTypeTable::AddPtr p
= objectTypeTable
->lookupForAdd(lookup
);
2784 JS_ASSERT(obj
->getProto() == p
->value().object
->proto().toObject());
2785 JS_ASSERT(obj
->lastProperty() == p
->value().shape
);
2787 UpdateObjectTableEntryTypes(cx
, p
->value(), properties
.begin(), properties
.length());
2788 obj
->setType(p
->value().object
);
2792 /* Make a new type to use for the object and similar future ones. */
2793 Rooted
<TaggedProto
> objProto(cx
, obj
->getTaggedProto());
2794 TypeObject
* objType
= newTypeObject(cx
, &JSObject::class_
, objProto
);
2795 if (!objType
|| !objType
->addDefiniteProperties(cx
, obj
))
2798 if (obj
->isIndexed())
2799 objType
->setFlags(cx
, OBJECT_FLAG_SPARSE_INDEXES
);
2801 ScopedJSFreePtr
<jsid
> ids(objType
->pod_calloc
<jsid
>(properties
.length()));
2805 ScopedJSFreePtr
<Type
> types(objType
->pod_calloc
<Type
>(properties
.length()));
2809 for (size_t i
= 0; i
< properties
.length(); i
++) {
2810 ids
[i
] = properties
[i
].id
;
2811 types
[i
] = GetValueTypeForTable(obj
->getSlot(i
));
2812 if (!objType
->unknownProperties())
2813 objType
->addPropertyType(cx
, IdToTypeId(ids
[i
]), types
[i
]);
2817 key
.properties
= ids
;
2818 key
.nproperties
= properties
.length();
2819 key
.nfixed
= obj
->numFixedSlots();
2820 JS_ASSERT(ObjectTableKey::match(key
, lookup
));
2822 ObjectTableEntry entry
;
2823 entry
.object
.set(objType
);
2824 entry
.shape
.set(obj
->lastProperty());
2825 entry
.types
= types
;
2827 obj
->setType(objType
);
2829 p
= objectTypeTable
->lookupForAdd(lookup
);
2830 if (objectTypeTable
->add(p
, key
, entry
)) {
2837 TypeCompartment::newTypedObject(JSContext
* cx
, IdValuePair
* properties
, size_t nproperties
)
2839 AutoEnterAnalysis
enter(cx
);
2841 if (!objectTypeTable
) {
2842 objectTypeTable
= cx
->new_
<ObjectTypeTable
>();
2843 if (!objectTypeTable
|| !objectTypeTable
->init()) {
2844 js_delete(objectTypeTable
);
2845 objectTypeTable
= nullptr;
2851 * Use the object type table to allocate an object with the specified
2852 * properties, filling in its final type and shape and failing if no cache
2853 * entry could be found for the properties.
2857 * Filter out a few cases where we don't want to use the object type table.
2858 * Note that if the properties contain any duplicates or dense indexes,
2859 * the lookup below will fail as such arrays of properties cannot be stored
2860 * in the object type table --- fixObjectType populates the table with
2861 * properties read off its input object, which cannot be duplicates, and
2862 * ignores objects with dense indexes.
2864 if (!nproperties
|| nproperties
>= PropertyTree::MAX_HEIGHT
)
2867 gc::AllocKind allocKind
= gc::GetGCObjectKind(nproperties
);
2868 size_t nfixed
= gc::GetGCKindSlots(allocKind
, &JSObject::class_
);
2870 ObjectTableKey::Lookup
lookup(properties
, nproperties
, nfixed
);
2871 ObjectTypeTable::AddPtr p
= objectTypeTable
->lookupForAdd(lookup
);
2876 RootedObject
obj(cx
, NewBuiltinClassInstance(cx
, &JSObject::class_
, allocKind
));
2878 cx
->clearPendingException();
2881 JS_ASSERT(obj
->getProto() == p
->value().object
->proto().toObject());
2883 RootedShape
shape(cx
, p
->value().shape
);
2884 if (!JSObject::setLastProperty(cx
, obj
, shape
)) {
2885 cx
->clearPendingException();
2889 UpdateObjectTableEntryTypes(cx
, p
->value(), properties
, nproperties
);
2891 for (size_t i
= 0; i
< nproperties
; i
++)
2892 obj
->setSlot(i
, properties
[i
].value
);
2894 obj
->setType(p
->value().object
);
2898 /////////////////////////////////////////////////////////////////////
2900 /////////////////////////////////////////////////////////////////////
2903 TypeObject::setProto(JSContext
* cx
, TaggedProto proto
)
2905 JS_ASSERT(singleton());
2907 if (proto
.isObject() && IsInsideNursery(proto
.toObject()))
2908 addFlags(OBJECT_FLAG_NURSERY_PROTO
);
2910 setProtoUnchecked(proto
);
2914 UpdatePropertyType(ExclusiveContext
* cx
, HeapTypeSet
* types
, JSObject
* obj
, Shape
* shape
,
2917 JS_ASSERT(obj
->hasSingletonType() && !obj
->hasLazyType());
2919 if (!shape
->writable())
2920 types
->setNonWritableProperty(cx
);
2922 if (shape
->hasGetterValue() || shape
->hasSetterValue()) {
2923 types
->setNonDataProperty(cx
);
2924 types
->TypeSet::addType(Type::UnknownType(), &cx
->typeLifoAlloc());
2925 } else if (shape
->hasDefaultGetter() && shape
->hasSlot()) {
2926 if (!indexed
&& types
->canSetDefinite(shape
->slot()))
2927 types
->setDefinite(shape
->slot());
2929 const Value
& value
= obj
->nativeGetSlot(shape
->slot());
2932 * Don't add initial undefined types for properties of global objects
2933 * that are not collated into the JSID_VOID property (see propertySet
2936 if (indexed
|| !value
.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj
)) {
2937 Type type
= GetValueType(value
);
2938 types
->TypeSet::addType(type
, &cx
->typeLifoAlloc());
2941 if (indexed
|| shape
->hadOverwrite()) {
2942 types
->setNonConstantProperty(cx
);
2944 InferSpew(ISpewOps
, "typeSet: %sT%p%s property %s %s - setConstant",
2945 InferSpewColor(types
), types
, InferSpewColorReset(),
2946 TypeObjectString(obj
->type()), TypeIdString(shape
->propid()));
2952 TypeObject::updateNewPropertyTypes(ExclusiveContext
* cx
, jsid id
, HeapTypeSet
* types
)
2954 InferSpew(ISpewOps
, "typeSet: %sT%p%s property %s %s",
2955 InferSpewColor(types
), types
, InferSpewColorReset(),
2956 TypeObjectString(this), TypeIdString(id
));
2958 if (!singleton() || !singleton()->isNative()) {
2959 types
->setNonConstantProperty(cx
);
2964 * Fill the property in with any type the object already has in an own
2965 * property. We are only interested in plain native properties and
2966 * dense elements which don't go through a barrier when read by the VM
2970 if (JSID_IS_VOID(id
)) {
2971 /* Go through all shapes on the object to get integer-valued properties. */
2972 RootedShape
shape(cx
, singleton()->lastProperty());
2973 while (!shape
->isEmptyShape()) {
2974 if (JSID_IS_VOID(IdToTypeId(shape
->propid())))
2975 UpdatePropertyType(cx
, types
, singleton(), shape
, true);
2976 shape
= shape
->previous();
2979 /* Also get values of any dense elements in the object. */
2980 for (size_t i
= 0; i
< singleton()->getDenseInitializedLength(); i
++) {
2981 const Value
& value
= singleton()->getDenseElement(i
);
2982 if (!value
.isMagic(JS_ELEMENTS_HOLE
)) {
2983 Type type
= GetValueType(value
);
2984 types
->TypeSet::addType(type
, &cx
->typeLifoAlloc());
2987 } else if (!JSID_IS_EMPTY(id
)) {
2988 RootedId
rootedId(cx
, id
);
2989 Shape
* shape
= singleton()->nativeLookup(cx
, rootedId
);
2991 UpdatePropertyType(cx
, types
, singleton(), shape
, false);
2994 if (singleton()->watched()) {
2996 * Mark the property as non-data, to inhibit optimizations on it
2997 * and avoid bypassing the watchpoint handler.
2999 types
->setNonDataProperty(cx
);
3004 TypeObject::addDefiniteProperties(ExclusiveContext
* cx
, JSObject
* obj
)
3006 if (unknownProperties())
3009 /* Mark all properties of obj as definite properties of this type. */
3010 AutoEnterAnalysis
enter(cx
);
3012 RootedShape
shape(cx
, obj
->lastProperty());
3013 while (!shape
->isEmptyShape()) {
3014 jsid id
= IdToTypeId(shape
->propid());
3015 if (!JSID_IS_VOID(id
) && obj
->isFixedSlot(shape
->slot())) {
3016 TypeSet
* types
= getProperty(cx
, id
);
3019 types
->setDefinite(shape
->slot());
3021 shape
= shape
->previous();
3028 TypeObject::matchDefiniteProperties(HandleObject obj
)
3030 unsigned count
= getPropertyCount();
3031 for (unsigned i
= 0; i
< count
; i
++) {
3032 Property
* prop
= getProperty(i
);
3035 if (prop
->types
.definiteProperty()) {
3036 unsigned slot
= prop
->types
.definiteSlot();
3039 Shape
* shape
= obj
->lastProperty();
3040 while (!shape
->isEmptyShape()) {
3041 if (shape
->slot() == slot
&& shape
->propid() == prop
->id
) {
3045 shape
= shape
->previous();
3056 InlineAddTypeProperty(ExclusiveContext
* cx
, TypeObject
* obj
, jsid id
, Type type
)
3058 JS_ASSERT(id
== IdToTypeId(id
));
3060 AutoEnterAnalysis
enter(cx
);
3062 HeapTypeSet
* types
= obj
->getProperty(cx
, id
);
3066 // Clear any constant flag if it exists.
3067 if (!types
->nonConstantProperty()) {
3068 InferSpew(ISpewOps
, "constantMutated: %sT%p%s %s",
3069 InferSpewColor(types
), types
, InferSpewColorReset(), TypeString(type
));
3070 types
->setNonConstantProperty(cx
);
3073 if (types
->hasType(type
))
3076 InferSpew(ISpewOps
, "externalType: property %s %s: %s",
3077 TypeObjectString(obj
), TypeIdString(id
), TypeString(type
));
3078 types
->addType(cx
, type
);
3082 TypeObject::addPropertyType(ExclusiveContext
* cx
, jsid id
, Type type
)
3084 InlineAddTypeProperty(cx
, this, id
, type
);
3088 TypeObject::addPropertyType(ExclusiveContext
* cx
, jsid id
, const Value
& value
)
3090 InlineAddTypeProperty(cx
, this, id
, GetValueType(value
));
3094 TypeObject::markPropertyNonData(ExclusiveContext
* cx
, jsid id
)
3096 AutoEnterAnalysis
enter(cx
);
3098 id
= IdToTypeId(id
);
3100 HeapTypeSet
* types
= getProperty(cx
, id
);
3102 types
->setNonDataProperty(cx
);
3106 TypeObject::markPropertyNonWritable(ExclusiveContext
* cx
, jsid id
)
3108 AutoEnterAnalysis
enter(cx
);
3110 id
= IdToTypeId(id
);
3112 HeapTypeSet
* types
= getProperty(cx
, id
);
3114 types
->setNonWritableProperty(cx
);
3118 TypeObject::isPropertyNonData(jsid id
)
3120 TypeSet
* types
= maybeGetProperty(id
);
3122 return types
->nonDataProperty();
3127 TypeObject::isPropertyNonWritable(jsid id
)
3129 TypeSet
* types
= maybeGetProperty(id
);
3131 return types
->nonWritableProperty();
3136 TypeObject::markStateChange(ExclusiveContext
* cxArg
)
3138 if (unknownProperties())
3141 AutoEnterAnalysis
enter(cxArg
);
3142 HeapTypeSet
* types
= maybeGetProperty(JSID_EMPTY
);
3144 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
3145 TypeConstraint
* constraint
= types
->constraintList
;
3146 while (constraint
) {
3147 constraint
->newObjectState(cx
, this);
3148 constraint
= constraint
->next
;
3151 JS_ASSERT(!types
->constraintList
);
3157 TypeObject::setFlags(ExclusiveContext
* cx
, TypeObjectFlags flags
)
3159 if (hasAllFlags(flags
))
3162 AutoEnterAnalysis
enter(cx
);
3165 /* Make sure flags are consistent with persistent object state. */
3166 JS_ASSERT_IF(flags
& OBJECT_FLAG_ITERATED
,
3167 singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON
));
3172 InferSpew(ISpewOps
, "%s: setFlags 0x%x", TypeObjectString(this), flags
);
3174 ObjectStateChange(cx
, this, false);
3178 TypeObject::markUnknown(ExclusiveContext
* cx
)
3180 AutoEnterAnalysis
enter(cx
);
3182 JS_ASSERT(cx
->compartment()->activeAnalysis
);
3183 JS_ASSERT(!unknownProperties());
3185 InferSpew(ISpewOps
, "UnknownProperties: %s", TypeObjectString(this));
3188 ObjectStateChange(cx
, this, true);
3191 * Existing constraints may have already been added to this object, which we need
3192 * to do the right thing for. We can't ensure that we will mark all unknown
3193 * objects before they have been accessed, as the __proto__ of a known object
3194 * could be dynamically set to an unknown object, and we can decide to ignore
3195 * properties of an object during analysis (i.e. hashmaps). Adding unknown for
3196 * any properties accessed already accounts for possible values read from them.
3199 unsigned count
= getPropertyCount();
3200 for (unsigned i
= 0; i
< count
; i
++) {
3201 Property
* prop
= getProperty(i
);
3203 prop
->types
.addType(cx
, Type::UnknownType());
3204 prop
->types
.setNonDataProperty(cx
);
3210 TypeObject::maybeClearNewScriptOnOOM()
3218 for (unsigned i
= 0; i
< getPropertyCount(); i
++) {
3219 Property
* prop
= getProperty(i
);
3222 if (prop
->types
.definiteProperty())
3223 prop
->types
.setNonDataPropertyIgnoringConstraints();
3226 // This method is called during GC sweeping, so there is no write barrier
3227 // that needs to be triggered.
3228 js_free(newScript_
);
3229 newScript_
.unsafeSet(nullptr);
3233 TypeObject::clearNewScript(ExclusiveContext
* cx
)
3238 TypeNewScript
* newScript
= newScript_
;
3239 newScript_
= nullptr;
3241 AutoEnterAnalysis
enter(cx
);
3244 * Any definite properties we added due to analysis of the new script when
3245 * the type object was created are now invalid: objects with the same type
3246 * can be created by using 'new' on a different script or through some
3247 * other mechanism (e.g. Object.create). Rather than clear out the definite
3248 * bits on the object's properties, just mark such properties as having
3249 * been deleted/reconfigured, which will have the same effect on JITs
3250 * wanting to use the definite bits to optimize property accesses.
3252 for (unsigned i
= 0; i
< getPropertyCount(); i
++) {
3253 Property
* prop
= getProperty(i
);
3256 if (prop
->types
.definiteProperty())
3257 prop
->types
.setNonDataProperty(cx
);
3261 * If we cleared the new script while in the middle of initializing an
3262 * object, it will still have the new script's shape and reflect the no
3263 * longer correct state of the object once its initialization is completed.
3264 * We can't detect the possibility of this statically while remaining
3265 * robust, but the new script keeps track of where each property is
3266 * initialized so we can walk the stack and fix up any such objects.
3268 if (cx
->isJSContext()) {
3269 Vector
<uint32_t, 32> pcOffsets(cx
);
3270 for (ScriptFrameIter
iter(cx
->asJSContext()); !iter
.done(); ++iter
) {
3271 pcOffsets
.append(iter
.script()->pcToOffset(iter
.pc()));
3272 if (!iter
.isConstructing() ||
3273 iter
.callee() != newScript
->fun
||
3274 !iter
.thisv().isObject() ||
3275 iter
.thisv().toObject().hasLazyType() ||
3276 iter
.thisv().toObject().type() != this)
3281 // Found a matching frame.
3282 RootedObject
obj(cx
, &iter
.thisv().toObject());
3284 // Whether all identified 'new' properties have been initialized.
3285 bool finished
= false;
3287 // If not finished, number of properties that have been added.
3288 uint32_t numProperties
= 0;
3290 // Whether the current SETPROP is within an inner frame which has
3291 // finished entirely.
3292 bool pastProperty
= false;
3294 // Index in pcOffsets of the outermost frame.
3295 int callDepth
= pcOffsets
.length() - 1;
3297 // Index in pcOffsets of the frame currently being checked for a SETPROP.
3298 int setpropDepth
= callDepth
;
3300 for (TypeNewScript::Initializer
* init
= newScript
->initializerList
;; init
++) {
3301 if (init
->kind
== TypeNewScript::Initializer::SETPROP
) {
3302 if (!pastProperty
&& pcOffsets
[setpropDepth
] < init
->offset
) {
3303 // Have not yet reached this setprop.
3306 // This setprop has executed, reset state for the next one.
3308 pastProperty
= false;
3309 setpropDepth
= callDepth
;
3310 } else if (init
->kind
== TypeNewScript::Initializer::SETPROP_FRAME
) {
3311 if (!pastProperty
) {
3312 if (pcOffsets
[setpropDepth
] < init
->offset
) {
3313 // Have not yet reached this inner call.
3315 } else if (pcOffsets
[setpropDepth
] > init
->offset
) {
3316 // Have advanced past this inner call.
3317 pastProperty
= true;
3318 } else if (setpropDepth
== 0) {
3319 // Have reached this call but not yet in it.
3322 // Somewhere inside this inner call.
3327 JS_ASSERT(init
->kind
== TypeNewScript::Initializer::DONE
);
3334 (void) JSObject::rollbackProperties(cx
, obj
, numProperties
);
3337 // Threads with an ExclusiveContext are not allowed to run scripts.
3338 JS_ASSERT(!cx
->perThreadData
->activation());
3342 markStateChange(cx
);
3348 TaggedProto
tagged(proto());
3349 fprintf(stderr
, "%s : %s",
3350 TypeObjectString(this),
3351 tagged
.isObject() ? TypeString(Type::ObjectType(tagged
.toObject()))
3352 : (tagged
.isLazy() ? "(lazy)" : "(null)"));
3354 if (unknownProperties()) {
3355 fprintf(stderr
, " unknown");
3357 if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES
))
3358 fprintf(stderr
, " dense");
3359 if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED
))
3360 fprintf(stderr
, " packed");
3361 if (!hasAnyFlags(OBJECT_FLAG_LENGTH_OVERFLOW
))
3362 fprintf(stderr
, " noLengthOverflow");
3363 if (hasAnyFlags(OBJECT_FLAG_ITERATED
))
3364 fprintf(stderr
, " iterated");
3365 if (interpretedFunction
)
3366 fprintf(stderr
, " ifun");
3369 unsigned count
= getPropertyCount();
3372 fprintf(stderr
, " {}\n");
3376 fprintf(stderr
, " {");
3378 for (unsigned i
= 0; i
< count
; i
++) {
3379 Property
* prop
= getProperty(i
);
3381 fprintf(stderr
, "\n %s:", TypeIdString(prop
->id
));
3382 prop
->types
.print();
3386 fprintf(stderr
, "\n}\n");
3389 /////////////////////////////////////////////////////////////////////
3391 /////////////////////////////////////////////////////////////////////
3394 * Persistent constraint clearing out newScript and definite properties from
3395 * an object should a property on another object get a getter or setter.
3397 class TypeConstraintClearDefiniteGetterSetter
: public TypeConstraint
3402 explicit TypeConstraintClearDefiniteGetterSetter(TypeObject
* object
)
3406 const char* kind() { return "clearDefiniteGetterSetter"; }
3408 void newPropertyState(JSContext
* cx
, TypeSet
* source
) {
3410 * Clear out the newScript shape and definite property information from
3411 * an object if the source type set could be a setter or could be
3414 if (source
->nonDataProperty() || source
->nonWritableProperty())
3415 object
->clearNewScript(cx
);
3418 void newType(JSContext
* cx
, TypeSet
* source
, Type type
) {}
3420 bool sweep(TypeZone
& zone
, TypeConstraint
** res
) {
3421 if (IsTypeObjectAboutToBeFinalized(&object
))
3423 *res
= zone
.typeLifoAlloc
.new_
<TypeConstraintClearDefiniteGetterSetter
>(object
);
3429 types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext
* cx
, TypeObject
* type
, HandleId id
)
3432 * Ensure that if the properties named here could have a getter, setter or
3433 * a permanent property in any transitive prototype, the definite
3434 * properties get cleared from the type.
3436 RootedObject
parent(cx
, type
->proto().toObjectOrNull());
3438 TypeObject
* parentObject
= parent
->getType(cx
);
3439 if (!parentObject
|| parentObject
->unknownProperties())
3441 HeapTypeSet
* parentTypes
= parentObject
->getProperty(cx
, id
);
3442 if (!parentTypes
|| parentTypes
->nonDataProperty() || parentTypes
->nonWritableProperty())
3444 if (!parentTypes
->addConstraint(cx
, cx
->typeLifoAlloc().new_
<TypeConstraintClearDefiniteGetterSetter
>(type
)))
3446 parent
= parent
->getProto();
3452 * Constraint which clears definite properties on an object should a type set
3453 * contain any types other than a single object.
3455 class TypeConstraintClearDefiniteSingle
: public TypeConstraint
3460 explicit TypeConstraintClearDefiniteSingle(TypeObject
* object
)
3464 const char* kind() { return "clearDefiniteSingle"; }
3466 void newType(JSContext
* cx
, TypeSet
* source
, Type type
) {
3467 if (source
->baseFlags() || source
->getObjectCount() > 1)
3468 object
->clearNewScript(cx
);
3471 bool sweep(TypeZone
& zone
, TypeConstraint
** res
) {
3472 if (IsTypeObjectAboutToBeFinalized(&object
))
3474 *res
= zone
.typeLifoAlloc
.new_
<TypeConstraintClearDefiniteSingle
>(object
);
3480 types::AddClearDefiniteFunctionUsesInScript(JSContext
* cx
, TypeObject
* type
,
3481 JSScript
* script
, JSScript
* calleeScript
)
3483 // Look for any uses of the specified calleeScript in type sets for
3484 // |script|, and add constraints to ensure that if the type sets' contents
3485 // change then the definite properties are cleared from the type.
3486 // This ensures that the inlining performed when the definite properties
3487 // analysis was done is stable. We only need to look at type sets which
3488 // contain a single object, as IonBuilder does not inline polymorphic sites
3489 // during the definite properties analysis.
3491 TypeObjectKey
* calleeKey
= Type::ObjectType(calleeScript
->functionNonDelazifying()).objectKey();
3493 unsigned count
= TypeScript::NumTypeSets(script
);
3494 StackTypeSet
* typeArray
= script
->types
->typeArray();
3496 for (unsigned i
= 0; i
< count
; i
++) {
3497 StackTypeSet
* types
= &typeArray
[i
];
3498 if (!types
->unknownObject() && types
->getObjectCount() == 1) {
3499 if (calleeKey
!= types
->getObject(0)) {
3500 // Also check if the object is the Function.call or
3501 // Function.apply native. IonBuilder uses the presence of these
3502 // functions during inlining.
3503 JSObject
* singleton
= types
->getSingleObject(0);
3504 if (!singleton
|| !singleton
->is
<JSFunction
>())
3506 JSFunction
* fun
= &singleton
->as
<JSFunction
>();
3507 if (!fun
->isNative())
3509 if (fun
->native() != js_fun_call
&& fun
->native() != js_fun_apply
)
3512 // This is a type set that might have been used when inlining
3513 // |calleeScript| into |script|.
3514 if (!types
->addConstraint(cx
, cx
->typeLifoAlloc().new_
<TypeConstraintClearDefiniteSingle
>(type
)))
3523 * Either make the newScript information for type when it is constructed
3524 * by the specified script, or regenerate the constraints for an existing
3525 * newScript on the type after they were cleared by a GC.
3528 CheckNewScriptProperties(JSContext
* cx
, TypeObject
* type
, JSFunction
* fun
)
3530 JS_ASSERT(cx
->compartment()->activeAnalysis
);
3531 JS_ASSERT(!type
->newScript());
3533 /* Strawman object to add properties to and watch for duplicates. */
3534 RootedObject
baseobj(cx
, NewBuiltinClassInstance(cx
, &JSObject::class_
, gc::FINALIZE_OBJECT16
));
3538 Vector
<TypeNewScript::Initializer
> initializerList(cx
);
3540 if (!jit::AnalyzeNewScriptProperties(cx
, fun
, type
, baseobj
, &initializerList
))
3543 if (baseobj
->slotSpan() == 0 || type
->unknownProperties())
3546 gc::AllocKind kind
= gc::GetGCObjectKind(baseobj
->slotSpan());
3548 /* We should not have overflowed the maximum number of fixed slots for an object. */
3549 JS_ASSERT(gc::GetGCKindSlots(kind
) >= baseobj
->slotSpan());
3551 TypeNewScript::Initializer
done(TypeNewScript::Initializer::DONE
, 0);
3554 * The base object may have been created with a different finalize kind
3555 * than we will use for subsequent new objects. Generate an object with the
3556 * appropriate final shape.
3558 Rooted
<TypeObject
*> rootedType(cx
, type
);
3559 RootedShape
shape(cx
, baseobj
->lastProperty());
3560 baseobj
= NewReshapedObject(cx
, rootedType
, baseobj
->getParent(), kind
, shape
, MaybeSingletonObject
);
3562 !type
->addDefiniteProperties(cx
, baseobj
) ||
3563 !initializerList
.append(done
))
3568 TypeNewScript
* newScript
= type
->pod_calloc_with_extra
<TypeNewScript
, TypeNewScript::Initializer
>(
3569 initializerList
.length());
3573 new (newScript
) TypeNewScript();
3575 type
->setNewScript(newScript
);
3577 newScript
->fun
= fun
;
3578 newScript
->templateObject
= baseobj
;
3580 newScript
->initializerList
= reinterpret_cast<TypeNewScript::Initializer
*>(newScript
+ 1);
3581 PodCopy(newScript
->initializerList
,
3582 initializerList
.begin(),
3583 initializerList
.length());
3585 js::gc::TraceTypeNewScript(type
);
3588 /////////////////////////////////////////////////////////////////////
3589 // Interface functions
3590 /////////////////////////////////////////////////////////////////////
3593 types::TypeMonitorCallSlow(JSContext
* cx
, JSObject
* callee
, const CallArgs
& args
,
3596 unsigned nargs
= callee
->as
<JSFunction
>().nargs();
3597 JSScript
* script
= callee
->as
<JSFunction
>().nonLazyScript();
3600 TypeScript::SetThis(cx
, script
, args
.thisv());
3603 * Add constraints going up to the minimum of the actual and formal count.
3604 * If there are more actuals than formals the later values can only be
3605 * accessed through the arguments object, which is monitored.
3608 for (; arg
< args
.length() && arg
< nargs
; arg
++)
3609 TypeScript::SetArgument(cx
, script
, arg
, args
[arg
]);
3611 /* Watch for fewer actuals than formals to the call. */
3612 for (; arg
< nargs
; arg
++)
3613 TypeScript::SetArgument(cx
, script
, arg
, UndefinedValue());
3617 IsAboutToBeFinalized(TypeObjectKey
** keyp
)
3619 /* Mask out the low bit indicating whether this is a type or JS object. */
3620 uintptr_t flagBit
= uintptr_t(*keyp
) & 1;
3621 gc::Cell
* tmp
= reinterpret_cast<gc::Cell
*>(uintptr_t(*keyp
) & ~1);
3622 bool isAboutToBeFinalized
= IsCellAboutToBeFinalized(&tmp
);
3623 *keyp
= reinterpret_cast<TypeObjectKey
*>(uintptr_t(tmp
) | flagBit
);
3624 return isAboutToBeFinalized
;
3628 types::FillBytecodeTypeMap(JSScript
* script
, uint32_t* bytecodeMap
)
3631 for (jsbytecode
* pc
= script
->code(); pc
< script
->codeEnd(); pc
+= GetBytecodeLength(pc
)) {
3632 JSOp op
= JSOp(*pc
);
3633 if (js_CodeSpec
[op
].format
& JOF_TYPESET
) {
3634 bytecodeMap
[added
++] = script
->pcToOffset(pc
);
3635 if (added
== script
->nTypeSets())
3639 JS_ASSERT(added
== script
->nTypeSets());
3643 types::GetOrFixupCopyOnWriteObject(JSContext
* cx
, HandleScript script
, jsbytecode
* pc
)
3645 // Make sure that the template object for script/pc has a type indicating
3646 // that the object and its copies have copy on write elements.
3647 RootedObject
obj(cx
, script
->getObject(GET_UINT32_INDEX(pc
)));
3648 JS_ASSERT(obj
->is
<ArrayObject
>());
3649 JS_ASSERT(obj
->denseElementsAreCopyOnWrite());
3651 if (obj
->type()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE
))
3654 RootedTypeObject
type(cx
, TypeScript::InitObject(cx
, script
, pc
, JSProto_Array
));
3658 type
->addFlags(OBJECT_FLAG_COPY_ON_WRITE
);
3660 // Update type information in the initializer object type.
3661 JS_ASSERT(obj
->slotSpan() == 0);
3662 for (size_t i
= 0; i
< obj
->getDenseInitializedLength(); i
++) {
3663 const Value
& v
= obj
->getDenseElement(i
);
3664 AddTypePropertyId(cx
, type
, JSID_VOID
, v
);
3672 types::GetCopyOnWriteObject(JSScript
* script
, jsbytecode
* pc
)
3674 // GetOrFixupCopyOnWriteObject should already have been called for
3675 // script/pc, ensuring that the template object has a type with the
3676 // COPY_ON_WRITE flag. We don't assert this here, due to a corner case
3677 // where this property doesn't hold. See jsop_newarray_copyonwrite in
3679 JSObject
* obj
= script
->getObject(GET_UINT32_INDEX(pc
));
3680 JS_ASSERT(obj
->is
<ArrayObject
>());
3681 JS_ASSERT(obj
->denseElementsAreCopyOnWrite());
3687 types::TypeMonitorResult(JSContext
* cx
, JSScript
* script
, jsbytecode
* pc
, const js::Value
& rval
)
3689 /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
3690 if (!(js_CodeSpec
[*pc
].format
& JOF_TYPESET
))
3693 if (!script
->hasBaselineScript())
3696 AutoEnterAnalysis
enter(cx
);
3698 Type type
= GetValueType(rval
);
3699 StackTypeSet
* types
= TypeScript::BytecodeTypes(script
, pc
);
3700 if (types
->hasType(type
))
3703 InferSpew(ISpewOps
, "bytecodeType: #%u:%05u: %s",
3704 script
->id(), script
->pcToOffset(pc
), TypeString(type
));
3705 types
->addType(cx
, type
);
3709 types::UseNewTypeForClone(JSFunction
* fun
)
3711 if (!fun
->isInterpreted())
3714 if (fun
->hasScript() && fun
->nonLazyScript()->shouldCloneAtCallsite())
3720 if (fun
->hasSingletonType())
3724 * When a function is being used as a wrapper for another function, it
3725 * improves precision greatly to distinguish between different instances of
3726 * the wrapper; otherwise we will conflate much of the information about
3727 * the wrapped functions.
3729 * An important example is the Class.create function at the core of the
3730 * Prototype.js library, which looks like:
3733 * create: function() {
3734 * return function() {
3735 * this.initialize.apply(this, arguments);
3740 * Each instance of the innermost function will have a different wrapped
3741 * initialize method. We capture this, along with similar cases, by looking
3742 * for short scripts which use both .apply and arguments. For such scripts,
3743 * whenever creating a new instance of the function we both give that
3744 * instance a singleton type and clone the underlying script.
3747 uint32_t begin
, end
;
3748 if (fun
->hasScript()) {
3749 if (!fun
->nonLazyScript()->usesArgumentsAndApply())
3751 begin
= fun
->nonLazyScript()->sourceStart();
3752 end
= fun
->nonLazyScript()->sourceEnd();
3754 if (!fun
->lazyScript()->usesArgumentsAndApply())
3756 begin
= fun
->lazyScript()->begin();
3757 end
= fun
->lazyScript()->end();
3760 return end
- begin
<= 100;
3762 /////////////////////////////////////////////////////////////////////
3764 /////////////////////////////////////////////////////////////////////
3767 JSScript::makeTypes(JSContext
* cx
)
3771 AutoEnterAnalysis
enter(cx
);
3773 unsigned count
= TypeScript::NumTypeSets(this);
3775 TypeScript
* typeScript
= (TypeScript
*)
3776 pod_calloc
<uint8_t>(TypeScript::SizeIncludingTypeArray(count
));
3783 StackTypeSet
* typeArray
= typeScript
->typeArray();
3784 for (unsigned i
= 0; i
< nTypeSets(); i
++) {
3785 InferSpew(ISpewOps
, "typeSet: %sT%p%s bytecode%u #%u",
3786 InferSpewColor(&typeArray
[i
]), &typeArray
[i
], InferSpewColorReset(),
3789 TypeSet
* thisTypes
= TypeScript::ThisTypes(this);
3790 InferSpew(ISpewOps
, "typeSet: %sT%p%s this #%u",
3791 InferSpewColor(thisTypes
), thisTypes
, InferSpewColorReset(),
3793 unsigned nargs
= functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
3794 for (unsigned i
= 0; i
< nargs
; i
++) {
3795 TypeSet
* types
= TypeScript::ArgTypes(this, i
);
3796 InferSpew(ISpewOps
, "typeSet: %sT%p%s arg%u #%u",
3797 InferSpewColor(types
), types
, InferSpewColorReset(),
3806 JSFunction::setTypeForScriptedFunction(ExclusiveContext
* cx
, HandleFunction fun
,
3807 bool singleton
/* = false */)
3810 if (!setSingletonType(cx
, fun
))
3813 RootedObject
funProto(cx
, fun
->getProto());
3814 Rooted
<TaggedProto
> taggedProto(cx
, TaggedProto(funProto
));
3816 cx
->compartment()->types
.newTypeObject(cx
, &JSFunction::class_
, taggedProto
);
3821 type
->interpretedFunction
= fun
;
3827 /////////////////////////////////////////////////////////////////////
3829 /////////////////////////////////////////////////////////////////////
3832 JSObject::shouldSplicePrototype(JSContext
* cx
)
3835 * During bootstrapping, if inference is enabled we need to make sure not
3836 * to splice a new prototype in for Function.prototype or the global
3837 * object if their __proto__ had previously been set to null, as this
3838 * will change the prototype for all other objects with the same type.
3840 if (getProto() != nullptr)
3842 return hasSingletonType();
3846 JSObject::splicePrototype(JSContext
* cx
, const Class
* clasp
, Handle
<TaggedProto
> proto
)
3848 JS_ASSERT(cx
->compartment() == compartment());
3850 RootedObject
self(cx
, this);
3853 * For singleton types representing only a single JSObject, the proto
3854 * can be rearranged as needed without destroying type information for
3855 * the old or new types.
3857 JS_ASSERT(self
->hasSingletonType());
3859 /* Inner objects may not appear on prototype chains. */
3860 JS_ASSERT_IF(proto
.isObject(), !proto
.toObject()->getClass()->ext
.outerObject
);
3863 * Force type instantiation when splicing lazy types. This may fail,
3864 * in which case inference will be disabled for the compartment.
3866 Rooted
<TypeObject
*> type(cx
, self
->getType(cx
));
3869 Rooted
<TypeObject
*> protoType(cx
, nullptr);
3870 if (proto
.isObject()) {
3871 protoType
= proto
.toObject()->getType(cx
);
3876 type
->setClasp(clasp
);
3877 type
->setProto(cx
, proto
);
3881 /* static */ TypeObject
*
3882 JSObject::makeLazyType(JSContext
* cx
, HandleObject obj
)
3884 JS_ASSERT(obj
->hasLazyType());
3885 JS_ASSERT(cx
->compartment() == obj
->compartment());
3887 /* De-lazification of functions can GC, so we need to do it up here. */
3888 if (obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().isInterpretedLazy()) {
3889 RootedFunction
fun(cx
, &obj
->as
<JSFunction
>());
3890 if (!fun
->getOrCreateScript(cx
))
3894 // Find flags which need to be specified immediately on the object.
3895 // Don't track whether singletons are packed.
3896 TypeObjectFlags initialFlags
= OBJECT_FLAG_NON_PACKED
;
3898 if (obj
->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON
))
3899 initialFlags
|= OBJECT_FLAG_ITERATED
;
3901 if (obj
->isIndexed())
3902 initialFlags
|= OBJECT_FLAG_SPARSE_INDEXES
;
3904 if (obj
->is
<ArrayObject
>() && obj
->as
<ArrayObject
>().length() > INT32_MAX
)
3905 initialFlags
|= OBJECT_FLAG_LENGTH_OVERFLOW
;
3907 Rooted
<TaggedProto
> proto(cx
, obj
->getTaggedProto());
3908 TypeObject
* type
= cx
->compartment()->types
.newTypeObject(cx
, obj
->getClass(), proto
, initialFlags
);
3912 AutoEnterAnalysis
enter(cx
);
3914 /* Fill in the type according to the state of this object. */
3916 type
->initSingleton(obj
);
3918 if (obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().isInterpreted())
3919 type
->interpretedFunction
= &obj
->as
<JSFunction
>();
3926 /* static */ inline HashNumber
3927 TypeObjectWithNewScriptEntry::hash(const Lookup
& lookup
)
3929 return PointerHasher
<JSObject
*, 3>::hash(lookup
.hashProto
.raw()) ^
3930 PointerHasher
<const Class
*, 3>::hash(lookup
.clasp
) ^
3931 PointerHasher
<JSFunction
*, 3>::hash(lookup
.newFunction
);
3934 /* static */ inline bool
3935 TypeObjectWithNewScriptEntry::match(const TypeObjectWithNewScriptEntry
& key
, const Lookup
& lookup
)
3937 return key
.object
->proto() == lookup
.matchProto
&&
3938 key
.object
->clasp() == lookup
.clasp
&&
3939 key
.newFunction
== lookup
.newFunction
;
3944 JSObject::hasNewType(const Class
* clasp
, TypeObject
* type
)
3946 TypeObjectWithNewScriptSet
& table
= compartment()->newTypeObjects
;
3948 if (!table
.initialized())
3951 TypeObjectWithNewScriptSet::Ptr p
= table
.lookup(TypeObjectWithNewScriptSet::Lookup(clasp
, TaggedProto(this), nullptr));
3952 return p
&& p
->object
== type
;
3957 JSObject::setNewTypeUnknown(JSContext
* cx
, const Class
* clasp
, HandleObject obj
)
3959 if (!obj
->setFlag(cx
, js::BaseShape::NEW_TYPE_UNKNOWN
))
3963 * If the object already has a new type, mark that type as unknown. It will
3964 * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
3965 * crawl if prototypes of the object change dynamically in the future.
3967 TypeObjectWithNewScriptSet
& table
= cx
->compartment()->newTypeObjects
;
3968 if (table
.initialized()) {
3969 Rooted
<TaggedProto
> taggedProto(cx
, TaggedProto(obj
));
3970 if (TypeObjectWithNewScriptSet::Ptr p
= table
.lookup(TypeObjectWithNewScriptSet::Lookup(clasp
, taggedProto
, nullptr)))
3971 MarkTypeObjectUnknownProperties(cx
, p
->object
);
3977 #ifdef JSGC_GENERATIONAL
3979 * This class is used to add a post barrier on the newTypeObjects set, as the
3980 * key is calculated from a prototype object which may be moved by generational
3983 class NewTypeObjectsSetRef
: public BufferableRef
3985 TypeObjectWithNewScriptSet
* set
;
3988 JSFunction
* newFunction
;
3991 NewTypeObjectsSetRef(TypeObjectWithNewScriptSet
* s
, const Class
* clasp
, JSObject
* proto
, JSFunction
* newFunction
)
3992 : set(s
), clasp(clasp
), proto(proto
), newFunction(newFunction
)
3995 void mark(JSTracer
* trc
) {
3996 JSObject
* prior
= proto
;
3997 trc
->setTracingLocation(&*prior
);
3998 Mark(trc
, &proto
, "newTypeObjects set prototype");
4002 TypeObjectWithNewScriptSet::Ptr p
= set
->lookup(TypeObjectWithNewScriptSet::Lookup(clasp
, TaggedProto(prior
), TaggedProto(proto
), newFunction
));
4003 JS_ASSERT(p
); // newTypeObjects set must still contain original entry.
4005 set
->rekeyAs(TypeObjectWithNewScriptSet::Lookup(clasp
, TaggedProto(prior
), TaggedProto(proto
), newFunction
),
4006 TypeObjectWithNewScriptSet::Lookup(clasp
, TaggedProto(proto
), newFunction
), *p
);
4011 TypeObjectTablePostBarrier(ExclusiveContext
* cx
, TypeObjectWithNewScriptSet
* table
,
4012 const Class
* clasp
, TaggedProto proto
, JSFunction
* fun
)
4014 JS_ASSERT_IF(fun
, !IsInsideNursery(fun
));
4016 if (!proto
.isObject())
4019 if (!cx
->isJSContext()) {
4020 JS_ASSERT(!IsInsideNursery(proto
.toObject()));
4024 if (IsInsideNursery(proto
.toObject())) {
4025 StoreBuffer
& sb
= cx
->asJSContext()->runtime()->gc
.storeBuffer
;
4026 sb
.putGeneric(NewTypeObjectsSetRef(table
, clasp
, proto
.toObject(), fun
));
4032 ExclusiveContext::getNewType(const Class
* clasp
, TaggedProto proto
, JSFunction
* fun
)
4034 JS_ASSERT_IF(fun
, proto
.isObject());
4035 JS_ASSERT_IF(proto
.isObject(), isInsideCurrentCompartment(proto
.toObject()));
4037 TypeObjectWithNewScriptSet
& newTypeObjects
= compartment()->newTypeObjects
;
4039 if (!newTypeObjects
.initialized() && !newTypeObjects
.init())
4042 // Canonicalize new functions to use the original one associated with its script.
4044 if (fun
->hasScript())
4045 fun
= fun
->nonLazyScript()->functionNonDelazifying();
4046 else if (fun
->isInterpretedLazy() && !fun
->isSelfHostedBuiltin())
4047 fun
= fun
->lazyScript()->functionNonDelazifying();
4052 TypeObjectWithNewScriptSet::AddPtr p
=
4053 newTypeObjects
.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp
, proto
, fun
));
4055 TypeObject
* type
= p
->object
;
4056 JS_ASSERT(type
->clasp() == clasp
);
4057 JS_ASSERT(type
->proto() == proto
);
4058 JS_ASSERT_IF(type
->newScript(), type
->newScript()->fun
== fun
);
4062 AutoEnterAnalysis
enter(this);
4064 if (proto
.isObject() && !proto
.toObject()->setDelegate(this))
4067 TypeObjectFlags initialFlags
= 0;
4068 if (!proto
.isObject() || proto
.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN
)) {
4069 // The new type is not present in any type sets, so mark the object as
4070 // unknown in all type sets it appears in. This allows the prototype of
4071 // such objects to mutate freely without triggering an expensive walk of
4072 // the compartment's type sets. (While scripts normally don't mutate
4073 // __proto__, the browser will for proxies and such, and we need to
4074 // accommodate this behavior).
4075 initialFlags
= OBJECT_FLAG_UNKNOWN_MASK
| OBJECT_FLAG_SETS_MARKED_UNKNOWN
;
4078 Rooted
<TaggedProto
> protoRoot(this, proto
);
4079 TypeObject
* type
= compartment()->types
.newTypeObject(this, clasp
, protoRoot
, initialFlags
);
4083 if (!newTypeObjects
.add(p
, TypeObjectWithNewScriptEntry(type
, fun
)))
4086 #ifdef JSGC_GENERATIONAL
4087 TypeObjectTablePostBarrier(this, &newTypeObjects
, clasp
, proto
, fun
);
4090 if (proto
.isObject()) {
4091 RootedObject
obj(this, proto
.toObject());
4094 CheckNewScriptProperties(asJSContext(), type
, fun
);
4097 * Some builtin objects have slotful native properties baked in at
4098 * creation via the Shape::{insert,get}initialShape mechanism. Since
4099 * these properties are never explicitly defined on new objects, update
4100 * the type information for them here.
4103 if (obj
->is
<RegExpObject
>()) {
4104 AddTypePropertyId(this, type
, NameToId(names().source
), Type::StringType());
4105 AddTypePropertyId(this, type
, NameToId(names().global
), Type::BooleanType());
4106 AddTypePropertyId(this, type
, NameToId(names().ignoreCase
), Type::BooleanType());
4107 AddTypePropertyId(this, type
, NameToId(names().multiline
), Type::BooleanType());
4108 AddTypePropertyId(this, type
, NameToId(names().sticky
), Type::BooleanType());
4109 AddTypePropertyId(this, type
, NameToId(names().lastIndex
), Type::Int32Type());
4112 if (obj
->is
<StringObject
>())
4113 AddTypePropertyId(this, type
, NameToId(names().length
), Type::Int32Type());
4115 if (obj
->is
<ErrorObject
>()) {
4116 AddTypePropertyId(this, type
, NameToId(names().fileName
), Type::StringType());
4117 AddTypePropertyId(this, type
, NameToId(names().lineNumber
), Type::Int32Type());
4118 AddTypePropertyId(this, type
, NameToId(names().columnNumber
), Type::Int32Type());
4119 AddTypePropertyId(this, type
, NameToId(names().stack
), Type::StringType());
4127 ExclusiveContext::getSingletonType(const Class
* clasp
, TaggedProto proto
)
4129 JS_ASSERT_IF(proto
.isObject(), compartment() == proto
.toObject()->compartment());
4131 AutoEnterAnalysis
enter(this);
4133 TypeObjectWithNewScriptSet
& table
= compartment()->lazyTypeObjects
;
4135 if (!table
.initialized() && !table
.init())
4138 TypeObjectWithNewScriptSet::AddPtr p
= table
.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp
, proto
, nullptr));
4140 TypeObject
* type
= p
->object
;
4141 JS_ASSERT(type
->lazy());
4146 Rooted
<TaggedProto
> protoRoot(this, proto
);
4147 TypeObject
* type
= compartment()->types
.newTypeObject(this, clasp
, protoRoot
);
4151 if (!table
.add(p
, TypeObjectWithNewScriptEntry(type
, nullptr)))
4154 #ifdef JSGC_GENERATIONAL
4155 TypeObjectTablePostBarrier(this, &table
, clasp
, proto
, nullptr);
4158 type
->initSingleton((JSObject
*) TypeObject::LAZY_SINGLETON
);
4159 MOZ_ASSERT(type
->singleton(), "created type must be a proper singleton");
4164 /////////////////////////////////////////////////////////////////////
4166 /////////////////////////////////////////////////////////////////////
4169 ConstraintTypeSet::sweep(Zone
* zone
, bool* oom
)
4172 * Purge references to type objects that are no longer live. Type sets hold
4173 * only weak references. For type sets containing more than one object,
4174 * live entries in the object hash need to be copied to the zone's
4177 unsigned objectCount
= baseObjectCount();
4178 if (objectCount
>= 2) {
4179 unsigned oldCapacity
= HashSetCapacity(objectCount
);
4180 TypeObjectKey
** oldArray
= objectSet
;
4184 for (unsigned i
= 0; i
< oldCapacity
; i
++) {
4185 TypeObjectKey
* object
= oldArray
[i
];
4186 if (object
&& !IsAboutToBeFinalized(&object
)) {
4187 TypeObjectKey
** pentry
=
4188 HashSetInsert
<TypeObjectKey
*,TypeObjectKey
,TypeObjectKey
>
4189 (zone
->types
.typeLifoAlloc
, objectSet
, objectCount
, object
);
4194 flags
|= TYPE_FLAG_ANYOBJECT
;
4201 setBaseObjectCount(objectCount
);
4202 } else if (objectCount
== 1) {
4203 TypeObjectKey
* object
= (TypeObjectKey
*) objectSet
;
4204 if (IsAboutToBeFinalized(&object
)) {
4205 objectSet
= nullptr;
4206 setBaseObjectCount(0);
4208 objectSet
= reinterpret_cast<TypeObjectKey
**>(object
);
4213 * Type constraints only hold weak references. Copy constraints referring
4214 * to data that is still live into the zone's new arena.
4216 TypeConstraint
* constraint
= constraintList
;
4217 constraintList
= nullptr;
4218 while (constraint
) {
4219 TypeConstraint
* copy
;
4220 if (constraint
->sweep(zone
->types
, ©
)) {
4222 copy
->next
= constraintList
;
4223 constraintList
= copy
;
4228 constraint
= constraint
->next
;
4233 TypeObject::clearProperties()
4235 setBasePropertyCount(0);
4236 propertySet
= nullptr;
4240 * Before sweeping the arenas themselves, scan all type objects in a
4241 * compartment to fixup weak references: property type sets referencing dead
4242 * JS and type objects, and singleton JS objects whose type is not referenced
4243 * elsewhere. This also releases memory associated with dead type objects,
4244 * so that type objects do not need later finalization.
4247 TypeObject::sweep(FreeOp
* fop
, bool* oom
)
4251 fop
->free_(newScript_
);
4255 LifoAlloc
& typeLifoAlloc
= zone()->types
.typeLifoAlloc
;
4258 * Properties were allocated from the old arena, and need to be copied over
4261 unsigned propertyCount
= basePropertyCount();
4262 if (propertyCount
>= 2) {
4263 unsigned oldCapacity
= HashSetCapacity(propertyCount
);
4264 Property
** oldArray
= propertySet
;
4268 for (unsigned i
= 0; i
< oldCapacity
; i
++) {
4269 Property
* prop
= oldArray
[i
];
4271 if (singleton() && !prop
->types
.constraintList
&& !zone()->isPreservingCode()) {
4273 * Don't copy over properties of singleton objects when their
4274 * presence will not be required by jitcode or type constraints
4275 * (i.e. for the definite properties analysis). The contents of
4276 * these type sets will be regenerated as necessary.
4281 Property
* newProp
= typeLifoAlloc
.new_
<Property
>(*prop
);
4284 HashSetInsert
<jsid
,Property
,Property
>
4285 (typeLifoAlloc
, propertySet
, propertyCount
, prop
->id
);
4288 newProp
->types
.sweep(zone(), oom
);
4294 addFlags(OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_UNKNOWN_PROPERTIES
);
4299 setBasePropertyCount(propertyCount
);
4300 } else if (propertyCount
== 1) {
4301 Property
* prop
= (Property
*) propertySet
;
4302 if (singleton() && !prop
->types
.constraintList
&& !zone()->isPreservingCode()) {
4306 Property
* newProp
= typeLifoAlloc
.new_
<Property
>(*prop
);
4308 propertySet
= (Property
**) newProp
;
4309 newProp
->types
.sweep(zone(), oom
);
4312 addFlags(OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_UNKNOWN_PROPERTIES
);
4321 TypeCompartment::clearTables()
4323 if (allocationSiteTable
&& allocationSiteTable
->initialized())
4324 allocationSiteTable
->clear();
4325 if (arrayTypeTable
&& arrayTypeTable
->initialized())
4326 arrayTypeTable
->clear();
4327 if (objectTypeTable
&& objectTypeTable
->initialized())
4328 objectTypeTable
->clear();
4332 TypeCompartment::sweep(FreeOp
* fop
)
4335 * Iterate through the array/object type tables and remove all entries
4336 * referencing collected data. These tables only hold weak references.
4339 if (arrayTypeTable
) {
4340 for (ArrayTypeTable::Enum
e(*arrayTypeTable
); !e
.empty(); e
.popFront()) {
4341 const ArrayTableKey
& key
= e
.front().key();
4342 JS_ASSERT(key
.type
.isUnknown() || !key
.type
.isSingleObject());
4344 bool remove
= false;
4345 TypeObject
* typeObject
= nullptr;
4346 if (!key
.type
.isUnknown() && key
.type
.isTypeObject()) {
4347 typeObject
= key
.type
.typeObjectNoBarrier();
4348 if (IsTypeObjectAboutToBeFinalized(&typeObject
))
4351 if (IsTypeObjectAboutToBeFinalized(e
.front().value().unsafeGet()))
4356 } else if (typeObject
&& typeObject
!= key
.type
.typeObjectNoBarrier()) {
4357 ArrayTableKey newKey
;
4358 newKey
.type
= Type::ObjectType(typeObject
);
4359 newKey
.proto
= key
.proto
;
4360 e
.rekeyFront(newKey
);
4365 if (objectTypeTable
) {
4366 for (ObjectTypeTable::Enum
e(*objectTypeTable
); !e
.empty(); e
.popFront()) {
4367 const ObjectTableKey
& key
= e
.front().key();
4368 ObjectTableEntry
& entry
= e
.front().value();
4370 bool remove
= false;
4371 if (IsTypeObjectAboutToBeFinalized(entry
.object
.unsafeGet()))
4373 if (IsShapeAboutToBeFinalized(entry
.shape
.unsafeGet()))
4375 for (unsigned i
= 0; !remove
&& i
< key
.nproperties
; i
++) {
4376 if (JSID_IS_STRING(key
.properties
[i
])) {
4377 JSString
* str
= JSID_TO_STRING(key
.properties
[i
]);
4378 if (IsStringAboutToBeFinalized(&str
))
4380 JS_ASSERT(AtomToId((JSAtom
*)str
) == key
.properties
[i
]);
4381 } else if (JSID_IS_SYMBOL(key
.properties
[i
])) {
4382 JS::Symbol
* sym
= JSID_TO_SYMBOL(key
.properties
[i
]);
4383 if (IsSymbolAboutToBeFinalized(&sym
))
4387 JS_ASSERT(!entry
.types
[i
].isSingleObject());
4388 TypeObject
* typeObject
= nullptr;
4389 if (entry
.types
[i
].isTypeObject()) {
4390 typeObject
= entry
.types
[i
].typeObjectNoBarrier();
4391 if (IsTypeObjectAboutToBeFinalized(&typeObject
))
4393 else if (typeObject
!= entry
.types
[i
].typeObjectNoBarrier())
4394 entry
.types
[i
] = Type::ObjectType(typeObject
);
4399 js_free(key
.properties
);
4400 js_free(entry
.types
);
4406 if (allocationSiteTable
) {
4407 for (AllocationSiteTable::Enum
e(*allocationSiteTable
); !e
.empty(); e
.popFront()) {
4408 AllocationSiteKey key
= e
.front().key();
4409 bool keyDying
= IsScriptAboutToBeFinalized(&key
.script
);
4410 bool valDying
= IsTypeObjectAboutToBeFinalized(e
.front().value().unsafeGet());
4411 if (keyDying
|| valDying
)
4413 else if (key
.script
!= e
.front().key().script
)
4420 JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet
& table
)
4422 JS_ASSERT(zone()->isCollecting());
4423 if (table
.initialized()) {
4424 for (TypeObjectWithNewScriptSet::Enum
e(table
); !e
.empty(); e
.popFront()) {
4425 TypeObjectWithNewScriptEntry entry
= e
.front();
4426 if (IsTypeObjectAboutToBeFinalized(entry
.object
.unsafeGet()) ||
4427 (entry
.newFunction
&& IsObjectAboutToBeFinalized(&entry
.newFunction
)))
4431 /* Any rekeying necessary is handled by fixupNewTypeObjectTable() below. */
4432 JS_ASSERT(entry
.object
== e
.front().object
);
4433 JS_ASSERT(entry
.newFunction
== e
.front().newFunction
);
4439 #ifdef JSGC_COMPACTING
4441 JSCompartment::fixupNewTypeObjectTable(TypeObjectWithNewScriptSet
& table
)
4444 * Each entry's hash depends on the object's prototype and we can't tell
4445 * whether that has been moved or not in sweepNewTypeObjectTable().
4447 JS_ASSERT(zone()->isCollecting());
4448 if (table
.initialized()) {
4449 for (TypeObjectWithNewScriptSet::Enum
e(table
); !e
.empty(); e
.popFront()) {
4450 TypeObjectWithNewScriptEntry entry
= e
.front();
4451 bool needRekey
= false;
4452 if (IsForwarded(entry
.object
.get())) {
4453 entry
.object
.set(Forwarded(entry
.object
.get()));
4456 TaggedProto proto
= entry
.object
->proto();
4457 if (proto
.isObject() && IsForwarded(proto
.toObject())) {
4458 proto
= TaggedProto(Forwarded(proto
.toObject()));
4461 if (entry
.newFunction
&& IsForwarded(entry
.newFunction
)) {
4462 entry
.newFunction
= Forwarded(entry
.newFunction
);
4466 TypeObjectWithNewScriptSet::Lookup
lookup(entry
.object
->clasp(),
4469 e
.rekeyFront(lookup
, entry
);
4476 #ifdef JSGC_HASH_TABLE_CHECKS
4479 JSCompartment::checkTypeObjectTablesAfterMovingGC()
4481 checkTypeObjectTableAfterMovingGC(newTypeObjects
);
4482 checkTypeObjectTableAfterMovingGC(lazyTypeObjects
);
4486 JSCompartment::checkTypeObjectTableAfterMovingGC(TypeObjectWithNewScriptSet
& table
)
4489 * Assert that nothing points into the nursery or needs to be relocated, and
4490 * that the hash table entries are discoverable.
4492 if (!table
.initialized())
4495 for (TypeObjectWithNewScriptSet::Enum
e(table
); !e
.empty(); e
.popFront()) {
4496 TypeObjectWithNewScriptEntry entry
= e
.front();
4497 CheckGCThingAfterMovingGC(entry
.object
.get());
4498 TaggedProto proto
= entry
.object
->proto();
4499 if (proto
.isObject())
4500 CheckGCThingAfterMovingGC(proto
.toObject());
4501 CheckGCThingAfterMovingGC(entry
.newFunction
);
4503 TypeObjectWithNewScriptEntry::Lookup
4504 lookup(entry
.object
->clasp(), proto
, entry
.newFunction
);
4505 TypeObjectWithNewScriptSet::Ptr ptr
= table
.lookup(lookup
);
4506 JS_ASSERT(ptr
.found() && &*ptr
== &e
.front());
4510 #endif // JSGC_HASH_TABLE_CHECKS
4512 TypeCompartment::~TypeCompartment()
4514 js_delete(arrayTypeTable
);
4515 js_delete(objectTypeTable
);
4516 js_delete(allocationSiteTable
);
4520 TypeScript::Sweep(FreeOp
* fop
, JSScript
* script
, bool* oom
)
4522 JSCompartment
* compartment
= script
->compartment();
4523 JS_ASSERT(compartment
->zone()->isGCSweepingOrCompacting());
4525 unsigned num
= NumTypeSets(script
);
4526 StackTypeSet
* typeArray
= script
->types
->typeArray();
4528 /* Remove constraints and references to dead objects from the persistent type sets. */
4529 for (unsigned i
= 0; i
< num
; i
++)
4530 typeArray
[i
].sweep(compartment
->zone(), oom
);
4534 TypeScript::destroy()
4540 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
,
4542 size_t* baselineStubsOptimized
)
4544 *typePool
+= types
.typeLifoAlloc
.sizeOfExcludingThis(mallocSizeOf
);
4546 *baselineStubsOptimized
+=
4547 jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf
);
4552 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
,
4553 size_t* allocationSiteTables
,
4554 size_t* arrayTypeTables
,
4555 size_t* objectTypeTables
)
4557 if (allocationSiteTable
)
4558 *allocationSiteTables
+= allocationSiteTable
->sizeOfIncludingThis(mallocSizeOf
);
4561 *arrayTypeTables
+= arrayTypeTable
->sizeOfIncludingThis(mallocSizeOf
);
4563 if (objectTypeTable
) {
4564 *objectTypeTables
+= objectTypeTable
->sizeOfIncludingThis(mallocSizeOf
);
4566 for (ObjectTypeTable::Enum
e(*objectTypeTable
);
4570 const ObjectTableKey
& key
= e
.front().key();
4571 const ObjectTableEntry
& value
= e
.front().value();
4573 /* key.ids and values.types have the same length. */
4574 *objectTypeTables
+= mallocSizeOf(key
.properties
) + mallocSizeOf(value
.types
);
4580 TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
4582 return mallocSizeOf(newScript_
);
4585 TypeZone::TypeZone(Zone
* zone
)
4587 typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE
),
4588 compilerOutputs(nullptr),
4589 pendingRecompiles(nullptr)
4593 TypeZone::~TypeZone()
4595 js_delete(compilerOutputs
);
4596 js_delete(pendingRecompiles
);
4600 TypeZone::sweep(FreeOp
* fop
, bool releaseTypes
, bool* oom
)
4602 JS_ASSERT(zone()->isGCSweepingOrCompacting());
4604 JSRuntime
* rt
= fop
->runtime();
4607 * Clear the analysis pool, but don't release its data yet. While
4608 * sweeping types any live data will be allocated into the pool.
4610 LifoAlloc
oldAlloc(typeLifoAlloc
.defaultChunkSize());
4611 oldAlloc
.steal(&typeLifoAlloc
);
4613 /* Sweep and find compressed indexes for each compiler output. */
4614 size_t newCompilerOutputCount
= 0;
4616 if (compilerOutputs
) {
4617 for (size_t i
= 0; i
< compilerOutputs
->length(); i
++) {
4618 CompilerOutput
& output
= (*compilerOutputs
)[i
];
4619 if (output
.isValid()) {
4620 JSScript
* script
= output
.script();
4621 if (IsScriptAboutToBeFinalized(&script
)) {
4622 jit::GetIonScript(script
, output
.mode())->recompileInfoRef() = RecompileInfo(uint32_t(-1));
4623 output
.invalidate();
4625 output
.setSweepIndex(newCompilerOutputCount
++);
4632 gcstats::MaybeAutoPhase
ap2(rt
->gc
.stats
, !rt
->isHeapCompacting(),
4633 gcstats::PHASE_DISCARD_TI
);
4635 for (ZoneCellIterUnderGC
i(zone(), FINALIZE_SCRIPT
); !i
.done(); i
.next()) {
4636 JSScript
* script
= i
.get
<JSScript
>();
4637 if (script
->types
) {
4638 types::TypeScript::Sweep(fop
, script
, oom
);
4641 script
->types
->destroy();
4642 script
->types
= nullptr;
4645 * Freeze constraints on stack type sets need to be
4646 * regenerated the next time the script is analyzed.
4648 script
->clearHasFreezeConstraints();
4650 JS_ASSERT(!script
->hasIonScript());
4651 JS_ASSERT(!script
->hasParallelIonScript());
4653 /* Update the recompile indexes in any IonScripts still on the script. */
4654 if (script
->hasIonScript())
4655 script
->ionScript()->recompileInfoRef().shouldSweep(*this);
4656 if (script
->hasParallelIonScript())
4657 script
->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
4664 gcstats::MaybeAutoPhase
ap2(rt
->gc
.stats
, !rt
->isHeapCompacting(),
4665 gcstats::PHASE_SWEEP_TYPES
);
4667 for (gc::ZoneCellIterUnderGC
iter(zone(), gc::FINALIZE_TYPE_OBJECT
);
4668 !iter
.done(); iter
.next())
4670 TypeObject
* object
= iter
.get
<TypeObject
>();
4671 object
->sweep(fop
, oom
);
4674 for (CompartmentsInZoneIter
comp(zone()); !comp
.done(); comp
.next())
4675 comp
->types
.sweep(fop
);
4678 if (compilerOutputs
) {
4679 size_t sweepIndex
= 0;
4680 for (size_t i
= 0; i
< compilerOutputs
->length(); i
++) {
4681 CompilerOutput output
= (*compilerOutputs
)[i
];
4682 if (output
.isValid()) {
4683 JS_ASSERT(sweepIndex
== output
.sweepIndex());
4684 output
.invalidateSweepIndex();
4685 (*compilerOutputs
)[sweepIndex
++] = output
;
4688 JS_ASSERT(sweepIndex
== newCompilerOutputCount
);
4689 JS_ALWAYS_TRUE(compilerOutputs
->resize(newCompilerOutputCount
));
4693 gcstats::MaybeAutoPhase
ap2(rt
->gc
.stats
, !rt
->isHeapCompacting(),
4694 gcstats::PHASE_FREE_TI_ARENA
);
4695 rt
->freeLifoAlloc
.transferFrom(&oldAlloc
);
4700 TypeZone::clearAllNewScriptsOnOOM()
4702 for (gc::ZoneCellIterUnderGC
iter(zone(), gc::FINALIZE_TYPE_OBJECT
);
4703 !iter
.done(); iter
.next())
4705 TypeObject
* object
= iter
.get
<TypeObject
>();
4706 object
->maybeClearNewScriptOnOOM();
4712 TypeScript::printTypes(JSContext
* cx
, HandleScript script
) const
4714 JS_ASSERT(script
->types
== this);
4716 if (!script
->hasBaselineScript())
4719 AutoEnterAnalysis
enter(nullptr, script
->compartment());
4721 if (script
->functionNonDelazifying())
4722 fprintf(stderr
, "Function");
4723 else if (script
->isForEval())
4724 fprintf(stderr
, "Eval");
4726 fprintf(stderr
, "Main");
4727 fprintf(stderr
, " #%u %s:%d ", script
->id(), script
->filename(), (int) script
->lineno());
4729 if (script
->functionNonDelazifying()) {
4730 if (js::PropertyName
* name
= script
->functionNonDelazifying()->name())
4731 name
->dumpCharsNoNewline();
4734 fprintf(stderr
, "\n this:");
4735 TypeScript::ThisTypes(script
)->print();
4737 for (unsigned i
= 0;
4738 script
->functionNonDelazifying() && i
< script
->functionNonDelazifying()->nargs();
4741 fprintf(stderr
, "\n arg%u:", i
);
4742 TypeScript::ArgTypes(script
, i
)->print();
4744 fprintf(stderr
, "\n");
4746 for (jsbytecode
* pc
= script
->code(); pc
< script
->codeEnd(); pc
+= GetBytecodeLength(pc
)) {
4748 fprintf(stderr
, "#%u:", script
->id());
4749 Sprinter
sprinter(cx
);
4750 if (!sprinter
.init())
4752 js_Disassemble1(cx
, script
, pc
, script
->pcToOffset(pc
), true, &sprinter
);
4753 fprintf(stderr
, "%s", sprinter
.string());
4756 if (js_CodeSpec
[*pc
].format
& JOF_TYPESET
) {
4757 StackTypeSet
* types
= TypeScript::BytecodeTypes(script
, pc
);
4758 fprintf(stderr
, " typeset %u:", unsigned(types
- typeArray()));
4760 fprintf(stderr
, "\n");
4764 fprintf(stderr
, "\n");
4769 TypeObject::setNewScript(TypeNewScript
* newScript
)
4771 JS_ASSERT(!newScript_
);
4772 newScript_
= newScript
;