Bug 1116017 - Don't scan all type sets in compartments on type mutations, r=jandem.

This commit is contained in:
Brian Hackett 2015-01-14 08:00:28 -07:00
parent 06cd80b23d
commit 493792ecf6
10 changed files with 229 additions and 187 deletions

View File

@ -0,0 +1,18 @@
// jitcode which uses objects of one type might not be invalidated when that
// object changes type, if the object isn't accessed in any way.
function foo(x) {
return x.f;
}
function bar() {
with({}){}
var a = {};
var b = { f: a };
foo(b);
a.__proto__ = null;
foo(b);
}
bar();

View File

@ -3610,6 +3610,14 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi
masm.jump(&ok);
masm.bind(&miss);
// Type set guards might miss when an object's type changes and its
// properties become unknown, so check for this case.
masm.loadPtr(Address(output, JSObject::offsetOfType()), temp);
masm.branchTestPtr(Assembler::NonZero,
Address(temp, types::TypeObject::offsetOfFlags()),
Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok);
masm.assumeUnreachable("MIR instruction returned object with unexpected type");
masm.bind(&ok);
@ -3679,6 +3687,18 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
masm.jump(&ok);
masm.bind(&miss);
// Type set guards might miss when an object's type changes and its
// properties become unknown, so check for this case.
Label realMiss;
masm.branchTestObject(Assembler::NotEqual, output, &realMiss);
Register payload = masm.extractObject(output, temp1);
masm.loadPtr(Address(payload, JSObject::offsetOfType()), temp1);
masm.branchTestPtr(Assembler::NonZero,
Address(temp1, types::TypeObject::offsetOfFlags()),
Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok);
masm.bind(&realMiss);
masm.assumeUnreachable("MIR instruction returned value with unexpected type");
masm.bind(&ok);

View File

