mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1116017 - Don't scan all type sets in compartments on type mutations, r=jandem.
This commit is contained in:
parent
06cd80b23d
commit
493792ecf6
18
js/src/jit-test/tests/ion/result-type-mutated.js
Normal file
18
js/src/jit-test/tests/ion/result-type-mutated.js
Normal 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();
|
@ -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);
|
||||
|
@ -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()))
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user