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: /* Inline members for javascript type inference. */ michael@0: michael@0: #ifndef jsinferinlines_h michael@0: #define jsinferinlines_h michael@0: michael@0: #include "jsinfer.h" michael@0: michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "jsanalyze.h" michael@0: michael@0: #include "vm/ArrayObject.h" michael@0: #include "vm/BooleanObject.h" michael@0: #include "vm/NumberObject.h" michael@0: #include "vm/SharedArrayObject.h" michael@0: #include "vm/StringObject.h" michael@0: #include "vm/TypedArrayObject.h" michael@0: michael@0: #include "jscntxtinlines.h" michael@0: michael@0: #include "jit/ExecutionMode-inl.h" michael@0: michael@0: namespace js { michael@0: namespace types { michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // CompilerOutput & RecompileInfo michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: inline jit::IonScript * michael@0: CompilerOutput::ion() const michael@0: { michael@0: #ifdef JS_ION michael@0: // Note: If type constraints are generated before compilation has finished michael@0: // (i.e. after IonBuilder but before CodeGenerator::link) then a valid michael@0: // CompilerOutput may not yet have an associated IonScript. michael@0: JS_ASSERT(isValid()); michael@0: jit::IonScript *ion = jit::GetIonScript(script(), mode()); michael@0: JS_ASSERT(ion != ION_COMPILING_SCRIPT); michael@0: return ion; michael@0: #endif michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput"); michael@0: } michael@0: michael@0: inline CompilerOutput* michael@0: RecompileInfo::compilerOutput(TypeZone &types) const michael@0: { michael@0: if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) michael@0: return nullptr; michael@0: return &(*types.compilerOutputs)[outputIndex]; michael@0: } michael@0: michael@0: inline CompilerOutput* michael@0: RecompileInfo::compilerOutput(JSContext *cx) const michael@0: { michael@0: return compilerOutput(cx->zone()->types); michael@0: } michael@0: michael@0: inline bool michael@0: RecompileInfo::shouldSweep(TypeZone &types) michael@0: { michael@0: CompilerOutput *output = compilerOutput(types); michael@0: if (!output || !output->isValid()) michael@0: return true; michael@0: michael@0: // Update this info for the output's new index in the zone's compiler outputs. michael@0: outputIndex = output->sweepIndex(); michael@0: return false; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // Types michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: /* static */ inline Type michael@0: Type::ObjectType(JSObject *obj) michael@0: { michael@0: if (obj->hasSingletonType()) michael@0: return Type(uintptr_t(obj) | 1); michael@0: return Type(uintptr_t(obj->type())); michael@0: } michael@0: michael@0: /* static */ inline Type michael@0: Type::ObjectType(TypeObject *obj) michael@0: { michael@0: if (obj->singleton()) michael@0: return Type(uintptr_t(obj->singleton()) | 1); michael@0: return Type(uintptr_t(obj)); michael@0: } michael@0: michael@0: /* static */ inline Type michael@0: Type::ObjectType(TypeObjectKey *obj) michael@0: { michael@0: return Type(uintptr_t(obj)); michael@0: } michael@0: michael@0: inline Type michael@0: GetValueType(const Value &val) michael@0: { michael@0: if (val.isDouble()) michael@0: return Type::DoubleType(); michael@0: if (val.isObject()) michael@0: return Type::ObjectType(&val.toObject()); michael@0: return Type::PrimitiveType(val.extractNonDoubleType()); michael@0: } michael@0: michael@0: inline Type michael@0: GetMaybeOptimizedOutValueType(const Value &val) michael@0: { michael@0: if (val.isMagic() && val.whyMagic() == JS_OPTIMIZED_OUT) michael@0: return Type::UnknownType(); michael@0: return GetValueType(val); michael@0: } michael@0: michael@0: inline TypeFlags michael@0: PrimitiveTypeFlag(JSValueType type) michael@0: { michael@0: switch (type) { michael@0: case JSVAL_TYPE_UNDEFINED: michael@0: return TYPE_FLAG_UNDEFINED; michael@0: case JSVAL_TYPE_NULL: michael@0: return TYPE_FLAG_NULL; michael@0: case JSVAL_TYPE_BOOLEAN: michael@0: return TYPE_FLAG_BOOLEAN; michael@0: case JSVAL_TYPE_INT32: michael@0: return TYPE_FLAG_INT32; michael@0: case JSVAL_TYPE_DOUBLE: michael@0: return TYPE_FLAG_DOUBLE; michael@0: case JSVAL_TYPE_STRING: michael@0: return TYPE_FLAG_STRING; michael@0: case JSVAL_TYPE_MAGIC: michael@0: return TYPE_FLAG_LAZYARGS; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad type"); michael@0: } michael@0: } michael@0: michael@0: inline JSValueType michael@0: TypeFlagPrimitive(TypeFlags flags) michael@0: { michael@0: switch (flags) { michael@0: case TYPE_FLAG_UNDEFINED: michael@0: return JSVAL_TYPE_UNDEFINED; michael@0: case TYPE_FLAG_NULL: michael@0: return JSVAL_TYPE_NULL; michael@0: case TYPE_FLAG_BOOLEAN: michael@0: return JSVAL_TYPE_BOOLEAN; michael@0: case TYPE_FLAG_INT32: michael@0: return JSVAL_TYPE_INT32; michael@0: case TYPE_FLAG_DOUBLE: michael@0: return JSVAL_TYPE_DOUBLE; michael@0: case TYPE_FLAG_STRING: michael@0: return JSVAL_TYPE_STRING; michael@0: case TYPE_FLAG_LAZYARGS: michael@0: return JSVAL_TYPE_MAGIC; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad type"); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Get the canonical representation of an id to use when doing inference. This michael@0: * maintains the constraint that if two different jsids map to the same property michael@0: * in JS (e.g. 3 and "3"), they have the same type representation. michael@0: */ michael@0: inline jsid michael@0: IdToTypeId(jsid id) michael@0: { michael@0: JS_ASSERT(!JSID_IS_EMPTY(id)); michael@0: michael@0: /* michael@0: * All integers must map to the aggregate property for index types, including michael@0: * negative integers. michael@0: */ michael@0: if (JSID_IS_INT(id)) michael@0: return JSID_VOID; michael@0: michael@0: /* michael@0: * Check for numeric strings, as in js_StringIsIndex, but allow negative michael@0: * and overflowing integers. michael@0: */ michael@0: if (JSID_IS_STRING(id)) { michael@0: JSAtom *atom = JSID_TO_ATOM(id); michael@0: JS::TwoByteChars cp = atom->range(); michael@0: if (cp.length() > 0 && (JS7_ISDEC(cp[0]) || cp[0] == '-')) { michael@0: for (size_t i = 1; i < cp.length(); ++i) { michael@0: if (!JS7_ISDEC(cp[i])) michael@0: return id; michael@0: } michael@0: return JSID_VOID; michael@0: } michael@0: return id; michael@0: } michael@0: michael@0: return JSID_VOID; michael@0: } michael@0: michael@0: const char * TypeIdStringImpl(jsid id); michael@0: michael@0: /* Convert an id for printing during debug. */ michael@0: static inline const char * michael@0: TypeIdString(jsid id) michael@0: { michael@0: #ifdef DEBUG michael@0: return TypeIdStringImpl(id); michael@0: #else michael@0: return "(missing)"; michael@0: #endif michael@0: } michael@0: michael@0: /* michael@0: * Structure for type inference entry point functions. All functions which can michael@0: * change type information must use this, and functions which depend on michael@0: * intermediate types (i.e. JITs) can use this to ensure that intermediate michael@0: * information is not collected and does not change. michael@0: * michael@0: * Pins inference results so that intermediate type information, TypeObjects michael@0: * and JSScripts won't be collected during GC. Does additional sanity checking michael@0: * that inference is not reentrant and that recompilations occur properly. michael@0: */ michael@0: struct AutoEnterAnalysis michael@0: { michael@0: /* Prevent GC activity in the middle of analysis. */ michael@0: gc::AutoSuppressGC suppressGC; michael@0: michael@0: FreeOp *freeOp; michael@0: JSCompartment *compartment; michael@0: bool oldActiveAnalysis; michael@0: michael@0: AutoEnterAnalysis(ExclusiveContext *cx) michael@0: : suppressGC(cx) michael@0: { michael@0: init(cx->defaultFreeOp(), cx->compartment()); michael@0: } michael@0: michael@0: AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp) michael@0: : suppressGC(comp) michael@0: { michael@0: init(fop, comp); michael@0: } michael@0: michael@0: ~AutoEnterAnalysis() michael@0: { michael@0: compartment->activeAnalysis = oldActiveAnalysis; michael@0: michael@0: /* michael@0: * If there are no more type inference activations on the stack, michael@0: * process any triggered recompilations. Note that we should not be michael@0: * invoking any scripted code while type inference is running. michael@0: */ michael@0: if (!compartment->activeAnalysis) { michael@0: TypeZone &types = compartment->zone()->types; michael@0: if (types.pendingRecompiles) michael@0: types.processPendingRecompiles(freeOp); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: void init(FreeOp *fop, JSCompartment *comp) { michael@0: freeOp = fop; michael@0: compartment = comp; michael@0: oldActiveAnalysis = compartment->activeAnalysis; michael@0: compartment->activeAnalysis = true; michael@0: } michael@0: }; michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // Interface functions michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: inline const Class * michael@0: GetClassForProtoKey(JSProtoKey key) michael@0: { michael@0: switch (key) { michael@0: case JSProto_Object: michael@0: return &JSObject::class_; michael@0: case JSProto_Array: michael@0: return &ArrayObject::class_; michael@0: michael@0: case JSProto_Number: michael@0: return &NumberObject::class_; michael@0: case JSProto_Boolean: michael@0: return &BooleanObject::class_; michael@0: case JSProto_String: michael@0: return &StringObject::class_; michael@0: case JSProto_RegExp: michael@0: return &RegExpObject::class_; michael@0: michael@0: case JSProto_Int8Array: michael@0: case JSProto_Uint8Array: michael@0: case JSProto_Int16Array: michael@0: case JSProto_Uint16Array: michael@0: case JSProto_Int32Array: michael@0: case JSProto_Uint32Array: michael@0: case JSProto_Float32Array: michael@0: case JSProto_Float64Array: michael@0: case JSProto_Uint8ClampedArray: michael@0: return &TypedArrayObject::classes[key - JSProto_Int8Array]; michael@0: michael@0: case JSProto_ArrayBuffer: michael@0: return &ArrayBufferObject::class_; michael@0: michael@0: case JSProto_SharedArrayBuffer: michael@0: return &SharedArrayBufferObject::class_; michael@0: michael@0: case JSProto_DataView: michael@0: return &DataViewObject::class_; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad proto key"); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Get the default 'new' object for a given standard class, per the currently michael@0: * active global. michael@0: */ michael@0: inline TypeObject * michael@0: GetTypeNewObject(JSContext *cx, JSProtoKey key) michael@0: { michael@0: RootedObject proto(cx); michael@0: if (!GetBuiltinPrototype(cx, key, &proto)) michael@0: return nullptr; michael@0: return cx->getNewType(GetClassForProtoKey(key), proto.get()); michael@0: } michael@0: michael@0: /* Get a type object for the immediate allocation site within a native. */ michael@0: inline TypeObject * michael@0: GetTypeCallerInitObject(JSContext *cx, JSProtoKey key) michael@0: { michael@0: jsbytecode *pc; michael@0: RootedScript script(cx, cx->currentScript(&pc)); michael@0: if (script) michael@0: return TypeScript::InitObject(cx, script, pc, key); michael@0: return GetTypeNewObject(cx, key); michael@0: } michael@0: michael@0: void MarkIteratorUnknownSlow(JSContext *cx); michael@0: michael@0: void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, michael@0: bool constructing); michael@0: michael@0: /* michael@0: * Monitor a javascript call, either on entry to the interpreter or made michael@0: * from within the interpreter. michael@0: */ michael@0: inline void michael@0: TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing) michael@0: { michael@0: if (args.callee().is()) { michael@0: JSFunction *fun = &args.callee().as(); michael@0: if (fun->isInterpreted() && fun->nonLazyScript()->types) michael@0: TypeMonitorCallSlow(cx, &args.callee(), args, constructing); michael@0: } michael@0: } michael@0: michael@0: inline bool michael@0: TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id) michael@0: { michael@0: if (obj->hasLazyType() || obj->type()->unknownProperties()) michael@0: return false; michael@0: michael@0: if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: inline void michael@0: EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id) michael@0: { michael@0: id = IdToTypeId(id); michael@0: michael@0: if (obj->hasSingletonType()) { michael@0: AutoEnterAnalysis enter(cx); michael@0: if (obj->hasLazyType() && !obj->getType(cx)) { michael@0: CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes"); michael@0: return; michael@0: } michael@0: if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) { michael@0: MOZ_ASSERT(obj->type()->unknownProperties()); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); michael@0: } michael@0: michael@0: inline bool michael@0: CanHaveEmptyPropertyTypesForOwnProperty(JSObject *obj) michael@0: { michael@0: // Per the comment on TypeSet::propertySet, property type sets for global michael@0: // objects may be empty for 'own' properties if the global property still michael@0: // has its initial undefined value. michael@0: return obj->is(); michael@0: } michael@0: michael@0: inline bool michael@0: HasTypePropertyId(JSObject *obj, jsid id, Type type) michael@0: { michael@0: if (obj->hasLazyType()) michael@0: return true; michael@0: michael@0: if (obj->type()->unknownProperties()) michael@0: return true; michael@0: michael@0: if (HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id))) michael@0: return types->hasType(type); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: inline bool michael@0: HasTypePropertyId(JSObject *obj, jsid id, const Value &value) michael@0: { michael@0: return HasTypePropertyId(obj, id, GetValueType(value)); michael@0: } michael@0: michael@0: /* Add a possible type for a property of obj. */ michael@0: inline void michael@0: AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type) michael@0: { michael@0: id = IdToTypeId(id); michael@0: if (TrackPropertyTypes(cx, obj, id)) michael@0: obj->type()->addPropertyType(cx, id, type); michael@0: } michael@0: michael@0: inline void michael@0: AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &value) michael@0: { michael@0: id = IdToTypeId(id); michael@0: if (TrackPropertyTypes(cx, obj, id)) michael@0: obj->type()->addPropertyType(cx, id, value); michael@0: } michael@0: michael@0: inline void michael@0: AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) michael@0: { michael@0: if (!obj->unknownProperties()) michael@0: obj->addPropertyType(cx, id, type); michael@0: } michael@0: michael@0: inline void michael@0: AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value) michael@0: { michael@0: if (!obj->unknownProperties()) michael@0: obj->addPropertyType(cx, id, value); michael@0: } michael@0: michael@0: /* Set one or more dynamic flags on a type object. */ michael@0: inline void michael@0: MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags) michael@0: { michael@0: if (!obj->hasLazyType() && !obj->type()->hasAllFlags(flags)) michael@0: obj->type()->setFlags(cx, flags); michael@0: } michael@0: michael@0: /* michael@0: * Mark all properties of a type object as unknown. If markSetsUnknown is set, michael@0: * scan the entire compartment and mark all type sets containing it as having michael@0: * an unknown object. This is needed for correctness in dealing with mutable michael@0: * __proto__, which can change the type of an object dynamically. michael@0: */ michael@0: inline void michael@0: MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj, michael@0: bool markSetsUnknown = false) michael@0: { michael@0: if (!obj->unknownProperties()) michael@0: obj->markUnknown(cx); michael@0: if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)) michael@0: cx->compartment()->types.markSetsUnknown(cx, obj); michael@0: } michael@0: michael@0: inline void michael@0: MarkTypePropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id) michael@0: { michael@0: id = IdToTypeId(id); michael@0: if (TrackPropertyTypes(cx, obj, id)) michael@0: obj->type()->markPropertyNonData(cx, id); michael@0: } michael@0: michael@0: inline void michael@0: MarkTypePropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id) michael@0: { michael@0: id = IdToTypeId(id); michael@0: if (TrackPropertyTypes(cx, obj, id)) michael@0: obj->type()->markPropertyNonWritable(cx, id); michael@0: } michael@0: michael@0: inline bool michael@0: IsTypePropertyIdMarkedNonData(JSObject *obj, jsid id) michael@0: { michael@0: return obj->type()->isPropertyNonData(id); michael@0: } michael@0: michael@0: inline bool michael@0: IsTypePropertyIdMarkedNonWritable(JSObject *obj, jsid id) michael@0: { michael@0: return obj->type()->isPropertyNonWritable(id); michael@0: } michael@0: michael@0: /* Mark a state change on a particular object. */ michael@0: inline void michael@0: MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) michael@0: { michael@0: if (!obj->hasLazyType() && !obj->type()->unknownProperties()) michael@0: obj->type()->markStateChange(cx); michael@0: } michael@0: michael@0: /* michael@0: * For an array or object which has not yet escaped and been referenced elsewhere, michael@0: * pick a new type based on the object's current contents. michael@0: */ michael@0: michael@0: inline void michael@0: FixArrayType(ExclusiveContext *cx, HandleObject obj) michael@0: { michael@0: cx->compartment()->types.fixArrayType(cx, obj); michael@0: } michael@0: michael@0: inline void michael@0: FixObjectType(ExclusiveContext *cx, HandleObject obj) michael@0: { michael@0: cx->compartment()->types.fixObjectType(cx, obj); michael@0: } michael@0: michael@0: /* Interface helpers for JSScript*. */ michael@0: extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: const js::Value &rval); michael@0: extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: js::types::Type type); michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // Script interface functions michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: /* static */ inline unsigned michael@0: TypeScript::NumTypeSets(JSScript *script) michael@0: { michael@0: return script->nTypeSets() + analyze::LocalSlot(script, 0); michael@0: } michael@0: michael@0: /* static */ inline StackTypeSet * michael@0: TypeScript::ThisTypes(JSScript *script) michael@0: { michael@0: return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot(); michael@0: } michael@0: michael@0: /* michael@0: * Note: for non-escaping arguments and locals, argTypes/localTypes reflect michael@0: * only the initial type of the variable (e.g. passed values for argTypes, michael@0: * or undefined for localTypes) and not types from subsequent assignments. michael@0: */ michael@0: michael@0: /* static */ inline StackTypeSet * michael@0: TypeScript::ArgTypes(JSScript *script, unsigned i) michael@0: { michael@0: JS_ASSERT(i < script->functionNonDelazifying()->nargs()); michael@0: return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i); michael@0: } michael@0: michael@0: template michael@0: /* static */ inline TYPESET * michael@0: TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, michael@0: uint32_t *hint, TYPESET *typeArray) michael@0: { michael@0: JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); michael@0: uint32_t offset = script->pcToOffset(pc); michael@0: michael@0: // See if this pc is the next typeset opcode after the last one looked up. michael@0: if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) { michael@0: (*hint)++; michael@0: return typeArray + *hint; michael@0: } michael@0: michael@0: // See if this pc is the same as the last one looked up. michael@0: if (bytecodeMap[*hint] == offset) michael@0: return typeArray + *hint; michael@0: michael@0: // Fall back to a binary search. michael@0: size_t bottom = 0; michael@0: size_t top = script->nTypeSets() - 1; michael@0: size_t mid = bottom + (top - bottom) / 2; michael@0: while (mid < top) { michael@0: if (bytecodeMap[mid] < offset) michael@0: bottom = mid + 1; michael@0: else if (bytecodeMap[mid] > offset) michael@0: top = mid; michael@0: else michael@0: break; michael@0: mid = bottom + (top - bottom) / 2; michael@0: } michael@0: michael@0: // We should have have zeroed in on either the exact offset, unless there michael@0: // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen michael@0: // if the script is very long). michael@0: JS_ASSERT(bytecodeMap[mid] == offset || mid == top); michael@0: michael@0: *hint = mid; michael@0: return typeArray + *hint; michael@0: } michael@0: michael@0: /* static */ inline StackTypeSet * michael@0: TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread())); michael@0: #ifdef JS_ION michael@0: uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets(); michael@0: return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(), michael@0: hint, script->types->typeArray()); michael@0: #else michael@0: MOZ_CRASH(); michael@0: #endif michael@0: } michael@0: michael@0: struct AllocationSiteKey : public DefaultHasher { michael@0: JSScript *script; michael@0: michael@0: uint32_t offset : 24; michael@0: JSProtoKey kind : 8; michael@0: michael@0: static const uint32_t OFFSET_LIMIT = (1 << 23); michael@0: michael@0: AllocationSiteKey() { mozilla::PodZero(this); } michael@0: michael@0: static inline uint32_t hash(AllocationSiteKey key) { michael@0: return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); michael@0: } michael@0: michael@0: static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { michael@0: return a.script == b.script && a.offset == b.offset && a.kind == b.kind; michael@0: } michael@0: }; michael@0: michael@0: /* Whether to use a new type object for an initializer opcode at script/pc. */ michael@0: js::NewObjectKind michael@0: UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); michael@0: michael@0: js::NewObjectKind michael@0: UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); michael@0: michael@0: /* static */ inline TypeObject * michael@0: TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) michael@0: { michael@0: JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind)); michael@0: michael@0: /* :XXX: Limit script->length so we don't need to check the offset up front? */ michael@0: uint32_t offset = script->pcToOffset(pc); michael@0: michael@0: if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT) michael@0: return GetTypeNewObject(cx, kind); michael@0: michael@0: AllocationSiteKey key; michael@0: key.script = script; michael@0: key.offset = offset; michael@0: key.kind = kind; michael@0: michael@0: if (!cx->compartment()->types.allocationSiteTable) michael@0: return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); michael@0: michael@0: AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key); michael@0: michael@0: if (p) michael@0: return p->value(); michael@0: return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); michael@0: } michael@0: michael@0: /* Set the type to use for obj according to the site it was allocated at. */ michael@0: static inline bool michael@0: SetInitializerObjectType(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, NewObjectKind kind) michael@0: { michael@0: JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); michael@0: JS_ASSERT(key != JSProto_Null); michael@0: JS_ASSERT(kind == UseNewTypeForInitializer(script, pc, key)); michael@0: michael@0: if (kind == SingletonObject) { michael@0: JS_ASSERT(obj->hasSingletonType()); michael@0: michael@0: /* michael@0: * Inference does not account for types of run-once initializer michael@0: * objects, as these may not be created until after the script michael@0: * has been analyzed. michael@0: */ michael@0: TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); michael@0: } else { michael@0: types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key); michael@0: if (!type) michael@0: return false; michael@0: obj->uninlinedSetType(type); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) michael@0: { michael@0: TypeMonitorResult(cx, script, pc, rval); michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::Monitor(JSContext *cx, const js::Value &rval) michael@0: { michael@0: jsbytecode *pc; michael@0: RootedScript script(cx, cx->currentScript(&pc)); michael@0: Monitor(cx, script, pc, rval); michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id) michael@0: { michael@0: if (!obj->hasSingletonType()) { michael@0: /* michael@0: * Mark as unknown any object which has had dynamic assignments to michael@0: * non-integer properties at SETELEM opcodes. This avoids making large michael@0: * numbers of type properties for hashmap-style objects. We don't need michael@0: * to do this for objects with singleton type, because type properties michael@0: * are only constructed for them when analyzed scripts depend on those michael@0: * specific properties. michael@0: */ michael@0: uint32_t i; michael@0: if (js_IdIsIndex(id, &i)) michael@0: return; michael@0: michael@0: // But if we don't have too many properties yet, don't do anything. The michael@0: // idea here is that normal object initialization should not trigger michael@0: // deoptimization in most cases, while actual usage as a hashmap should. michael@0: TypeObject* type = obj->type(); michael@0: if (type->getPropertyCount() < 8) michael@0: return; michael@0: MarkTypeObjectUnknownProperties(cx, type); michael@0: } michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) michael@0: { michael@0: if (!script->types) michael@0: return; michael@0: michael@0: if (!ThisTypes(script)->hasType(type)) { michael@0: AutoEnterAnalysis enter(cx); michael@0: michael@0: InferSpew(ISpewOps, "externalType: setThis #%u: %s", michael@0: script->id(), TypeString(type)); michael@0: ThisTypes(script)->addType(cx, type); michael@0: } michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) michael@0: { michael@0: SetThis(cx, script, GetValueType(value)); michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type) michael@0: { michael@0: if (!script->types) michael@0: return; michael@0: michael@0: if (!ArgTypes(script, arg)->hasType(type)) { michael@0: AutoEnterAnalysis enter(cx); michael@0: michael@0: InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", michael@0: script->id(), arg, TypeString(type)); michael@0: ArgTypes(script, arg)->addType(cx, type); michael@0: } michael@0: } michael@0: michael@0: /* static */ inline void michael@0: TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value) michael@0: { michael@0: Type type = GetValueType(value); michael@0: SetArgument(cx, script, arg, type); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // TypeCompartment michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: inline JSCompartment * michael@0: TypeCompartment::compartment() michael@0: { michael@0: return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // TypeSet michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: /* michael@0: * The sets of objects and scripts in a type set grow monotonically, are usually michael@0: * empty, almost always small, and sometimes big. For empty or singleton sets, michael@0: * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE, michael@0: * an array of this length is used to store the elements. For larger sets, a hash michael@0: * table filled to 25%-50% of capacity is used, with collisions resolved by linear michael@0: * probing. TODO: replace these with jshashtables. michael@0: */ michael@0: const unsigned SET_ARRAY_SIZE = 8; michael@0: const unsigned SET_CAPACITY_OVERFLOW = 1u << 30; michael@0: michael@0: /* Get the capacity of a set with the given element count. */ michael@0: static inline unsigned michael@0: HashSetCapacity(unsigned count) michael@0: { michael@0: JS_ASSERT(count >= 2); michael@0: JS_ASSERT(count < SET_CAPACITY_OVERFLOW); michael@0: michael@0: if (count <= SET_ARRAY_SIZE) michael@0: return SET_ARRAY_SIZE; michael@0: michael@0: return 1u << (mozilla::FloorLog2(count) + 2); michael@0: } michael@0: michael@0: /* Compute the FNV hash for the low 32 bits of v. */ michael@0: template michael@0: static inline uint32_t michael@0: HashKey(T v) michael@0: { michael@0: uint32_t nv = KEY::keyBits(v); michael@0: michael@0: uint32_t hash = 84696351 ^ (nv & 0xff); michael@0: hash = (hash * 16777619) ^ ((nv >> 8) & 0xff); michael@0: hash = (hash * 16777619) ^ ((nv >> 16) & 0xff); michael@0: return (hash * 16777619) ^ ((nv >> 24) & 0xff); michael@0: } michael@0: michael@0: /* michael@0: * Insert space for an element into the specified set and grow its capacity if needed. michael@0: * returned value is an existing or new entry (nullptr if new). michael@0: */ michael@0: template michael@0: static U ** michael@0: HashSetInsertTry(LifoAlloc &alloc, U **&values, unsigned &count, T key) michael@0: { michael@0: unsigned capacity = HashSetCapacity(count); michael@0: unsigned insertpos = HashKey(key) & (capacity - 1); michael@0: michael@0: /* Whether we are converting from a fixed array to hashtable. */ michael@0: bool converting = (count == SET_ARRAY_SIZE); michael@0: michael@0: if (!converting) { michael@0: while (values[insertpos] != nullptr) { michael@0: if (KEY::getKey(values[insertpos]) == key) michael@0: return &values[insertpos]; michael@0: insertpos = (insertpos + 1) & (capacity - 1); michael@0: } michael@0: } michael@0: michael@0: if (count >= SET_CAPACITY_OVERFLOW) michael@0: return nullptr; michael@0: michael@0: count++; michael@0: unsigned newCapacity = HashSetCapacity(count); michael@0: michael@0: if (newCapacity == capacity) { michael@0: JS_ASSERT(!converting); michael@0: return &values[insertpos]; michael@0: } michael@0: michael@0: U **newValues = alloc.newArray(newCapacity); michael@0: if (!newValues) michael@0: return nullptr; michael@0: mozilla::PodZero(newValues, newCapacity); michael@0: michael@0: for (unsigned i = 0; i < capacity; i++) { michael@0: if (values[i]) { michael@0: unsigned pos = HashKey(KEY::getKey(values[i])) & (newCapacity - 1); michael@0: while (newValues[pos] != nullptr) michael@0: pos = (pos + 1) & (newCapacity - 1); michael@0: newValues[pos] = values[i]; michael@0: } michael@0: } michael@0: michael@0: values = newValues; michael@0: michael@0: insertpos = HashKey(key) & (newCapacity - 1); michael@0: while (values[insertpos] != nullptr) michael@0: insertpos = (insertpos + 1) & (newCapacity - 1); michael@0: return &values[insertpos]; michael@0: } michael@0: michael@0: /* michael@0: * Insert an element into the specified set if it is not already there, returning michael@0: * an entry which is nullptr if the element was not there. michael@0: */ michael@0: template michael@0: static inline U ** michael@0: HashSetInsert(LifoAlloc &alloc, U **&values, unsigned &count, T key) michael@0: { michael@0: if (count == 0) { michael@0: JS_ASSERT(values == nullptr); michael@0: count++; michael@0: return (U **) &values; michael@0: } michael@0: michael@0: if (count == 1) { michael@0: U *oldData = (U*) values; michael@0: if (KEY::getKey(oldData) == key) michael@0: return (U **) &values; michael@0: michael@0: values = alloc.newArray(SET_ARRAY_SIZE); michael@0: if (!values) { michael@0: values = (U **) oldData; michael@0: return nullptr; michael@0: } michael@0: mozilla::PodZero(values, SET_ARRAY_SIZE); michael@0: count++; michael@0: michael@0: values[0] = oldData; michael@0: return &values[1]; michael@0: } michael@0: michael@0: if (count <= SET_ARRAY_SIZE) { michael@0: for (unsigned i = 0; i < count; i++) { michael@0: if (KEY::getKey(values[i]) == key) michael@0: return &values[i]; michael@0: } michael@0: michael@0: if (count < SET_ARRAY_SIZE) { michael@0: count++; michael@0: return &values[count - 1]; michael@0: } michael@0: } michael@0: michael@0: return HashSetInsertTry(alloc, values, count, key); michael@0: } michael@0: michael@0: /* Lookup an entry in a hash set, return nullptr if it does not exist. */ michael@0: template michael@0: static inline U * michael@0: HashSetLookup(U **values, unsigned count, T key) michael@0: { michael@0: if (count == 0) michael@0: return nullptr; michael@0: michael@0: if (count == 1) michael@0: return (KEY::getKey((U *) values) == key) ? (U *) values : nullptr; michael@0: michael@0: if (count <= SET_ARRAY_SIZE) { michael@0: for (unsigned i = 0; i < count; i++) { michael@0: if (KEY::getKey(values[i]) == key) michael@0: return values[i]; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: unsigned capacity = HashSetCapacity(count); michael@0: unsigned pos = HashKey(key) & (capacity - 1); michael@0: michael@0: while (values[pos] != nullptr) { michael@0: if (KEY::getKey(values[pos]) == key) michael@0: return values[pos]; michael@0: pos = (pos + 1) & (capacity - 1); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: inline TypeObjectKey * michael@0: Type::objectKey() const michael@0: { michael@0: JS_ASSERT(isObject()); michael@0: if (isTypeObject()) michael@0: TypeObject::readBarrier((TypeObject *) data); michael@0: else michael@0: JSObject::readBarrier((JSObject *) (data ^ 1)); michael@0: return (TypeObjectKey *) data; michael@0: } michael@0: michael@0: inline JSObject * michael@0: Type::singleObject() const michael@0: { michael@0: JS_ASSERT(isSingleObject()); michael@0: JSObject::readBarrier((JSObject *) (data ^ 1)); michael@0: return (JSObject *) (data ^ 1); michael@0: } michael@0: michael@0: inline TypeObject * michael@0: Type::typeObject() const michael@0: { michael@0: JS_ASSERT(isTypeObject()); michael@0: TypeObject::readBarrier((TypeObject *) data); michael@0: return (TypeObject *) data; michael@0: } michael@0: michael@0: inline bool michael@0: TypeSet::hasType(Type type) const michael@0: { michael@0: if (unknown()) michael@0: return true; michael@0: michael@0: if (type.isUnknown()) { michael@0: return false; michael@0: } else if (type.isPrimitive()) { michael@0: return !!(flags & PrimitiveTypeFlag(type.primitive())); michael@0: } else if (type.isAnyObject()) { michael@0: return !!(flags & TYPE_FLAG_ANYOBJECT); michael@0: } else { michael@0: return !!(flags & TYPE_FLAG_ANYOBJECT) || michael@0: HashSetLookup michael@0: (objectSet, baseObjectCount(), type.objectKey()) != nullptr; michael@0: } michael@0: } michael@0: michael@0: inline void michael@0: TypeSet::setBaseObjectCount(uint32_t count) michael@0: { michael@0: JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT); michael@0: flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK) michael@0: | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT); michael@0: } michael@0: michael@0: inline void michael@0: HeapTypeSet::newPropertyState(ExclusiveContext *cxArg) michael@0: { michael@0: /* Propagate the change to all constraints. */ michael@0: if (JSContext *cx = cxArg->maybeJSContext()) { michael@0: TypeConstraint *constraint = constraintList; michael@0: while (constraint) { michael@0: constraint->newPropertyState(cx, this); michael@0: constraint = constraint->next; michael@0: } michael@0: } else { michael@0: JS_ASSERT(!constraintList); michael@0: } michael@0: } michael@0: michael@0: inline void michael@0: HeapTypeSet::setNonDataPropertyIgnoringConstraints() michael@0: { michael@0: flags |= TYPE_FLAG_NON_DATA_PROPERTY; michael@0: } michael@0: michael@0: inline void michael@0: HeapTypeSet::setNonDataProperty(ExclusiveContext *cx) michael@0: { michael@0: if (flags & TYPE_FLAG_NON_DATA_PROPERTY) michael@0: return; michael@0: michael@0: setNonDataPropertyIgnoringConstraints(); michael@0: newPropertyState(cx); michael@0: } michael@0: michael@0: inline void michael@0: HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx) michael@0: { michael@0: if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY) michael@0: return; michael@0: michael@0: flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY; michael@0: newPropertyState(cx); michael@0: } michael@0: michael@0: inline unsigned michael@0: TypeSet::getObjectCount() const michael@0: { michael@0: JS_ASSERT(!unknownObject()); michael@0: uint32_t count = baseObjectCount(); michael@0: if (count > SET_ARRAY_SIZE) michael@0: return HashSetCapacity(count); michael@0: return count; michael@0: } michael@0: michael@0: inline TypeObjectKey * michael@0: TypeSet::getObject(unsigned i) const michael@0: { michael@0: JS_ASSERT(i < getObjectCount()); michael@0: if (baseObjectCount() == 1) { michael@0: JS_ASSERT(i == 0); michael@0: return (TypeObjectKey *) objectSet; michael@0: } michael@0: return objectSet[i]; michael@0: } michael@0: michael@0: inline JSObject * michael@0: TypeSet::getSingleObject(unsigned i) const michael@0: { michael@0: TypeObjectKey *key = getObject(i); michael@0: return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr; michael@0: } michael@0: michael@0: inline TypeObject * michael@0: TypeSet::getTypeObject(unsigned i) const michael@0: { michael@0: TypeObjectKey *key = getObject(i); michael@0: return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr; michael@0: } michael@0: michael@0: inline const Class * michael@0: TypeSet::getObjectClass(unsigned i) const michael@0: { michael@0: if (JSObject *object = getSingleObject(i)) michael@0: return object->getClass(); michael@0: if (TypeObject *object = getTypeObject(i)) michael@0: return object->clasp(); michael@0: return nullptr; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: // TypeObject michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags) michael@0: { michael@0: mozilla::PodZero(this); michael@0: michael@0: /* Inner objects may not appear on prototype chains. */ michael@0: JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); michael@0: michael@0: this->clasp_ = clasp; michael@0: this->proto_ = proto.raw(); michael@0: this->flags_ = initialFlags; michael@0: michael@0: InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); michael@0: } michael@0: michael@0: inline uint32_t michael@0: TypeObject::basePropertyCount() const michael@0: { michael@0: return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; michael@0: } michael@0: michael@0: inline void michael@0: TypeObject::setBasePropertyCount(uint32_t count) michael@0: { michael@0: // Note: Callers must ensure they are performing threadsafe operations. michael@0: JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); michael@0: flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) michael@0: | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); michael@0: } michael@0: michael@0: inline HeapTypeSet * michael@0: TypeObject::getProperty(ExclusiveContext *cx, jsid id) michael@0: { michael@0: JS_ASSERT(cx->compartment()->activeAnalysis); michael@0: michael@0: JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); michael@0: JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); michael@0: JS_ASSERT(!unknownProperties()); michael@0: michael@0: if (HeapTypeSet *types = maybeGetProperty(id)) michael@0: return types; michael@0: michael@0: Property *base = cx->typeLifoAlloc().new_(id); michael@0: if (!base) { michael@0: markUnknown(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t propertyCount = basePropertyCount(); michael@0: Property **pprop = HashSetInsert michael@0: (cx->typeLifoAlloc(), propertySet, propertyCount, id); michael@0: if (!pprop) { michael@0: markUnknown(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: JS_ASSERT(!*pprop); michael@0: michael@0: setBasePropertyCount(propertyCount); michael@0: *pprop = base; michael@0: michael@0: updateNewPropertyTypes(cx, id, &base->types); michael@0: michael@0: if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { michael@0: // We hit the maximum number of properties the object can have, mark michael@0: // the object unknown so that new properties will not be added in the michael@0: // future. michael@0: markUnknown(cx); michael@0: } michael@0: michael@0: return &base->types; michael@0: } michael@0: michael@0: inline HeapTypeSet * michael@0: TypeObject::maybeGetProperty(jsid id) michael@0: { michael@0: JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); michael@0: JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); michael@0: JS_ASSERT(!unknownProperties()); michael@0: michael@0: Property *prop = HashSetLookup michael@0: (propertySet, basePropertyCount(), id); michael@0: michael@0: return prop ? &prop->types : nullptr; michael@0: } michael@0: michael@0: inline unsigned michael@0: TypeObject::getPropertyCount() michael@0: { michael@0: uint32_t count = basePropertyCount(); michael@0: if (count > SET_ARRAY_SIZE) michael@0: return HashSetCapacity(count); michael@0: return count; michael@0: } michael@0: michael@0: inline Property * michael@0: TypeObject::getProperty(unsigned i) michael@0: { michael@0: JS_ASSERT(i < getPropertyCount()); michael@0: if (basePropertyCount() == 1) { michael@0: JS_ASSERT(i == 0); michael@0: return (Property *) propertySet; michael@0: } michael@0: return propertySet[i]; michael@0: } michael@0: michael@0: inline void michael@0: TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type) michael@0: { michael@0: #ifdef JSGC_INCREMENTAL michael@0: if (!type) michael@0: return; michael@0: michael@0: switch (type->kind) { michael@0: case NewScript: michael@0: return TypeNewScript::writeBarrierPre(type->asNewScript()); michael@0: michael@0: case TypedObject: michael@0: return TypeTypedObject::writeBarrierPre(type->asTypedObject()); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: inline void michael@0: TypeNewScript::writeBarrierPre(TypeNewScript *newScript) michael@0: { michael@0: #ifdef JSGC_INCREMENTAL michael@0: if (!newScript || !newScript->fun->runtimeFromAnyThread()->needsBarrier()) michael@0: return; michael@0: michael@0: JS::Zone *zone = newScript->fun->zoneFromAnyThread(); michael@0: if (zone->needsBarrier()) { michael@0: MarkObject(zone->barrierTracer(), &newScript->fun, "write barrier"); michael@0: MarkObject(zone->barrierTracer(), &newScript->templateObject, "write barrier"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: } } /* namespace js::types */ michael@0: michael@0: inline bool michael@0: JSScript::ensureHasTypes(JSContext *cx) michael@0: { michael@0: return types || makeTypes(cx); michael@0: } michael@0: michael@0: namespace js { michael@0: michael@0: template <> michael@0: struct GCMethods michael@0: { michael@0: static types::Type initial() { return types::Type::UnknownType(); } michael@0: static ThingRootKind kind() { return THING_ROOT_TYPE; } michael@0: static bool poisoned(const types::Type &v) { michael@0: return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) michael@0: || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); michael@0: } michael@0: }; michael@0: michael@0: template <> michael@0: struct GCMethods michael@0: { michael@0: static types::Type initial() { return types::Type::UnknownType(); } michael@0: static ThingRootKind kind() { return THING_ROOT_TYPE; } michael@0: static bool poisoned(const types::Type &v) { michael@0: return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) michael@0: || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); michael@0: } michael@0: }; michael@0: michael@0: } // namespace js michael@0: michael@0: namespace JS { michael@0: template<> class AnchorPermitted { }; michael@0: } // namespace JS michael@0: michael@0: #endif /* jsinferinlines_h */