@ -295,9 +295,10 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr
JSObject *obj = calleeTypes->getSingleObject(i);
if (!obj) {
types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
MOZ_ASSERT(typeObj);
obj = typeObj->maybeInterpretedFunction();
if (!typeObj)
continue;
obj = typeObj->maybeInterpretedFunction();
if (!obj) {
targets.clear();
return true;
@ -3367,7 +3368,7 @@ IonBuilder::improveTypesAtCompare(MCompare *ins, bool trueBranch, MTest *test)
if (altersUndefined) {
flags |= types::TYPE_FLAG_UNDEFINED;
// If TypeSet emulates undefined, then we cannot filter the objects.
if (subject->resultTypeSet()->maybeEmulatesUndefined())
if (subject->resultTypeSet()->maybeEmulatesUndefined(constraints()))
flags |= types::TYPE_FLAG_ANYOBJECT;
}
@ -3491,14 +3492,14 @@ IonBuilder::improveTypesAtTest(MDefinition *ins, bool trueBranch, MTest *test)
// If the typeset does emulate undefined, then we cannot filter out
// objects.
if (oldType->maybeEmulatesUndefined())
if (oldType->maybeEmulatesUndefined(constraints()))
flags |= types::TYPE_FLAG_ANYOBJECT;
// Only intersect the typesets if it will generate a more narrow
// typeset. The first part takes care of primitives and AnyObject,
// while the second line specific (type)objects.
if (!oldType->hasAnyFlag(~flags & types::TYPE_FLAG_BASE_MASK) &&
(oldType->maybeEmulatesUndefined() || !oldType->maybeObject()))
(oldType->maybeEmulatesUndefined(constraints()) || !oldType->maybeObject()))
{
return true;
}
@ -3740,7 +3741,7 @@ IonBuilder::processCondSwitchCase(CFGState &state)
MDefinition *caseOperand = current->pop();
MDefinition *switchOperand = current->peek(-1);
MCompare *cmpResult = MCompare::New(alloc(), switchOperand, caseOperand, JSOP_STRICTEQ);
cmpResult->infer(inspector, pc);
cmpResult->infer(constraints(), inspector, pc);
MOZ_ASSERT(!cmpResult->isEffectful());
current->add(cmpResult);
current->end(newTest(cmpResult, bodyBlock, caseBlock));
@ -5748,6 +5749,9 @@ IonBuilder::testShouldDOMCall(types::TypeSet *inTypes,
if (!curType)
continue;
if (!curType->hasStableClassAndProto(constraints()))
return false;
if (!instanceChecker(curType->clasp(), jinfo->protoID, jinfo->depth))
return false;
}
@ -5822,7 +5826,7 @@ IonBuilder::makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtC
types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
if (thisTypes &&
thisTypes->getKnownMIRType() == MIRType_Object &&
thisTypes->isDOMClass() &&
thisTypes->isDOMClass(constraints()) &&
testShouldDOMCall(thisTypes, target, JSJitInfo::Method))
{
isDOMCall = true;
@ -6023,7 +6027,7 @@ IonBuilder::jsop_compare(JSOp op)
current->add(ins);
current->push(ins);
ins->infer(inspector, pc);
ins->infer(constraints(), inspector, pc);
if (ins->isEffectful() && !resumeAfter(ins))
return false;
@ -6586,7 +6590,7 @@ MTest *
IonBuilder::newTest(MDefinition *ins, MBasicBlock *ifTrue, MBasicBlock *ifFalse)
{
MTest *test = MTest::New(alloc(), ins, ifTrue, ifFalse);
test->cacheOperandMightEmulateUndefined();
test->cacheOperandMightEmulateUndefined(constraints());
return test;
}
@ -7643,7 +7647,7 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
// immutable).
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
const Class *expectedClass = nullptr;
if (const Class *objClass = objTypes ? objTypes->getKnownClass() : nullptr) {
if (const Class *objClass = objTypes ? objTypes->getKnownClass(constraints()) : nullptr) {
MOZ_ASSERT(IsTypedObjectClass(objClass));
expectedClass = GetOutlineTypedObjectClass(IsOpaqueTypedObjectClass(objClass));
}
@ -7653,8 +7657,8 @@ IonBuilder::pushDerivedTypedObject(bool *emitted,
// Determine (if possible) the class/proto that the observed type set
// describes.
types::TemporaryTypeSet *observedTypes = bytecodeTypes(pc);
const Class *observedClass = observedTypes->getKnownClass();
JSObject *observedProto = observedTypes->getCommonPrototype();
const Class *observedClass = observedTypes->getKnownClass(constraints());
JSObject *observedProto = observedTypes->getCommonPrototype(constraints());
// If expectedClass/expectedProto are both non-null (and hence
// known), we can predict precisely what TI type object
@ -7692,7 +7696,7 @@ IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index)
{
MOZ_ASSERT(*emitted == false);
if (!ElementAccessIsDenseNative(obj, index))
if (!ElementAccessIsDenseNative(constraints(), obj, index))
return true;
// Don't generate a fast path if there have been bounds check failures
@ -7719,7 +7723,7 @@ IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *
MOZ_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsAnyTypedArray(obj, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(constraints(), obj, index, &arrayType))
return true;
if (!LIRGenerator::allowStaticTypedArrayAccesses())
@ -7785,7 +7789,7 @@ IonBuilder::getElemTryTypedArray(bool *emitted, MDefinition *obj, MDefinition *i
MOZ_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsAnyTypedArray(obj, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(constraints(), obj, index, &arrayType))
return true;
// Emit typed getelem variant.
@ -8406,7 +8410,7 @@ IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object,
MOZ_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsAnyTypedArray(object, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(constraints(), object, index, &arrayType))
return true;
if (!LIRGenerator::allowStaticTypedArrayAccesses())
@ -8466,7 +8470,7 @@ IonBuilder::setElemTryTypedArray(bool *emitted, MDefinition *object,
MOZ_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsAnyTypedArray(object, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(constraints(), object, index, &arrayType))
return true;
// Emit typed setelem variant.
@ -8483,7 +8487,7 @@ IonBuilder::setElemTryDense(bool *emitted, MDefinition *object,
{
MOZ_ASSERT(*emitted == false);
if (!ElementAccessIsDenseNative(object, index))
if (!ElementAccessIsDenseNative(constraints(), object, index))
return true;
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
&object, nullptr, &value, /* canModify = */ true))
@ -8803,7 +8807,7 @@ IonBuilder::jsop_length_fastPath()
// Compute the length for array objects.
if (objTypes &&
objTypes->getKnownClass() == &ArrayObject::class_ &&
objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
!objTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_LENGTH_OVERFLOW))
{
current->pop();
@ -9002,7 +9006,7 @@ IonBuilder::jsop_not()
MNot *ins = MNot::New(alloc(), value);
current->add(ins);
current->push(ins);
ins->cacheOperandMightEmulateUndefined();
ins->cacheOperandMightEmulateUndefined(constraints());
return true;
}
@ -9726,7 +9730,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
if (!canUseCommonGetter)
return true;
bool isDOM = objTypes->isDOMClass();
bool isDOM = objTypes->isDOMClass(constraints());
if (isDOM && testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter)) {
const JSJitInfo *jitinfo = commonGetter->jitInfo();
@ -10169,7 +10173,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
if (!canUseCommonSetter)
return true;
bool isDOM = objTypes->isDOMClass();
bool isDOM = objTypes->isDOMClass(constraints());
// Emit common setter.
@ -10843,7 +10847,7 @@ IonBuilder::jsop_typeof()
MDefinition *input = current->pop();
MTypeOf *ins = MTypeOf::New(alloc(), input, input->type());
ins->cacheInputMaybeCallableOrEmulatesUndefined();
ins->cacheInputMaybeCallableOrEmulatesUndefined(constraints());
current->add(ins);
current->push(ins);
@ -11093,7 +11097,7 @@ IonBuilder::jsop_in()
MDefinition *obj = current->peek(-1);
MDefinition *id = current->peek(-2);
if (ElementAccessIsDenseNative(obj, id) &&
if (ElementAccessIsDenseNative(constraints(), obj, id) &&
!ElementAccessHasExtraIndexedProperty(constraints(), obj))
{
return jsop_in_dense();
@ -11151,16 +11155,13 @@ HasOnProtoChain(types::CompilerConstraintList *constraints, types::TypeObjectKey
MOZ_ASSERT(protoObject);
while (true) {
if (object->unknownProperties() ||
if (!object->hasStableClassAndProto(constraints) ||
!object->clasp()->isNative() ||
!object->hasTenuredProto())
{
return false;
}
// Guard against mutating __proto__.
object->hasFlags(constraints, types::OBJECT_FLAG_UNKNOWN_PROPERTIES);
JSObject *proto = object->proto().toObjectOrNull();
if (!proto) {
*hasOnProto = false;
@ -11396,7 +11397,7 @@ IonBuilder::typedObjectPrediction(types::TemporaryTypeSet *types)
TypedObjectPrediction out;
for (uint32_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObject *type = types->getTypeObject(i);
if (!type || type->unknownProperties())
if (!type || !types::TypeObjectKey::get(type)->hasStableClassAndProto(constraints()))
return TypedObjectPrediction();
if (!IsTypedObjectClass(type->clasp()))
@ -11478,7 +11479,7 @@ IonBuilder::loadTypedObjectElements(MDefinition *typedObj,
setForceAbort();
types::TemporaryTypeSet *ownerTypes = owner->resultTypeSet();
const Class *clasp = ownerTypes ? ownerTypes->getKnownClass() : nullptr;
const Class *clasp = ownerTypes ? ownerTypes->getKnownClass(constraints()) : nullptr;
if (clasp && IsInlineTypedObjectClass(clasp)) {
// Perform the load directly from the owner pointer.
if (!ownerByteOffset.add(InlineTypedObject::offsetOfDataStart()))

View File

@ -264,7 +264,7 @@ IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target)
if (thisTypes) {
Scalar::Type type;
type = thisTypes->getTypedArrayType();
type = thisTypes->getTypedArrayType(constraints());
if (type != Scalar::MaxTypedArrayViewType &&
TypedArrayObject::isOriginalLengthGetter(native))
{
@ -273,7 +273,7 @@ IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target)
return InliningStatus_Inlined;
}
type = thisTypes->getSharedTypedArrayType();
type = thisTypes->getSharedTypedArrayType(constraints());
if (type != Scalar::MaxTypedArrayViewType &&
SharedTypedArrayObject::isOriginalLengthGetter(type, native))
{
@ -487,7 +487,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
MDefinition *obj = callInfo.thisArg();
types::TemporaryTypeSet *thisTypes = obj->resultTypeSet();
if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags))
return InliningStatus_NotInlined;
@ -602,7 +602,7 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo)
return InliningStatus_NotInlined;
types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
types::OBJECT_FLAG_LENGTH_OVERFLOW))
@ -663,7 +663,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
if (!thisTypes || !argTypes)
return InliningStatus_NotInlined;
if (thisTypes->getKnownClass() != &ArrayObject::class_)
if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
types::OBJECT_FLAG_LENGTH_OVERFLOW))
@ -671,7 +671,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo)
return InliningStatus_NotInlined;
}
if (argTypes->getKnownClass() != &ArrayObject::class_)
if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_)
return InliningStatus_NotInlined;
if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
types::OBJECT_FLAG_LENGTH_OVERFLOW))
@ -1468,7 +1468,7 @@ IonBuilder::inlineRegExpExec(CallInfo &callInfo)
return InliningStatus_NotInlined;
types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr;
const Class *clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
if (clasp != &RegExpObject::class_)
return InliningStatus_NotInlined;
@ -1507,7 +1507,7 @@ IonBuilder::inlineRegExpTest(CallInfo &callInfo)
if (callInfo.thisArg()->type() != MIRType_Object)
return InliningStatus_NotInlined;
types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr;
const Class *clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
if (clasp != &RegExpObject::class_)
return InliningStatus_NotInlined;
if (callInfo.getArg(0)->mightBeType(MIRType_Object))
@ -1544,7 +1544,7 @@ IonBuilder::inlineStrReplace(CallInfo &callInfo)
// Arg 0: RegExp.
types::TemporaryTypeSet *arg0Type = callInfo.getArg(0)->resultTypeSet();
const Class *clasp = arg0Type ? arg0Type->getKnownClass() : nullptr;
const Class *clasp = arg0Type ? arg0Type->getKnownClass(constraints()) : nullptr;
if (clasp != &RegExpObject::class_ && callInfo.getArg(0)->type() != MIRType_String)
return InliningStatus_NotInlined;
@ -1625,7 +1625,7 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
MDefinition *id = callInfo.getArg(idxi);
MDefinition *elem = callInfo.getArg(elemi);
bool isDenseNative = ElementAccessIsDenseNative(obj, id);
bool isDenseNative = ElementAccessIsDenseNative(constraints(), obj, id);
bool writeNeedsBarrier = false;
if (isDenseNative) {
@ -1638,7 +1638,7 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
// barriers and on typed arrays and on typed object arrays.
Scalar::Type arrayType;
if ((!isDenseNative || writeNeedsBarrier) &&
!ElementAccessIsAnyTypedArray(obj, id, &arrayType) &&
!ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType) &&
!elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType))
{
return InliningStatus_NotInlined;
@ -1660,14 +1660,14 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
MDefinition *obj = callInfo.getArg(arri);
MDefinition *id = callInfo.getArg(idxi);
if (ElementAccessIsDenseNative(obj, id)) {
if (ElementAccessIsDenseNative(constraints(), obj, id)) {
if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
return InliningStatus_Error;
continue;
}
Scalar::Type arrayType;
if (ElementAccessIsAnyTypedArray(obj, id, &arrayType)) {
if (ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType)) {
if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
return InliningStatus_Error;
continue;
@ -1782,7 +1782,7 @@ IonBuilder::inlineHasClass(CallInfo &callInfo,
return InliningStatus_NotInlined;
types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
const Class *knownClass = types ? types->getKnownClass() : nullptr;
const Class *knownClass = types ? types->getKnownClass(constraints()) : nullptr;
if (knownClass) {
pushConstant(BooleanValue(knownClass == clasp1 ||
knownClass == clasp2 ||
@ -1808,10 +1808,10 @@ IonBuilder::inlineHasClass(CallInfo &callInfo,
// Convert to bool with the '!!' idiom
MNot *resultInverted = MNot::New(alloc(), last);
resultInverted->cacheOperandMightEmulateUndefined();
resultInverted->cacheOperandMightEmulateUndefined(constraints());
current->add(resultInverted);
MNot *result = MNot::New(alloc(), resultInverted);
result->cacheOperandMightEmulateUndefined();
result->cacheOperandMightEmulateUndefined(constraints());
current->add(result);
current->push(result);
}
@ -1839,7 +1839,7 @@ IonBuilder::inlineIsTypedArray(CallInfo &callInfo)
return InliningStatus_NotInlined;
bool result = false;
switch (types->forAllClasses(IsTypedArrayClass)) {
switch (types->forAllClasses(constraints(), IsTypedArrayClass)) {
case types::TemporaryTypeSet::ForAllResult::ALL_FALSE:
case types::TemporaryTypeSet::ForAllResult::EMPTY:
result = false;
@ -1897,7 +1897,7 @@ IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo)
return InliningStatus_NotInlined;
bool result = false;
switch (types->forAllClasses(IsTypeDescrClass)) {
switch (types->forAllClasses(constraints(), IsTypeDescrClass)) {
case types::TemporaryTypeSet::ForAllResult::ALL_FALSE:
case types::TemporaryTypeSet::ForAllResult::EMPTY:
result = false;
@ -1937,7 +1937,7 @@ IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo)
types::TemporaryTypeSet *types = typedObj->resultTypeSet();
if (typedObj->type() != MIRType_Object || !types)
return InliningStatus_NotInlined;
switch (types->forAllClasses(IsTypedObjectClass)) {
switch (types->forAllClasses(constraints(), IsTypedObjectClass)) {
case types::TemporaryTypeSet::ForAllResult::ALL_FALSE:
case types::TemporaryTypeSet::ForAllResult::EMPTY:
case types::TemporaryTypeSet::ForAllResult::MIXED:
@ -2047,7 +2047,7 @@ IonBuilder::inlineIsCallable(CallInfo &callInfo)
isCallableConstant = false;
} else {
types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
const Class *clasp = types ? types->getKnownClass() : nullptr;
const Class *clasp = types ? types->getKnownClass(constraints()) : nullptr;
if (clasp && !clasp->isProxy()) {
isCallableKnown = true;
isCallableConstant = clasp->nonProxyCallable();
@ -2432,7 +2432,7 @@ IonBuilder::atomicsMeetsPreconditions(CallInfo &callInfo, Scalar::Type *arrayTyp
if (!arg0Types)
return false;
*arrayType = arg0Types->getSharedTypedArrayType();
*arrayType = arg0Types->getSharedTypedArrayType(constraints());
switch (*arrayType) {
case Scalar::Int8:
case Scalar::Uint8:

View File

@ -333,7 +333,7 @@ MInstruction::clearResumePoint()
}
static bool
MaybeEmulatesUndefined(MDefinition *op)
MaybeEmulatesUndefined(types::CompilerConstraintList *constraints, MDefinition *op)
{
if (!op->mightBeType(MIRType_Object))
return false;
@ -342,11 +342,11 @@ MaybeEmulatesUndefined(MDefinition *op)
if (!types)
return true;
return types->maybeEmulatesUndefined();
return types->maybeEmulatesUndefined(constraints);
}
static bool
MaybeCallable(MDefinition *op)
MaybeCallable(types::CompilerConstraintList *constraints, MDefinition *op)
{
if (!op->mightBeType(MIRType_Object))
return false;
@ -355,7 +355,7 @@ MaybeCallable(MDefinition *op)
if (!types)
return true;
return types->maybeCallable();
return types->maybeCallable(constraints);
}
MTest *
@ -365,11 +365,11 @@ MTest::New(TempAllocator &alloc, MDefinition *ins, MBasicBlock *ifTrue, MBasicBl
}
void
MTest::cacheOperandMightEmulateUndefined()
MTest::cacheOperandMightEmulateUndefined(types::CompilerConstraintList *constraints)
{
MOZ_ASSERT(operandMightEmulateUndefined());
if (!MaybeEmulatesUndefined(getOperand(0)))
if (!MaybeEmulatesUndefined(constraints, getOperand(0)))
markOperandCantEmulateUndefined();
}
@ -644,7 +644,7 @@ jit::MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *
// (because mutating __proto__ will change an object's TypeObject).
MOZ_ASSERT(constraints);
types::TypeObjectKey *objType = types::TypeObjectKey::get(obj);
objType->hasFlags(constraints, types::OBJECT_FLAG_UNKNOWN_PROPERTIES);
objType->hasStableClassAndProto(constraints);
LifoAlloc *alloc = GetJitContext()->temp->lifoAlloc();
return alloc->new_<types::TemporaryTypeSet>(alloc, types::Type::ObjectType(obj));
@ -2307,7 +2307,8 @@ ObjectOrSimplePrimitive(MDefinition *op)
}
static bool
CanDoValueBitwiseCmp(MDefinition *lhs, MDefinition *rhs, bool looseEq)
CanDoValueBitwiseCmp(types::CompilerConstraintList *constraints,
MDefinition *lhs, MDefinition *rhs, bool looseEq)
{
// Only primitive (not double/string) or objects are supported.
// I.e. Undefined/Null/Boolean/Int32 and Object
@ -2315,7 +2316,7 @@ CanDoValueBitwiseCmp(MDefinition *lhs, MDefinition *rhs, bool looseEq)
return false;
// Objects that emulate undefined are not supported.
if (MaybeEmulatesUndefined(lhs) || MaybeEmulatesUndefined(rhs))
if (MaybeEmulatesUndefined(constraints, lhs) || MaybeEmulatesUndefined(constraints, rhs))
return false;
// In the loose comparison more values could be the same,
@ -2429,12 +2430,15 @@ MBinaryInstruction::tryUseUnsignedOperands()
}
void
MCompare::infer(BaselineInspector *inspector, jsbytecode *pc)
MCompare::infer(types::CompilerConstraintList *constraints, BaselineInspector *inspector, jsbytecode *pc)
{
MOZ_ASSERT(operandMightEmulateUndefined());
if (!MaybeEmulatesUndefined(getOperand(0)) && !MaybeEmulatesUndefined(getOperand(1)))
if (!MaybeEmulatesUndefined(constraints, getOperand(0)) &&
!MaybeEmulatesUndefined(constraints, getOperand(1)))
{
markNoOperandEmulatesUndefined();
}
MIRType lhs = getOperand(0)->type();
MIRType rhs = getOperand(1)->type();
@ -2538,7 +2542,7 @@ MCompare::infer(BaselineInspector *inspector, jsbytecode *pc)
}
// Determine if we can do the compare based on a quick value check.
if (!relationalEq && CanDoValueBitwiseCmp(getOperand(0), getOperand(1), looseEq)) {
if (!relationalEq && CanDoValueBitwiseCmp(constraints, getOperand(0), getOperand(1), looseEq)) {
compareType_ = Compare_Value;
return;
}
@ -2634,11 +2638,11 @@ MTypeOf::foldsTo(TempAllocator &alloc)
}
void
MTypeOf::cacheInputMaybeCallableOrEmulatesUndefined()
MTypeOf::cacheInputMaybeCallableOrEmulatesUndefined(types::CompilerConstraintList *constraints)
{
MOZ_ASSERT(inputMaybeCallableOrEmulatesUndefined());
if (!MaybeEmulatesUndefined(input()) && !MaybeCallable(input()))
if (!MaybeEmulatesUndefined(constraints, input()) && !MaybeCallable(constraints, input()))
markInputNotCallableOrEmulatesUndefined();
}
@ -3349,11 +3353,11 @@ MCompare::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *f
}
void
MNot::cacheOperandMightEmulateUndefined()
MNot::cacheOperandMightEmulateUndefined(types::CompilerConstraintList *constraints)
{
MOZ_ASSERT(operandMightEmulateUndefined());
if (!MaybeEmulatesUndefined(getOperand(0)))
if (!MaybeEmulatesUndefined(constraints, getOperand(0)))
markOperandCantEmulateUndefined();
}
@ -4057,7 +4061,8 @@ MArrayJoin::foldsTo(TempAllocator &alloc) {
}
bool
jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id)
jit::ElementAccessIsDenseNative(types::CompilerConstraintList *constraints,
MDefinition *obj, MDefinition *id)
{
if (obj->mightBeType(MIRType_String))
return false;
@ -4070,12 +4075,13 @@ jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id)
return false;
// Typed arrays are native classes but do not have dense elements.
const Class *clasp = types->getKnownClass();
const Class *clasp = types->getKnownClass(constraints);
return clasp && clasp->isNative() && !IsAnyTypedArrayClass(clasp);
}
bool
jit::ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
jit::ElementAccessIsAnyTypedArray(types::CompilerConstraintList *constraints,
MDefinition *obj, MDefinition *id,
Scalar::Type *arrayType)
{
if (obj->mightBeType(MIRType_String))
@ -4088,10 +4094,10 @@ jit::ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
if (!types)
return false;
*arrayType = types->getTypedArrayType();
*arrayType = types->getTypedArrayType(constraints);
if (*arrayType != Scalar::MaxTypedArrayViewType)
return true;
*arrayType = types->getSharedTypedArrayType();
*arrayType = types->getSharedTypedArrayType(constraints);
return *arrayType != Scalar::MaxTypedArrayViewType;
}
@ -4303,7 +4309,7 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *cons
if (!object)
continue;
while (true) {
if (!object->hasTenuredProto())
if (!object->hasStableClassAndProto(constraints) || !object->hasTenuredProto())
return BarrierKind::TypeSet;
if (!object->proto().isObject())
break;

View File

@ -2494,7 +2494,7 @@ class MTest
// background threads. So make callers explicitly call it if they want us
// to check whether the operand might do this. If this method is never
// called, we'll assume our operand can emulate undefined.
void cacheOperandMightEmulateUndefined();
void cacheOperandMightEmulateUndefined(types::CompilerConstraintList *constraints);
MDefinition *foldsTo(TempAllocator &alloc) MOZ_OVERRIDE;
void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined,
bool *filtersNull);
@ -3823,7 +3823,8 @@ class MCompare
void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined,
bool *filtersNull);
void infer(BaselineInspector *inspector, jsbytecode *pc);
void infer(types::CompilerConstraintList *constraints,
BaselineInspector *inspector, jsbytecode *pc);
CompareType compareType() const {
return compareType_;
}
@ -4856,7 +4857,7 @@ class MTypeOf
}
MDefinition *foldsTo(TempAllocator &alloc) MOZ_OVERRIDE;
void cacheInputMaybeCallableOrEmulatesUndefined();
void cacheInputMaybeCallableOrEmulatesUndefined(types::CompilerConstraintList *constraints);
bool inputMaybeCallableOrEmulatesUndefined() const {
return inputMaybeCallableOrEmulatesUndefined_;
@ -7658,7 +7659,7 @@ class MNot
INSTRUCTION_HEADER(Not)
void cacheOperandMightEmulateUndefined();
void cacheOperandMightEmulateUndefined(types::CompilerConstraintList *constraints);
MDefinition *foldsTo(TempAllocator &alloc) MOZ_OVERRIDE;
void markOperandCantEmulateUndefined() {
@ -12545,8 +12546,10 @@ MControlInstruction *MDefinition::toControlInstruction() {
// Helper functions used to decide how to build MIR.
bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
bool ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
bool ElementAccessIsDenseNative(types::CompilerConstraintList *constraints,
MDefinition *obj, MDefinition *id);
bool ElementAccessIsAnyTypedArray(types::CompilerConstraintList *constraints,
MDefinition *obj, MDefinition *id,
Scalar::Type *arrayType);
bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj);
bool ElementAccessMightBeCopyOnWrite(types::CompilerConstraintList *constraints, MDefinition *obj);

View File

@ -255,6 +255,15 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val
Type type = GetValueType(value);
// Type set guards might miss when an object's type changes and its
// properties become unknown.
if (value.isObject() &&
!value.toObject().hasLazyType() &&
value.toObject().type()->flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES)
{
return true;
}
AutoEnterAnalysis enter(cx);
/*
@ -542,8 +551,13 @@ TypeSet::addType(Type type, LifoAlloc *alloc)
JS_STATIC_ASSERT(TYPE_FLAG_DOMOBJECT_COUNT_LIMIT >= TYPE_FLAG_OBJECT_COUNT_LIMIT);
// Examining the entire type set is only required when we first hit
// the normal object limit.
if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT && !isDOMClass())
goto unknownObject;
if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT) {
for (unsigned i = 0; i < objectCount; i++) {
const Class *clasp = getObjectClass(i);
if (clasp && !clasp->isDOMClass())
goto unknownObject;
}
}
// Make sure the newly added object is also a DOM object.
if (!object->clasp()->isDOMClass())
@ -1596,6 +1610,12 @@ TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags fla
return false;
}
bool
TypeObjectKey::hasStableClassAndProto(CompilerConstraintList *constraints)
{
return !hasFlags(constraints, OBJECT_FLAG_UNKNOWN_PROPERTIES);
}
bool
TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
{
@ -2013,7 +2033,7 @@ TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
}
const Class *
TemporaryTypeSet::getKnownClass()
TemporaryTypeSet::getKnownClass(CompilerConstraintList *constraints)
{
if (unknownObject())
return nullptr;
@ -2026,16 +2046,28 @@ TemporaryTypeSet::getKnownClass()
if (!nclasp)
continue;
if (getObject(i)->unknownProperties())
return nullptr;
if (clasp && clasp != nclasp)
return nullptr;
clasp = nclasp;
}
if (clasp) {
for (unsigned i = 0; i < count; i++) {
TypeObjectKey *type = getObject(i);
if (type && !type->hasStableClassAndProto(constraints))
return nullptr;
}
}
return clasp;
}
TemporaryTypeSet::ForAllResult
TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp))
TemporaryTypeSet::forAllClasses(CompilerConstraintList *constraints,
bool (*func)(const Class* clasp))
{
if (unknownObject())
return ForAllResult::MIXED;
@ -2049,14 +2081,18 @@ TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp))
for (unsigned i = 0; i < count; i++) {
const Class *clasp = getObjectClass(i);
if (!clasp)
continue;
if (!getObject(i)->hasStableClassAndProto(constraints))
return ForAllResult::MIXED;
if (func(clasp)) {
true_results = true;
if (false_results) return ForAllResult::MIXED;
if (false_results)
return ForAllResult::MIXED;
}
else {
false_results = true;
if (true_results) return ForAllResult::MIXED;
if (true_results)
return ForAllResult::MIXED;
}
}
@ -2066,9 +2102,9 @@ TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp))
}
Scalar::Type
TemporaryTypeSet::getTypedArrayType()
TemporaryTypeSet::getTypedArrayType(CompilerConstraintList *constraints)
{
const Class *clasp = getKnownClass();
const Class *clasp = getKnownClass(constraints);
if (clasp && IsTypedArrayClass(clasp))
return (Scalar::Type) (clasp - &TypedArrayObject::classes[0]);
@ -2076,9 +2112,9 @@ TemporaryTypeSet::getTypedArrayType()
}
Scalar::Type
TemporaryTypeSet::getSharedTypedArrayType()
TemporaryTypeSet::getSharedTypedArrayType(CompilerConstraintList *constraints)
{
const Class *clasp = getKnownClass();
const Class *clasp = getKnownClass(constraints);
if (clasp && IsSharedTypedArrayClass(clasp))
return (Scalar::Type) (clasp - &SharedTypedArrayObject::classes[0]);
@ -2086,7 +2122,7 @@ TemporaryTypeSet::getSharedTypedArrayType()
}
bool
TypeSet::isDOMClass()
TemporaryTypeSet::isDOMClass(CompilerConstraintList *constraints)
{
if (unknownObject())
return false;
@ -2094,7 +2130,9 @@ TypeSet::isDOMClass()
unsigned count = getObjectCount();
for (unsigned i = 0; i < count; i++) {
const Class *clasp = getObjectClass(i);
if (clasp && !clasp->isDOMClass())
if (!clasp)
continue;
if (!clasp->isDOMClass() || !getObject(i)->hasStableClassAndProto(constraints))
return false;
}
@ -2102,7 +2140,7 @@ TypeSet::isDOMClass()
}
bool
TemporaryTypeSet::maybeCallable()
TemporaryTypeSet::maybeCallable(CompilerConstraintList *constraints)
{
if (!maybeObject())
return false;
@ -2113,7 +2151,11 @@ TemporaryTypeSet::maybeCallable()
unsigned count = getObjectCount();
for (unsigned i = 0; i < count; i++) {
const Class *clasp = getObjectClass(i);
if (clasp && (clasp->isProxy() || clasp->nonProxyCallable()))
if (!clasp)
continue;
if (clasp->isProxy() || clasp->nonProxyCallable())
return true;
if (!getObject(i)->hasStableClassAndProto(constraints))
return true;
}
@ -2121,7 +2163,7 @@ TemporaryTypeSet::maybeCallable()
}
bool
TemporaryTypeSet::maybeEmulatesUndefined()
TemporaryTypeSet::maybeEmulatesUndefined(CompilerConstraintList *constraints)
{
if (!maybeObject())
return false;
@ -2135,7 +2177,11 @@ TemporaryTypeSet::maybeEmulatesUndefined()
// it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
// proxies, we can just check for that.
const Class *clasp = getObjectClass(i);
if (clasp && (clasp->emulatesUndefined() || clasp->isProxy()))
if (!clasp)
continue;
if (clasp->emulatesUndefined() || clasp->isProxy())
return true;
if (!getObject(i)->hasStableClassAndProto(constraints))
return true;
}
@ -2143,7 +2189,7 @@ TemporaryTypeSet::maybeEmulatesUndefined()
}
JSObject *
TemporaryTypeSet::getCommonPrototype()
TemporaryTypeSet::getCommonPrototype(CompilerConstraintList *constraints)
{
if (unknownObject())
return nullptr;
@ -2156,7 +2202,7 @@ TemporaryTypeSet::getCommonPrototype()
if (!object)
continue;
if (!object->hasTenuredProto())
if (object->unknownProperties() || !object->hasTenuredProto())
return nullptr;
TaggedProto nproto = object->proto();
@ -2170,6 +2216,13 @@ TemporaryTypeSet::getCommonPrototype()
}
}
// Guard against mutating __proto__.
for (unsigned i = 0; i < count; i++) {
TypeObjectKey *object = getObject(i);
if (object)
JS_ALWAYS_TRUE(object->hasStableClassAndProto(constraints));
}
return proto;
}
@ -2407,7 +2460,7 @@ bool
types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
TemporaryTypeSet *types)
{
const Class *clasp = types->getKnownClass();
const Class *clasp = types->getKnownClass(constraints);
// Note: typed arrays have indexed properties not accounted for by type
// information, though these are all in bounds and will be accounted for
@ -2418,7 +2471,7 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
return true;
JSObject *proto = types->getCommonPrototype();
JSObject *proto = types->getCommonPrototype(constraints);
if (!proto)
return true;
@ -2483,43 +2536,6 @@ TypeZone::addPendingRecompile(JSContext *cx, JSScript *script)
ObjectStateChange(cx, script->functionNonDelazifying()->type(), false);
}
void
TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
{
MOZ_ASSERT(this == &cx->compartment()->types);
MOZ_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
MOZ_ASSERT(!target->singleton());
MOZ_ASSERT(target->unknownProperties());
AutoEnterAnalysis enter(cx);
/* Mark type sets which contain obj as having a generic object types. */
for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
TypeObject *object = i.get<TypeObject>();
unsigned count = object->getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop && prop->types.hasType(Type::ObjectType(target)))
prop->types.addType(cx, Type::AnyObjectType());
}
}
for (gc::ZoneCellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types()) {
unsigned count = TypeScript::NumTypeSets(script);
StackTypeSet *typeArray = script->types()->typeArray();
for (unsigned i = 0; i < count; i++) {
if (typeArray[i].hasType(Type::ObjectType(target)))
typeArray[i].addType(cx, Type::AnyObjectType());
}
}
}
target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN);
}
void
TypeCompartment::print(JSContext *cx, bool force)
{
@ -4071,7 +4087,7 @@ TypeNewScript::maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, b
// to represent partially initialized objects.
MOZ_ASSERT(prefixShape->slotSpan() > templateObject()->slotSpan());
TypeObjectFlags initialFlags = type->flags() & OBJECT_FLAG_UNKNOWN_MASK;
TypeObjectFlags initialFlags = type->flags() & OBJECT_FLAG_DYNAMIC_MASK;
Rooted<TaggedProto> protoRoot(cx, type->proto());
TypeObject *initialType =
@ -4469,15 +4485,8 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSObject *as
return nullptr;
TypeObjectFlags initialFlags = 0;
if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) {
// The new type is not present in any type sets, so mark the object as
// unknown in all type sets it appears in. This allows the prototype of
// such objects to mutate freely without triggering an expensive walk of
// the compartment's type sets. (While scripts normally don't mutate
// __proto__, the browser will for proxies and such, and we need to
// accommodate this behavior).
initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN;
}
if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN))
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
Rooted<TaggedProto> protoRoot(this, proto);
TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags);

View File

@ -417,12 +417,6 @@ enum MOZ_ENUM_TYPE(uint32_t) {
*/
OBJECT_FLAG_NURSERY_PROTO = 0x2,
/*
* Whether we have ensured all type sets in the compartment contain
* ANYOBJECT instead of this object.
*/
OBJECT_FLAG_SETS_MARKED_UNKNOWN = 0x4,
/* Mask/shift for the number of properties in propertySet */
OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8,
OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3,
@ -477,11 +471,6 @@ enum MOZ_ENUM_TYPE(uint32_t) {
/* Flags which indicate dynamic properties of represented objects. */
OBJECT_FLAG_DYNAMIC_MASK = 0x03ff0000,
/* Mask for objects created with unknown properties. */
OBJECT_FLAG_UNKNOWN_MASK =
OBJECT_FLAG_DYNAMIC_MASK
| OBJECT_FLAG_SETS_MARKED_UNKNOWN,
// Mask/shift for the kind of addendum attached to this type object.
OBJECT_FLAG_ADDENDUM_MASK = 0x0c000000,
OBJECT_FLAG_ADDENDUM_SHIFT = 26,
@ -511,6 +500,13 @@ class TemporaryTypeSet;
*
* - TemporaryTypeSet are created during compilation and do not outlive
* that compilation.
*
* The contents of a type set completely describe the values that a particular
* lvalue might have, except in cases where an object's type is mutated. In
* such cases, type sets which had the object's old type might not have the
* object's new type. Type mutation occurs only in specific circumstances ---
* when an object's prototype changes, and when it is swapped with another
* object --- and will cause the object's properties to be marked as unknown.
*/
class TypeSet
{
@ -597,9 +593,6 @@ class TypeSet
/* Whether any values in this set might have the specified type. */
bool mightBeMIRType(jit::MIRType type);
/* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
bool isDOMClass();
/*
* Get whether this type set is known to be a subset of other.
* This variant doesn't freeze constraints. That variant is called knownSubset
@ -757,7 +750,7 @@ class TemporaryTypeSet : public TypeSet
bool hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags);
/* Get the class shared by all objects in this set, or nullptr. */
const Class *getKnownClass();
const Class *getKnownClass(CompilerConstraintList *constraints);
/* Result returned from forAllClasses */
enum ForAllResult {
@ -772,22 +765,26 @@ class TemporaryTypeSet : public TypeSet
/* Apply func to the members of the set and return an appropriate result.
* The iteration may end early if the result becomes known early.
*/
ForAllResult forAllClasses(bool (*func)(const Class *clasp));
ForAllResult forAllClasses(CompilerConstraintList *constraints,
bool (*func)(const Class *clasp));
/* Get the prototype shared by all objects in this set, or nullptr. */
JSObject *getCommonPrototype();
JSObject *getCommonPrototype(CompilerConstraintList *constraints);
/* Get the typed array type of all objects in this set, or Scalar::MaxTypedArrayViewType. */
Scalar::Type getTypedArrayType();
Scalar::Type getTypedArrayType(CompilerConstraintList *constraints);
/* Get the shared typed array type of all objects in this set, or Scalar::MaxTypedArrayViewType. */
Scalar::Type getSharedTypedArrayType();
Scalar::Type getSharedTypedArrayType(CompilerConstraintList *constraints);
/* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
bool isDOMClass(CompilerConstraintList *constraints);
/* Whether clasp->isCallable() is true for one or more objects in this set. */
bool maybeCallable();
bool maybeCallable(CompilerConstraintList *constraints);
/* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
bool maybeEmulatesUndefined();
bool maybeEmulatesUndefined(CompilerConstraintList *constraints);
/* Get the single value which can appear in this type set, otherwise nullptr. */
JSObject *getSingleton();
@ -1559,6 +1556,7 @@ struct TypeObjectKey
bool unknownProperties();
bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags);
bool hasStableClassAndProto(CompilerConstraintList *constraints);
void watchStateChangeForInlinedCall(CompilerConstraintList *constraints);
void watchStateChangeForTypedArrayData(CompilerConstraintList *constraints);
HeapTypeSetKey property(jsid id);
@ -1741,9 +1739,6 @@ struct TypeCompartment
/* Get or make an object for an allocation site, and add to the allocation site table. */
TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
/* Mark any type set containing obj as having a generic object type. */
void markSetsUnknown(JSContext *cx, TypeObject *obj);
void clearTables();
void sweep(FreeOp *fop);
void finalizeObjects();

View File

@ -509,20 +509,11 @@ MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags)
obj->type()->setFlags(cx, flags);
}
/*
* Mark all properties of a type object as unknown. If markSetsUnknown is set,
* scan the entire compartment and mark all type sets containing it as having
* an unknown object. This is needed for correctness in dealing with mutable
* __proto__, which can change the type of an object dynamically.
*/
inline void
MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
bool markSetsUnknown = false)
MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj)
{
if (!obj->unknownProperties())
obj->markUnknown(cx);
if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
cx->compartment()->types.markSetsUnknown(cx, obj);
}
inline void

View File

@ -2382,8 +2382,8 @@ JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
// Swapping the contents of two objects invalidates type sets which contain
// either of the objects, so mark all such sets as unknown.
MarkTypeObjectUnknownProperties(cx, a->type(), !a->hasSingletonType());
MarkTypeObjectUnknownProperties(cx, b->type(), !b->hasSingletonType());
MarkTypeObjectUnknownProperties(cx, a->type());
MarkTypeObjectUnknownProperties(cx, b->type());
/*
* We need a write barrier here. If |a| was marked and |b| was not, then
@ -2701,12 +2701,11 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj,
* Setting __proto__ on an object that has escaped and may be referenced by
* other heap objects can only be done if the properties of both objects
* are unknown. Type sets containing this object will contain the original
* type but not the new type of the object, so we need to go and scan the
* entire compartment for type sets which have these objects and mark them
* as containing generic objects.
* type but not the new type of the object, so we need to treat all such
* type sets as unknown.
*/
MarkTypeObjectUnknownProperties(cx, obj->type(), true);
MarkTypeObjectUnknownProperties(cx, type, true);
MarkTypeObjectUnknownProperties(cx, obj->type());
MarkTypeObjectUnknownProperties(cx, type);
obj->setType(type);