diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/vm/TypeInference-inl.h | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/TypeInference-inl.h')
-rw-r--r-- | js/src/vm/TypeInference-inl.h | 1130 |
1 files changed, 1130 insertions, 0 deletions
diff --git a/js/src/vm/TypeInference-inl.h b/js/src/vm/TypeInference-inl.h new file mode 100644 index 000000000..da47fa898 --- /dev/null +++ b/js/src/vm/TypeInference-inl.h @@ -0,0 +1,1130 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Inline members for javascript type inference. */ + +#ifndef vm_TypeInference_inl_h +#define vm_TypeInference_inl_h + +#include "vm/TypeInference.h" + +#include "mozilla/BinarySearch.h" +#include "mozilla/Casting.h" +#include "mozilla/PodOperations.h" + +#include "builtin/SymbolObject.h" +#include "jit/BaselineJIT.h" +#include "vm/ArrayObject.h" +#include "vm/BooleanObject.h" +#include "vm/NumberObject.h" +#include "vm/SharedArrayObject.h" +#include "vm/StringObject.h" +#include "vm/TypedArrayObject.h" +#include "vm/UnboxedObject.h" + +#include "jscntxtinlines.h" + +#include "vm/ObjectGroup-inl.h" + +namespace js { + +///////////////////////////////////////////////////////////////////// +// CompilerOutput & RecompileInfo +///////////////////////////////////////////////////////////////////// + +inline jit::IonScript* +CompilerOutput::ion() const +{ + // Note: If type constraints are generated before compilation has finished + // (i.e. after IonBuilder but before CodeGenerator::link) then a valid + // CompilerOutput may not yet have an associated IonScript. + MOZ_ASSERT(isValid()); + jit::IonScript* ion = script()->maybeIonScript(); + MOZ_ASSERT(ion != ION_COMPILING_SCRIPT); + return ion; +} + +inline CompilerOutput* +RecompileInfo::compilerOutput(TypeZone& types) const +{ + if (generation != types.generation) { + if (!types.sweepCompilerOutputs || outputIndex >= types.sweepCompilerOutputs->length()) + return nullptr; + CompilerOutput* output = &(*types.sweepCompilerOutputs)[outputIndex]; + if (!output->isValid()) + return nullptr; + output = &(*types.compilerOutputs)[output->sweepIndex()]; + return output->isValid() ? output : nullptr; + } + + if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) + return nullptr; + CompilerOutput* output = &(*types.compilerOutputs)[outputIndex]; + return output->isValid() ? output : nullptr; +} + +inline CompilerOutput* +RecompileInfo::compilerOutput(JSContext* cx) const +{ + return compilerOutput(cx->zone()->types); +} + +inline bool +RecompileInfo::shouldSweep(TypeZone& types) +{ + CompilerOutput* output = compilerOutput(types); + if (!output || !output->isValid()) + return true; + + // If this info is for a compilation that occurred after sweeping started, + // the index is already correct. + MOZ_ASSERT_IF(generation == types.generation, + outputIndex == output - types.compilerOutputs->begin()); + + // Update this info for the output's index in the zone's compiler outputs. + outputIndex = output - types.compilerOutputs->begin(); + generation = types.generation; + return false; +} + +///////////////////////////////////////////////////////////////////// +// Types +///////////////////////////////////////////////////////////////////// + +/* static */ inline TypeSet::ObjectKey* +TypeSet::ObjectKey::get(JSObject* obj) +{ + MOZ_ASSERT(obj); + if (obj->isSingleton()) + return (ObjectKey*) (uintptr_t(obj) | 1); + return (ObjectKey*) obj->group(); +} + +/* static */ inline TypeSet::ObjectKey* +TypeSet::ObjectKey::get(ObjectGroup* group) +{ + MOZ_ASSERT(group); + if (group->singleton()) + return (ObjectKey*) (uintptr_t(group->singleton()) | 1); + return (ObjectKey*) group; +} + +inline ObjectGroup* +TypeSet::ObjectKey::groupNoBarrier() +{ + MOZ_ASSERT(isGroup()); + return (ObjectGroup*) this; +} + +inline JSObject* +TypeSet::ObjectKey::singletonNoBarrier() +{ + MOZ_ASSERT(isSingleton()); + return (JSObject*) (uintptr_t(this) & ~1); +} + +inline ObjectGroup* +TypeSet::ObjectKey::group() +{ + ObjectGroup* res = groupNoBarrier(); + ObjectGroup::readBarrier(res); + return res; +} + +inline JSObject* +TypeSet::ObjectKey::singleton() +{ + JSObject* res = singletonNoBarrier(); + JSObject::readBarrier(res); + return res; +} + +inline JSCompartment* +TypeSet::ObjectKey::maybeCompartment() +{ + if (isSingleton()) + return singleton()->compartment(); + + return group()->compartment(); +} + +/* static */ inline TypeSet::Type +TypeSet::ObjectType(JSObject* obj) +{ + if (obj->isSingleton()) + return Type(uintptr_t(obj) | 1); + return Type(uintptr_t(obj->group())); +} + +/* static */ inline TypeSet::Type +TypeSet::ObjectType(ObjectGroup* group) +{ + if (group->singleton()) + return Type(uintptr_t(group->singleton()) | 1); + return Type(uintptr_t(group)); +} + +/* static */ inline TypeSet::Type +TypeSet::ObjectType(ObjectKey* obj) +{ + return Type(uintptr_t(obj)); +} + +inline TypeSet::Type +TypeSet::GetValueType(const Value& val) +{ + if (val.isDouble()) + return TypeSet::DoubleType(); + if (val.isObject()) + return TypeSet::ObjectType(&val.toObject()); + return TypeSet::PrimitiveType(val.extractNonDoubleType()); +} + +inline bool +TypeSet::IsUntrackedValue(const Value& val) +{ + return val.isMagic() && (val.whyMagic() == JS_OPTIMIZED_OUT || + val.whyMagic() == JS_UNINITIALIZED_LEXICAL); +} + +inline TypeSet::Type +TypeSet::GetMaybeUntrackedValueType(const Value& val) +{ + return IsUntrackedValue(val) ? UnknownType() : GetValueType(val); +} + +inline TypeFlags +PrimitiveTypeFlag(JSValueType type) +{ + switch (type) { + case JSVAL_TYPE_UNDEFINED: + return TYPE_FLAG_UNDEFINED; + case JSVAL_TYPE_NULL: + return TYPE_FLAG_NULL; + case JSVAL_TYPE_BOOLEAN: + return TYPE_FLAG_BOOLEAN; + case JSVAL_TYPE_INT32: + return TYPE_FLAG_INT32; + case JSVAL_TYPE_DOUBLE: + return TYPE_FLAG_DOUBLE; + case JSVAL_TYPE_STRING: + return TYPE_FLAG_STRING; + case JSVAL_TYPE_SYMBOL: + return TYPE_FLAG_SYMBOL; + case JSVAL_TYPE_MAGIC: + return TYPE_FLAG_LAZYARGS; + default: + MOZ_CRASH("Bad JSValueType"); + } +} + +inline JSValueType +TypeFlagPrimitive(TypeFlags flags) +{ + switch (flags) { + case TYPE_FLAG_UNDEFINED: + return JSVAL_TYPE_UNDEFINED; + case TYPE_FLAG_NULL: + return JSVAL_TYPE_NULL; + case TYPE_FLAG_BOOLEAN: + return JSVAL_TYPE_BOOLEAN; + case TYPE_FLAG_INT32: + return JSVAL_TYPE_INT32; + case TYPE_FLAG_DOUBLE: + return JSVAL_TYPE_DOUBLE; + case TYPE_FLAG_STRING: + return JSVAL_TYPE_STRING; + case TYPE_FLAG_SYMBOL: + return JSVAL_TYPE_SYMBOL; + case TYPE_FLAG_LAZYARGS: + return JSVAL_TYPE_MAGIC; + default: + MOZ_CRASH("Bad TypeFlags"); + } +} + +/* + * Get the canonical representation of an id to use when doing inference. This + * maintains the constraint that if two different jsids map to the same property + * in JS (e.g. 3 and "3"), they have the same type representation. + */ +inline jsid +IdToTypeId(jsid id) +{ + MOZ_ASSERT(!JSID_IS_EMPTY(id)); + + // All properties which can be stored in an object's dense elements must + // map to the aggregate property for index types. + return JSID_IS_INT(id) ? JSID_VOID : id; +} + +const char * TypeIdStringImpl(jsid id); + +/* Convert an id for printing during debug. */ +static inline const char* +TypeIdString(jsid id) +{ +#ifdef DEBUG + return TypeIdStringImpl(id); +#else + return "(missing)"; +#endif +} + +/* + * Structure for type inference entry point functions. All functions which can + * change type information must use this, and functions which depend on + * intermediate types (i.e. JITs) can use this to ensure that intermediate + * information is not collected and does not change. + * + * Ensures that GC cannot occur. Does additional sanity checking that inference + * is not reentrant and that recompilations occur properly. + */ +struct AutoEnterAnalysis +{ + // For use when initializing an UnboxedLayout. The UniquePtr's destructor + // must run when GC is not suppressed. + UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp; + + // Prevent GC activity in the middle of analysis. + gc::AutoSuppressGC suppressGC; + + // Allow clearing inference info on OOM during incremental sweeping. + AutoClearTypeInferenceStateOnOOM oom; + + // Pending recompilations to perform before execution of JIT code can resume. + RecompileInfoVector pendingRecompiles; + + // Prevent us from calling the objectMetadataCallback. + js::AutoSuppressAllocationMetadataBuilder suppressMetadata; + + FreeOp* freeOp; + Zone* zone; + + explicit AutoEnterAnalysis(ExclusiveContext* cx) + : suppressGC(cx), oom(cx->zone()), suppressMetadata(cx) + { + init(cx->defaultFreeOp(), cx->zone()); + } + + AutoEnterAnalysis(FreeOp* fop, Zone* zone) + : suppressGC(zone->runtimeFromMainThread()->contextFromMainThread()), + oom(zone), suppressMetadata(zone) + { + init(fop, zone); + } + + ~AutoEnterAnalysis() + { + if (this != zone->types.activeAnalysis) + return; + + zone->types.activeAnalysis = nullptr; + + if (!pendingRecompiles.empty()) + zone->types.processPendingRecompiles(freeOp, pendingRecompiles); + } + + private: + void init(FreeOp* fop, Zone* zone) { + this->freeOp = fop; + this->zone = zone; + + if (!zone->types.activeAnalysis) + zone->types.activeAnalysis = this; + } +}; + +///////////////////////////////////////////////////////////////////// +// Interface functions +///////////////////////////////////////////////////////////////////// + +void MarkIteratorUnknownSlow(JSContext* cx); + +void TypeMonitorCallSlow(JSContext* cx, JSObject* callee, const CallArgs& args, + bool constructing); + +/* + * Monitor a javascript call, either on entry to the interpreter or made + * from within the interpreter. + */ +inline void +TypeMonitorCall(JSContext* cx, const js::CallArgs& args, bool constructing) +{ + if (args.callee().is<JSFunction>()) { + JSFunction* fun = &args.callee().as<JSFunction>(); + if (fun->isInterpreted() && fun->nonLazyScript()->types()) + TypeMonitorCallSlow(cx, &args.callee(), args, constructing); + } +} + +inline bool +TrackPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id) +{ + if (obj->hasLazyGroup() || obj->group()->unknownProperties()) + return false; + + if (obj->isSingleton() && !obj->group()->maybeGetProperty(id)) + return false; + + return true; +} + +void +EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id); + +inline bool +CanHaveEmptyPropertyTypesForOwnProperty(JSObject* obj) +{ + // Per the comment on TypeSet::propertySet, property type sets for global + // objects may be empty for 'own' properties if the global property still + // has its initial undefined value. + return obj->is<GlobalObject>(); +} + +inline bool +PropertyHasBeenMarkedNonConstant(JSObject* obj, jsid id) +{ + // Non-constant properties are only relevant for singleton objects. + if (!obj->isSingleton()) + return true; + + // EnsureTrackPropertyTypes must have been called on this object. + if (obj->group()->unknownProperties()) + return true; + HeapTypeSet* types = obj->group()->maybeGetProperty(IdToTypeId(id)); + return types->nonConstantProperty(); +} + +inline bool +HasTypePropertyId(JSObject* obj, jsid id, TypeSet::Type type) +{ + if (obj->hasLazyGroup()) + return true; + + if (obj->group()->unknownProperties()) + return true; + + if (HeapTypeSet* types = obj->group()->maybeGetProperty(IdToTypeId(id))) + return types->hasType(type); + + return false; +} + +inline bool +HasTypePropertyId(JSObject* obj, jsid id, const Value& value) +{ + return HasTypePropertyId(obj, id, TypeSet::GetValueType(value)); +} + +void AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, jsid id, TypeSet::Type type); +void AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, jsid id, const Value& value); + +/* Add a possible type for a property of obj. */ +inline void +AddTypePropertyId(ExclusiveContext* cx, JSObject* obj, jsid id, TypeSet::Type type) +{ + id = IdToTypeId(id); + if (TrackPropertyTypes(cx, obj, id)) + AddTypePropertyId(cx, obj->group(), obj, id, type); +} + +inline void +AddTypePropertyId(ExclusiveContext* cx, JSObject* obj, jsid id, const Value& value) +{ + id = IdToTypeId(id); + if (TrackPropertyTypes(cx, obj, id)) + AddTypePropertyId(cx, obj->group(), obj, id, value); +} + +inline void +MarkObjectGroupFlags(ExclusiveContext* cx, JSObject* obj, ObjectGroupFlags flags) +{ + if (!obj->hasLazyGroup() && !obj->group()->hasAllFlags(flags)) + obj->group()->setFlags(cx, flags); +} + +inline void +MarkObjectGroupUnknownProperties(ExclusiveContext* cx, ObjectGroup* obj) +{ + if (!obj->unknownProperties()) + obj->markUnknown(cx); +} + +inline void +MarkTypePropertyNonData(ExclusiveContext* cx, JSObject* obj, jsid id) +{ + id = IdToTypeId(id); + if (TrackPropertyTypes(cx, obj, id)) + obj->group()->markPropertyNonData(cx, obj, id); +} + +inline void +MarkTypePropertyNonWritable(ExclusiveContext* cx, JSObject* obj, jsid id) +{ + id = IdToTypeId(id); + if (TrackPropertyTypes(cx, obj, id)) + obj->group()->markPropertyNonWritable(cx, obj, id); +} + +/* Mark a state change on a particular object. */ +inline void +MarkObjectStateChange(ExclusiveContext* cx, JSObject* obj) +{ + if (!obj->hasLazyGroup() && !obj->group()->unknownProperties()) + obj->group()->markStateChange(cx); +} + +/* Interface helpers for JSScript*. */ +extern void TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type); +extern void TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const Value& rval); + +///////////////////////////////////////////////////////////////////// +// Script interface functions +///////////////////////////////////////////////////////////////////// + +/* static */ inline unsigned +TypeScript::NumTypeSets(JSScript* script) +{ + size_t num = script->nTypeSets() + 1 /* this */; + if (JSFunction* fun = script->functionNonDelazifying()) + num += fun->nargs(); + return num; +} + +/* static */ inline StackTypeSet* +TypeScript::ThisTypes(JSScript* script) +{ + TypeScript* types = script->types(); + return types ? types->typeArray() + script->nTypeSets() : nullptr; +} + +/* + * Note: for non-escaping arguments, argTypes reflect only the initial type of + * the variable (e.g. passed values for argTypes, or undefined for localTypes) + * and not types from subsequent assignments. + */ + +/* static */ inline StackTypeSet* +TypeScript::ArgTypes(JSScript* script, unsigned i) +{ + MOZ_ASSERT(i < script->functionNonDelazifying()->nargs()); + TypeScript* types = script->types(); + return types ? types->typeArray() + script->nTypeSets() + 1 + i : nullptr; +} + +template <typename TYPESET> +/* static */ inline TYPESET* +TypeScript::BytecodeTypes(JSScript* script, jsbytecode* pc, uint32_t* bytecodeMap, + uint32_t* hint, TYPESET* typeArray) +{ + MOZ_ASSERT(CodeSpec[*pc].format & JOF_TYPESET); + uint32_t offset = script->pcToOffset(pc); + + // See if this pc is the next typeset opcode after the last one looked up. + if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) { + (*hint)++; + return typeArray + *hint; + } + + // See if this pc is the same as the last one looked up. + if (bytecodeMap[*hint] == offset) + return typeArray + *hint; + + // Fall back to a binary search. We'll either find the exact offset, or + // there are more JOF_TYPESET opcodes than nTypeSets in the script (as can + // happen if the script is very long) and we'll use the last location. + size_t loc; +#ifdef DEBUG + bool found = +#endif + mozilla::BinarySearch(bytecodeMap, 0, script->nTypeSets() - 1, offset, &loc); + + MOZ_ASSERT_IF(found, bytecodeMap[loc] == offset); + *hint = mozilla::AssertedCast<uint32_t>(loc); + return typeArray + *hint; +} + +/* static */ inline StackTypeSet* +TypeScript::BytecodeTypes(JSScript* script, jsbytecode* pc) +{ + MOZ_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread())); + TypeScript* types = script->types(); + if (!types) + return nullptr; + uint32_t* hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets(); + return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(), + hint, types->typeArray()); +} + +/* static */ inline void +TypeScript::Monitor(JSContext* cx, JSScript* script, jsbytecode* pc, const js::Value& rval) +{ + TypeMonitorResult(cx, script, pc, rval); +} + +/* static */ inline void +TypeScript::Monitor(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type) +{ + TypeMonitorResult(cx, script, pc, type); +} + +/* static */ inline void +TypeScript::Monitor(JSContext* cx, const js::Value& rval) +{ + jsbytecode* pc; + RootedScript script(cx, cx->currentScript(&pc)); + Monitor(cx, script, pc, rval); +} + +/* static */ inline void +TypeScript::MonitorAssign(JSContext* cx, HandleObject obj, jsid id) +{ + if (!obj->isSingleton()) { + /* + * Mark as unknown any object which has had dynamic assignments to + * non-integer properties at SETELEM opcodes. This avoids making large + * numbers of type properties for hashmap-style objects. We don't need + * to do this for objects with singleton type, because type properties + * are only constructed for them when analyzed scripts depend on those + * specific properties. + */ + uint32_t i; + if (IdIsIndex(id, &i)) + return; + + // But if we don't have too many properties yet, don't do anything. The + // idea here is that normal object initialization should not trigger + // deoptimization in most cases, while actual usage as a hashmap should. + ObjectGroup* group = obj->group(); + if (group->basePropertyCount() < 128) + return; + MarkObjectGroupUnknownProperties(cx, group); + } +} + +/* static */ inline void +TypeScript::SetThis(JSContext* cx, JSScript* script, TypeSet::Type type) +{ + assertSameCompartment(cx, script, type); + + StackTypeSet* types = ThisTypes(script); + if (!types) + return; + + if (!types->hasType(type)) { + AutoEnterAnalysis enter(cx); + + InferSpew(ISpewOps, "externalType: setThis %p: %s", + script, TypeSet::TypeString(type)); + types->addType(cx, type); + } +} + +/* static */ inline void +TypeScript::SetThis(JSContext* cx, JSScript* script, const js::Value& value) +{ + SetThis(cx, script, TypeSet::GetValueType(value)); +} + +/* static */ inline void +TypeScript::SetArgument(JSContext* cx, JSScript* script, unsigned arg, TypeSet::Type type) +{ + assertSameCompartment(cx, script, type); + + StackTypeSet* types = ArgTypes(script, arg); + if (!types) + return; + + if (!types->hasType(type)) { + AutoEnterAnalysis enter(cx); + + InferSpew(ISpewOps, "externalType: setArg %p %u: %s", + script, arg, TypeSet::TypeString(type)); + types->addType(cx, type); + } +} + +/* static */ inline void +TypeScript::SetArgument(JSContext* cx, JSScript* script, unsigned arg, const js::Value& value) +{ + SetArgument(cx, script, arg, TypeSet::GetValueType(value)); +} + +///////////////////////////////////////////////////////////////////// +// TypeHashSet +///////////////////////////////////////////////////////////////////// + +// Hashing code shared by objects in TypeSets and properties in ObjectGroups. +struct TypeHashSet +{ + // The sets of objects in a type set grow monotonically, are usually empty, + // almost always small, and sometimes big. For empty or singleton sets, the + // the pointer refers directly to the value. For sets fitting into + // SET_ARRAY_SIZE, an array of this length is used to store the elements. + // For larger sets, a hash table filled to 25%-50% of capacity is used, + // with collisions resolved by linear probing. + static const unsigned SET_ARRAY_SIZE = 8; + static const unsigned SET_CAPACITY_OVERFLOW = 1u << 30; + + // Get the capacity of a set with the given element count. + static inline unsigned + Capacity(unsigned count) + { + MOZ_ASSERT(count >= 2); + MOZ_ASSERT(count < SET_CAPACITY_OVERFLOW); + + if (count <= SET_ARRAY_SIZE) + return SET_ARRAY_SIZE; + + return 1u << (mozilla::FloorLog2(count) + 2); + } + + // Compute the FNV hash for the low 32 bits of v. + template <class T, class KEY> + static inline uint32_t + HashKey(T v) + { + uint32_t nv = KEY::keyBits(v); + + uint32_t hash = 84696351 ^ (nv & 0xff); + hash = (hash * 16777619) ^ ((nv >> 8) & 0xff); + hash = (hash * 16777619) ^ ((nv >> 16) & 0xff); + return (hash * 16777619) ^ ((nv >> 24) & 0xff); + } + + // Insert space for an element into the specified set and grow its capacity + // if needed. returned value is an existing or new entry (nullptr if new). + template <class T, class U, class KEY> + static U** + InsertTry(LifoAlloc& alloc, U**& values, unsigned& count, T key) + { + unsigned capacity = Capacity(count); + unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1); + + // Whether we are converting from a fixed array to hashtable. + bool converting = (count == SET_ARRAY_SIZE); + + if (!converting) { + while (values[insertpos] != nullptr) { + if (KEY::getKey(values[insertpos]) == key) + return &values[insertpos]; + insertpos = (insertpos + 1) & (capacity - 1); + } + } + + if (count >= SET_CAPACITY_OVERFLOW) + return nullptr; + + count++; + unsigned newCapacity = Capacity(count); + + if (newCapacity == capacity) { + MOZ_ASSERT(!converting); + return &values[insertpos]; + } + + U** newValues = alloc.newArray<U*>(newCapacity); + if (!newValues) + return nullptr; + mozilla::PodZero(newValues, newCapacity); + + for (unsigned i = 0; i < capacity; i++) { + if (values[i]) { + unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1); + while (newValues[pos] != nullptr) + pos = (pos + 1) & (newCapacity - 1); + newValues[pos] = values[i]; + } + } + + values = newValues; + + insertpos = HashKey<T,KEY>(key) & (newCapacity - 1); + while (values[insertpos] != nullptr) + insertpos = (insertpos + 1) & (newCapacity - 1); + return &values[insertpos]; + } + + // Insert an element into the specified set if it is not already there, + // returning an entry which is nullptr if the element was not there. + template <class T, class U, class KEY> + static inline U** + Insert(LifoAlloc& alloc, U**& values, unsigned& count, T key) + { + if (count == 0) { + MOZ_ASSERT(values == nullptr); + count++; + return (U**) &values; + } + + if (count == 1) { + U* oldData = (U*) values; + if (KEY::getKey(oldData) == key) + return (U**) &values; + + values = alloc.newArray<U*>(SET_ARRAY_SIZE); + if (!values) { + values = (U**) oldData; + return nullptr; + } + mozilla::PodZero(values, SET_ARRAY_SIZE); + count++; + + values[0] = oldData; + return &values[1]; + } + + if (count <= SET_ARRAY_SIZE) { + for (unsigned i = 0; i < count; i++) { + if (KEY::getKey(values[i]) == key) + return &values[i]; + } + + if (count < SET_ARRAY_SIZE) { + count++; + return &values[count - 1]; + } + } + + return InsertTry<T,U,KEY>(alloc, values, count, key); + } + + // Lookup an entry in a hash set, return nullptr if it does not exist. + template <class T, class U, class KEY> + static inline U* + Lookup(U** values, unsigned count, T key) + { + if (count == 0) + return nullptr; + + if (count == 1) + return (KEY::getKey((U*) values) == key) ? (U*) values : nullptr; + + if (count <= SET_ARRAY_SIZE) { + for (unsigned i = 0; i < count; i++) { + if (KEY::getKey(values[i]) == key) + return values[i]; + } + return nullptr; + } + + unsigned capacity = Capacity(count); + unsigned pos = HashKey<T,KEY>(key) & (capacity - 1); + + while (values[pos] != nullptr) { + if (KEY::getKey(values[pos]) == key) + return values[pos]; + pos = (pos + 1) & (capacity - 1); + } + + return nullptr; + } +}; + +///////////////////////////////////////////////////////////////////// +// TypeSet +///////////////////////////////////////////////////////////////////// + +inline TypeSet::ObjectKey* +TypeSet::Type::objectKey() const +{ + MOZ_ASSERT(isObject()); + return (ObjectKey*) data; +} + +inline JSObject* +TypeSet::Type::singleton() const +{ + return objectKey()->singleton(); +} + +inline ObjectGroup* +TypeSet::Type::group() const +{ + return objectKey()->group(); +} + +inline JSObject* +TypeSet::Type::singletonNoBarrier() const +{ + return objectKey()->singletonNoBarrier(); +} + +inline ObjectGroup* +TypeSet::Type::groupNoBarrier() const +{ + return objectKey()->groupNoBarrier(); +} + +inline void +TypeSet::Type::trace(JSTracer* trc) +{ + if (isSingletonUnchecked()) { + JSObject* obj = singletonNoBarrier(); + TraceManuallyBarrieredEdge(trc, &obj, "TypeSet::Object"); + *this = TypeSet::ObjectType(obj); + } else if (isGroupUnchecked()) { + ObjectGroup* group = groupNoBarrier(); + TraceManuallyBarrieredEdge(trc, &group, "TypeSet::Group"); + *this = TypeSet::ObjectType(group); + } +} + +inline JSCompartment* +TypeSet::Type::maybeCompartment() +{ + if (isSingletonUnchecked()) + return singletonNoBarrier()->compartment(); + + if (isGroupUnchecked()) + return groupNoBarrier()->compartment(); + + return nullptr; +} + +inline bool +TypeSet::hasType(Type type) const +{ + if (unknown()) + return true; + + if (type.isUnknown()) { + return false; + } else if (type.isPrimitive()) { + return !!(flags & PrimitiveTypeFlag(type.primitive())); + } else if (type.isAnyObject()) { + return !!(flags & TYPE_FLAG_ANYOBJECT); + } else { + return !!(flags & TYPE_FLAG_ANYOBJECT) || + TypeHashSet::Lookup<ObjectKey*, ObjectKey, ObjectKey> + (objectSet, baseObjectCount(), type.objectKey()) != nullptr; + } +} + +inline void +TypeSet::setBaseObjectCount(uint32_t count) +{ + MOZ_ASSERT(count <= TYPE_FLAG_DOMOBJECT_COUNT_LIMIT); + flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK) + | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT); +} + +inline void +HeapTypeSet::newPropertyState(ExclusiveContext* cxArg) +{ + /* Propagate the change to all constraints. */ + if (JSContext* cx = cxArg->maybeJSContext()) { + TypeConstraint* constraint = constraintList; + while (constraint) { + constraint->newPropertyState(cx, this); + constraint = constraint->next; + } + } else { + MOZ_ASSERT(!constraintList); + } +} + +inline void +HeapTypeSet::setNonDataProperty(ExclusiveContext* cx) +{ + if (flags & TYPE_FLAG_NON_DATA_PROPERTY) + return; + + flags |= TYPE_FLAG_NON_DATA_PROPERTY; + newPropertyState(cx); +} + +inline void +HeapTypeSet::setNonWritableProperty(ExclusiveContext* cx) +{ + if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY) + return; + + flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY; + newPropertyState(cx); +} + +inline void +HeapTypeSet::setNonConstantProperty(ExclusiveContext* cx) +{ + if (flags & TYPE_FLAG_NON_CONSTANT_PROPERTY) + return; + + flags |= TYPE_FLAG_NON_CONSTANT_PROPERTY; + newPropertyState(cx); +} + +inline unsigned +TypeSet::getObjectCount() const +{ + MOZ_ASSERT(!unknownObject()); + uint32_t count = baseObjectCount(); + if (count > TypeHashSet::SET_ARRAY_SIZE) + return TypeHashSet::Capacity(count); + return count; +} + +inline TypeSet::ObjectKey* +TypeSet::getObject(unsigned i) const +{ + MOZ_ASSERT(i < getObjectCount()); + if (baseObjectCount() == 1) { + MOZ_ASSERT(i == 0); + return (ObjectKey*) objectSet; + } + return objectSet[i]; +} + +inline JSObject* +TypeSet::getSingleton(unsigned i) const +{ + ObjectKey* key = getObject(i); + return (key && key->isSingleton()) ? key->singleton() : nullptr; +} + +inline ObjectGroup* +TypeSet::getGroup(unsigned i) const +{ + ObjectKey* key = getObject(i); + return (key && key->isGroup()) ? key->group() : nullptr; +} + +inline JSObject* +TypeSet::getSingletonNoBarrier(unsigned i) const +{ + ObjectKey* key = getObject(i); + return (key && key->isSingleton()) ? key->singletonNoBarrier() : nullptr; +} + +inline ObjectGroup* +TypeSet::getGroupNoBarrier(unsigned i) const +{ + ObjectKey* key = getObject(i); + return (key && key->isGroup()) ? key->groupNoBarrier() : nullptr; +} + +inline const Class* +TypeSet::getObjectClass(unsigned i) const +{ + if (JSObject* object = getSingleton(i)) + return object->getClass(); + if (ObjectGroup* group = getGroup(i)) + return group->clasp(); + return nullptr; +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroup +///////////////////////////////////////////////////////////////////// + +inline uint32_t +ObjectGroup::basePropertyCount() +{ + return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; +} + +inline void +ObjectGroup::setBasePropertyCount(uint32_t count) +{ + // Note: Callers must ensure they are performing threadsafe operations. + MOZ_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); + flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) + | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); +} + +inline HeapTypeSet* +ObjectGroup::getProperty(ExclusiveContext* cx, JSObject* obj, jsid id) +{ + MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); + MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); + MOZ_ASSERT(!unknownProperties()); + MOZ_ASSERT_IF(obj, obj->group() == this); + MOZ_ASSERT_IF(singleton(), obj); + + if (HeapTypeSet* types = maybeGetProperty(id)) + return types; + + Property* base = cx->typeLifoAlloc().new_<Property>(id); + if (!base) { + markUnknown(cx); + return nullptr; + } + + uint32_t propertyCount = basePropertyCount(); + Property** pprop = TypeHashSet::Insert<jsid, Property, Property> + (cx->typeLifoAlloc(), propertySet, propertyCount, id); + if (!pprop) { + markUnknown(cx); + return nullptr; + } + + MOZ_ASSERT(!*pprop); + + setBasePropertyCount(propertyCount); + *pprop = base; + + updateNewPropertyTypes(cx, obj, id, &base->types); + + if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { + // We hit the maximum number of properties the object can have, mark + // the object unknown so that new properties will not be added in the + // future. + markUnknown(cx); + } + + return &base->types; +} + +inline HeapTypeSet* +ObjectGroup::maybeGetProperty(jsid id) +{ + MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); + MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); + MOZ_ASSERT(!unknownProperties()); + + Property* prop = TypeHashSet::Lookup<jsid, Property, Property> + (propertySet, basePropertyCount(), id); + + return prop ? &prop->types : nullptr; +} + +inline unsigned +ObjectGroup::getPropertyCount() +{ + uint32_t count = basePropertyCount(); + if (count > TypeHashSet::SET_ARRAY_SIZE) + return TypeHashSet::Capacity(count); + return count; +} + +inline ObjectGroup::Property* +ObjectGroup::getProperty(unsigned i) +{ + MOZ_ASSERT(i < getPropertyCount()); + if (basePropertyCount() == 1) { + MOZ_ASSERT(i == 0); + return (Property*) propertySet; + } + return propertySet[i]; +} + +} // namespace js + +inline js::TypeScript* +JSScript::types() +{ + maybeSweepTypes(nullptr); + return types_; +} + +inline bool +JSScript::ensureHasTypes(JSContext* cx) +{ + return types() || makeTypes(cx); +} + +#endif /* vm_TypeInference_inl_h */ |