Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsinferinlines.h
blobeaf2dd180f9252e5380d3e955e88028693b30a9b
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
12 #include "jsinfer.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"
29 namespace js {
30 namespace types {
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);
45 return ion;
48 inline CompilerOutput*
49 RecompileInfo::compilerOutput(TypeZone& types) const
51 if (generation != types.generation) {
52 if (!types.sweepCompilerOutputs || outputIndex >= types.sweepCompilerOutputs->length())
53 return nullptr;
54 CompilerOutput* output = &(*types.sweepCompilerOutputs)[outputIndex];
55 if (!output->isValid())
56 return nullptr;
57 output = &(*types.compilerOutputs)[output->sweepIndex()];
58 return output->isValid() ? output : nullptr;
61 if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
62 return nullptr;
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);
73 inline bool
74 RecompileInfo::shouldSweep(TypeZone& types)
76 CompilerOutput* output = compilerOutput(types);
77 if (!output || !output->isValid())
78 return true;
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;
88 return false;
91 /////////////////////////////////////////////////////////////////////
92 // Types
93 /////////////////////////////////////////////////////////////////////
95 inline TypeObject*
96 TypeObjectKey::asTypeObjectNoBarrier()
98 MOZ_ASSERT(isTypeObject());
99 return (TypeObject*) this;
102 inline JSObject*
103 TypeObjectKey::asSingleObjectNoBarrier()
105 MOZ_ASSERT(isSingleObject());
106 return (JSObject*) (uintptr_t(this) & ~1);
109 inline TypeObject*
110 TypeObjectKey::asTypeObject()
112 TypeObject* res = asTypeObjectNoBarrier();
113 TypeObject::readBarrier(res);
114 return res;
117 inline JSObject*
118 TypeObjectKey::asSingleObject()
120 JSObject* res = asSingleObjectNoBarrier();
121 JSObject::readBarrier(res);
122 return 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));
147 inline Type
148 GetValueType(const Value& val)
150 if (val.isDouble())
151 return Type::DoubleType();
152 if (val.isObject())
153 return Type::ObjectType(&val.toObject());
154 return Type::PrimitiveType(val.extractNonDoubleType());
157 inline bool
158 IsUntrackedValue(const Value& val)
160 return val.isMagic() && (val.whyMagic() == JS_OPTIMIZED_OUT ||
161 val.whyMagic() == JS_UNINITIALIZED_LEXICAL);
164 inline Type
165 GetMaybeUntrackedValueType(const Value& val)
167 return IsUntrackedValue(val) ? Type::UnknownType() : GetValueType(val);
170 inline TypeFlags
171 PrimitiveTypeFlag(JSValueType type)
173 switch (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;
190 default:
191 MOZ_CRASH("Bad JSValueType");
195 inline JSValueType
196 TypeFlagPrimitive(TypeFlags flags)
198 switch (flags) {
199 case TYPE_FLAG_UNDEFINED:
200 return JSVAL_TYPE_UNDEFINED;
201 case TYPE_FLAG_NULL:
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;
215 default:
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.
225 inline jsid
226 IdToTypeId(jsid id)
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)
241 #ifdef DEBUG
242 return TypeIdStringImpl(id);
243 #else
244 return "(missing)";
245 #endif
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;
269 FreeOp* freeOp;
270 Zone* zone;
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)
281 init(fop, zone);
284 ~AutoEnterAnalysis()
286 if (this != zone->types.activeAnalysis)
287 return;
289 zone->types.activeAnalysis = nullptr;
291 if (!pendingRecompiles.empty())
292 zone->types.processPendingRecompiles(freeOp, pendingRecompiles);
295 private:
296 void init(FreeOp* fop, Zone* zone) {
297 this->freeOp = fop;
298 this->zone = zone;
300 if (!zone->types.activeAnalysis)
301 zone->types.activeAnalysis = this;
305 /////////////////////////////////////////////////////////////////////
306 // Interface functions
307 /////////////////////////////////////////////////////////////////////
309 inline const Class*
310 GetClassForProtoKey(JSProtoKey key)
312 switch (key) {
313 case JSProto_Object:
314 return &PlainObject::class_;
315 case JSProto_Array:
316 return &ArrayObject::class_;
318 case JSProto_Number:
319 return &NumberObject::class_;
320 case JSProto_Boolean:
321 return &BooleanObject::class_;
322 case JSProto_String:
323 return &StringObject::class_;
324 case JSProto_Symbol:
325 return &SymbolObject::class_;
326 case JSProto_RegExp:
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_;
360 default:
361 MOZ_CRASH("Bad proto key");
366 * Get the default 'new' object for a given standard class, per the currently
367 * active global.
369 inline TypeObject*
370 GetTypeNewObject(JSContext* cx, JSProtoKey key)
372 RootedObject proto(cx);
373 if (!GetBuiltinPrototype(cx, key, &proto))
374 return nullptr;
375 return cx->getNewType(GetClassForProtoKey(key), TaggedProto(proto.get()));
378 /* Get a type object for the immediate allocation site within a native. */
379 inline TypeObject*
380 GetTypeCallerInitObject(JSContext* cx, JSProtoKey key)
382 jsbytecode* pc;
383 RootedScript script(cx, cx->currentScript(&pc));
384 if (script)
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,
392 bool constructing);
395 * Monitor a javascript call, either on entry to the interpreter or made
396 * from within the interpreter.
398 inline void
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);
408 inline bool
409 TrackPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id)
411 if (obj->hasLazyType() || obj->type()->unknownProperties())
412 return false;
414 if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id))
415 return false;
417 return true;
420 inline void
421 EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id)
423 id = IdToTypeId(id);
425 if (obj->hasSingletonType()) {
426 AutoEnterAnalysis enter(cx);
427 if (obj->hasLazyType() && !obj->getType(cx)) {
428 CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes");
429 return;
431 if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) {
432 MOZ_ASSERT(obj->type()->unknownProperties());
433 return;
437 MOZ_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
440 inline bool
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>();
449 inline bool
450 PropertyHasBeenMarkedNonConstant(JSObject* obj, jsid id)
452 // Non-constant properties are only relevant for singleton objects.
453 if (!obj->hasSingletonType())
454 return true;
456 // EnsureTrackPropertyTypes must have been called on this object.
457 if (obj->type()->unknownProperties())
458 return true;
459 HeapTypeSet* types = obj->type()->maybeGetProperty(IdToTypeId(id));
460 return types->nonConstantProperty();
463 inline bool
464 HasTypePropertyId(JSObject* obj, jsid id, Type type)
466 if (obj->hasLazyType())
467 return true;
469 if (obj->type()->unknownProperties())
470 return true;
472 if (HeapTypeSet* types = obj->type()->maybeGetProperty(IdToTypeId(id)))
473 return types->hasType(type);
475 return false;
478 inline bool
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. */
488 inline void
489 AddTypePropertyId(ExclusiveContext* cx, JSObject* obj, jsid id, Type type)
491 id = IdToTypeId(id);
492 if (TrackPropertyTypes(cx, obj, id))
493 AddTypePropertyId(cx, obj->type(), id, type);
496 inline void
497 AddTypePropertyId(ExclusiveContext* cx, JSObject* obj, jsid id, const Value& value)
499 id = IdToTypeId(id);
500 if (TrackPropertyTypes(cx, obj, id))
501 AddTypePropertyId(cx, obj->type(), id, value);
504 /* Set one or more dynamic flags on a type object. */
505 inline void
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.
518 inline void
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);
528 inline void
529 MarkTypePropertyNonData(ExclusiveContext* cx, JSObject* obj, jsid id)
531 id = IdToTypeId(id);
532 if (TrackPropertyTypes(cx, obj, id))
533 obj->type()->markPropertyNonData(cx, id);
536 inline void
537 MarkTypePropertyNonWritable(ExclusiveContext* cx, JSObject* obj, jsid id)
539 id = IdToTypeId(id);
540 if (TrackPropertyTypes(cx, obj, id))
541 obj->type()->markPropertyNonWritable(cx, id);
544 inline bool
545 IsTypePropertyIdMarkedNonData(JSObject* obj, jsid id)
547 return obj->type()->isPropertyNonData(id);
550 inline bool
551 IsTypePropertyIdMarkedNonWritable(JSObject* obj, jsid id)
553 return obj->type()->isPropertyNonWritable(id);
556 /* Mark a state change on a particular object. */
557 inline void
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.
569 inline void
570 FixArrayType(ExclusiveContext* cx, ArrayObject* obj)
572 cx->compartment()->types.fixArrayType(cx, obj);
575 inline void
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())
596 num += fun->nargs();
597 return num;
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) {
631 (*hint)++;
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.
640 size_t bottom = 0;
641 size_t top = script->nTypeSets() - 1;
642 size_t mid = bottom + (top - bottom) / 2;
643 while (mid < top) {
644 if (bytecodeMap[mid] < offset)
645 bottom = mid + 1;
646 else if (bytecodeMap[mid] > offset)
647 top = mid;
648 else
649 break;
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);
658 *hint = mid;
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();
667 if (!types)
668 return nullptr;
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> {
675 JSScript* script;
677 uint32_t offset : 24;
678 JSProtoKey kind : 8;
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. */
694 js::NewObjectKind
695 UseNewTypeForInitializer(JSScript* script, jsbytecode* pc, JSProtoKey key);
697 js::NewObjectKind
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;
711 key.script = script;
712 key.offset = offset;
713 key.kind = kind;
715 if (!cx->compartment()->types.allocationSiteTable)
716 return cx->compartment()->types.addAllocationSiteTypeObject(cx, key);
718 AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key);
720 if (p)
721 return p->value();
722 return cx->compartment()->types.addAllocationSiteTypeObject(cx, key);
725 /* Set the type to use for obj according to the site it was allocated at. */
726 static inline bool
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
739 * has been analyzed.
741 TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
742 } else {
743 types::TypeObject* type = TypeScript::InitObject(cx, script, pc, key);
744 if (!type)
745 return false;
746 obj->uninlinedSetType(type);
749 return true;
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)
761 jsbytecode* pc;
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.
778 uint32_t i;
779 if (js_IdIsIndex(id, &i))
780 return;
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)
787 return;
788 MarkTypeObjectUnknownProperties(cx, type);
792 /* static */ inline void
793 TypeScript::SetThis(JSContext* cx, JSScript* script, Type type)
795 StackTypeSet* types = ThisTypes(script);
796 if (!types)
797 return;
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);
818 if (!types)
819 return;
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 /////////////////////////////////////////////////////////////////////
838 // TypeCompartment
839 /////////////////////////////////////////////////////////////////////
841 inline JSCompartment*
842 TypeCompartment::compartment()
844 return (JSCompartment*)((char*)this - offsetof(JSCompartment, types));
847 /////////////////////////////////////////////////////////////////////
848 // TypeSet
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
878 HashKey(T v)
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>
893 static U**
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);
902 if (!converting) {
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)
911 return nullptr;
913 count++;
914 unsigned newCapacity = HashSetCapacity(count);
916 if (newCapacity == capacity) {
917 MOZ_ASSERT(!converting);
918 return &values[insertpos];
921 U** newValues = alloc.newArray<U*>(newCapacity);
922 if (!newValues)
923 return nullptr;
924 mozilla::PodZero(newValues, newCapacity);
926 for (unsigned i = 0; i < capacity; i++) {
927 if (values[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];
935 values = newValues;
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>
948 static inline U**
949 HashSetInsert(LifoAlloc& alloc, U**& values, unsigned& count, T key)
951 if (count == 0) {
952 MOZ_ASSERT(values == nullptr);
953 count++;
954 return (U**) &values;
957 if (count == 1) {
958 U* oldData = (U*) values;
959 if (KEY::getKey(oldData) == key)
960 return (U**) &values;
962 values = alloc.newArray<U*>(SET_ARRAY_SIZE);
963 if (!values) {
964 values = (U**) oldData;
965 return nullptr;
967 mozilla::PodZero(values, SET_ARRAY_SIZE);
968 count++;
970 values[0] = oldData;
971 return &values[1];
974 if (count <= SET_ARRAY_SIZE) {
975 for (unsigned i = 0; i < count; i++) {
976 if (KEY::getKey(values[i]) == key)
977 return &values[i];
980 if (count < SET_ARRAY_SIZE) {
981 count++;
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>
991 static inline U*
992 HashSetLookup(U** values, unsigned count, T key)
994 if (count == 0)
995 return nullptr;
997 if (count == 1)
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)
1003 return values[i];
1005 return nullptr;
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)
1013 return values[pos];
1014 pos = (pos + 1) & (capacity - 1);
1017 return nullptr;
1020 inline TypeObjectKey*
1021 Type::objectKey() const
1023 MOZ_ASSERT(isObject());
1024 return (TypeObjectKey*) data;
1027 inline JSObject*
1028 Type::singleObject() const
1030 return objectKey()->asSingleObject();
1033 inline TypeObject*
1034 Type::typeObject() const
1036 return objectKey()->asTypeObject();
1039 inline JSObject*
1040 Type::singleObjectNoBarrier() const
1042 return objectKey()->asSingleObjectNoBarrier();
1045 inline TypeObject*
1046 Type::typeObjectNoBarrier() const
1048 return objectKey()->asTypeObjectNoBarrier();
1051 inline bool
1052 TypeSet::hasType(Type type) const
1054 if (unknown())
1055 return true;
1057 if (type.isUnknown()) {
1058 return false;
1059 } else if (type.isPrimitive()) {
1060 return !!(flags & PrimitiveTypeFlag(type.primitive()));
1061 } else if (type.isAnyObject()) {
1062 return !!(flags & TYPE_FLAG_ANYOBJECT);
1063 } else {
1064 return !!(flags & TYPE_FLAG_ANYOBJECT) ||
1065 HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
1066 (objectSet, baseObjectCount(), type.objectKey()) != nullptr;
1070 inline void
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);
1078 inline void
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;
1088 } else {
1089 MOZ_ASSERT(!constraintList);
1093 inline void
1094 HeapTypeSet::setNonDataPropertyIgnoringConstraints()
1096 flags |= TYPE_FLAG_NON_DATA_PROPERTY;
1099 inline void
1100 HeapTypeSet::setNonDataProperty(ExclusiveContext* cx)
1102 if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
1103 return;
1105 setNonDataPropertyIgnoringConstraints();
1106 newPropertyState(cx);
1109 inline void
1110 HeapTypeSet::setNonWritableProperty(ExclusiveContext* cx)
1112 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
1113 return;
1115 flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY;
1116 newPropertyState(cx);
1119 inline void
1120 HeapTypeSet::setNonConstantProperty(ExclusiveContext* cx)
1122 if (flags & TYPE_FLAG_NON_CONSTANT_PROPERTY)
1123 return;
1125 flags |= TYPE_FLAG_NON_CONSTANT_PROPERTY;
1126 newPropertyState(cx);
1129 inline unsigned
1130 TypeSet::getObjectCount() const
1132 MOZ_ASSERT(!unknownObject());
1133 uint32_t count = baseObjectCount();
1134 if (count > SET_ARRAY_SIZE)
1135 return HashSetCapacity(count);
1136 return count;
1139 inline TypeObjectKey*
1140 TypeSet::getObject(unsigned i) const
1142 MOZ_ASSERT(i < getObjectCount());
1143 if (baseObjectCount() == 1) {
1144 MOZ_ASSERT(i == 0);
1145 return (TypeObjectKey*) objectSet;
1147 return objectSet[i];
1150 inline JSObject*
1151 TypeSet::getSingleObject(unsigned i) const
1153 TypeObjectKey* key = getObject(i);
1154 return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr;
1157 inline TypeObject*
1158 TypeSet::getTypeObject(unsigned i) const
1160 TypeObjectKey* key = getObject(i);
1161 return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
1164 inline JSObject*
1165 TypeSet::getSingleObjectNoBarrier(unsigned i) const
1167 TypeObjectKey* key = getObject(i);
1168 return (key && key->isSingleObject()) ? key->asSingleObjectNoBarrier() : nullptr;
1171 inline TypeObject*
1172 TypeSet::getTypeObjectNoBarrier(unsigned i) const
1174 TypeObjectKey* key = getObject(i);
1175 return (key && key->isTypeObject()) ? key->asTypeObjectNoBarrier() : nullptr;
1178 inline const Class*
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();
1185 return nullptr;
1188 /////////////////////////////////////////////////////////////////////
1189 // TypeObject
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));
1208 inline void
1209 TypeObject::finalize(FreeOp* fop)
1211 fop->delete_(newScriptDontCheckGeneration());
1214 inline uint32_t
1215 TypeObject::basePropertyCount()
1217 return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
1220 inline void
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);
1229 inline HeapTypeSet*
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))
1237 return types;
1239 Property* base = cx->typeLifoAlloc().new_<Property>(id);
1240 if (!base) {
1241 markUnknown(cx);
1242 return nullptr;
1245 uint32_t propertyCount = basePropertyCount();
1246 Property** pprop = HashSetInsert<jsid,Property,Property>
1247 (cx->typeLifoAlloc(), propertySet, propertyCount, id);
1248 if (!pprop) {
1249 markUnknown(cx);
1250 return nullptr;
1253 MOZ_ASSERT(!*pprop);
1255 setBasePropertyCount(propertyCount);
1256 *pprop = base;
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
1263 // future.
1264 markUnknown(cx);
1267 return &base->types;
1270 inline HeapTypeSet*
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;
1283 inline unsigned
1284 TypeObject::getPropertyCount()
1286 uint32_t count = basePropertyCount();
1287 if (count > SET_ARRAY_SIZE)
1288 return HashSetCapacity(count);
1289 return count;
1292 inline Property*
1293 TypeObject::getProperty(unsigned i)
1295 MOZ_ASSERT(i < getPropertyCount());
1296 if (basePropertyCount() == 1) {
1297 MOZ_ASSERT(i == 0);
1298 return (Property*) propertySet;
1300 return propertySet[i];
1303 inline void
1304 TypeNewScript::writeBarrierPre(TypeNewScript* newScript)
1306 if (!newScript->fun->runtimeFromAnyThread()->needsIncrementalBarrier())
1307 return;
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*
1317 JSScript::types()
1319 maybeSweepTypes(nullptr);
1320 return types_;
1323 inline bool
1324 JSScript::ensureHasTypes(JSContext* cx)
1326 return types() || makeTypes(cx);
1329 namespace js {
1331 template <>
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()));
1341 template <>
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()));
1351 } // namespace js
1353 #endif /* jsinferinlines_h */