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 /* Inline members for javascript type inference. */
9 #ifndef jsinferinlines_h
10 #define jsinferinlines_h
14 #include "mozilla/PodOperations.h"
16 #include "builtin/SymbolObject.h"
17 #include "vm/ArrayObject.h"
18 #include "vm/BooleanObject.h"
19 #include "vm/NumberObject.h"
20 #include "vm/SharedArrayObject.h"
21 #include "vm/SharedTypedArrayObject.h"
22 #include "vm/StringObject.h"
23 #include "vm/TypedArrayObject.h"
25 #include "jscntxtinlines.h"
27 #include "jit/ExecutionMode-inl.h"
32 /////////////////////////////////////////////////////////////////////
33 // CompilerOutput & RecompileInfo
34 /////////////////////////////////////////////////////////////////////
36 inline jit::IonScript
*
37 CompilerOutput::ion() const
39 // Note: If type constraints are generated before compilation has finished
40 // (i.e. after IonBuilder but before CodeGenerator::link) then a valid
41 // CompilerOutput may not yet have an associated IonScript.
42 MOZ_ASSERT(isValid());
43 jit::IonScript
* ion
= jit::GetIonScript(script(), mode());
44 MOZ_ASSERT(ion
!= ION_COMPILING_SCRIPT
);
48 inline CompilerOutput
*
49 RecompileInfo::compilerOutput(TypeZone
& types
) const
51 if (generation
!= types
.generation
) {
52 if (!types
.sweepCompilerOutputs
|| outputIndex
>= types
.sweepCompilerOutputs
->length())
54 CompilerOutput
* output
= &(*types
.sweepCompilerOutputs
)[outputIndex
];
55 if (!output
->isValid())
57 output
= &(*types
.compilerOutputs
)[output
->sweepIndex()];
58 return output
->isValid() ? output
: nullptr;
61 if (!types
.compilerOutputs
|| outputIndex
>= types
.compilerOutputs
->length())
63 CompilerOutput
* output
= &(*types
.compilerOutputs
)[outputIndex
];
64 return output
->isValid() ? output
: nullptr;
67 inline CompilerOutput
*
68 RecompileInfo::compilerOutput(JSContext
* cx
) const
70 return compilerOutput(cx
->zone()->types
);
74 RecompileInfo::shouldSweep(TypeZone
& types
)
76 CompilerOutput
* output
= compilerOutput(types
);
77 if (!output
|| !output
->isValid())
80 // If this info is for a compilation that occurred after sweeping started,
81 // the index is already correct.
82 MOZ_ASSERT_IF(generation
== types
.generation
,
83 outputIndex
== output
- types
.compilerOutputs
->begin());
85 // Update this info for the output's index in the zone's compiler outputs.
86 outputIndex
= output
- types
.compilerOutputs
->begin();
87 generation
= types
.generation
;
91 /////////////////////////////////////////////////////////////////////
93 /////////////////////////////////////////////////////////////////////
96 TypeObjectKey::asTypeObjectNoBarrier()
98 MOZ_ASSERT(isTypeObject());
99 return (TypeObject
*) this;
103 TypeObjectKey::asSingleObjectNoBarrier()
105 MOZ_ASSERT(isSingleObject());
106 return (JSObject
*) (uintptr_t(this) & ~1);
110 TypeObjectKey::asTypeObject()
112 TypeObject
* res
= asTypeObjectNoBarrier();
113 TypeObject::readBarrier(res
);
118 TypeObjectKey::asSingleObject()
120 JSObject
* res
= asSingleObjectNoBarrier();
121 JSObject::readBarrier(res
);
125 /* static */ inline Type
126 Type::ObjectType(JSObject
* obj
)
128 if (obj
->hasSingletonType())
129 return Type(uintptr_t(obj
) | 1);
130 return Type(uintptr_t(obj
->type()));
133 /* static */ inline Type
134 Type::ObjectType(TypeObject
* obj
)
136 if (obj
->singleton())
137 return Type(uintptr_t(obj
->singleton()) | 1);
138 return Type(uintptr_t(obj
));
141 /* static */ inline Type
142 Type::ObjectType(TypeObjectKey
* obj
)
144 return Type(uintptr_t(obj
));
148 GetValueType(const Value
& val
)
151 return Type::DoubleType();
153 return Type::ObjectType(&val
.toObject());
154 return Type::PrimitiveType(val
.extractNonDoubleType());
158 IsUntrackedValue(const Value
& val
)
160 return val
.isMagic() && (val
.whyMagic() == JS_OPTIMIZED_OUT
||
161 val
.whyMagic() == JS_UNINITIALIZED_LEXICAL
);
165 GetMaybeUntrackedValueType(const Value
& val
)
167 return IsUntrackedValue(val
) ? Type::UnknownType() : GetValueType(val
);
171 PrimitiveTypeFlag(JSValueType type
)
174 case JSVAL_TYPE_UNDEFINED
:
175 return TYPE_FLAG_UNDEFINED
;
176 case JSVAL_TYPE_NULL
:
177 return TYPE_FLAG_NULL
;
178 case JSVAL_TYPE_BOOLEAN
:
179 return TYPE_FLAG_BOOLEAN
;
180 case JSVAL_TYPE_INT32
:
181 return TYPE_FLAG_INT32
;
182 case JSVAL_TYPE_DOUBLE
:
183 return TYPE_FLAG_DOUBLE
;
184 case JSVAL_TYPE_STRING
:
185 return TYPE_FLAG_STRING
;
186 case JSVAL_TYPE_SYMBOL
:
187 return TYPE_FLAG_SYMBOL
;
188 case JSVAL_TYPE_MAGIC
:
189 return TYPE_FLAG_LAZYARGS
;
191 MOZ_CRASH("Bad JSValueType");
196 TypeFlagPrimitive(TypeFlags flags
)
199 case TYPE_FLAG_UNDEFINED
:
200 return JSVAL_TYPE_UNDEFINED
;
202 return JSVAL_TYPE_NULL
;
203 case TYPE_FLAG_BOOLEAN
:
204 return JSVAL_TYPE_BOOLEAN
;
205 case TYPE_FLAG_INT32
:
206 return JSVAL_TYPE_INT32
;
207 case TYPE_FLAG_DOUBLE
:
208 return JSVAL_TYPE_DOUBLE
;
209 case TYPE_FLAG_STRING
:
210 return JSVAL_TYPE_STRING
;
211 case TYPE_FLAG_SYMBOL
:
212 return JSVAL_TYPE_SYMBOL
;
213 case TYPE_FLAG_LAZYARGS
:
214 return JSVAL_TYPE_MAGIC
;
216 MOZ_CRASH("Bad TypeFlags");
221 * Get the canonical representation of an id to use when doing inference. This
222 * maintains the constraint that if two different jsids map to the same property
223 * in JS (e.g. 3 and "3"), they have the same type representation.
228 MOZ_ASSERT(!JSID_IS_EMPTY(id
));
230 // All properties which can be stored in an object's dense elements must
231 // map to the aggregate property for index types.
232 return JSID_IS_INT(id
) ? JSID_VOID
: id
;
235 const char * TypeIdStringImpl(jsid id
);
237 /* Convert an id for printing during debug. */
238 static inline const char*
239 TypeIdString(jsid id
)
242 return TypeIdStringImpl(id
);
249 * Structure for type inference entry point functions. All functions which can
250 * change type information must use this, and functions which depend on
251 * intermediate types (i.e. JITs) can use this to ensure that intermediate
252 * information is not collected and does not change.
254 * Pins inference results so that intermediate type information, TypeObjects
255 * and JSScripts won't be collected during GC. Does additional sanity checking
256 * that inference is not reentrant and that recompilations occur properly.
258 struct AutoEnterAnalysis
260 /* Prevent GC activity in the middle of analysis. */
261 gc::AutoSuppressGC suppressGC
;
263 // Allow clearing inference info on OOM during incremental sweeping.
264 AutoClearTypeInferenceStateOnOOM oom
;
266 // Pending recompilations to perform before execution of JIT code can resume.
267 RecompileInfoVector pendingRecompiles
;
272 explicit AutoEnterAnalysis(ExclusiveContext
* cx
)
273 : suppressGC(cx
), oom(cx
->zone())
275 init(cx
->defaultFreeOp(), cx
->zone());
278 AutoEnterAnalysis(FreeOp
* fop
, Zone
* zone
)
279 : suppressGC(zone
->runtimeFromMainThread()), oom(zone
)
286 if (this != zone
->types
.activeAnalysis
)
289 zone
->types
.activeAnalysis
= nullptr;
291 if (!pendingRecompiles
.empty())
292 zone
->types
.processPendingRecompiles(freeOp
, pendingRecompiles
);
296 void init(FreeOp
* fop
, Zone
* zone
) {
300 if (!zone
->types
.activeAnalysis
)
301 zone
->types
.activeAnalysis
= this;
305 /////////////////////////////////////////////////////////////////////
306 // Interface functions
307 /////////////////////////////////////////////////////////////////////
310 GetClassForProtoKey(JSProtoKey key
)
314 return &PlainObject::class_
;
316 return &ArrayObject::class_
;
319 return &NumberObject::class_
;
320 case JSProto_Boolean
:
321 return &BooleanObject::class_
;
323 return &StringObject::class_
;
325 return &SymbolObject::class_
;
327 return &RegExpObject::class_
;
329 case JSProto_Int8Array
:
330 case JSProto_Uint8Array
:
331 case JSProto_Int16Array
:
332 case JSProto_Uint16Array
:
333 case JSProto_Int32Array
:
334 case JSProto_Uint32Array
:
335 case JSProto_Float32Array
:
336 case JSProto_Float64Array
:
337 case JSProto_Uint8ClampedArray
:
338 return &TypedArrayObject::classes
[key
- JSProto_Int8Array
];
340 case JSProto_SharedInt8Array
:
341 case JSProto_SharedUint8Array
:
342 case JSProto_SharedInt16Array
:
343 case JSProto_SharedUint16Array
:
344 case JSProto_SharedInt32Array
:
345 case JSProto_SharedUint32Array
:
346 case JSProto_SharedFloat32Array
:
347 case JSProto_SharedFloat64Array
:
348 case JSProto_SharedUint8ClampedArray
:
349 return &SharedTypedArrayObject::classes
[key
- JSProto_SharedInt8Array
];
351 case JSProto_ArrayBuffer
:
352 return &ArrayBufferObject::class_
;
354 case JSProto_SharedArrayBuffer
:
355 return &SharedArrayBufferObject::class_
;
357 case JSProto_DataView
:
358 return &DataViewObject::class_
;
361 MOZ_CRASH("Bad proto key");
366 * Get the default 'new' object for a given standard class, per the currently
370 GetTypeNewObject(JSContext
* cx
, JSProtoKey key
)
372 RootedObject
proto(cx
);
373 if (!GetBuiltinPrototype(cx
, key
, &proto
))
375 return cx
->getNewType(GetClassForProtoKey(key
), TaggedProto(proto
.get()));
378 /* Get a type object for the immediate allocation site within a native. */
380 GetTypeCallerInitObject(JSContext
* cx
, JSProtoKey key
)
383 RootedScript
script(cx
, cx
->currentScript(&pc
));
385 return TypeScript::InitObject(cx
, script
, pc
, key
);
386 return GetTypeNewObject(cx
, key
);
389 void MarkIteratorUnknownSlow(JSContext
* cx
);
391 void TypeMonitorCallSlow(JSContext
* cx
, JSObject
* callee
, const CallArgs
& args
,
395 * Monitor a javascript call, either on entry to the interpreter or made
396 * from within the interpreter.
399 TypeMonitorCall(JSContext
* cx
, const js::CallArgs
& args
, bool constructing
)
401 if (args
.callee().is
<JSFunction
>()) {
402 JSFunction
* fun
= &args
.callee().as
<JSFunction
>();
403 if (fun
->isInterpreted() && fun
->nonLazyScript()->types())
404 TypeMonitorCallSlow(cx
, &args
.callee(), args
, constructing
);
409 TrackPropertyTypes(ExclusiveContext
* cx
, JSObject
* obj
, jsid id
)
411 if (obj
->hasLazyType() || obj
->type()->unknownProperties())
414 if (obj
->hasSingletonType() && !obj
->type()->maybeGetProperty(id
))
421 EnsureTrackPropertyTypes(JSContext
* cx
, JSObject
* obj
, jsid id
)
425 if (obj
->hasSingletonType()) {
426 AutoEnterAnalysis
enter(cx
);
427 if (obj
->hasLazyType() && !obj
->getType(cx
)) {
428 CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes");
431 if (!obj
->type()->unknownProperties() && !obj
->type()->getProperty(cx
, id
)) {
432 MOZ_ASSERT(obj
->type()->unknownProperties());
437 MOZ_ASSERT(obj
->type()->unknownProperties() || TrackPropertyTypes(cx
, obj
, id
));
441 CanHaveEmptyPropertyTypesForOwnProperty(JSObject
* obj
)
443 // Per the comment on TypeSet::propertySet, property type sets for global
444 // objects may be empty for 'own' properties if the global property still
445 // has its initial undefined value.
446 return obj
->is
<GlobalObject
>();
450 PropertyHasBeenMarkedNonConstant(JSObject
* obj
, jsid id
)
452 // Non-constant properties are only relevant for singleton objects.
453 if (!obj
->hasSingletonType())
456 // EnsureTrackPropertyTypes must have been called on this object.
457 if (obj
->type()->unknownProperties())
459 HeapTypeSet
* types
= obj
->type()->maybeGetProperty(IdToTypeId(id
));
460 return types
->nonConstantProperty();
464 HasTypePropertyId(JSObject
* obj
, jsid id
, Type type
)
466 if (obj
->hasLazyType())
469 if (obj
->type()->unknownProperties())
472 if (HeapTypeSet
* types
= obj
->type()->maybeGetProperty(IdToTypeId(id
)))
473 return types
->hasType(type
);
479 HasTypePropertyId(JSObject
* obj
, jsid id
, const Value
& value
)
481 return HasTypePropertyId(obj
, id
, GetValueType(value
));
484 void AddTypePropertyId(ExclusiveContext
* cx
, TypeObject
* obj
, jsid id
, Type type
);
485 void AddTypePropertyId(ExclusiveContext
* cx
, TypeObject
* obj
, jsid id
, const Value
& value
);
487 /* Add a possible type for a property of obj. */
489 AddTypePropertyId(ExclusiveContext
* cx
, JSObject
* obj
, jsid id
, Type type
)
492 if (TrackPropertyTypes(cx
, obj
, id
))
493 AddTypePropertyId(cx
, obj
->type(), id
, type
);
497 AddTypePropertyId(ExclusiveContext
* cx
, JSObject
* obj
, jsid id
, const Value
& value
)
500 if (TrackPropertyTypes(cx
, obj
, id
))
501 AddTypePropertyId(cx
, obj
->type(), id
, value
);
504 /* Set one or more dynamic flags on a type object. */
506 MarkTypeObjectFlags(ExclusiveContext
* cx
, JSObject
* obj
, TypeObjectFlags flags
)
508 if (!obj
->hasLazyType() && !obj
->type()->hasAllFlags(flags
))
509 obj
->type()->setFlags(cx
, flags
);
513 * Mark all properties of a type object as unknown. If markSetsUnknown is set,
514 * scan the entire compartment and mark all type sets containing it as having
515 * an unknown object. This is needed for correctness in dealing with mutable
516 * __proto__, which can change the type of an object dynamically.
519 MarkTypeObjectUnknownProperties(JSContext
* cx
, TypeObject
* obj
,
520 bool markSetsUnknown
= false)
522 if (!obj
->unknownProperties())
523 obj
->markUnknown(cx
);
524 if (markSetsUnknown
&& !(obj
->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN
))
525 cx
->compartment()->types
.markSetsUnknown(cx
, obj
);
529 MarkTypePropertyNonData(ExclusiveContext
* cx
, JSObject
* obj
, jsid id
)
532 if (TrackPropertyTypes(cx
, obj
, id
))
533 obj
->type()->markPropertyNonData(cx
, id
);
537 MarkTypePropertyNonWritable(ExclusiveContext
* cx
, JSObject
* obj
, jsid id
)
540 if (TrackPropertyTypes(cx
, obj
, id
))
541 obj
->type()->markPropertyNonWritable(cx
, id
);
545 IsTypePropertyIdMarkedNonData(JSObject
* obj
, jsid id
)
547 return obj
->type()->isPropertyNonData(id
);
551 IsTypePropertyIdMarkedNonWritable(JSObject
* obj
, jsid id
)
553 return obj
->type()->isPropertyNonWritable(id
);
556 /* Mark a state change on a particular object. */
558 MarkObjectStateChange(ExclusiveContext
* cx
, JSObject
* obj
)
560 if (!obj
->hasLazyType() && !obj
->type()->unknownProperties())
561 obj
->type()->markStateChange(cx
);
565 * For an array or object which has not yet escaped and been referenced elsewhere,
566 * pick a new type based on the object's current contents.
570 FixArrayType(ExclusiveContext
* cx
, ArrayObject
* obj
)
572 cx
->compartment()->types
.fixArrayType(cx
, obj
);
576 FixObjectType(ExclusiveContext
* cx
, PlainObject
* obj
)
578 cx
->compartment()->types
.fixObjectType(cx
, obj
);
581 /* Interface helpers for JSScript*. */
582 extern void TypeMonitorResult(JSContext
* cx
, JSScript
* script
, jsbytecode
* pc
,
583 const js::Value
& rval
);
584 extern void TypeDynamicResult(JSContext
* cx
, JSScript
* script
, jsbytecode
* pc
,
585 js::types::Type type
);
587 /////////////////////////////////////////////////////////////////////
588 // Script interface functions
589 /////////////////////////////////////////////////////////////////////
591 /* static */ inline unsigned
592 TypeScript::NumTypeSets(JSScript
* script
)
594 size_t num
= script
->nTypeSets() + 1 /* this */;
595 if (JSFunction
* fun
= script
->functionNonDelazifying())
600 /* static */ inline StackTypeSet
*
601 TypeScript::ThisTypes(JSScript
* script
)
603 TypeScript
* types
= script
->types();
604 return types
? types
->typeArray() + script
->nTypeSets() : nullptr;
608 * Note: for non-escaping arguments, argTypes reflect only the initial type of
609 * the variable (e.g. passed values for argTypes, or undefined for localTypes)
610 * and not types from subsequent assignments.
613 /* static */ inline StackTypeSet
*
614 TypeScript::ArgTypes(JSScript
* script
, unsigned i
)
616 MOZ_ASSERT(i
< script
->functionNonDelazifying()->nargs());
617 TypeScript
* types
= script
->types();
618 return types
? types
->typeArray() + script
->nTypeSets() + 1 + i
: nullptr;
621 template <typename TYPESET
>
622 /* static */ inline TYPESET
*
623 TypeScript::BytecodeTypes(JSScript
* script
, jsbytecode
* pc
, uint32_t* bytecodeMap
,
624 uint32_t* hint
, TYPESET
* typeArray
)
626 MOZ_ASSERT(js_CodeSpec
[*pc
].format
& JOF_TYPESET
);
627 uint32_t offset
= script
->pcToOffset(pc
);
629 // See if this pc is the next typeset opcode after the last one looked up.
630 if ((*hint
+ 1) < script
->nTypeSets() && bytecodeMap
[*hint
+ 1] == offset
) {
632 return typeArray
+ *hint
;
635 // See if this pc is the same as the last one looked up.
636 if (bytecodeMap
[*hint
] == offset
)
637 return typeArray
+ *hint
;
639 // Fall back to a binary search.
641 size_t top
= script
->nTypeSets() - 1;
642 size_t mid
= bottom
+ (top
- bottom
) / 2;
644 if (bytecodeMap
[mid
] < offset
)
646 else if (bytecodeMap
[mid
] > offset
)
650 mid
= bottom
+ (top
- bottom
) / 2;
653 // We should have have zeroed in on either the exact offset, unless there
654 // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen
655 // if the script is very long).
656 MOZ_ASSERT(bytecodeMap
[mid
] == offset
|| mid
== top
);
659 return typeArray
+ *hint
;
662 /* static */ inline StackTypeSet
*
663 TypeScript::BytecodeTypes(JSScript
* script
, jsbytecode
* pc
)
665 MOZ_ASSERT(CurrentThreadCanAccessRuntime(script
->runtimeFromMainThread()));
666 TypeScript
* types
= script
->types();
669 uint32_t* hint
= script
->baselineScript()->bytecodeTypeMap() + script
->nTypeSets();
670 return BytecodeTypes(script
, pc
, script
->baselineScript()->bytecodeTypeMap(),
671 hint
, types
->typeArray());
674 struct AllocationSiteKey
: public DefaultHasher
<AllocationSiteKey
> {
677 uint32_t offset
: 24;
680 static const uint32_t OFFSET_LIMIT
= (1 << 23);
682 AllocationSiteKey() { mozilla::PodZero(this); }
684 static inline uint32_t hash(AllocationSiteKey key
) {
685 return uint32_t(size_t(key
.script
->offsetToPC(key
.offset
)) ^ key
.kind
);
688 static inline bool match(const AllocationSiteKey
& a
, const AllocationSiteKey
& b
) {
689 return a
.script
== b
.script
&& a
.offset
== b
.offset
&& a
.kind
== b
.kind
;
693 /* Whether to use a new type object for an initializer opcode at script/pc. */
695 UseNewTypeForInitializer(JSScript
* script
, jsbytecode
* pc
, JSProtoKey key
);
698 UseNewTypeForInitializer(JSScript
* script
, jsbytecode
* pc
, const Class
* clasp
);
700 /* static */ inline TypeObject
*
701 TypeScript::InitObject(JSContext
* cx
, JSScript
* script
, jsbytecode
* pc
, JSProtoKey kind
)
703 MOZ_ASSERT(!UseNewTypeForInitializer(script
, pc
, kind
));
705 uint32_t offset
= script
->pcToOffset(pc
);
707 if (offset
>= AllocationSiteKey::OFFSET_LIMIT
)
708 return GetTypeNewObject(cx
, kind
);
710 AllocationSiteKey key
;
715 if (!cx
->compartment()->types
.allocationSiteTable
)
716 return cx
->compartment()->types
.addAllocationSiteTypeObject(cx
, key
);
718 AllocationSiteTable::Ptr p
= cx
->compartment()->types
.allocationSiteTable
->lookup(key
);
722 return cx
->compartment()->types
.addAllocationSiteTypeObject(cx
, key
);
725 /* Set the type to use for obj according to the site it was allocated at. */
727 SetInitializerObjectType(JSContext
* cx
, HandleScript script
, jsbytecode
* pc
, HandleObject obj
, NewObjectKind kind
)
729 JSProtoKey key
= JSCLASS_CACHED_PROTO_KEY(obj
->getClass());
730 MOZ_ASSERT(key
!= JSProto_Null
);
731 MOZ_ASSERT(kind
== UseNewTypeForInitializer(script
, pc
, key
));
733 if (kind
== SingletonObject
) {
734 MOZ_ASSERT(obj
->hasSingletonType());
737 * Inference does not account for types of run-once initializer
738 * objects, as these may not be created until after the script
741 TypeScript::Monitor(cx
, script
, pc
, ObjectValue(*obj
));
743 types::TypeObject
* type
= TypeScript::InitObject(cx
, script
, pc
, key
);
746 obj
->uninlinedSetType(type
);
752 /* static */ inline void
753 TypeScript::Monitor(JSContext
* cx
, JSScript
* script
, jsbytecode
* pc
, const js::Value
& rval
)
755 TypeMonitorResult(cx
, script
, pc
, rval
);
758 /* static */ inline void
759 TypeScript::Monitor(JSContext
* cx
, const js::Value
& rval
)
762 RootedScript
script(cx
, cx
->currentScript(&pc
));
763 Monitor(cx
, script
, pc
, rval
);
766 /* static */ inline void
767 TypeScript::MonitorAssign(JSContext
* cx
, HandleObject obj
, jsid id
)
769 if (!obj
->hasSingletonType()) {
771 * Mark as unknown any object which has had dynamic assignments to
772 * non-integer properties at SETELEM opcodes. This avoids making large
773 * numbers of type properties for hashmap-style objects. We don't need
774 * to do this for objects with singleton type, because type properties
775 * are only constructed for them when analyzed scripts depend on those
776 * specific properties.
779 if (js_IdIsIndex(id
, &i
))
782 // But if we don't have too many properties yet, don't do anything. The
783 // idea here is that normal object initialization should not trigger
784 // deoptimization in most cases, while actual usage as a hashmap should.
785 TypeObject
* type
= obj
->type();
786 if (type
->getPropertyCount() < 128)
788 MarkTypeObjectUnknownProperties(cx
, type
);
792 /* static */ inline void
793 TypeScript::SetThis(JSContext
* cx
, JSScript
* script
, Type type
)
795 StackTypeSet
* types
= ThisTypes(script
);
799 if (!types
->hasType(type
)) {
800 AutoEnterAnalysis
enter(cx
);
802 InferSpew(ISpewOps
, "externalType: setThis #%u: %s",
803 script
->id(), TypeString(type
));
804 types
->addType(cx
, type
);
808 /* static */ inline void
809 TypeScript::SetThis(JSContext
* cx
, JSScript
* script
, const js::Value
& value
)
811 SetThis(cx
, script
, GetValueType(value
));
814 /* static */ inline void
815 TypeScript::SetArgument(JSContext
* cx
, JSScript
* script
, unsigned arg
, Type type
)
817 StackTypeSet
* types
= ArgTypes(script
, arg
);
821 if (!types
->hasType(type
)) {
822 AutoEnterAnalysis
enter(cx
);
824 InferSpew(ISpewOps
, "externalType: setArg #%u %u: %s",
825 script
->id(), arg
, TypeString(type
));
826 types
->addType(cx
, type
);
830 /* static */ inline void
831 TypeScript::SetArgument(JSContext
* cx
, JSScript
* script
, unsigned arg
, const js::Value
& value
)
833 Type type
= GetValueType(value
);
834 SetArgument(cx
, script
, arg
, type
);
837 /////////////////////////////////////////////////////////////////////
839 /////////////////////////////////////////////////////////////////////
841 inline JSCompartment
*
842 TypeCompartment::compartment()
844 return (JSCompartment
*)((char*)this - offsetof(JSCompartment
, types
));
847 /////////////////////////////////////////////////////////////////////
849 /////////////////////////////////////////////////////////////////////
852 * The sets of objects and scripts in a type set grow monotonically, are usually
853 * empty, almost always small, and sometimes big. For empty or singleton sets,
854 * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE,
855 * an array of this length is used to store the elements. For larger sets, a hash
856 * table filled to 25%-50% of capacity is used, with collisions resolved by linear
857 * probing. TODO: replace these with jshashtables.
859 const unsigned SET_ARRAY_SIZE
= 8;
860 const unsigned SET_CAPACITY_OVERFLOW
= 1u << 30;
862 /* Get the capacity of a set with the given element count. */
863 static inline unsigned
864 HashSetCapacity(unsigned count
)
866 MOZ_ASSERT(count
>= 2);
867 MOZ_ASSERT(count
< SET_CAPACITY_OVERFLOW
);
869 if (count
<= SET_ARRAY_SIZE
)
870 return SET_ARRAY_SIZE
;
872 return 1u << (mozilla::FloorLog2(count
) + 2);
875 /* Compute the FNV hash for the low 32 bits of v. */
876 template <class T
, class KEY
>
877 static inline uint32_t
880 uint32_t nv
= KEY::keyBits(v
);
882 uint32_t hash
= 84696351 ^ (nv
& 0xff);
883 hash
= (hash
* 16777619) ^ ((nv
>> 8) & 0xff);
884 hash
= (hash
* 16777619) ^ ((nv
>> 16) & 0xff);
885 return (hash
* 16777619) ^ ((nv
>> 24) & 0xff);
889 * Insert space for an element into the specified set and grow its capacity if needed.
890 * returned value is an existing or new entry (nullptr if new).
892 template <class T
, class U
, class KEY
>
894 HashSetInsertTry(LifoAlloc
& alloc
, U
**& values
, unsigned& count
, T key
)
896 unsigned capacity
= HashSetCapacity(count
);
897 unsigned insertpos
= HashKey
<T
,KEY
>(key
) & (capacity
- 1);
899 /* Whether we are converting from a fixed array to hashtable. */
900 bool converting
= (count
== SET_ARRAY_SIZE
);
903 while (values
[insertpos
] != nullptr) {
904 if (KEY::getKey(values
[insertpos
]) == key
)
905 return &values
[insertpos
];
906 insertpos
= (insertpos
+ 1) & (capacity
- 1);
910 if (count
>= SET_CAPACITY_OVERFLOW
)
914 unsigned newCapacity
= HashSetCapacity(count
);
916 if (newCapacity
== capacity
) {
917 MOZ_ASSERT(!converting
);
918 return &values
[insertpos
];
921 U
** newValues
= alloc
.newArray
<U
*>(newCapacity
);
924 mozilla::PodZero(newValues
, newCapacity
);
926 for (unsigned i
= 0; i
< capacity
; i
++) {
928 unsigned pos
= HashKey
<T
,KEY
>(KEY::getKey(values
[i
])) & (newCapacity
- 1);
929 while (newValues
[pos
] != nullptr)
930 pos
= (pos
+ 1) & (newCapacity
- 1);
931 newValues
[pos
] = values
[i
];
937 insertpos
= HashKey
<T
,KEY
>(key
) & (newCapacity
- 1);
938 while (values
[insertpos
] != nullptr)
939 insertpos
= (insertpos
+ 1) & (newCapacity
- 1);
940 return &values
[insertpos
];
944 * Insert an element into the specified set if it is not already there, returning
945 * an entry which is nullptr if the element was not there.
947 template <class T
, class U
, class KEY
>
949 HashSetInsert(LifoAlloc
& alloc
, U
**& values
, unsigned& count
, T key
)
952 MOZ_ASSERT(values
== nullptr);
954 return (U
**) &values
;
958 U
* oldData
= (U
*) values
;
959 if (KEY::getKey(oldData
) == key
)
960 return (U
**) &values
;
962 values
= alloc
.newArray
<U
*>(SET_ARRAY_SIZE
);
964 values
= (U
**) oldData
;
967 mozilla::PodZero(values
, SET_ARRAY_SIZE
);
974 if (count
<= SET_ARRAY_SIZE
) {
975 for (unsigned i
= 0; i
< count
; i
++) {
976 if (KEY::getKey(values
[i
]) == key
)
980 if (count
< SET_ARRAY_SIZE
) {
982 return &values
[count
- 1];
986 return HashSetInsertTry
<T
,U
,KEY
>(alloc
, values
, count
, key
);
989 /* Lookup an entry in a hash set, return nullptr if it does not exist. */
990 template <class T
, class U
, class KEY
>
992 HashSetLookup(U
** values
, unsigned count
, T key
)
998 return (KEY::getKey((U
*) values
) == key
) ? (U
*) values
: nullptr;
1000 if (count
<= SET_ARRAY_SIZE
) {
1001 for (unsigned i
= 0; i
< count
; i
++) {
1002 if (KEY::getKey(values
[i
]) == key
)
1008 unsigned capacity
= HashSetCapacity(count
);
1009 unsigned pos
= HashKey
<T
,KEY
>(key
) & (capacity
- 1);
1011 while (values
[pos
] != nullptr) {
1012 if (KEY::getKey(values
[pos
]) == key
)
1014 pos
= (pos
+ 1) & (capacity
- 1);
1020 inline TypeObjectKey
*
1021 Type::objectKey() const
1023 MOZ_ASSERT(isObject());
1024 return (TypeObjectKey
*) data
;
1028 Type::singleObject() const
1030 return objectKey()->asSingleObject();
1034 Type::typeObject() const
1036 return objectKey()->asTypeObject();
1040 Type::singleObjectNoBarrier() const
1042 return objectKey()->asSingleObjectNoBarrier();
1046 Type::typeObjectNoBarrier() const
1048 return objectKey()->asTypeObjectNoBarrier();
1052 TypeSet::hasType(Type type
) const
1057 if (type
.isUnknown()) {
1059 } else if (type
.isPrimitive()) {
1060 return !!(flags
& PrimitiveTypeFlag(type
.primitive()));
1061 } else if (type
.isAnyObject()) {
1062 return !!(flags
& TYPE_FLAG_ANYOBJECT
);
1064 return !!(flags
& TYPE_FLAG_ANYOBJECT
) ||
1065 HashSetLookup
<TypeObjectKey
*,TypeObjectKey
,TypeObjectKey
>
1066 (objectSet
, baseObjectCount(), type
.objectKey()) != nullptr;
1071 TypeSet::setBaseObjectCount(uint32_t count
)
1073 MOZ_ASSERT(count
<= TYPE_FLAG_DOMOBJECT_COUNT_LIMIT
);
1074 flags
= (flags
& ~TYPE_FLAG_OBJECT_COUNT_MASK
)
1075 | (count
<< TYPE_FLAG_OBJECT_COUNT_SHIFT
);
1079 HeapTypeSet::newPropertyState(ExclusiveContext
* cxArg
)
1081 /* Propagate the change to all constraints. */
1082 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
1083 TypeConstraint
* constraint
= constraintList
;
1084 while (constraint
) {
1085 constraint
->newPropertyState(cx
, this);
1086 constraint
= constraint
->next
;
1089 MOZ_ASSERT(!constraintList
);
1094 HeapTypeSet::setNonDataPropertyIgnoringConstraints()
1096 flags
|= TYPE_FLAG_NON_DATA_PROPERTY
;
1100 HeapTypeSet::setNonDataProperty(ExclusiveContext
* cx
)
1102 if (flags
& TYPE_FLAG_NON_DATA_PROPERTY
)
1105 setNonDataPropertyIgnoringConstraints();
1106 newPropertyState(cx
);
1110 HeapTypeSet::setNonWritableProperty(ExclusiveContext
* cx
)
1112 if (flags
& TYPE_FLAG_NON_WRITABLE_PROPERTY
)
1115 flags
|= TYPE_FLAG_NON_WRITABLE_PROPERTY
;
1116 newPropertyState(cx
);
1120 HeapTypeSet::setNonConstantProperty(ExclusiveContext
* cx
)
1122 if (flags
& TYPE_FLAG_NON_CONSTANT_PROPERTY
)
1125 flags
|= TYPE_FLAG_NON_CONSTANT_PROPERTY
;
1126 newPropertyState(cx
);
1130 TypeSet::getObjectCount() const
1132 MOZ_ASSERT(!unknownObject());
1133 uint32_t count
= baseObjectCount();
1134 if (count
> SET_ARRAY_SIZE
)
1135 return HashSetCapacity(count
);
1139 inline TypeObjectKey
*
1140 TypeSet::getObject(unsigned i
) const
1142 MOZ_ASSERT(i
< getObjectCount());
1143 if (baseObjectCount() == 1) {
1145 return (TypeObjectKey
*) objectSet
;
1147 return objectSet
[i
];
1151 TypeSet::getSingleObject(unsigned i
) const
1153 TypeObjectKey
* key
= getObject(i
);
1154 return (key
&& key
->isSingleObject()) ? key
->asSingleObject() : nullptr;
1158 TypeSet::getTypeObject(unsigned i
) const
1160 TypeObjectKey
* key
= getObject(i
);
1161 return (key
&& key
->isTypeObject()) ? key
->asTypeObject() : nullptr;
1165 TypeSet::getSingleObjectNoBarrier(unsigned i
) const
1167 TypeObjectKey
* key
= getObject(i
);
1168 return (key
&& key
->isSingleObject()) ? key
->asSingleObjectNoBarrier() : nullptr;
1172 TypeSet::getTypeObjectNoBarrier(unsigned i
) const
1174 TypeObjectKey
* key
= getObject(i
);
1175 return (key
&& key
->isTypeObject()) ? key
->asTypeObjectNoBarrier() : nullptr;
1179 TypeSet::getObjectClass(unsigned i
) const
1181 if (JSObject
* object
= getSingleObject(i
))
1182 return object
->getClass();
1183 if (TypeObject
* object
= getTypeObject(i
))
1184 return object
->clasp();
1188 /////////////////////////////////////////////////////////////////////
1190 /////////////////////////////////////////////////////////////////////
1192 inline TypeObject::TypeObject(const Class
* clasp
, TaggedProto proto
, TypeObjectFlags initialFlags
)
1194 mozilla::PodZero(this);
1196 /* Inner objects may not appear on prototype chains. */
1197 MOZ_ASSERT_IF(proto
.isObject(), !proto
.toObject()->getClass()->ext
.outerObject
);
1199 this->clasp_
= clasp
;
1200 this->proto_
= proto
.raw();
1201 this->flags_
= initialFlags
;
1203 setGeneration(zone()->types
.generation
);
1205 InferSpew(ISpewOps
, "newObject: %s", TypeObjectString(this));
1209 TypeObject::finalize(FreeOp
* fop
)
1211 fop
->delete_(newScriptDontCheckGeneration());
1215 TypeObject::basePropertyCount()
1217 return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK
) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT
;
1221 TypeObject::setBasePropertyCount(uint32_t count
)
1223 // Note: Callers must ensure they are performing threadsafe operations.
1224 MOZ_ASSERT(count
<= OBJECT_FLAG_PROPERTY_COUNT_LIMIT
);
1225 flags_
= (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK
)
1226 | (count
<< OBJECT_FLAG_PROPERTY_COUNT_SHIFT
);
1230 TypeObject::getProperty(ExclusiveContext
* cx
, jsid id
)
1232 MOZ_ASSERT(JSID_IS_VOID(id
) || JSID_IS_EMPTY(id
) || JSID_IS_STRING(id
) || JSID_IS_SYMBOL(id
));
1233 MOZ_ASSERT_IF(!JSID_IS_EMPTY(id
), id
== IdToTypeId(id
));
1234 MOZ_ASSERT(!unknownProperties());
1236 if (HeapTypeSet
* types
= maybeGetProperty(id
))
1239 Property
* base
= cx
->typeLifoAlloc().new_
<Property
>(id
);
1245 uint32_t propertyCount
= basePropertyCount();
1246 Property
** pprop
= HashSetInsert
<jsid
,Property
,Property
>
1247 (cx
->typeLifoAlloc(), propertySet
, propertyCount
, id
);
1253 MOZ_ASSERT(!*pprop
);
1255 setBasePropertyCount(propertyCount
);
1258 updateNewPropertyTypes(cx
, id
, &base
->types
);
1260 if (propertyCount
== OBJECT_FLAG_PROPERTY_COUNT_LIMIT
) {
1261 // We hit the maximum number of properties the object can have, mark
1262 // the object unknown so that new properties will not be added in the
1267 return &base
->types
;
1271 TypeObject::maybeGetProperty(jsid id
)
1273 MOZ_ASSERT(JSID_IS_VOID(id
) || JSID_IS_EMPTY(id
) || JSID_IS_STRING(id
) || JSID_IS_SYMBOL(id
));
1274 MOZ_ASSERT_IF(!JSID_IS_EMPTY(id
), id
== IdToTypeId(id
));
1275 MOZ_ASSERT(!unknownProperties());
1277 Property
* prop
= HashSetLookup
<jsid
,Property
,Property
>
1278 (propertySet
, basePropertyCount(), id
);
1280 return prop
? &prop
->types
: nullptr;
1284 TypeObject::getPropertyCount()
1286 uint32_t count
= basePropertyCount();
1287 if (count
> SET_ARRAY_SIZE
)
1288 return HashSetCapacity(count
);
1293 TypeObject::getProperty(unsigned i
)
1295 MOZ_ASSERT(i
< getPropertyCount());
1296 if (basePropertyCount() == 1) {
1298 return (Property
*) propertySet
;
1300 return propertySet
[i
];
1304 TypeNewScript::writeBarrierPre(TypeNewScript
* newScript
)
1306 if (!newScript
->fun
->runtimeFromAnyThread()->needsIncrementalBarrier())
1309 JS::Zone
* zone
= newScript
->fun
->zoneFromAnyThread();
1310 if (zone
->needsIncrementalBarrier())
1311 newScript
->trace(zone
->barrierTracer());
1314 } } /* namespace js::types */
1316 inline js::types::TypeScript
*
1319 maybeSweepTypes(nullptr);
1324 JSScript::ensureHasTypes(JSContext
* cx
)
1326 return types() || makeTypes(cx
);
1332 struct GCMethods
<const types::Type
>
1334 static types::Type
initial() { return types::Type::UnknownType(); }
1335 static bool poisoned(const types::Type
& v
) {
1336 return (v
.isTypeObject() && IsPoisonedPtr(v
.typeObject()))
1337 || (v
.isSingleObject() && IsPoisonedPtr(v
.singleObject()));
1342 struct GCMethods
<types::Type
>
1344 static types::Type
initial() { return types::Type::UnknownType(); }
1345 static bool poisoned(const types::Type
& v
) {
1346 return (v
.isTypeObject() && IsPoisonedPtr(v
.typeObject()))
1347 || (v
.isSingleObject() && IsPoisonedPtr(v
.singleObject()));
1353 #endif /* jsinferinlines_h */