author | Brian Hackett <bhackett1024@gmail.com> |
Fri, 27 Sep 2013 11:29:35 -0600 | |
changeset 149027 | 3f8e57e07eee72c5ec94b53c7ca4f955c95657e1 |
parent 149026 | 19af7baaf26e984eceec910174b968386bbd7ed2 |
child 149028 | 3ed8d1a1b5942d8bf44fa9c6f9fed537fc2f670e |
push id | 34413 |
push user | bhackett@mozilla.com |
push date | Fri, 27 Sep 2013 17:29:45 +0000 |
treeherder | mozilla-inbound@3f8e57e07eee [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 920689 |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/jit/IonBuilder.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/IonBuilder.h | file | annotate | diff | comparison | revisions | |
js/src/jit/MCallOptimize.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/MIR.cpp | file | annotate | diff | comparison | revisions | |
js/src/jit/MIR.h | file | annotate | diff | comparison | revisions | |
js/src/jsarray.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsinfer.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsinfer.h | file | annotate | diff | comparison | revisions | |
js/src/jsinferinlines.h | file | annotate | diff | comparison | revisions | |
js/src/jsobj.cpp | file | annotate | diff | comparison | revisions |
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -4639,17 +4639,17 @@ IonBuilder::getSingletonPrototype(JSFunc { if (!target || !target->hasSingletonType()) return NULL; types::TypeObject *targetType = target->getType(cx); if (targetType->unknownProperties()) return NULL; jsid protoid = NameToId(cx->names().classPrototype); - types::HeapTypeSet *protoTypes = targetType->getProperty(cx, protoid, false); + types::HeapTypeSet *protoTypes = targetType->getProperty(cx, protoid); if (!protoTypes) return NULL; return protoTypes->getSingleton(cx); } MDefinition * IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) @@ -5495,17 +5495,17 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; types::TypeObject *initializer = obj->resultTypeSet()->getTypeObject(0); if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) { if (!(initializer->flags & types::OBJECT_FLAG_NON_PACKED)) needStub = true; } else if (!initializer->unknownProperties()) { - types::HeapTypeSet *elemTypes = initializer->getProperty(cx, JSID_VOID, false); + types::HeapTypeSet *elemTypes = initializer->getProperty(cx, JSID_VOID); if (!elemTypes) return false; if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) { elemTypes->addFreeze(cx); needStub = true; } } @@ -6046,28 +6046,41 @@ TestSingletonProperty(JSContext *cx, JSO if (!shape->hasDefaultGetter()) return true; if (!shape->hasSlot()) return true; if (holder->getSlot(shape->slot()).isUndefined()) return true; - types::TypeObject *objType = obj->getType(cx); - if (!objType) - return false; - if (objType->unknownProperties()) - return true; - - types::HeapTypeSet *property = objType->getProperty(cx, id, false); - if (!property) - return false; - objType->getFromPrototypes(cx, id, property); - if (property->getSingleton(cx) != singleton) - return true; + // Ensure the property does not appear anywhere on the prototype chain + // before |holder|, and that |holder| only has the result object for its + // property. + while (true) { + types::TypeObject *objType = obj->getType(cx); + if (!objType) + return false; + if (objType->unknownProperties()) + return true; + + types::HeapTypeSet *property = objType->getProperty(cx, id); + if (!property) + return false; + if (obj != holder) { + if (!property->empty()) + return true; + property->addFreeze(cx); + } else { + if (property->getSingleton(cx) != singleton) + return true; + break; + } + + obj = obj->getProto(); + } *isKnownConstant = true; return true; } static inline bool TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, JSObject *globalObj, jsid id, @@ -6140,22 +6153,22 @@ TestSingletonPropertyTypes(JSContext *cx continue; object = curObj->getType(cx); if (!object) return false; } if (object->unknownProperties()) return true; - types::HeapTypeSet *property = object->getProperty(cx, id, false); + types::HeapTypeSet *property = object->getProperty(cx, id); if (!property) return false; - object->getFromPrototypes(cx, id, property); - if (property->getSingleton(cx) != singleton) + if (!property->empty()) return true; + property->addFreeze(cx); if (object->proto) { // Test this type. bool thoughtConstant = false; if (!TestSingletonProperty(cx, object->proto, singleton, id, &thoughtConstant)) return false; if (!thoughtConstant) return true; @@ -6282,21 +6295,21 @@ IonBuilder::getStaticName(JSObject *stat return true; } types::TypeObject *staticType = staticObject->getType(cx); if (!staticType) return false; types::HeapTypeSet *propertyTypes = NULL; if (!staticType->unknownProperties()) { - propertyTypes = staticType->getProperty(cx, id, false); + propertyTypes = staticType->getProperty(cx, id); if (!propertyTypes) return false; } - if (propertyTypes && propertyTypes->isOwnProperty(cx, staticType, true)) { + if (propertyTypes && propertyTypes->isConfiguredProperty(cx, staticType)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. *psucceeded = false; return true; } types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc); bool barrier; @@ -6393,21 +6406,21 @@ IonBuilder::setStaticName(JSObject *stat if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot()) return jsop_setprop(name); types::TypeObject *staticType = staticObject->getType(cx); if (!staticType) return false; types::HeapTypeSet *propertyTypes = NULL; if (!staticType->unknownProperties()) { - propertyTypes = staticType->getProperty(cx, id, false); + propertyTypes = staticType->getProperty(cx, id); if (!propertyTypes) return false; } - if (!propertyTypes || propertyTypes->isOwnProperty(cx, staticType, true)) { + if (!propertyTypes || propertyTypes->isConfiguredProperty(cx, staticType)) { // The property has been reconfigured as non-configurable, non-enumerable // or non-writable. return jsop_setprop(name); } if (!TypeSetIncludes(propertyTypes, value->type(), value->resultTypeSet())) return jsop_setprop(name); current->pop(); @@ -6424,25 +6437,18 @@ IonBuilder::setStaticName(JSObject *stat if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(obj, value)); // If the property has a known type, we may be able to optimize typed stores by not // storing the type tag. This only works if the property does not have its initial // |undefined| value; if |undefined| is assigned at a later point, it will be added // to the type set. - // - // We also need to make sure the typeset reflects the inherited types from - // the prototype by calling getFromPrototype. Otherwise we may specialize - // on a typeset that changes before compilation ends, which would mean the - // current script wouldn't be recompiled even when our assumption here is - // made false. MIRType slotType = MIRType_None; if (propertyTypes && !staticObject->getSlot(shape->slot()).isUndefined()) { - staticType->getFromPrototypes(cx, id, propertyTypes); JSValueType knownType = propertyTypes->getKnownTypeTag(cx); if (knownType != JSVAL_TYPE_UNKNOWN) slotType = MIRTypeFromValueType(knownType); } bool needsBarrier = !propertyTypes || propertyTypes->needsBarrier(cx); return storeSlot(obj, shape, value, needsBarrier, slotType); } @@ -7680,20 +7686,20 @@ GetDefiniteSlot(JSContext *cx, types::Te types::TypeObject *type = types->getTypeObject(0); if (!type || type->unknownProperties()) return NULL; jsid id = AtomToId(atom); if (id != types::IdToTypeId(id)) return NULL; - types::HeapTypeSet *propertyTypes = type->getProperty(cx, id, false); + types::HeapTypeSet *propertyTypes = type->getProperty(cx, id); if (!propertyTypes || !propertyTypes->definiteProperty() || - propertyTypes->isOwnProperty(cx, type, true)) + propertyTypes->isConfiguredProperty(cx, type)) { return NULL; } return propertyTypes; } bool @@ -7725,21 +7731,22 @@ TestClassHasAccessorHook(const Class *cl return true; return false; } inline bool TestTypeHasOwnProperty(JSContext *cx, types::TypeObject *typeObj, jsid id, bool &cont) { cont = true; - types::HeapTypeSet *propSet = typeObj->getProperty(cx, types::IdToTypeId(id), false); + types::HeapTypeSet *propSet = typeObj->getProperty(cx, types::IdToTypeId(id)); if (!propSet) return false; - if (propSet->ownProperty(false)) + if (!propSet->empty()) cont = false; + // Note: Callers must explicitly freeze the property type set later on if optimizing. return true; } inline bool TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *foundProto, JSObject *obj, bool &cont) { cont = false; @@ -7900,23 +7907,21 @@ FreezePropTypeSets(JSContext *cx, types: // If we found a Singleton object's own-property, there's nothing to // freeze. if (obj != foundProto) { // Walk the prototype chain. Everyone has to have the property, since we // just checked, so propSet cannot be NULL. jsid typeId = types::IdToTypeId(id); while (true) { - types::HeapTypeSet *propSet = curType->getProperty(cx, typeId, false); + types::HeapTypeSet *propSet = curType->getProperty(cx, typeId); // This assert is now assured, since we have faulted them in // above. - JS_ASSERT(propSet); - // Asking, freeze by asking. - DebugOnly<bool> isOwn = propSet->isOwnProperty(cx, curType, false); - JS_ASSERT(!isOwn); + JS_ASSERT(propSet && propSet->empty()); + propSet->addFreeze(cx); // Don't mark the proto. It will be held down by the shape // guard. This allows us tp use properties found on prototypes // with properties unknown to TI. if (curType->proto == foundProto) break; curType = curType->proto->getType(cx); if (!curType) return false; @@ -8011,47 +8016,59 @@ IonBuilder::annotateGetPropertyCache(JSC // Ensure that the relevant property typeset for each type object is // is a single-object typeset containing a JSFunction for (unsigned int i = 0; i < objCount; i++) { types::TypeObject *typeObj = objTypes->getTypeObject(i); if (!typeObj || typeObj->unknownProperties() || !typeObj->proto) continue; - types::HeapTypeSet *ownTypes = typeObj->getProperty(cx, id, false); + types::HeapTypeSet *ownTypes = typeObj->getProperty(cx, id); if (!ownTypes) continue; - if (ownTypes->isOwnProperty(cx, typeObj, false)) + if (!ownTypes->empty()) continue; - - RootedObject proto(cx, typeObj->proto); - types::TypeObject *protoType = proto->getType(cx); - if (!protoType) - return false; - if (protoType->unknownProperties()) - continue; - - types::HeapTypeSet *protoTypes = protoType->getProperty(cx, id, false); - if (!protoTypes) - return false; - - JSObject *obj = protoTypes->getSingleton(cx); - if (!obj || !obj->is<JSFunction>()) + ownTypes->addFreeze(cx); + + JSObject *singleton = NULL; + JSObject *proto = typeObj->proto; + while (true) { + types::TypeObject *protoType = proto->getType(cx); + if (!protoType) + return false; + if (!protoType->unknownProperties()) { + types::HeapTypeSet *protoTypes = protoType->getProperty(cx, id); + if (!protoTypes) + return false; + + singleton = protoTypes->getSingleton(cx); + if (singleton) { + if (singleton->is<JSFunction>()) + break; + singleton = NULL; + } + } + TaggedProto taggedProto = proto->getTaggedProto(); + if (!taggedProto.isObject()) + break; + proto = taggedProto.toObject(); + } + if (!singleton) continue; bool knownConstant = false; - if (!TestSingletonProperty(cx, proto, obj, id, &knownConstant)) + if (!TestSingletonProperty(cx, proto, singleton, id, &knownConstant)) return false; // Don't add cases corresponding to non-observed pushes - if (!pushedTypes->hasType(types::Type::ObjectType(obj))) + if (!pushedTypes->hasType(types::Type::ObjectType(singleton))) continue; - if (!inlinePropTable->addEntry(typeObj, &obj->as<JSFunction>())) + if (!inlinePropTable->addEntry(typeObj, &singleton->as<JSFunction>())) return false; } if (inlinePropTable->numEntries() == 0) { getPropCache->clearInlinePropertyTable(); return true; } @@ -8186,17 +8203,17 @@ IonBuilder::jsop_getprop(PropertyName *n if (!getPropTryTypedObject(&emitted, id, types) || emitted) return emitted; // Try to emit loads from definite slots. if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted) return emitted; // Try to inline a common property getter, or make a call. - if (!getPropTryCommonGetter(&emitted, id, barrier, types) || emitted) + if (!getPropTryCommonGetter(&emitted, id, types) || emitted) return emitted; // Try to emit a monomorphic/polymorphic access based on baseline caches. if (!getPropTryInlineAccess(&emitted, name, id, barrier, types) || emitted) return emitted; // Try to emit a polymorphic cache. if (!getPropTryCache(&emitted, name, id, barrier, types) || emitted) @@ -8409,18 +8426,17 @@ IonBuilder::getPropTryDefiniteSlot(bool if (!pushTypeBarrier(fixed, types, barrier)) return false; *emitted = true; return true; } bool -IonBuilder::getPropTryCommonGetter(bool *emitted, jsid id, - bool barrier, types::TemporaryTypeSet *types) +IonBuilder::getPropTryCommonGetter(bool *emitted, jsid id, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); JSFunction *commonGetter; bool isDOM; MDefinition *guard; types::TemporaryTypeSet *objTypes = current->peek(-1)->resultTypeSet(); @@ -8434,18 +8450,17 @@ IonBuilder::getPropTryCommonGetter(bool if (isDOM && TestShouldDOMCall(cx, objTypes, commonGetter, JSJitInfo::Getter)) { const JSJitInfo *jitinfo = commonGetter->jitInfo(); MGetDOMProperty *get = MGetDOMProperty::New(jitinfo, obj, guard); current->add(get); current->push(get); if (get->isEffectful() && !resumeAfter(get)) return false; - if (!DOMCallNeedsBarrier(jitinfo, types)) - barrier = false; + bool barrier = DOMCallNeedsBarrier(jitinfo, types); if (!pushTypeBarrier(get, types, barrier)) return false; *emitted = true; return true; } // Don't call the getter with a primitive value. @@ -8570,19 +8585,17 @@ IonBuilder::getPropTryCache(bool *emitte types::TemporaryTypeSet *types = obj->resultTypeSet(); if (!types || !types->objectOrSentinel()) return true; } current->pop(); MGetPropertyCache *load = MGetPropertyCache::New(obj, name); - // Try to mark the cache as idempotent. We only do this if JM is enabled - // (its ICs are used to mark property reads as likely non-idempotent) or - // if we are compiling eagerly (to improve test coverage). + // Try to mark the cache as idempotent. // // In parallel execution, idempotency of caches is ignored, since we // repeat the entire ForkJoin workload if we bail out. Note that it's // overly restrictive to mark everything as idempotent, because we can // treat non-idempotent caches in parallel as repeatable. if (obj->type() == MIRType_Object && !invalidatedIdempotentCache() && info().executionMode() != ParallelExecution) { @@ -8610,16 +8623,21 @@ IonBuilder::getPropTryCache(bool *emitte return false; if (accessGetter) barrier = true; if (needsToMonitorMissingProperties(types)) barrier = true; + // Caches can read values from prototypes, so update the barrier to + // reflect such possible values. + if (!barrier && !PropertyReadOnPrototypeNeedsTypeBarrier(cx, obj, name, types, &barrier)) + return false; + MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag()); if (barrier || IsNullOrUndefined(rvalType)) rvalType = MIRType_Value; load->setResultType(rvalType); if (!pushTypeBarrier(load, types, barrier)) return false; @@ -9463,17 +9481,17 @@ IonBuilder::jsop_instanceof() if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction()) break; types::TypeObject *rhsType = rhsObject->getType(cx); if (!rhsType || rhsType->unknownProperties()) break; types::HeapTypeSet *protoTypes = - rhsType->getProperty(cx, NameToId(cx->names().classPrototype), false); + rhsType->getProperty(cx, NameToId(cx->names().classPrototype)); JSObject *protoObject = protoTypes ? protoTypes->getSingleton(cx) : NULL; if (!protoObject) break; rhs->setFoldedUnchecked(); MInstanceOf *ins = new MInstanceOf(obj, protoObject);
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -356,18 +356,17 @@ class IonBuilder : public MIRGenerator bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier, MIRType slotType = MIRType_None); // jsop_getprop() helpers. bool getPropTryArgumentsLength(bool *emitted); bool getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types); bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types); - bool getPropTryCommonGetter(bool *emitted, jsid id, - bool barrier, types::TemporaryTypeSet *types); + bool getPropTryCommonGetter(bool *emitted, jsid id, types::TemporaryTypeSet *types); bool getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types); bool getPropTryTypedObject(bool *emitted, jsid id, types::TemporaryTypeSet *resultTypes); bool getPropTryScalarPropOfTypedObject(bool *emitted, int32_t fieldOffset, TypeRepresentationSet fieldTypeReprs, types::TemporaryTypeSet *resultTypes);
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -213,17 +213,17 @@ IonBuilder::inlineArray(CallInfo &callIn if (callInfo.argc() >= 2) { initLength = callInfo.argc(); allocating = MNewArray::NewArray_Allocating; types::TypeObject *type = types::TypeScript::InitObject(cx, script(), pc, JSProto_Array); if (!type) return InliningStatus_Error; if (!type->unknownProperties()) { - types::HeapTypeSet *elemTypes = type->getProperty(cx, JSID_VOID, false); + types::HeapTypeSet *elemTypes = type->getProperty(cx, JSID_VOID); if (!elemTypes) return InliningStatus_Error; for (uint32_t i = 0; i < initLength; i++) { MDefinition *value = callInfo.getArg(i); if (!TypeSetIncludes(elemTypes, value->type(), value->resultTypeSet())) { elemTypes->addFreeze(cx); return InliningStatus_NotInlined; @@ -480,17 +480,17 @@ IonBuilder::inlineArrayConcat(CallInfo & argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED)) { return InliningStatus_NotInlined; } // Constraints modeling this concat have not been generated by inference, // so check that type information already reflects possible side effects of // this call. - types::HeapTypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false); + types::HeapTypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID); if (!thisElemTypes) return InliningStatus_Error; types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet(); if (!resTypes->hasType(types::Type::ObjectType(thisType))) return InliningStatus_NotInlined; for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { @@ -499,17 +499,17 @@ IonBuilder::inlineArrayConcat(CallInfo & types::TypeObject *argType = argTypes->getTypeObject(i); if (!argType) continue; if (argType->unknownProperties()) return InliningStatus_NotInlined; - types::HeapTypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false); + types::HeapTypeSet *elemTypes = argType->getProperty(cx, JSID_VOID); if (!elemTypes) return InliningStatus_Error; if (!elemTypes->knownSubset(cx, thisElemTypes)) return InliningStatus_NotInlined; } // Inline the call.
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -16,16 +16,17 @@ #include "jit/BaselineInspector.h" #include "jit/IonBuilder.h" #include "jit/IonSpewer.h" #include "jit/MIRGraph.h" #include "jit/RangeAnalysis.h" #include "jsatominlines.h" #include "jsinferinlines.h" +#include "jsobjinlines.h" using namespace js; using namespace js::jit; using mozilla::DoublesAreIdentical; void MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op) @@ -2725,17 +2726,17 @@ jit::DenseNativeElementType(JSContext *c return false; if (!object) continue; if (object->unknownProperties()) return true; - types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false); + types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID); if (!elementTypes) return true; MIRType type = MIRTypeFromValueType(elementTypes->getKnownTypeTag(cx)); if (type == MIRType_None) return true; if (elementType == MIRType_None) @@ -2743,68 +2744,41 @@ jit::DenseNativeElementType(JSContext *c else if (elementType != type) return true; } *result = elementType; return true; } -bool -jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, - types::StackTypeSet *observed, bool updateObserved, bool *result) +static bool +PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, + types::TypeSet *observed, bool *result) { + jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; + // If the object being read from has types for the property which haven't // been observed at this access site, the read could produce a new type and // a barrier is needed. Note that this only covers reads from properties // which are accounted for by type information, i.e. native data properties // and elements. JS_ASSERT(result); *result = false; if (object->unknownProperties()) { *result = true; return true; } - jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; - - // If this access has never executed, try to add types to the observed set - // according to any property which exists on the object or its prototype. - if (updateObserved && observed->empty() && observed->noConstraints() && !JSID_IS_VOID(id)) { - JSObject *obj = object->singleton ? object->singleton : object->proto; - - while (obj) { - if (!obj->isNative()) - break; - - Value v; - if (HasDataProperty(cx, obj, id, &v)) { - if (v.isUndefined()) - break; - observed->addType(cx, types::GetValueType(v)); - } - - obj = obj->getProto(); - } - } - - types::HeapTypeSet *property = object->getProperty(cx, id, false); + types::HeapTypeSet *property = object->getProperty(cx, id); if (!property) { *result = true; return true; } - // We need to consider possible types for the property both as an 'own' - // property on the object and as inherited from any prototype. Type sets - // for a property do not, however, reflect inherited types until a - // getFromPrototypes() call has been performed. - if (!property->hasPropagatedProperty()) - object->getFromPrototypes(cx, id, property); - if (!TypeSetIncludes(observed, MIRType_Value, property)) { *result = true; return true; } // Type information for singleton objects is not required to reflect the // initial 'undefined' value for native properties, in particular global // variables declared with 'var'. Until the property is assigned a value @@ -2821,16 +2795,45 @@ jit::PropertyReadNeedsTypeBarrier(JSCont } property->addFreeze(cx); *result = false; return true; } bool +jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, + types::StackTypeSet *observed, bool updateObserved, bool *result) +{ + jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID; + + // If this access has never executed, try to add types to the observed set + // according to any property which exists on the object or its prototype. + if (updateObserved && observed->empty() && observed->noConstraints() && !JSID_IS_VOID(id)) { + JSObject *obj = object->singleton ? object->singleton : object->proto; + + while (obj) { + if (!obj->isNative()) + break; + + Value v; + if (HasDataProperty(cx, obj, id, &v)) { + if (v.isUndefined()) + break; + observed->addType(cx, types::GetValueType(v)); + } + + obj = obj->getProto(); + } + } + + return PropertyReadNeedsTypeBarrier(cx, object, name, observed, result); +} + +bool jit::PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, types::StackTypeSet *observed, bool *result) { JS_ASSERT(result); *result = false; if (observed->unknown()) return true; @@ -2851,17 +2854,54 @@ jit::PropertyReadNeedsTypeBarrier(JSCont if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, updateObserved, result)) return false; if (*result) return true; } } + return true; +} + +bool +jit::PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, + types::TemporaryTypeSet *observed, bool *result) +{ + JS_ASSERT(result); *result = false; + + if (observed->unknown()) + return true; + + types::TypeSet *types = obj->resultTypeSet(); + if (!types || types->unknownObject()) { + *result = true; + return true; + } + + for (size_t i = 0; i < types->getObjectCount(); i++) { + types::TypeObject *object; + if (!types->getTypeOrSingleObject(cx, i, &object)) + return false; + + if (!object) + continue; + while (object->proto) { + object = object->proto->getType(cx); + if (!object) + return false; + + if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, result)) + return false; + if (*result) + return true; + } + } + return true; } bool jit::PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result) { JS_ASSERT(result); *result = false; @@ -2878,18 +2918,18 @@ jit::PropertyReadIsIdempotent(JSContext if (!types->getTypeOrSingleObject(cx, i, &object)) return false; if (object) { if (object->unknownProperties()) return true; // Check if the property has been reconfigured or is a getter. - types::HeapTypeSet *property = object->getProperty(cx, id, false); - if (!property || property->isOwnProperty(cx, object, true)) + types::HeapTypeSet *property = object->getProperty(cx, id); + if (!property || property->isConfiguredProperty(cx, object)) return true; } } *result = true; return true; } @@ -2918,17 +2958,17 @@ jit::AddObjectsForPropertyRead(JSContext if (!object) continue; if (object->unknownProperties()) { observed->addType(cx, types::Type::AnyObjectType()); return true; } - types::HeapTypeSet *property = object->getProperty(cx, id, false); + types::HeapTypeSet *property = object->getProperty(cx, id); if (property->unknownObject()) { observed->addType(cx, types::Type::AnyObjectType()); return true; } for (size_t i = 0; i < property->getObjectCount(); i++) { if (types::TypeObject *object = property->getTypeObject(i)) observed->addType(cx, types::Type::ObjectType(object)); @@ -2959,17 +2999,17 @@ TryAddTypeBarrierForWrite(JSContext *cx, return false; if (!object) continue; if (object->unknownProperties()) return false; - types::HeapTypeSet *property = object->getProperty(cx, id, false); + types::HeapTypeSet *property = object->getProperty(cx, id); if (!property) return false; if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) return false; // This freeze is not required for correctness, but ensures that we // will recompile if the property types change and the barrier can @@ -3069,17 +3109,17 @@ jit::PropertyWriteNeedsTypeBarrier(JSCon if (!object || object->unknownProperties()) continue; // TI doesn't track TypedArray objects and should never insert a type // barrier for them. if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX) continue; - types::HeapTypeSet *property = object->getProperty(cx, id, false); + types::HeapTypeSet *property = object->getProperty(cx, id); if (!property) { success = false; break; } if (!TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) { // Either pobj or pvalue needs to be modified to filter out the // types which the value could have but are not in the property, // or a VM call is required. A VM call is always required if pobj @@ -3111,17 +3151,17 @@ jit::PropertyWriteNeedsTypeBarrier(JSCon if (!types->getTypeOrSingleObject(cx, i, &object)) return false; if (!object || object->unknownProperties()) continue; if (object->getTypedArrayType() < ScalarTypeRepresentation::TYPE_MAX) continue; - types::HeapTypeSet *property = object->getProperty(cx, id, false); + types::HeapTypeSet *property = object->getProperty(cx, id); if (!property) { *result = true; return true; } if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) continue;
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8937,16 +8937,18 @@ bool ElementAccessIsTypedArray(MDefiniti ScalarTypeRepresentation::Type *arrayType); bool ElementAccessIsPacked(JSContext *cx, MDefinition *obj); bool ElementAccessHasExtraIndexedProperty(JSContext *cx, MDefinition *obj); bool DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result); bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name, types::StackTypeSet *observed, bool updateObserved, bool *result); bool PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, types::StackTypeSet *observed, bool *result); +bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name, + types::TemporaryTypeSet *observed, bool *result); bool PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result); bool AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name, types::StackTypeSet *observed); bool PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj, PropertyName *name, MDefinition **pvalue, bool canModify, bool *result); } // namespace jit
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1129,17 +1129,17 @@ array_join(JSContext *cx, unsigned argc, } static inline bool InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count) { if (cx->typeInferenceEnabled() && !type->unknownProperties()) { AutoEnterAnalysis enter(cx); - TypeSet *types = type->getProperty(cx, JSID_VOID, true); + TypeSet *types = type->getProperty(cx, JSID_VOID); if (!types) return false; for (unsigned i = 0; i < count; i++) { if (vector[i].isMagic(JS_ELEMENTS_HOLE)) continue; Type valtype = GetValueType(vector[i]); types->addType(cx, valtype);
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -258,24 +258,16 @@ types::TypeHasProperty(JSContext *cx, Ty * We don't track types for properties inherited from prototypes which * haven't yet been accessed during analysis of the inheriting object. * Don't do the property instantiation now. */ TypeSet *types = obj->maybeGetProperty(cx, id); if (!types) return true; - /* - * If the types inherited from prototypes are not being propagated into - * this set (because we haven't analyzed code which accesses the - * property), skip. - */ - if (!types->hasPropagatedProperty()) - return true; - if (!types->hasType(type)) { TypeFailure(cx, "Missing type in object %s %s: %s", TypeObjectString(obj), TypeIdString(id), TypeString(type)); } } return true; } @@ -412,18 +404,16 @@ TypeSet::add(JSContext *cx, TypeConstrai if (callExisting) addTypesToConstraint(cx, constraint); } void TypeSet::print() { - if (flags & TYPE_FLAG_OWN_PROPERTY) - fprintf(stderr, " [own]"); if (flags & TYPE_FLAG_CONFIGURED_PROPERTY) fprintf(stderr, " [configured]"); if (definiteProperty()) fprintf(stderr, " [definite:%d]", definiteSlot()); if (baseFlags() == 0 && !baseObjectCount()) { fprintf(stderr, " missing"); @@ -527,57 +517,16 @@ TypeSet::unionSets(TypeSet *a, TypeSet * return NULL; } } return res; } ///////////////////////////////////////////////////////////////////// -// TypeSet constraints -///////////////////////////////////////////////////////////////////// - -namespace { - -/* Standard subset constraint, propagate all types from one set to another. */ -class TypeConstraintSubset : public TypeConstraint -{ - public: - TypeSet *target; - - TypeConstraintSubset(TypeSet *target) - : target(target) - { - JS_ASSERT(target); - } - - const char *kind() { return "subset"; } - - void newType(JSContext *cx, TypeSet *source, Type type) - { - /* Basic subset constraint, move all types to the target. */ - target->addType(cx, type); - } -}; - -} /* anonymous namespace */ - -void -StackTypeSet::addSubset(JSContext *cx, StackTypeSet *target) -{ - add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubset>(target)); -} - -void -HeapTypeSet::addSubset(JSContext *cx, HeapTypeSet *target) -{ - add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubset>(target)); -} - -///////////////////////////////////////////////////////////////////// // Freeze constraints ///////////////////////////////////////////////////////////////////// namespace { /* Constraint which triggers recompilation of a script if any type is added to a type set. */ class TypeConstraintFreeze : public TypeConstraint { @@ -759,33 +708,33 @@ TemporaryTypeSet::hasObjectFlags(JSConte } if (object->hasAnyFlags(flags)) return true; /* * Add a constraint on the the object to pick up changes in the * object's properties. */ - HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY, false); + HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY); if (!types) return true; types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>( cx->compartment()->types.compiledInfo, flags), false); } return false; } bool HeapTypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags) { if (object->hasAnyFlags(flags)) return true; - HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY, false); + HeapTypeSet *types = object->getProperty(cx, JSID_EMPTY); if (!types) return true; types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>( cx->compartment()->types.compiledInfo, flags), false); return false; } static inline void @@ -813,55 +762,48 @@ ObjectStateChange(ExclusiveContext *cxAr } } } void HeapTypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj) { JS_ASSERT(!obj->unknownProperties()); - HeapTypeSet *types = obj->getProperty(cx, JSID_EMPTY, false); + HeapTypeSet *types = obj->getProperty(cx, JSID_EMPTY); if (!types) return; /* * Use a constraint which triggers recompilation when markStateChange is * called, which will set 'force' to true. */ types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>( cx->compartment()->types.compiledInfo, 0)); } namespace { -class TypeConstraintFreezeOwnProperty : public TypeConstraint +class TypeConstraintFreezeConfiguredProperty : public TypeConstraint { public: RecompileInfo info; - bool updated; - bool configurable; - - TypeConstraintFreezeOwnProperty(RecompileInfo info, bool configurable) - : info(info), updated(false), configurable(configurable) + TypeConstraintFreezeConfiguredProperty(RecompileInfo info) + : info(info) {} const char *kind() { return "freezeOwnProperty"; } void newType(JSContext *cx, TypeSet *source, Type type) {} void newPropertyState(JSContext *cx, TypeSet *source) { - if (updated) - return; - if (source->ownProperty(configurable)) { - updated = true; + if (source->configuredProperty()) cx->compartment()->types.addPendingRecompile(cx, info); - } } }; } /* anonymous namespace */ static void CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, HandleFunction fun); @@ -876,17 +818,17 @@ TypeObject::incrementTenureCount() flags = (flags & ~OBJECT_FLAG_TENURE_COUNT_MASK) | ((count + 1) << OBJECT_FLAG_TENURE_COUNT_SHIFT); return count >= MaxJITAllocTenures; } bool -HeapTypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) +HeapTypeSet::isConfiguredProperty(JSContext *cx, TypeObject *object) { /* * Everywhere compiled code depends on definite properties associated with * a type object's newScript, we need to make sure there are constraints * in place which will mark those properties as configured should the * definite properties be invalidated. */ if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { @@ -896,22 +838,21 @@ HeapTypeSet::isOwnProperty(JSContext *cx RootedFunction fun(cx, object->newScript()->fun); CheckNewScriptProperties(cx, typeObj, fun); } else { JS_ASSERT(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED); object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; } } - if (ownProperty(configurable)) + if (configuredProperty()) return true; - add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeOwnProperty>( - cx->compartment()->types.compiledInfo, - configurable), false); + add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeConfiguredProperty>( + cx->compartment()->types.compiledInfo), false); return false; } bool HeapTypeSet::knownNonEmpty(JSContext *cx) { if (baseFlags() != 0 || baseObjectCount() != 0) return true; @@ -970,17 +911,17 @@ TemporaryTypeSet::convertDoubleElements( } } if (type->unknownProperties()) { alwaysConvert = false; continue; } - HeapTypeSet *types = type->getProperty(cx, JSID_VOID, false); + HeapTypeSet *types = type->getProperty(cx, JSID_VOID); if (!types) return AmbiguousDoubleConversion; types->addFreeze(cx); // We can't convert to double elements for objects which do not have // double in their element types (as the conversion may render the type // information incorrect), nor for non-array objects (as their elements @@ -1535,18 +1476,18 @@ PrototypeHasIndexedProperty(JSContext *c do { TypeObject *type = obj->getType(cx); if (!type) return true; if (ClassCanHaveExtraProperties(type->clasp)) return true; if (type->unknownProperties()) return true; - HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false); - if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx)) + HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID); + if (!indexTypes || indexTypes->isConfiguredProperty(cx, type) || indexTypes->knownNonEmpty(cx)) return true; obj = obj->getProto(); } while (obj); return false; } bool @@ -2241,57 +2182,25 @@ TypeCompartment::newTypedObject(JSContex obj->setType(p->value.object); return obj; } ///////////////////////////////////////////////////////////////////// // TypeObject ///////////////////////////////////////////////////////////////////// -void -TypeObject::getFromPrototypes(JSContext *cx, jsid id, HeapTypeSet *types, bool force) -{ - if (!force && types->hasPropagatedProperty()) - return; - - types->setPropagatedProperty(); - - if (!proto) - return; - - if (proto == Proxy::LazyProto) { - JS_ASSERT(unknownProperties()); - return; - } - - types::TypeObject *protoType = proto->getType(cx); - if (!protoType || protoType->unknownProperties()) { - types->addType(cx, Type::UnknownType()); - return; - } - - HeapTypeSet *protoTypes = protoType->getProperty(cx, id, false); - if (!protoTypes) - return; - - protoTypes->addSubset(cx, types); - - protoType->getFromPrototypes(cx, id, protoTypes); -} - static inline void UpdatePropertyType(ExclusiveContext *cx, TypeSet *types, JSObject *obj, Shape *shape, bool force) { - types->setOwnProperty(cx, false); if (!shape->writable()) - types->setOwnProperty(cx, true); + types->setConfiguredProperty(cx); if (shape->hasGetterValue() || shape->hasSetterValue()) { - types->setOwnProperty(cx, true); + types->setConfiguredProperty(cx); types->addType(cx, Type::UnknownType()); } else if (shape->hasDefaultGetter() && shape->hasSlot()) { const Value &value = obj->nativeGetSlot(shape->slot()); /* * Don't add initial undefined types for singleton properties that are * not collated into the JSID_VOID property (see propertySet comment). */ @@ -2330,33 +2239,32 @@ TypeObject::addProperty(ExclusiveContext shape = shape->previous(); } /* Also get values of any dense elements in the object. */ for (size_t i = 0; i < singleton->getDenseInitializedLength(); i++) { const Value &value = singleton->getDenseElement(i); if (!value.isMagic(JS_ELEMENTS_HOLE)) { Type type = GetValueType(value); - base->types.setOwnProperty(cx, false); base->types.addType(cx, type); } } } else if (!JSID_IS_EMPTY(id)) { RootedId rootedId(cx, id); Shape *shape = singleton->nativeLookup(cx, rootedId); if (shape) UpdatePropertyType(cx, &base->types, rSingleton, shape, false); } if (singleton->watched()) { /* * Mark the property as configured, to inhibit optimizations on it * and avoid bypassing the watchpoint handler. */ - base->types.setOwnProperty(cx, true); + base->types.setConfiguredProperty(cx); } } *pprop = base; InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s", InferSpewColor(&base->types), &base->types, InferSpewColorReset(), TypeObjectString(this), TypeIdString(id)); @@ -2372,18 +2280,19 @@ TypeObject::addDefiniteProperties(Exclus /* Mark all properties of obj as definite properties of this type. */ AutoEnterAnalysis enter(cx); RootedShape shape(cx, obj->lastProperty()); while (!shape->isEmptyShape()) { jsid id = IdToTypeId(shape->propid()); if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) && - shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) { - TypeSet *types = getProperty(cx, id, true); + shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) + { + TypeSet *types = getProperty(cx, id); if (!types) return false; types->setDefinite(shape->slot()); } shape = shape->previous(); } return true; @@ -2419,17 +2328,17 @@ TypeObject::matchDefiniteProperties(Hand inline void InlineAddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) { JS_ASSERT(id == IdToTypeId(id)); AutoEnterAnalysis enter(cx); - TypeSet *types = obj->getProperty(cx, id, true); + TypeSet *types = obj->getProperty(cx, id); if (!types || types->hasType(type)) return; InferSpew(ISpewOps, "externalType: property %s %s: %s", TypeObjectString(obj), TypeIdString(id), TypeString(type)); types->addType(cx, type); } @@ -2469,19 +2378,19 @@ TypeObject::addPropertyType(ExclusiveCon void TypeObject::markPropertyConfigured(ExclusiveContext *cx, jsid id) { AutoEnterAnalysis enter(cx); id = IdToTypeId(id); - TypeSet *types = getProperty(cx, id, true); + TypeSet *types = getProperty(cx, id); if (types) - types->setOwnProperty(cx, true); + types->setConfiguredProperty(cx); } void TypeObject::markStateChange(ExclusiveContext *cxArg) { if (unknownProperties()) return; @@ -2545,17 +2454,17 @@ TypeObject::markUnknown(ExclusiveContext * any properties accessed already accounts for possible values read from them. */ unsigned count = getPropertyCount(); for (unsigned i = 0; i < count; i++) { Property *prop = getProperty(i); if (prop) { prop->types.addType(cx, Type::UnknownType()); - prop->types.setOwnProperty(cx, true); + prop->types.setConfiguredProperty(cx); } } } void TypeObject::clearAddendum(ExclusiveContext *cx) { JS_ASSERT(!(flags & OBJECT_FLAG_ADDENDUM_CLEARED)); @@ -2606,17 +2515,17 @@ TypeObject::clearNewScriptAddendum(Exclu * been deleted/reconfigured, which will have the same effect on JITs * wanting to use the definite bits to optimize property accesses. */ for (unsigned i = 0; i < getPropertyCount(); i++) { Property *prop = getProperty(i); if (!prop) continue; if (prop->types.definiteProperty()) - prop->types.setOwnProperty(cx, true); + prop->types.setConfiguredProperty(cx); } /* * If we cleared the new script while in the middle of initializing an * object, it will still have the new script's shape and reflect the no * longer correct state of the object once its initialization is completed. * We can't really detect the possibility of this statically, but the new * script keeps track of where each property is initialized so we can walk @@ -2769,17 +2678,17 @@ class TypeConstraintClearDefiniteGetterS if (!object->hasNewScript()) return; /* * Clear out the newScript shape and definite property information from * an object if the source type set could be a setter or could be * non-writable, both of which are indicated by the source type set * being marked as configured. */ - if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->ownProperty(true)) + if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty()) object->clearAddendum(cx); } void newType(JSContext *cx, TypeSet *source, Type type) {} }; bool types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id) @@ -2789,18 +2698,18 @@ types::AddClearDefiniteGetterSetterForPr * a permanent property in any transitive prototype, the definite * properties get cleared from the type. */ RootedObject parent(cx, type->proto); while (parent) { TypeObject *parentObject = parent->getType(cx); if (!parentObject || parentObject->unknownProperties()) return false; - HeapTypeSet *parentTypes = parentObject->getProperty(cx, id, false); - if (!parentTypes || parentTypes->ownProperty(true)) + HeapTypeSet *parentTypes = parentObject->getProperty(cx, id); + if (!parentTypes || parentTypes->configuredProperty()) return false; parentTypes->add(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type)); parent = parent->getProto(); } return true; } /* @@ -3221,34 +3130,16 @@ JSScript::makeTypes(JSContext *cx) new(types) TypeScript(); TypeSet *typeArray = types->typeArray(); for (unsigned i = 0; i < count; i++) new (&typeArray[i]) StackTypeSet(); - if (isCallsiteClone) { - /* - * For callsite clones, flow the types from the specific clone back to - * the original function. - */ - JS_ASSERT(function()); - JS_ASSERT(originalFunction()); - JS_ASSERT(function()->nargs == originalFunction()->nargs); - - JSScript *original = originalFunction()->nonLazyScript(); - if (!original->ensureHasTypes(cx)) - return false; - - TypeScript::ThisTypes(this)->addSubset(cx, TypeScript::ThisTypes(original)); - for (unsigned i = 0; i < function()->nargs; i++) - TypeScript::ArgTypes(this, i)->addSubset(cx, TypeScript::ArgTypes(original, i)); - } - #ifdef DEBUG for (unsigned i = 0; i < nTypeSets; i++) InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u", InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), i, id()); TypeSet *thisTypes = TypeScript::ThisTypes(this); InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u", InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(), @@ -3401,33 +3292,16 @@ JSObject::splicePrototype(JSContext *cx, return false; self->type_ = type; return true; } type->clasp = clasp; type->proto = proto.raw(); - AutoEnterAnalysis enter(cx); - - if (protoType && protoType->unknownProperties() && !type->unknownProperties()) { - type->markUnknown(cx); - return true; - } - - if (!type->unknownProperties()) { - /* Update properties on this type with any shared with the prototype. */ - unsigned count = type->getPropertyCount(); - for (unsigned i = 0; i < count; i++) { - Property *prop = type->getProperty(i); - if (prop && prop->types.hasPropagatedProperty()) - type->getFromPrototypes(cx, prop->id, &prop->types, true); - } - } - return true; } /* static */ TypeObject * JSObject::makeLazyType(JSContext *cx, HandleObject obj) { JS_ASSERT(obj->hasLazyType()); JS_ASSERT(cx->compartment() == obj->compartment()); @@ -3744,22 +3618,18 @@ TypeSet::sweep(Zone *zone) } else if (objectCount == 1) { TypeObjectKey *object = (TypeObjectKey *) objectSet; if (IsAboutToBeFinalized(object)) { objectSet = NULL; setBaseObjectCount(0); } } - /* - * All constraints are wiped out on each GC, including those propagating - * into this type set from prototype properties. - */ + /* All constraints are wiped out on each GC. */ constraintList = NULL; - flags &= ~TYPE_FLAG_PROPAGATED_PROPERTY; } inline void TypeObject::clearProperties() { setBasePropertyCount(0); propertySet = NULL; } @@ -3791,30 +3661,28 @@ TypeObject::sweep(FreeOp *fop) fop->free_(addendum); return; } js::LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc; /* * Properties were allocated from the old arena, and need to be copied over - * to the new one. Don't hang onto properties without the OWN_PROPERTY - * flag; these were never directly assigned, and get any possible values - * from the object's prototype. + * to the new one. */ unsigned propertyCount = basePropertyCount(); if (propertyCount >= 2) { unsigned oldCapacity = HashSetCapacity(propertyCount); Property **oldArray = propertySet; clearProperties(); propertyCount = 0; for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; - if (prop && prop->types.ownProperty(false)) { + if (prop) { Property *newProp = typeLifoAlloc.new_<Property>(*prop); if (newProp) { Property **pentry = HashSetInsert<jsid,Property,Property> (typeLifoAlloc, propertySet, propertyCount, prop->id); if (pentry) { *pentry = newProp; newProp->types.sweep(zone()); @@ -3824,27 +3692,22 @@ TypeObject::sweep(FreeOp *fop) } else { zone()->types.setPendingNukeTypes(); } } } setBasePropertyCount(propertyCount); } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; - if (prop->types.ownProperty(false)) { - Property *newProp = typeLifoAlloc.new_<Property>(*prop); - if (newProp) { - propertySet = (Property **) newProp; - newProp->types.sweep(zone()); - } else { - zone()->types.setPendingNukeTypes(); - } + Property *newProp = typeLifoAlloc.new_<Property>(*prop); + if (newProp) { + propertySet = (Property **) newProp; + newProp->types.sweep(zone()); } else { - propertySet = NULL; - setBasePropertyCount(0); + zone()->types.setPendingNukeTypes(); } } if (basePropertyCount() <= SET_ARRAY_SIZE) { for (unsigned i = 0; i < basePropertyCount(); i++) JS_ASSERT(propertySet[i]); }
--- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -303,37 +303,28 @@ enum { /* * Flags describing the kind of type set this is. * * - StackTypeSet are associated with TypeScripts, for arguments and values * observed at property reads. These are implicitly frozen on compilation * and do not have constraints attached to them. * * - HeapTypeSet are associated with the properties of TypeObjects. These - * may have constraints added to them to propagate types around or to - * trigger invalidation of compiled code. + * may have constraints added to them to trigger invalidation of compiled + * code. * * - TemporaryTypeSet are created during compilation and do not outlive * that compilation. */ TYPE_FLAG_STACK_SET = 0x00020000, TYPE_FLAG_HEAP_SET = 0x00040000, /* Additional flags for HeapTypeSet sets. */ /* - * Whether there are subset constraints propagating the possible types - * for this property inherited from the object's prototypes. Reset on GC. - */ - TYPE_FLAG_PROPAGATED_PROPERTY = 0x00080000, - - /* Whether this property has ever been directly written. */ - TYPE_FLAG_OWN_PROPERTY = 0x00100000, - - /* * Whether the property has ever been deleted or reconfigured to behave * differently from a normal native property (e.g. made non-writable or * given a scripted getter or setter). */ TYPE_FLAG_CONFIGURED_PROPERTY = 0x00200000, /* * Whether the property is definitely in a particular inline slot on all @@ -467,64 +458,59 @@ class TypeSet bool empty() const { return !baseFlags() && !baseObjectCount(); } bool noConstraints() const { return constraintList == NULL; } bool hasAnyFlag(TypeFlags flags) const { JS_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags); return !!(baseFlags() & flags); } - bool ownProperty(bool configurable) const { - return flags & (configurable ? TYPE_FLAG_CONFIGURED_PROPERTY : TYPE_FLAG_OWN_PROPERTY); + bool configuredProperty() const { + return flags & TYPE_FLAG_CONFIGURED_PROPERTY; } bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_PROPERTY; } unsigned definiteSlot() const { JS_ASSERT(definiteProperty()); return flags >> TYPE_FLAG_DEFINITE_SHIFT; } /* Join two type sets into a new set. The result should not be modified further. */ static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc); /* * Add a type to this set, calling any constraint handlers if this is a new * possible type. */ inline void addType(ExclusiveContext *cx, Type type); - /* Mark this type set as representing an own property or configured property. */ - inline void setOwnProperty(ExclusiveContext *cx, bool configured); + /* Mark this type set as representing a configured property. */ + inline void setConfiguredProperty(ExclusiveContext *cx); /* * Iterate through the objects in this set. getObjectCount overapproximates * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject * may return NULL. */ inline unsigned getObjectCount() const; inline TypeObjectKey *getObject(unsigned i) const; inline JSObject *getSingleObject(unsigned i) const; inline TypeObject *getTypeObject(unsigned i) const; inline bool getTypeOrSingleObject(JSContext *cx, unsigned i, TypeObject **obj) const; /* The Class of an object in this set. */ inline const Class *getObjectClass(unsigned i) const; - void setOwnProperty(bool configurable) { - flags |= TYPE_FLAG_OWN_PROPERTY; - if (configurable) - flags |= TYPE_FLAG_CONFIGURED_PROPERTY; + void setConfiguredProperty() { + flags |= TYPE_FLAG_CONFIGURED_PROPERTY; } void setDefinite(unsigned slot) { JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)); flags |= TYPE_FLAG_DEFINITE_PROPERTY | (slot << TYPE_FLAG_DEFINITE_SHIFT); } - bool hasPropagatedProperty() { return !!(flags & TYPE_FLAG_PROPAGATED_PROPERTY); } - void setPropagatedProperty() { flags |= TYPE_FLAG_PROPAGATED_PROPERTY; } - bool isStackSet() { return flags & TYPE_FLAG_STACK_SET; } bool isHeapSet() { return flags & TYPE_FLAG_HEAP_SET; } /* @@ -556,29 +542,23 @@ class TypeSet inline void clearObjects(); }; class StackTypeSet : public TypeSet { public: StackTypeSet() { flags |= TYPE_FLAG_STACK_SET; } - - /* Propagate any types from this set into target. */ - void addSubset(JSContext *cx, StackTypeSet *target); }; class HeapTypeSet : public TypeSet { public: HeapTypeSet() { flags |= TYPE_FLAG_HEAP_SET; } - /* Propagate any types from this set into target. */ - void addSubset(JSContext *cx, HeapTypeSet *target); - /* Completely freeze the contents of this type set. */ void addFreeze(JSContext *cx); /* * Watch for a generic object state change on a type object. This currently * includes reallocations of slot pointers for global objects, and changes * to newScript data on types. */ @@ -589,17 +569,17 @@ class HeapTypeSet : public TypeSet /* * For type sets on a property, return true if the property has any 'own' * values assigned. If configurable is set, return 'true' if the property * has additionally been reconfigured as non-configurable, non-enumerable * or non-writable (this only applies to properties that have changed after * having been created, not to e.g. properties non-writable on creation). */ - bool isOwnProperty(JSContext *cx, TypeObject *object, bool configurable); + bool isConfiguredProperty(JSContext *cx, TypeObject *object); /* Get whether this type set is non-empty. */ bool knownNonEmpty(JSContext *cx); /* Get whether this type set is known to be a subset of other. */ bool knownSubset(JSContext *cx, HeapTypeSet *other); /* Get the single value which can appear in this type set, otherwise NULL. */ @@ -753,101 +733,16 @@ struct TypeResult TypeResult(uint32_t offset, Type type) : offset(offset), type(type), next(NULL) {} }; /* Is this a reasonable PC to be doing inlining on? */ inline bool isInlinableCall(jsbytecode *pc); -/* - * Type barriers overview. - * - * Type barriers are a technique for using dynamic type information to improve - * the inferred types within scripts. At certain opcodes --- those with the - * JOF_TYPESET format --- we will construct a type set storing the set of types - * which we have observed to be pushed at that opcode, and will only use those - * observed types when doing propagation downstream from the bytecode. For - * example, in the following script: - * - * function foo(x) { - * return x.f + 10; - * } - * - * Suppose we know the type of 'x' and that the type of its 'f' property is - * either an int or float. To account for all possible behaviors statically, - * we would mark the result of the 'x.f' access as an int or float, as well - * as the result of the addition and the return value of foo (and everywhere - * the result of 'foo' is used). When dealing with polymorphic code, this is - * undesirable behavior --- the type imprecision surrounding the polymorphism - * will tend to leak to many places in the program. - * - * Instead, we will keep track of the types that have been dynamically observed - * to have been produced by the 'x.f', and only use those observed types - * downstream from the access. If the 'x.f' has only ever produced integers, - * we will treat its result as an integer and mark the result of foo as an - * integer. - * - * The set of observed types will be a subset of the set of possible types, - * and if the two sets are different, a type barriers will be added at the - * bytecode which checks the dynamic result every time the bytecode executes - * and makes sure it is in the set of observed types. If it is not, that - * observed set is updated, and the new type information is automatically - * propagated along the already-generated type constraints to the places - * where the result of the bytecode is used. - * - * Observing new types at a bytecode removes type barriers at the bytecode - * (this removal happens lazily, see ScriptAnalysis::pruneTypeBarriers), and if - * all type barriers at a bytecode are removed --- the set of observed types - * grows to match the set of possible types --- then the result of the bytecode - * no longer needs to be dynamically checked (unless the set of possible types - * grows, triggering the generation of new type barriers). - * - * Barriers are only relevant for accesses on properties whose types inference - * actually tracks (see propertySet comment under TypeObject). Accesses on - * other properties may be able to produce additional unobserved types even - * without a barrier present, and can only be compiled to jitcode with special - * knowledge of the property in question (e.g. for lengths of arrays, or - * elements of typed arrays). - */ - -/* - * Barrier introduced at some bytecode. These are added when, during inference, - * we block a type from being propagated as would normally be done for a subset - * constraint. The propagation is technically possible, but we suspect it will - * not happen dynamically and this type needs to be watched for. These are only - * added at reads of properties and at scripted call sites. - */ -struct TypeBarrier -{ - /* Next barrier on the same bytecode. */ - TypeBarrier *next; - - /* Target type set into which propagation was blocked. */ - TypeSet *target; - - /* - * Type which was not added to the target. If target ends up containing the - * type somehow, this barrier can be removed. - */ - Type type; - - /* - * If specified, this barrier can be removed if object has a non-undefined - * value in property id. - */ - JSObject *singleton; - jsid singletonId; - - TypeBarrier(TypeSet *target, Type type, JSObject *singleton, jsid singletonId) - : next(NULL), target(target), type(type), - singleton(singleton), singletonId(singletonId) - {} -}; - /* Type information about a property. */ struct Property { /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ HeapId id; /* Possible types for this property, including types inherited from prototypes. */ HeapTypeSet types; @@ -1102,21 +997,19 @@ struct TypeObject : gc::BarrieredCell<Ty bool unknownProperties() { JS_ASSERT_IF(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES, hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES); } /* * Get or create a property of this object. Only call this for properties which - * a script accesses explicitly. 'assign' indicates whether this is for an - * assignment, and the own types of the property will be used instead of - * aggregate types. + * a script accesses explicitly. */ - inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id, bool own); + inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id); /* Get a property only if it already exists. */ inline HeapTypeSet *maybeGetProperty(ExclusiveContext *cx, jsid id); inline unsigned getPropertyCount(); inline Property *getProperty(unsigned i); /* Get the typed array element type if clasp is a typed array. */ @@ -1173,17 +1066,16 @@ struct TypeObject : gc::BarrieredCell<Ty void addPropertyType(ExclusiveContext *cx, const char *name, const Value &value); void markPropertyConfigured(ExclusiveContext *cx, jsid id); void markStateChange(ExclusiveContext *cx); void setFlags(ExclusiveContext *cx, TypeObjectFlags flags); void markUnknown(ExclusiveContext *cx); void clearAddendum(ExclusiveContext *cx); void clearNewScriptAddendum(ExclusiveContext *cx); void clearTypedObjectAddendum(ExclusiveContext *cx); - void getFromPrototypes(JSContext *cx, jsid id, HeapTypeSet *types, bool force = false); void print(); inline void clearProperties(); inline void sweep(FreeOp *fop); size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
--- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -547,17 +547,17 @@ EnsureTrackPropertyTypes(JSContext *cx, if (!cx->typeInferenceEnabled() || obj->type()->unknownProperties()) return; id = IdToTypeId(id); if (obj->hasSingletonType()) { AutoEnterAnalysis enter(cx); - obj->type()->getProperty(cx, id, true); + obj->type()->getProperty(cx, id); } JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); } /* Add a possible type for a property of obj. */ inline void AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type) @@ -1332,24 +1332,22 @@ TypeSet::addType(ExclusiveContext *cxArg } cx->compartment()->types.resolvePending(cx); } else { JS_ASSERT(!constraintList); } } inline void -TypeSet::setOwnProperty(ExclusiveContext *cxArg, bool configured) +TypeSet::setConfiguredProperty(ExclusiveContext *cxArg) { - TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0); - - if ((flags & nflags) == nflags) + if (flags & TYPE_FLAG_CONFIGURED_PROPERTY) return; - flags |= nflags; + flags |= TYPE_FLAG_CONFIGURED_PROPERTY; /* Propagate the change to all constraints. */ if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = constraintList; while (constraint) { constraint->newPropertyState(cx, this); constraint = constraint->next; } @@ -1459,17 +1457,17 @@ inline void TypeObject::setBasePropertyCount(uint32_t count) { JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); } inline HeapTypeSet * -TypeObject::getProperty(ExclusiveContext *cx, jsid id, bool own) +TypeObject::getProperty(ExclusiveContext *cx, jsid id) { JS_ASSERT(cx->compartment()->activeAnalysis); JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); JS_ASSERT(!unknownProperties()); uint32_t propertyCount = basePropertyCount(); @@ -1499,21 +1497,17 @@ TypeObject::getProperty(ExclusiveContext if (Property *prop = getProperty(i)) return &prop->types; } MOZ_ASSUME_UNREACHABLE("Missing property"); } } - HeapTypeSet *types = &(*pprop)->types; - if (own) - types->setOwnProperty(cx, false); - - return types; + return &(*pprop)->types; } inline HeapTypeSet * TypeObject::maybeGetProperty(ExclusiveContext *cx, jsid id) { JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); JS_ASSERT(!unknownProperties());
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4023,17 +4023,19 @@ NativeGetInline(JSContext *cx, typename MaybeRooted<Shape*, allowGC>::HandleType shape, typename MaybeRooted<Value, allowGC>::MutableHandleType vp) { JS_ASSERT(pobj->isNative()); if (shape->hasSlot()) { vp.set(pobj->nativeGetSlot(shape->slot())); JS_ASSERT(!vp.isMagic()); - JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetter(), + JS_ASSERT_IF(!pobj->hasSingletonType() && + !pobj->template is<ScopeObject>() && + shape->hasDefaultGetter(), js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp)); } else { vp.setUndefined(); } if (shape->hasDefaultGetter()) return true; {