michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Definitions related to javascript type inference. */ michael@0: michael@0: #ifndef jsinfer_h michael@0: #define jsinfer_h michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/TypedEnum.h" michael@0: michael@0: #include "jsalloc.h" michael@0: #include "jsfriendapi.h" michael@0: #include "jstypes.h" michael@0: michael@0: #include "ds/IdValuePair.h" michael@0: #include "ds/LifoAlloc.h" michael@0: #include "gc/Barrier.h" michael@0: #include "gc/Marking.h" michael@0: #include "jit/IonTypes.h" michael@0: #include "js/Utility.h" michael@0: #include "js/Vector.h" michael@0: michael@0: namespace js { michael@0: michael@0: class TypeDescr; michael@0: michael@0: class TaggedProto michael@0: { michael@0: public: michael@0: static JSObject * const LazyProto; michael@0: michael@0: TaggedProto() : proto(nullptr) {} michael@0: TaggedProto(JSObject *proto) : proto(proto) {} michael@0: michael@0: uintptr_t toWord() const { return uintptr_t(proto); } michael@0: michael@0: bool isLazy() const { michael@0: return proto == LazyProto; michael@0: } michael@0: bool isObject() const { michael@0: /* Skip nullptr and LazyProto. */ michael@0: return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); michael@0: } michael@0: JSObject *toObject() const { michael@0: JS_ASSERT(isObject()); michael@0: return proto; michael@0: } michael@0: JSObject *toObjectOrNull() const { michael@0: JS_ASSERT(!proto || isObject()); michael@0: return proto; michael@0: } michael@0: JSObject *raw() const { return proto; } michael@0: michael@0: bool operator ==(const TaggedProto &other) { return proto == other.proto; } michael@0: bool operator !=(const TaggedProto &other) { return proto != other.proto; } michael@0: michael@0: private: michael@0: JSObject *proto; michael@0: }; michael@0: michael@0: template <> michael@0: struct RootKind michael@0: { michael@0: static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } michael@0: }; michael@0: michael@0: template <> struct GCMethods michael@0: { michael@0: static TaggedProto initial() { return TaggedProto(); } michael@0: static ThingRootKind kind() { return THING_ROOT_OBJECT; } michael@0: static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } michael@0: }; michael@0: michael@0: template <> struct GCMethods michael@0: { michael@0: static TaggedProto initial() { return TaggedProto(); } michael@0: static ThingRootKind kind() { return THING_ROOT_OBJECT; } michael@0: static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } michael@0: }; michael@0: michael@0: template michael@0: class TaggedProtoOperations michael@0: { michael@0: const TaggedProto *value() const { michael@0: return static_cast(this)->extract(); michael@0: } michael@0: michael@0: public: michael@0: uintptr_t toWord() const { return value()->toWord(); } michael@0: inline bool isLazy() const { return value()->isLazy(); } michael@0: inline bool isObject() const { return value()->isObject(); } michael@0: inline JSObject *toObject() const { return value()->toObject(); } michael@0: inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } michael@0: JSObject *raw() const { return value()->raw(); } michael@0: }; michael@0: michael@0: template <> michael@0: class HandleBase : public TaggedProtoOperations > michael@0: { michael@0: friend class TaggedProtoOperations >; michael@0: const TaggedProto * extract() const { michael@0: return static_cast*>(this)->address(); michael@0: } michael@0: }; michael@0: michael@0: template <> michael@0: class RootedBase : public TaggedProtoOperations > michael@0: { michael@0: friend class TaggedProtoOperations >; michael@0: const TaggedProto *extract() const { michael@0: return static_cast *>(this)->address(); michael@0: } michael@0: }; michael@0: michael@0: class CallObject; michael@0: michael@0: /* michael@0: * Execution Mode Overview michael@0: * michael@0: * JavaScript code can execute either sequentially or in parallel, such as in michael@0: * PJS. Functions which behave identically in either execution mode can take a michael@0: * ThreadSafeContext, and functions which have similar but not identical michael@0: * behavior between execution modes can be templated on the mode. Such michael@0: * functions use a context parameter type from ExecutionModeTraits below michael@0: * indicating whether they are only permitted constrained operations (such as michael@0: * thread safety, and side effects limited to being thread-local), or whether michael@0: * they can have arbitrary side effects. michael@0: */ michael@0: michael@0: enum ExecutionMode { michael@0: /* Normal JavaScript execution. */ michael@0: SequentialExecution, michael@0: michael@0: /* michael@0: * JavaScript code to be executed in parallel worker threads in PJS in a michael@0: * fork join fashion. michael@0: */ michael@0: ParallelExecution, michael@0: michael@0: /* michael@0: * Modes after this point are internal and are not counted in michael@0: * NumExecutionModes below. michael@0: */ michael@0: michael@0: /* michael@0: * MIR analysis performed when invoking 'new' on a script, to determine michael@0: * definite properties. Used by the optimizing JIT. michael@0: */ michael@0: DefinitePropertiesAnalysis, michael@0: michael@0: /* michael@0: * MIR analysis performed when executing a script which uses its arguments, michael@0: * when it is not known whether a lazy arguments value can be used. michael@0: */ michael@0: ArgumentsUsageAnalysis michael@0: }; michael@0: michael@0: /* michael@0: * Not as part of the enum so we don't get warnings about unhandled enum michael@0: * values. michael@0: */ michael@0: static const unsigned NumExecutionModes = ParallelExecution + 1; michael@0: michael@0: template michael@0: struct ExecutionModeTraits michael@0: { michael@0: }; michael@0: michael@0: template <> struct ExecutionModeTraits michael@0: { michael@0: typedef JSContext * ContextType; michael@0: typedef ExclusiveContext * ExclusiveContextType; michael@0: michael@0: static inline JSContext *toContextType(ExclusiveContext *cx); michael@0: }; michael@0: michael@0: template <> struct ExecutionModeTraits michael@0: { michael@0: typedef ForkJoinContext * ContextType; michael@0: typedef ForkJoinContext * ExclusiveContextType; michael@0: michael@0: static inline ForkJoinContext *toContextType(ForkJoinContext *cx) { return cx; } michael@0: }; michael@0: michael@0: namespace jit { michael@0: struct IonScript; michael@0: class IonAllocPolicy; michael@0: class TempAllocator; michael@0: } michael@0: michael@0: namespace types { michael@0: michael@0: class TypeZone; michael@0: class TypeSet; michael@0: class TypeObjectKey; michael@0: michael@0: /* michael@0: * Information about a single concrete type. We pack this into a single word, michael@0: * where small values are particular primitive or other singleton types, and michael@0: * larger values are either specific JS objects or type objects. michael@0: */ michael@0: class Type michael@0: { michael@0: uintptr_t data; michael@0: Type(uintptr_t data) : data(data) {} michael@0: michael@0: public: michael@0: michael@0: uintptr_t raw() const { return data; } michael@0: michael@0: bool isPrimitive() const { michael@0: return data < JSVAL_TYPE_OBJECT; michael@0: } michael@0: michael@0: bool isPrimitive(JSValueType type) const { michael@0: JS_ASSERT(type < JSVAL_TYPE_OBJECT); michael@0: return (uintptr_t) type == data; michael@0: } michael@0: michael@0: JSValueType primitive() const { michael@0: JS_ASSERT(isPrimitive()); michael@0: return (JSValueType) data; michael@0: } michael@0: michael@0: bool isMagicArguments() const { michael@0: return primitive() == JSVAL_TYPE_MAGIC; michael@0: } michael@0: michael@0: bool isSomeObject() const { michael@0: return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: michael@0: bool isAnyObject() const { michael@0: return data == JSVAL_TYPE_OBJECT; michael@0: } michael@0: michael@0: bool isUnknown() const { michael@0: return data == JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: michael@0: /* Accessors for types that are either JSObject or TypeObject. */ michael@0: michael@0: bool isObject() const { michael@0: JS_ASSERT(!isAnyObject() && !isUnknown()); michael@0: return data > JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: michael@0: bool isObjectUnchecked() const { michael@0: return data > JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: michael@0: inline TypeObjectKey *objectKey() const; michael@0: michael@0: /* Accessors for JSObject types */ michael@0: michael@0: bool isSingleObject() const { michael@0: return isObject() && !!(data & 1); michael@0: } michael@0: michael@0: inline JSObject *singleObject() const; michael@0: michael@0: /* Accessors for TypeObject types */ michael@0: michael@0: bool isTypeObject() const { michael@0: return isObject() && !(data & 1); michael@0: } michael@0: michael@0: inline TypeObject *typeObject() const; michael@0: michael@0: bool operator == (Type o) const { return data == o.data; } michael@0: bool operator != (Type o) const { return data != o.data; } michael@0: michael@0: static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); } michael@0: static inline Type NullType() { return Type(JSVAL_TYPE_NULL); } michael@0: static inline Type BooleanType() { return Type(JSVAL_TYPE_BOOLEAN); } michael@0: static inline Type Int32Type() { return Type(JSVAL_TYPE_INT32); } michael@0: static inline Type DoubleType() { return Type(JSVAL_TYPE_DOUBLE); } michael@0: static inline Type StringType() { return Type(JSVAL_TYPE_STRING); } michael@0: static inline Type MagicArgType() { return Type(JSVAL_TYPE_MAGIC); } michael@0: static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); } michael@0: static inline Type UnknownType() { return Type(JSVAL_TYPE_UNKNOWN); } michael@0: michael@0: static inline Type PrimitiveType(JSValueType type) { michael@0: JS_ASSERT(type < JSVAL_TYPE_UNKNOWN); michael@0: return Type(type); michael@0: } michael@0: michael@0: static inline Type ObjectType(JSObject *obj); michael@0: static inline Type ObjectType(TypeObject *obj); michael@0: static inline Type ObjectType(TypeObjectKey *obj); michael@0: }; michael@0: michael@0: /* Get the type of a jsval, or zero for an unknown special value. */ michael@0: inline Type GetValueType(const Value &val); michael@0: michael@0: /* michael@0: * Get the type of a possibly optimized out value. This generally only michael@0: * happens on unconditional type monitors on bailing out of Ion, such as michael@0: * for argument and local types. michael@0: */ michael@0: inline Type GetMaybeOptimizedOutValueType(const Value &val); michael@0: michael@0: /* michael@0: * Type inference memory management overview. michael@0: * michael@0: * Type information about the values observed within scripts and about the michael@0: * contents of the heap is accumulated as the program executes. Compilation michael@0: * accumulates constraints relating type information on the heap with the michael@0: * compilations that should be invalidated when those types change. Type michael@0: * information and constraints are allocated in the zone's typeLifoAlloc, michael@0: * and on GC all data referring to live things is copied into a new allocator. michael@0: * Thus, type set and constraints only hold weak references. michael@0: */ michael@0: michael@0: /* michael@0: * A constraint which listens to additions to a type set and propagates those michael@0: * changes to other type sets. michael@0: */ michael@0: class TypeConstraint michael@0: { michael@0: public: michael@0: /* Next constraint listening to the same type set. */ michael@0: TypeConstraint *next; michael@0: michael@0: TypeConstraint() michael@0: : next(nullptr) michael@0: {} michael@0: michael@0: /* Debugging name for this kind of constraint. */ michael@0: virtual const char *kind() = 0; michael@0: michael@0: /* Register a new type for the set this constraint is listening to. */ michael@0: virtual void newType(JSContext *cx, TypeSet *source, Type type) = 0; michael@0: michael@0: /* michael@0: * For constraints attached to an object property's type set, mark the michael@0: * property as having its configuration changed. michael@0: */ michael@0: virtual void newPropertyState(JSContext *cx, TypeSet *source) {} michael@0: michael@0: /* michael@0: * For constraints attached to the JSID_EMPTY type set on an object, michael@0: * indicate a change in one of the object's dynamic property flags or other michael@0: * state. michael@0: */ michael@0: virtual void newObjectState(JSContext *cx, TypeObject *object) {} michael@0: michael@0: /* michael@0: * If the data this constraint refers to is still live, copy it into the michael@0: * zone's new allocator. Type constraints only hold weak references. michael@0: */ michael@0: virtual bool sweep(TypeZone &zone, TypeConstraint **res) = 0; michael@0: }; michael@0: michael@0: /* Flags and other state stored in TypeSet::flags */ michael@0: enum MOZ_ENUM_TYPE(uint32_t) { michael@0: TYPE_FLAG_UNDEFINED = 0x1, michael@0: TYPE_FLAG_NULL = 0x2, michael@0: TYPE_FLAG_BOOLEAN = 0x4, michael@0: TYPE_FLAG_INT32 = 0x8, michael@0: TYPE_FLAG_DOUBLE = 0x10, michael@0: TYPE_FLAG_STRING = 0x20, michael@0: TYPE_FLAG_LAZYARGS = 0x40, michael@0: TYPE_FLAG_ANYOBJECT = 0x80, michael@0: michael@0: /* Mask containing all primitives */ michael@0: TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN | michael@0: TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING, michael@0: michael@0: /* Mask/shift for the number of objects in objectSet */ michael@0: TYPE_FLAG_OBJECT_COUNT_MASK = 0x1f00, michael@0: TYPE_FLAG_OBJECT_COUNT_SHIFT = 8, michael@0: TYPE_FLAG_OBJECT_COUNT_LIMIT = michael@0: TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT, michael@0: michael@0: /* Whether the contents of this type set are totally unknown. */ michael@0: TYPE_FLAG_UNKNOWN = 0x00002000, michael@0: michael@0: /* Mask of normal type flags on a type set. */ michael@0: TYPE_FLAG_BASE_MASK = 0x000020ff, michael@0: michael@0: /* Additional flags for HeapTypeSet sets. */ michael@0: michael@0: /* michael@0: * Whether the property has ever been deleted or reconfigured to behave michael@0: * differently from a plain data property, other than making the property michael@0: * non-writable. michael@0: */ michael@0: TYPE_FLAG_NON_DATA_PROPERTY = 0x00004000, michael@0: michael@0: /* Whether the property has ever been made non-writable. */ michael@0: TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00008000, michael@0: michael@0: /* michael@0: * Whether the property is definitely in a particular slot on all objects michael@0: * from which it has not been deleted or reconfigured. For singletons michael@0: * this may be a fixed or dynamic slot, and for other objects this will be michael@0: * a fixed slot. michael@0: * michael@0: * If the property is definite, mask and shift storing the slot + 1. michael@0: * Otherwise these bits are clear. michael@0: */ michael@0: TYPE_FLAG_DEFINITE_MASK = 0xffff0000, michael@0: TYPE_FLAG_DEFINITE_SHIFT = 16 michael@0: }; michael@0: typedef uint32_t TypeFlags; michael@0: michael@0: /* Flags and other state stored in TypeObject::flags */ michael@0: enum MOZ_ENUM_TYPE(uint32_t) { michael@0: /* Whether this type object is associated with some allocation site. */ michael@0: OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1, michael@0: michael@0: /* If set, addendum information should not be installed on this object. */ michael@0: OBJECT_FLAG_ADDENDUM_CLEARED = 0x2, michael@0: michael@0: /* michael@0: * If set, the object's prototype might be in the nursery and can't be michael@0: * used during Ion compilation (which may be occurring off thread). michael@0: */ michael@0: OBJECT_FLAG_NURSERY_PROTO = 0x4, michael@0: michael@0: /* michael@0: * Whether we have ensured all type sets in the compartment contain michael@0: * ANYOBJECT instead of this object. michael@0: */ michael@0: OBJECT_FLAG_SETS_MARKED_UNKNOWN = 0x8, michael@0: michael@0: /* Mask/shift for the number of properties in propertySet */ michael@0: OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff0, michael@0: OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 4, michael@0: OBJECT_FLAG_PROPERTY_COUNT_LIMIT = michael@0: OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, michael@0: michael@0: /* Whether any objects this represents may have sparse indexes. */ michael@0: OBJECT_FLAG_SPARSE_INDEXES = 0x00010000, michael@0: michael@0: /* Whether any objects this represents may not have packed dense elements. */ michael@0: OBJECT_FLAG_NON_PACKED = 0x00020000, michael@0: michael@0: /* michael@0: * Whether any objects this represents may be arrays whose length does not michael@0: * fit in an int32. michael@0: */ michael@0: OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000, michael@0: michael@0: /* Whether any objects have been iterated over. */ michael@0: OBJECT_FLAG_ITERATED = 0x00080000, michael@0: michael@0: /* For a global object, whether flags were set on the RegExpStatics. */ michael@0: OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00100000, michael@0: michael@0: /* michael@0: * For the function on a run-once script, whether the function has actually michael@0: * run multiple times. michael@0: */ michael@0: OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000, michael@0: michael@0: /* michael@0: * Whether objects with this type should be allocated directly in the michael@0: * tenured heap. michael@0: */ michael@0: OBJECT_FLAG_PRE_TENURE = 0x00400000, michael@0: michael@0: /* michael@0: * Whether all properties of this object are considered unknown. michael@0: * If set, all other flags in DYNAMIC_MASK will also be set. michael@0: */ michael@0: OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x00800000, michael@0: michael@0: /* Flags which indicate dynamic properties of represented objects. */ michael@0: OBJECT_FLAG_DYNAMIC_MASK = 0x00ff0000, michael@0: michael@0: /* Mask for objects created with unknown properties. */ michael@0: OBJECT_FLAG_UNKNOWN_MASK = michael@0: OBJECT_FLAG_DYNAMIC_MASK michael@0: | OBJECT_FLAG_SETS_MARKED_UNKNOWN michael@0: }; michael@0: typedef uint32_t TypeObjectFlags; michael@0: michael@0: class StackTypeSet; michael@0: class HeapTypeSet; michael@0: class TemporaryTypeSet; michael@0: michael@0: /* michael@0: * Information about the set of types associated with an lvalue. There are michael@0: * three kinds of type sets: michael@0: * michael@0: * - StackTypeSet are associated with TypeScripts, for arguments and values michael@0: * observed at property reads. These are implicitly frozen on compilation michael@0: * and do not have constraints attached to them. michael@0: * michael@0: * - HeapTypeSet are associated with the properties of TypeObjects. These michael@0: * may have constraints added to them to trigger invalidation of compiled michael@0: * code. michael@0: * michael@0: * - TemporaryTypeSet are created during compilation and do not outlive michael@0: * that compilation. michael@0: */ michael@0: class TypeSet michael@0: { michael@0: protected: michael@0: /* Flags for this type set. */ michael@0: TypeFlags flags; michael@0: michael@0: /* Possible objects this type set can represent. */ michael@0: TypeObjectKey **objectSet; michael@0: michael@0: public: michael@0: michael@0: TypeSet() michael@0: : flags(0), objectSet(nullptr) michael@0: {} michael@0: michael@0: void print(); michael@0: michael@0: /* Whether this set contains a specific type. */ michael@0: inline bool hasType(Type type) const; michael@0: michael@0: TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; } michael@0: bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); } michael@0: bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); } michael@0: bool empty() const { return !baseFlags() && !baseObjectCount(); } michael@0: michael@0: bool hasAnyFlag(TypeFlags flags) const { michael@0: JS_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags); michael@0: return !!(baseFlags() & flags); michael@0: } michael@0: michael@0: bool nonDataProperty() const { michael@0: return flags & TYPE_FLAG_NON_DATA_PROPERTY; michael@0: } michael@0: bool nonWritableProperty() const { michael@0: return flags & TYPE_FLAG_NON_WRITABLE_PROPERTY; michael@0: } michael@0: bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_MASK; } michael@0: unsigned definiteSlot() const { michael@0: JS_ASSERT(definiteProperty()); michael@0: return (flags >> TYPE_FLAG_DEFINITE_SHIFT) - 1; michael@0: } michael@0: michael@0: /* Join two type sets into a new set. The result should not be modified further. */ michael@0: static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc); michael@0: michael@0: /* Add a type to this set using the specified allocator. */ michael@0: void addType(Type type, LifoAlloc *alloc); michael@0: michael@0: /* Get a list of all types in this set. */ michael@0: typedef Vector TypeList; michael@0: bool enumerateTypes(TypeList *list); michael@0: michael@0: /* michael@0: * Iterate through the objects in this set. getObjectCount overapproximates michael@0: * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject michael@0: * may return nullptr. michael@0: */ michael@0: inline unsigned getObjectCount() const; michael@0: inline TypeObjectKey *getObject(unsigned i) const; michael@0: inline JSObject *getSingleObject(unsigned i) const; michael@0: inline TypeObject *getTypeObject(unsigned i) const; michael@0: michael@0: /* The Class of an object in this set. */ michael@0: inline const Class *getObjectClass(unsigned i) const; michael@0: michael@0: bool canSetDefinite(unsigned slot) { michael@0: // Note: the cast is required to work around an MSVC issue. michael@0: return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT); michael@0: } michael@0: void setDefinite(unsigned slot) { michael@0: JS_ASSERT(canSetDefinite(slot)); michael@0: flags |= ((slot + 1) << TYPE_FLAG_DEFINITE_SHIFT); michael@0: JS_ASSERT(definiteSlot() == slot); michael@0: } michael@0: michael@0: /* Whether any values in this set might have the specified type. */ michael@0: bool mightBeMIRType(jit::MIRType type); michael@0: michael@0: /* michael@0: * Get whether this type set is known to be a subset of other. michael@0: * This variant doesn't freeze constraints. That variant is called knownSubset michael@0: */ michael@0: bool isSubset(TypeSet *other); michael@0: michael@0: /* Forward all types in this set to the specified constraint. */ michael@0: bool addTypesToConstraint(JSContext *cx, TypeConstraint *constraint); michael@0: michael@0: // Clone a type set into an arbitrary allocator. michael@0: TemporaryTypeSet *clone(LifoAlloc *alloc) const; michael@0: bool clone(LifoAlloc *alloc, TemporaryTypeSet *result) const; michael@0: michael@0: // Create a new TemporaryTypeSet where undefined and/or null has been filtered out. michael@0: TemporaryTypeSet *filter(LifoAlloc *alloc, bool filterUndefined, bool filterNull) const; michael@0: michael@0: protected: michael@0: uint32_t baseObjectCount() const { michael@0: return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT; michael@0: } michael@0: inline void setBaseObjectCount(uint32_t count); michael@0: michael@0: void clearObjects(); michael@0: }; michael@0: michael@0: /* Superclass common to stack and heap type sets. */ michael@0: class ConstraintTypeSet : public TypeSet michael@0: { michael@0: public: michael@0: /* Chain of constraints which propagate changes out from this type set. */ michael@0: TypeConstraint *constraintList; michael@0: michael@0: ConstraintTypeSet() : constraintList(nullptr) {} michael@0: michael@0: /* michael@0: * Add a type to this set, calling any constraint handlers if this is a new michael@0: * possible type. michael@0: */ michael@0: void addType(ExclusiveContext *cx, Type type); michael@0: michael@0: /* Add a new constraint to this set. */ michael@0: bool addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting = true); michael@0: michael@0: inline void sweep(JS::Zone *zone, bool *oom); michael@0: }; michael@0: michael@0: class StackTypeSet : public ConstraintTypeSet michael@0: { michael@0: public: michael@0: }; michael@0: michael@0: class HeapTypeSet : public ConstraintTypeSet michael@0: { michael@0: inline void newPropertyState(ExclusiveContext *cx); michael@0: michael@0: public: michael@0: /* Mark this type set as representing a non-data property. */ michael@0: inline void setNonDataProperty(ExclusiveContext *cx); michael@0: inline void setNonDataPropertyIgnoringConstraints(); // Variant for use during GC. michael@0: michael@0: /* Mark this type set as representing a non-writable property. */ michael@0: inline void setNonWritableProperty(ExclusiveContext *cx); michael@0: }; michael@0: michael@0: class CompilerConstraintList; michael@0: michael@0: CompilerConstraintList * michael@0: NewCompilerConstraintList(jit::TempAllocator &alloc); michael@0: michael@0: class TemporaryTypeSet : public TypeSet michael@0: { michael@0: public: michael@0: TemporaryTypeSet() {} michael@0: TemporaryTypeSet(Type type); michael@0: michael@0: TemporaryTypeSet(uint32_t flags, TypeObjectKey **objectSet) { michael@0: this->flags = flags; michael@0: this->objectSet = objectSet; michael@0: } michael@0: michael@0: /* michael@0: * Constraints for JIT compilation. michael@0: * michael@0: * Methods for JIT compilation. These must be used when a script is michael@0: * currently being compiled (see AutoEnterCompilation) and will add michael@0: * constraints ensuring that if the return value change in the future due michael@0: * to new type information, the script's jitcode will be discarded. michael@0: */ michael@0: michael@0: /* Get any type tag which all values in this set must have. */ michael@0: jit::MIRType getKnownMIRType(); michael@0: michael@0: bool isMagicArguments() { return getKnownMIRType() == jit::MIRType_MagicOptimizedArguments; } michael@0: michael@0: /* Whether this value may be an object. */ michael@0: bool maybeObject() { return unknownObject() || baseObjectCount() > 0; } michael@0: michael@0: /* michael@0: * Whether this typeset represents a potentially sentineled object value: michael@0: * the value may be an object or null or undefined. michael@0: * Returns false if the value cannot ever be an object. michael@0: */ michael@0: bool objectOrSentinel() { michael@0: TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT; michael@0: if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK)) michael@0: return false; michael@0: michael@0: return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0; michael@0: } michael@0: michael@0: /* Whether the type set contains objects with any of a set of flags. */ michael@0: bool hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); michael@0: michael@0: /* Get the class shared by all objects in this set, or nullptr. */ michael@0: const Class *getKnownClass(); michael@0: michael@0: /* Result returned from forAllClasses */ michael@0: enum ForAllResult { michael@0: EMPTY=1, // Set empty michael@0: ALL_TRUE, // Set not empty and predicate returned true for all classes michael@0: ALL_FALSE, // Set not empty and predicate returned false for all classes michael@0: MIXED, // Set not empty and predicate returned false for some classes michael@0: // and true for others, or set contains an unknown or non-object michael@0: // type michael@0: }; michael@0: michael@0: /* Apply func to the members of the set and return an appropriate result. michael@0: * The iteration may end early if the result becomes known early. michael@0: */ michael@0: ForAllResult forAllClasses(bool (*func)(const Class *clasp)); michael@0: michael@0: /* Get the prototype shared by all objects in this set, or nullptr. */ michael@0: JSObject *getCommonPrototype(); michael@0: michael@0: /* Get the typed array type of all objects in this set, or TypedArrayObject::TYPE_MAX. */ michael@0: int getTypedArrayType(); michael@0: michael@0: /* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */ michael@0: bool isDOMClass(); michael@0: michael@0: /* Whether clasp->isCallable() is true for one or more objects in this set. */ michael@0: bool maybeCallable(); michael@0: michael@0: /* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */ michael@0: bool maybeEmulatesUndefined(); michael@0: michael@0: /* Get the single value which can appear in this type set, otherwise nullptr. */ michael@0: JSObject *getSingleton(); michael@0: michael@0: /* Whether any objects in the type set needs a barrier on id. */ michael@0: bool propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id); michael@0: michael@0: /* michael@0: * Whether this set contains all types in other, except (possibly) the michael@0: * specified type. michael@0: */ michael@0: bool filtersType(const TemporaryTypeSet *other, Type type) const; michael@0: michael@0: enum DoubleConversion { michael@0: /* All types in the set should use eager double conversion. */ michael@0: AlwaysConvertToDoubles, michael@0: michael@0: /* Some types in the set should use eager double conversion. */ michael@0: MaybeConvertToDoubles, michael@0: michael@0: /* No types should use eager double conversion. */ michael@0: DontConvertToDoubles, michael@0: michael@0: /* Some types should use eager double conversion, others cannot. */ michael@0: AmbiguousDoubleConversion michael@0: }; michael@0: michael@0: /* michael@0: * Whether known double optimizations are possible for element accesses on michael@0: * objects in this type set. michael@0: */ michael@0: DoubleConversion convertDoubleElements(CompilerConstraintList *constraints); michael@0: }; michael@0: michael@0: bool michael@0: AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id); michael@0: michael@0: bool michael@0: AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, michael@0: JSScript *script, JSScript *calleeScript); michael@0: michael@0: /* Is this a reasonable PC to be doing inlining on? */ michael@0: inline bool isInlinableCall(jsbytecode *pc); michael@0: michael@0: /* Type information about a property. */ michael@0: struct Property michael@0: { michael@0: /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ michael@0: HeapId id; michael@0: michael@0: /* Possible types for this property, including types inherited from prototypes. */ michael@0: HeapTypeSet types; michael@0: michael@0: Property(jsid id) michael@0: : id(id) michael@0: {} michael@0: michael@0: Property(const Property &o) michael@0: : id(o.id.get()), types(o.types) michael@0: {} michael@0: michael@0: static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); } michael@0: static jsid getKey(Property *p) { return p->id; } michael@0: }; michael@0: michael@0: struct TypeNewScript; michael@0: struct TypeTypedObject; michael@0: michael@0: struct TypeObjectAddendum michael@0: { michael@0: enum Kind { michael@0: NewScript, michael@0: TypedObject michael@0: }; michael@0: michael@0: TypeObjectAddendum(Kind kind); michael@0: michael@0: const Kind kind; michael@0: michael@0: bool isNewScript() { michael@0: return kind == NewScript; michael@0: } michael@0: michael@0: TypeNewScript *asNewScript() { michael@0: JS_ASSERT(isNewScript()); michael@0: return (TypeNewScript*) this; michael@0: } michael@0: michael@0: bool isTypedObject() { michael@0: return kind == TypedObject; michael@0: } michael@0: michael@0: TypeTypedObject *asTypedObject() { michael@0: JS_ASSERT(isTypedObject()); michael@0: return (TypeTypedObject*) this; michael@0: } michael@0: michael@0: static inline void writeBarrierPre(TypeObjectAddendum *type); michael@0: michael@0: static void writeBarrierPost(TypeObjectAddendum *newScript, void *addr) {} michael@0: }; michael@0: michael@0: /* michael@0: * Information attached to a TypeObject if it is always constructed using 'new' michael@0: * on a particular script. This is used to manage state related to the definite michael@0: * properties on the type object: these definite properties depend on type michael@0: * information which could change as the script executes (e.g. a scripted michael@0: * setter is added to a prototype object), and we need to ensure both that the michael@0: * appropriate type constraints are in place when necessary, and that we can michael@0: * remove the definite property information and repair the JS stack if the michael@0: * constraints are violated. michael@0: */ michael@0: struct TypeNewScript : public TypeObjectAddendum michael@0: { michael@0: TypeNewScript(); michael@0: michael@0: HeapPtrFunction fun; michael@0: michael@0: /* michael@0: * Template object to use for newly constructed objects. Reflects all michael@0: * definite properties the object will have and the allocation kind to use michael@0: * for the object. The allocation kind --- and template object itself --- michael@0: * is subject to change if objects allocated with this type are given michael@0: * dynamic slots later on due to new properties being added after the michael@0: * constructor function finishes. michael@0: */ michael@0: HeapPtrObject templateObject; michael@0: michael@0: /* michael@0: * Order in which properties become initialized. We need this in case a michael@0: * scripted setter is added to one of the object's prototypes while it is michael@0: * in the middle of being initialized, so we can walk the stack and fixup michael@0: * any objects which look for in-progress objects which were prematurely michael@0: * set with their final shape. Property assignments in inner frames are michael@0: * preceded by a series of SETPROP_FRAME entries specifying the stack down michael@0: * to the frame containing the write. michael@0: */ michael@0: struct Initializer { michael@0: enum Kind { michael@0: SETPROP, michael@0: SETPROP_FRAME, michael@0: DONE michael@0: } kind; michael@0: uint32_t offset; michael@0: Initializer(Kind kind, uint32_t offset) michael@0: : kind(kind), offset(offset) michael@0: {} michael@0: }; michael@0: Initializer *initializerList; michael@0: michael@0: static inline void writeBarrierPre(TypeNewScript *newScript); michael@0: }; michael@0: michael@0: struct TypeTypedObject : public TypeObjectAddendum michael@0: { michael@0: private: michael@0: HeapPtrObject descr_; michael@0: michael@0: public: michael@0: TypeTypedObject(Handle descr); michael@0: michael@0: HeapPtrObject &descrHeapPtr() { michael@0: return descr_; michael@0: } michael@0: michael@0: TypeDescr &descr(); michael@0: }; michael@0: michael@0: /* michael@0: * Lazy type objects overview. michael@0: * michael@0: * Type objects which represent at most one JS object are constructed lazily. michael@0: * These include types for native functions, standard classes, scripted michael@0: * functions defined at the top level of global/eval scripts, and in some michael@0: * other cases. Typical web workloads often create many windows (and many michael@0: * copies of standard natives) and many scripts, with comparatively few michael@0: * non-singleton types. michael@0: * michael@0: * We can recover the type information for the object from examining it, michael@0: * so don't normally track the possible types of its properties as it is michael@0: * updated. Property type sets for the object are only constructed when an michael@0: * analyzed script attaches constraints to it: the script is querying that michael@0: * property off the object or another which delegates to it, and the analysis michael@0: * information is sensitive to changes in the property's type. Future changes michael@0: * to the property (whether those uncovered by analysis or those occurring michael@0: * in the VM) will treat these properties like those of any other type object. michael@0: */ michael@0: michael@0: /* Type information about an object accessed by a script. */ michael@0: struct TypeObject : gc::BarrieredCell michael@0: { michael@0: private: michael@0: /* Class shared by object using this type. */ michael@0: const Class *clasp_; michael@0: michael@0: /* Prototype shared by objects using this type. */ michael@0: HeapPtrObject proto_; michael@0: michael@0: /* michael@0: * Whether there is a singleton JS object with this type. That JS object michael@0: * must appear in type sets instead of this; we include the back reference michael@0: * here to allow reverting the JS object to a lazy type. michael@0: */ michael@0: HeapPtrObject singleton_; michael@0: michael@0: public: michael@0: michael@0: const Class *clasp() const { michael@0: return clasp_; michael@0: } michael@0: michael@0: void setClasp(const Class *clasp) { michael@0: JS_ASSERT(singleton()); michael@0: clasp_ = clasp; michael@0: } michael@0: michael@0: TaggedProto proto() const { michael@0: return TaggedProto(proto_); michael@0: } michael@0: michael@0: JSObject *singleton() const { michael@0: return singleton_; michael@0: } michael@0: michael@0: // For use during marking, don't call otherwise. michael@0: HeapPtrObject &protoRaw() { return proto_; } michael@0: HeapPtrObject &singletonRaw() { return singleton_; } michael@0: michael@0: void setProto(JSContext *cx, TaggedProto proto); michael@0: void setProtoUnchecked(TaggedProto proto) { michael@0: proto_ = proto.raw(); michael@0: } michael@0: michael@0: void initSingleton(JSObject *singleton) { michael@0: singleton_ = singleton; michael@0: } michael@0: michael@0: /* michael@0: * Value held by singleton if this is a standin type for a singleton JS michael@0: * object whose type has not been constructed yet. michael@0: */ michael@0: static const size_t LAZY_SINGLETON = 1; michael@0: bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } michael@0: michael@0: private: michael@0: /* Flags for this object. */ michael@0: TypeObjectFlags flags_; michael@0: michael@0: /* michael@0: * This field allows various special classes of objects to attach michael@0: * additional information to a type object: michael@0: * michael@0: * - `TypeNewScript`: If addendum is a `TypeNewScript`, it michael@0: * indicates that objects of this type have always been michael@0: * constructed using 'new' on the specified script, which adds michael@0: * some number of properties to the object in a definite order michael@0: * before the object escapes. michael@0: */ michael@0: HeapPtr addendum; michael@0: public: michael@0: michael@0: TypeObjectFlags flags() const { michael@0: return flags_; michael@0: } michael@0: michael@0: void addFlags(TypeObjectFlags flags) { michael@0: flags_ |= flags; michael@0: } michael@0: michael@0: void clearFlags(TypeObjectFlags flags) { michael@0: flags_ &= ~flags; michael@0: } michael@0: michael@0: bool hasNewScript() const { michael@0: return addendum && addendum->isNewScript(); michael@0: } michael@0: michael@0: TypeNewScript *newScript() { michael@0: return addendum->asNewScript(); michael@0: } michael@0: michael@0: bool hasTypedObject() { michael@0: return addendum && addendum->isTypedObject(); michael@0: } michael@0: michael@0: TypeTypedObject *typedObject() { michael@0: return addendum->asTypedObject(); michael@0: } michael@0: michael@0: void setAddendum(TypeObjectAddendum *addendum); michael@0: michael@0: /* michael@0: * Tag the type object for a binary data type descriptor, instance, michael@0: * or handle with the type representation of the data it points at. michael@0: * If this type object is already tagged with a binary data addendum, michael@0: * this addendum must already be associated with the same TypeRepresentation, michael@0: * and the method has no effect. michael@0: */ michael@0: bool addTypedObjectAddendum(JSContext *cx, Handle descr); michael@0: michael@0: private: michael@0: /* michael@0: * Properties of this object. This may contain JSID_VOID, representing the michael@0: * types of all integer indexes of the object, and/or JSID_EMPTY, holding michael@0: * constraints listening to changes to the object's state. michael@0: * michael@0: * The type sets in the properties of a type object describe the possible michael@0: * values that can be read out of that property in actual JS objects. michael@0: * Properties only account for native properties (those with a slot and no michael@0: * specialized getter hook) and the elements of dense arrays. For accesses michael@0: * on such properties, the correspondence is as follows: michael@0: * michael@0: * 1. If the type has unknownProperties(), the possible properties and michael@0: * value types for associated JSObjects are unknown. michael@0: * michael@0: * 2. Otherwise, for any JSObject obj with TypeObject type, and any jsid id michael@0: * which is a property in obj, before obj->getProperty(id) the property michael@0: * in type for id must reflect the result of the getProperty. michael@0: * michael@0: * There is an exception for properties of global JS objects which michael@0: * are undefined at the point where the property was (lazily) generated. michael@0: * In such cases the property type set will remain empty, and the michael@0: * 'undefined' type will only be added after a subsequent assignment or michael@0: * deletion. After these properties have been assigned a defined value, michael@0: * the only way they can become undefined again is after such an assign michael@0: * or deletion. michael@0: * michael@0: * There is another exception for array lengths, which are special cased michael@0: * by the compiler and VM and are not reflected in property types. michael@0: * michael@0: * We establish these by using write barriers on calls to setProperty and michael@0: * defineProperty which are on native properties, and on any jitcode which michael@0: * might update the property with a new type. michael@0: */ michael@0: Property **propertySet; michael@0: public: michael@0: michael@0: /* If this is an interpreted function, the function object. */ michael@0: HeapPtrFunction interpretedFunction; michael@0: michael@0: #if JS_BITS_PER_WORD == 32 michael@0: uint32_t padding; michael@0: #endif michael@0: michael@0: inline TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags); michael@0: michael@0: bool hasAnyFlags(TypeObjectFlags flags) { michael@0: JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); michael@0: return !!(this->flags() & flags); michael@0: } michael@0: bool hasAllFlags(TypeObjectFlags flags) { michael@0: JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); michael@0: return (this->flags() & flags) == flags; michael@0: } michael@0: michael@0: bool unknownProperties() { michael@0: JS_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, michael@0: hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); michael@0: return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); michael@0: } michael@0: michael@0: bool shouldPreTenure() { michael@0: return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); michael@0: } michael@0: michael@0: bool hasTenuredProto() const { michael@0: return !(flags() & OBJECT_FLAG_NURSERY_PROTO); michael@0: } michael@0: michael@0: gc::InitialHeap initialHeap(CompilerConstraintList *constraints); michael@0: michael@0: bool canPreTenure() { michael@0: // Only types associated with particular allocation sites or 'new' michael@0: // scripts can be marked as needing pretenuring. Other types can be michael@0: // used for different purposes across the compartment and can't use michael@0: // this bit reliably. michael@0: if (unknownProperties()) michael@0: return false; michael@0: return (flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE) || hasNewScript(); michael@0: } michael@0: michael@0: void setShouldPreTenure(ExclusiveContext *cx) { michael@0: JS_ASSERT(canPreTenure()); michael@0: setFlags(cx, OBJECT_FLAG_PRE_TENURE); michael@0: } michael@0: michael@0: /* michael@0: * Get or create a property of this object. Only call this for properties which michael@0: * a script accesses explicitly. michael@0: */ michael@0: inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id); michael@0: michael@0: /* Get a property only if it already exists. */ michael@0: inline HeapTypeSet *maybeGetProperty(jsid id); michael@0: michael@0: inline unsigned getPropertyCount(); michael@0: inline Property *getProperty(unsigned i); michael@0: michael@0: /* Helpers */ michael@0: michael@0: void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types); michael@0: bool addDefiniteProperties(ExclusiveContext *cx, JSObject *obj); michael@0: bool matchDefiniteProperties(HandleObject obj); michael@0: void addPrototype(JSContext *cx, TypeObject *proto); michael@0: void addPropertyType(ExclusiveContext *cx, jsid id, Type type); michael@0: void addPropertyType(ExclusiveContext *cx, jsid id, const Value &value); michael@0: void markPropertyNonData(ExclusiveContext *cx, jsid id); michael@0: void markPropertyNonWritable(ExclusiveContext *cx, jsid id); michael@0: void markStateChange(ExclusiveContext *cx); michael@0: void setFlags(ExclusiveContext *cx, TypeObjectFlags flags); michael@0: void markUnknown(ExclusiveContext *cx); michael@0: void clearAddendum(ExclusiveContext *cx); michael@0: void clearNewScriptAddendum(ExclusiveContext *cx); michael@0: void clearTypedObjectAddendum(ExclusiveContext *cx); michael@0: void maybeClearNewScriptAddendumOnOOM(); michael@0: bool isPropertyNonData(jsid id); michael@0: bool isPropertyNonWritable(jsid id); michael@0: michael@0: void print(); michael@0: michael@0: inline void clearProperties(); michael@0: inline void sweep(FreeOp *fop, bool *oom); michael@0: michael@0: size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: michael@0: /* michael@0: * Type objects don't have explicit finalizers. Memory owned by a type michael@0: * object pending deletion is released when weak references are sweeped michael@0: * from all the compartment's type objects. michael@0: */ michael@0: void finalize(FreeOp *fop) {} michael@0: michael@0: static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; } michael@0: michael@0: static inline uint32_t offsetOfClasp() { michael@0: return offsetof(TypeObject, clasp_); michael@0: } michael@0: michael@0: static inline uint32_t offsetOfProto() { michael@0: return offsetof(TypeObject, proto_); michael@0: } michael@0: michael@0: private: michael@0: inline uint32_t basePropertyCount() const; michael@0: inline void setBasePropertyCount(uint32_t count); michael@0: michael@0: static void staticAsserts() { michael@0: JS_STATIC_ASSERT(offsetof(TypeObject, proto_) == offsetof(js::shadow::TypeObject, proto)); michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Entries for the per-compartment set of type objects which are 'new' types to michael@0: * use for some prototype and constructed with an optional script. This also michael@0: * includes entries for the set of lazy type objects in the compartment, which michael@0: * use a null script (though there are only a few of these per compartment). michael@0: */ michael@0: struct TypeObjectWithNewScriptEntry michael@0: { michael@0: ReadBarriered object; michael@0: michael@0: // Note: This pointer is only used for equality and does not need a read barrier. michael@0: JSFunction *newFunction; michael@0: michael@0: TypeObjectWithNewScriptEntry(TypeObject *object, JSFunction *newFunction) michael@0: : object(object), newFunction(newFunction) michael@0: {} michael@0: michael@0: struct Lookup { michael@0: const Class *clasp; michael@0: TaggedProto hashProto; michael@0: TaggedProto matchProto; michael@0: JSFunction *newFunction; michael@0: michael@0: Lookup(const Class *clasp, TaggedProto proto, JSFunction *newFunction) michael@0: : clasp(clasp), hashProto(proto), matchProto(proto), newFunction(newFunction) michael@0: {} michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: /* michael@0: * For use by generational post barriers only. Look up an entry whose michael@0: * proto has been moved, but was hashed with the original value. michael@0: */ michael@0: Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSFunction *newFunction) michael@0: : clasp(clasp), hashProto(hashProto), matchProto(matchProto), newFunction(newFunction) michael@0: {} michael@0: #endif michael@0: michael@0: }; michael@0: michael@0: static inline HashNumber hash(const Lookup &lookup); michael@0: static inline bool match(const TypeObjectWithNewScriptEntry &key, const Lookup &lookup); michael@0: static void rekey(TypeObjectWithNewScriptEntry &k, const TypeObjectWithNewScriptEntry& newKey) { k = newKey; } michael@0: }; michael@0: typedef HashSet TypeObjectWithNewScriptSet; michael@0: michael@0: /* Whether to use a new type object when calling 'new' at script/pc. */ michael@0: bool michael@0: UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc); michael@0: michael@0: bool michael@0: UseNewTypeForClone(JSFunction *fun); michael@0: michael@0: /* michael@0: * Whether Array.prototype, or an object on its proto chain, has an michael@0: * indexed property. michael@0: */ michael@0: bool michael@0: ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSScript *script); michael@0: michael@0: /* Whether obj or any of its prototypes have an indexed property. */ michael@0: bool michael@0: TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, TemporaryTypeSet *types); michael@0: michael@0: /* Persistent type information for a script, retained across GCs. */ michael@0: class TypeScript michael@0: { michael@0: friend class ::JSScript; michael@0: michael@0: // Variable-size array michael@0: StackTypeSet typeArray_[1]; michael@0: michael@0: public: michael@0: /* Array of type type sets for variables and JOF_TYPESET ops. */ michael@0: StackTypeSet *typeArray() const { michael@0: // Ensure typeArray_ is the last data member of TypeScript. michael@0: JS_STATIC_ASSERT(sizeof(TypeScript) == michael@0: sizeof(typeArray_) + offsetof(TypeScript, typeArray_)); michael@0: return const_cast(typeArray_); michael@0: } michael@0: michael@0: static inline size_t SizeIncludingTypeArray(size_t arraySize) { michael@0: // Ensure typeArray_ is the last data member of TypeScript. michael@0: JS_STATIC_ASSERT(sizeof(TypeScript) == michael@0: sizeof(StackTypeSet) + offsetof(TypeScript, typeArray_)); michael@0: return offsetof(TypeScript, typeArray_) + arraySize * sizeof(StackTypeSet); michael@0: } michael@0: michael@0: static inline unsigned NumTypeSets(JSScript *script); michael@0: michael@0: static inline StackTypeSet *ThisTypes(JSScript *script); michael@0: static inline StackTypeSet *ArgTypes(JSScript *script, unsigned i); michael@0: michael@0: /* Get the type set for values observed at an opcode. */ michael@0: static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc); michael@0: michael@0: template michael@0: static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, michael@0: uint32_t *hint, TYPESET *typeArray); michael@0: michael@0: /* Get a type object for an allocation site in this script. */ michael@0: static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: JSProtoKey kind); michael@0: michael@0: /* michael@0: * Monitor a bytecode pushing any value. This must be called for any opcode michael@0: * which is JOF_TYPESET, and where either the script has not been analyzed michael@0: * by type inference or where the pc has type barriers. For simplicity, we michael@0: * always monitor JOF_TYPESET opcodes in the interpreter and stub calls, michael@0: * and only look at barriers when generating JIT code for the script. michael@0: */ michael@0: static inline void Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: const js::Value &val); michael@0: static inline void Monitor(JSContext *cx, const js::Value &rval); michael@0: michael@0: /* Monitor an assignment at a SETELEM on a non-integer identifier. */ michael@0: static inline void MonitorAssign(JSContext *cx, HandleObject obj, jsid id); michael@0: michael@0: /* Add a type for a variable in a script. */ michael@0: static inline void SetThis(JSContext *cx, JSScript *script, Type type); michael@0: static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value); michael@0: static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type); michael@0: static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, michael@0: const js::Value &value); michael@0: michael@0: /* michael@0: * Freeze all the stack type sets in a script, for a compilation. Returns michael@0: * copies of the type sets which will be checked against the actual ones michael@0: * under FinishCompilation, to detect any type changes. michael@0: */ michael@0: static bool FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script, michael@0: TemporaryTypeSet **pThisTypes, michael@0: TemporaryTypeSet **pArgTypes, michael@0: TemporaryTypeSet **pBytecodeTypes); michael@0: michael@0: static void Purge(JSContext *cx, HandleScript script); michael@0: michael@0: static void Sweep(FreeOp *fop, JSScript *script, bool *oom); michael@0: void destroy(); michael@0: michael@0: size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { michael@0: return mallocSizeOf(this); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void printTypes(JSContext *cx, HandleScript script) const; michael@0: #endif michael@0: }; michael@0: michael@0: void michael@0: FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap); michael@0: michael@0: class RecompileInfo; michael@0: michael@0: // Allocate a CompilerOutput for a finished compilation and generate the type michael@0: // constraints for the compilation. Returns whether the type constraints michael@0: // still hold. michael@0: bool michael@0: FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode, michael@0: CompilerConstraintList *constraints, RecompileInfo *precompileInfo); michael@0: michael@0: // Update the actual types in any scripts queried by constraints with any michael@0: // speculative types added during the definite properties analysis. michael@0: void michael@0: FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints); michael@0: michael@0: struct ArrayTableKey; michael@0: typedef HashMap,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable; michael@0: michael@0: struct ObjectTableKey; michael@0: struct ObjectTableEntry; michael@0: typedef HashMap ObjectTypeTable; michael@0: michael@0: struct AllocationSiteKey; michael@0: typedef HashMap,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable; michael@0: michael@0: class HeapTypeSetKey; michael@0: michael@0: // Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. michael@0: struct TypeObjectKey michael@0: { michael@0: static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } michael@0: static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; } michael@0: michael@0: static TypeObjectKey *get(JSObject *obj) { michael@0: JS_ASSERT(obj); michael@0: return (TypeObjectKey *) (uintptr_t(obj) | 1); michael@0: } michael@0: static TypeObjectKey *get(TypeObject *obj) { michael@0: JS_ASSERT(obj); michael@0: return (TypeObjectKey *) obj; michael@0: } michael@0: michael@0: bool isTypeObject() { michael@0: return (uintptr_t(this) & 1) == 0; michael@0: } michael@0: bool isSingleObject() { michael@0: return (uintptr_t(this) & 1) != 0; michael@0: } michael@0: michael@0: TypeObject *asTypeObject() { michael@0: JS_ASSERT(isTypeObject()); michael@0: return (TypeObject *) this; michael@0: } michael@0: JSObject *asSingleObject() { michael@0: JS_ASSERT(isSingleObject()); michael@0: return (JSObject *) (uintptr_t(this) & ~1); michael@0: } michael@0: michael@0: const Class *clasp(); michael@0: TaggedProto proto(); michael@0: bool hasTenuredProto(); michael@0: JSObject *singleton(); michael@0: TypeNewScript *newScript(); michael@0: michael@0: bool unknownProperties(); michael@0: bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); michael@0: void watchStateChangeForInlinedCall(CompilerConstraintList *constraints); michael@0: void watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints); michael@0: void watchStateChangeForTypedArrayData(CompilerConstraintList *constraints); michael@0: HeapTypeSetKey property(jsid id); michael@0: void ensureTrackedProperty(JSContext *cx, jsid id); michael@0: michael@0: TypeObject *maybeType(); michael@0: }; michael@0: michael@0: // Representation of a heap type property which may or may not be instantiated. michael@0: // Heap properties for singleton types are instantiated lazily as they are used michael@0: // by the compiler, but this is only done on the main thread. If we are michael@0: // compiling off thread and use a property which has not yet been instantiated, michael@0: // it will be treated as empty and non-configured and will be instantiated when michael@0: // rejoining to the main thread. If it is in fact not empty, the compilation michael@0: // will fail; to avoid this, we try to instantiate singleton property types michael@0: // during generation of baseline caches. michael@0: class HeapTypeSetKey michael@0: { michael@0: friend class TypeObjectKey; michael@0: michael@0: // Object and property being accessed. michael@0: TypeObjectKey *object_; michael@0: jsid id_; michael@0: michael@0: // If instantiated, the underlying heap type set. michael@0: HeapTypeSet *maybeTypes_; michael@0: michael@0: public: michael@0: HeapTypeSetKey() michael@0: : object_(nullptr), id_(JSID_EMPTY), maybeTypes_(nullptr) michael@0: {} michael@0: michael@0: TypeObjectKey *object() const { return object_; } michael@0: jsid id() const { return id_; } michael@0: HeapTypeSet *maybeTypes() const { return maybeTypes_; } michael@0: michael@0: bool instantiate(JSContext *cx); michael@0: michael@0: void freeze(CompilerConstraintList *constraints); michael@0: jit::MIRType knownMIRType(CompilerConstraintList *constraints); michael@0: bool nonData(CompilerConstraintList *constraints); michael@0: bool nonWritable(CompilerConstraintList *constraints); michael@0: bool isOwnProperty(CompilerConstraintList *constraints); michael@0: bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); michael@0: JSObject *singleton(CompilerConstraintList *constraints); michael@0: bool needsBarrier(CompilerConstraintList *constraints); michael@0: }; michael@0: michael@0: /* michael@0: * Information about the result of the compilation of a script. This structure michael@0: * stored in the TypeCompartment is indexed by the RecompileInfo. This michael@0: * indirection enables the invalidation of all constraints related to the same michael@0: * compilation. michael@0: */ michael@0: class CompilerOutput michael@0: { michael@0: // If this compilation has not been invalidated, the associated script and michael@0: // kind of compilation being performed. michael@0: JSScript *script_; michael@0: ExecutionMode mode_ : 2; michael@0: michael@0: // Whether this compilation is about to be invalidated. michael@0: bool pendingInvalidation_ : 1; michael@0: michael@0: // During sweeping, the list of compiler outputs is compacted and invalidated michael@0: // outputs are removed. This gives the new index for a valid compiler output. michael@0: uint32_t sweepIndex_ : 29; michael@0: michael@0: public: michael@0: static const uint32_t INVALID_SWEEP_INDEX = (1 << 29) - 1; michael@0: michael@0: CompilerOutput() michael@0: : script_(nullptr), mode_(SequentialExecution), michael@0: pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX) michael@0: {} michael@0: michael@0: CompilerOutput(JSScript *script, ExecutionMode mode) michael@0: : script_(script), mode_(mode), michael@0: pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX) michael@0: {} michael@0: michael@0: JSScript *script() const { return script_; } michael@0: inline ExecutionMode mode() const { return mode_; } michael@0: michael@0: inline jit::IonScript *ion() const; michael@0: michael@0: bool isValid() const { michael@0: return script_ != nullptr; michael@0: } michael@0: void invalidate() { michael@0: script_ = nullptr; michael@0: } michael@0: michael@0: void setPendingInvalidation() { michael@0: pendingInvalidation_ = true; michael@0: } michael@0: bool pendingInvalidation() { michael@0: return pendingInvalidation_; michael@0: } michael@0: michael@0: void setSweepIndex(uint32_t index) { michael@0: if (index >= INVALID_SWEEP_INDEX) michael@0: MOZ_CRASH(); michael@0: sweepIndex_ = index; michael@0: } michael@0: void invalidateSweepIndex() { michael@0: sweepIndex_ = INVALID_SWEEP_INDEX; michael@0: } michael@0: uint32_t sweepIndex() { michael@0: JS_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX); michael@0: return sweepIndex_; michael@0: } michael@0: }; michael@0: michael@0: class RecompileInfo michael@0: { michael@0: uint32_t outputIndex; michael@0: michael@0: public: michael@0: RecompileInfo(uint32_t outputIndex = uint32_t(-1)) michael@0: : outputIndex(outputIndex) michael@0: {} michael@0: michael@0: bool operator == (const RecompileInfo &o) const { michael@0: return outputIndex == o.outputIndex; michael@0: } michael@0: CompilerOutput *compilerOutput(TypeZone &types) const; michael@0: CompilerOutput *compilerOutput(JSContext *cx) const; michael@0: bool shouldSweep(TypeZone &types); michael@0: }; michael@0: michael@0: /* Type information for a compartment. */ michael@0: struct TypeCompartment michael@0: { michael@0: /* Constraint solving worklist structures. */ michael@0: michael@0: /* Number of scripts in this compartment. */ michael@0: unsigned scriptCount; michael@0: michael@0: /* Table for referencing types of objects keyed to an allocation site. */ michael@0: AllocationSiteTable *allocationSiteTable; michael@0: michael@0: /* Tables for determining types of singleton/JSON objects. */ michael@0: michael@0: ArrayTypeTable *arrayTypeTable; michael@0: ObjectTypeTable *objectTypeTable; michael@0: michael@0: private: michael@0: void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type); michael@0: michael@0: public: michael@0: void fixArrayType(ExclusiveContext *cx, JSObject *obj); michael@0: void fixObjectType(ExclusiveContext *cx, JSObject *obj); michael@0: void fixRestArgumentsType(ExclusiveContext *cx, JSObject *obj); michael@0: michael@0: JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties); michael@0: michael@0: TypeCompartment(); michael@0: ~TypeCompartment(); michael@0: michael@0: inline JSCompartment *compartment(); michael@0: michael@0: /* Prints results of this compartment if spew is enabled or force is set. */ michael@0: void print(JSContext *cx, bool force); michael@0: michael@0: /* michael@0: * Make a function or non-function object associated with an optional michael@0: * script. The 'key' parameter here may be an array, typed array, function michael@0: * or JSProto_Object to indicate a type whose class is unknown (not just michael@0: * js_ObjectClass). michael@0: */ michael@0: TypeObject *newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle proto, michael@0: TypeObjectFlags initialFlags = 0); michael@0: michael@0: /* Get or make an object for an allocation site, and add to the allocation site table. */ michael@0: TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key); michael@0: michael@0: /* Mark any type set containing obj as having a generic object type. */ michael@0: void markSetsUnknown(JSContext *cx, TypeObject *obj); michael@0: michael@0: void clearTables(); michael@0: void sweep(FreeOp *fop); michael@0: void finalizeObjects(); michael@0: michael@0: void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, michael@0: size_t *allocationSiteTables, michael@0: size_t *arrayTypeTables, michael@0: size_t *objectTypeTables); michael@0: }; michael@0: michael@0: void FixRestArgumentsType(ExclusiveContext *cxArg, JSObject *obj); michael@0: michael@0: struct TypeZone michael@0: { michael@0: JS::Zone *zone_; michael@0: michael@0: /* Pool for type information in this zone. */ michael@0: static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; michael@0: js::LifoAlloc typeLifoAlloc; michael@0: michael@0: /* michael@0: * All Ion compilations that have occured in this zone, for indexing via michael@0: * RecompileInfo. This includes both valid and invalid compilations, though michael@0: * invalidated compilations are swept on GC. michael@0: */ michael@0: Vector *compilerOutputs; michael@0: michael@0: /* Pending recompilations to perform before execution of JIT code can resume. */ michael@0: Vector *pendingRecompiles; michael@0: michael@0: TypeZone(JS::Zone *zone); michael@0: ~TypeZone(); michael@0: michael@0: JS::Zone *zone() const { return zone_; } michael@0: michael@0: void sweep(FreeOp *fop, bool releaseTypes, bool *oom); michael@0: void clearAllNewScriptAddendumsOnOOM(); michael@0: michael@0: /* Mark a script as needing recompilation once inference has finished. */ michael@0: void addPendingRecompile(JSContext *cx, const RecompileInfo &info); michael@0: void addPendingRecompile(JSContext *cx, JSScript *script); michael@0: michael@0: void processPendingRecompiles(FreeOp *fop); michael@0: }; michael@0: michael@0: enum SpewChannel { michael@0: ISpewOps, /* ops: New constraints and types. */ michael@0: ISpewResult, /* result: Final type sets. */ michael@0: SPEW_COUNT michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: const char * InferSpewColorReset(); michael@0: const char * InferSpewColor(TypeConstraint *constraint); michael@0: const char * InferSpewColor(TypeSet *types); michael@0: michael@0: void InferSpew(SpewChannel which, const char *fmt, ...); michael@0: const char * TypeString(Type type); michael@0: const char * TypeObjectString(TypeObject *type); michael@0: michael@0: /* Check that the type property for id in obj contains value. */ michael@0: bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value); michael@0: michael@0: #else michael@0: michael@0: inline const char * InferSpewColorReset() { return nullptr; } michael@0: inline const char * InferSpewColor(TypeConstraint *constraint) { return nullptr; } michael@0: inline const char * InferSpewColor(TypeSet *types) { return nullptr; } michael@0: inline void InferSpew(SpewChannel which, const char *fmt, ...) {} michael@0: inline const char * TypeString(Type type) { return nullptr; } michael@0: inline const char * TypeObjectString(TypeObject *type) { return nullptr; } michael@0: michael@0: #endif michael@0: michael@0: /* Print a warning, dump state and abort the program. */ michael@0: MOZ_NORETURN void TypeFailure(JSContext *cx, const char *fmt, ...); michael@0: michael@0: } /* namespace types */ michael@0: } /* namespace js */ michael@0: michael@0: #endif /* jsinfer_h */