Bug 973238 Part 4 -- Use prototype rather than reserved slot to uncover descriptor r=jandem

This commit is contained in:
Nicholas D. Matsakis 2014-03-11 12:50:32 -04:00
parent a51d6e0272
commit 00a3b11d2e
18 changed files with 144 additions and 27 deletions

View File

@ -22,5 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 904723 (Array.from) needs a clobber once again because of changes to js.msg
and self-hosted code (see bug 1019955).
Bug 973238 part 4 needs clobber due to self-hosted code (bug 1019955).

View File

@ -1483,7 +1483,6 @@ TypedObject::createUnattachedWithClass(JSContext *cx,
obj->initReservedSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(length));
obj->initReservedSlot(JS_BUFVIEW_SLOT_OWNER, NullValue());
obj->initReservedSlot(JS_BUFVIEW_SLOT_NEXT_VIEW, PrivateValue(nullptr));
obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type));
// Tag the type object for this instance with the type
// representation, if that has not been done already.
@ -1637,9 +1636,6 @@ ReportTypedObjTypeError(JSContext *cx,
/*static*/ void
TypedObject::obj_trace(JSTracer *trace, JSObject *object)
{
gc::MarkSlot(trace, &object->getReservedSlotRef(JS_TYPEDOBJ_SLOT_TYPE_DESCR),
"TypedObjectTypeDescr");
ArrayBufferViewObject::trace(trace, object);
JS_ASSERT(object->is<TypedObject>());

View File

@ -652,8 +652,12 @@ class TypedObject : public ArrayBufferViewObject
return getReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObject().as<ArrayBufferObject>();
}
TypedProto &typedProto() const {
return getProto()->as<TypedProto>();
}
TypeDescr &typeDescr() const {
return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as<TypeDescr>();
return typedProto().typeDescr();
}
uint8_t *typedMem() const {

View File

@ -28,12 +28,15 @@
#define DESCR_STRUCT_FIELD_OFFSETS(obj) \
UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS)
// Typed prototype slots
#define TYPROTO_DESCR(obj) \
UnsafeGetReservedSlot(obj, JS_TYPROTO_SLOT_DESCR)
// Typed object slots
#define TYPEDOBJ_BYTEOFFSET(obj) \
TO_INT32(UnsafeGetReservedSlot(obj, JS_BUFVIEW_SLOT_BYTEOFFSET))
#define TYPEDOBJ_TYPE_DESCR(obj) \
UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_DESCR)
#define TYPEDOBJ_OWNER(obj) \
UnsafeGetReservedSlot(obj, JS_BUFVIEW_SLOT_OWNER)
#define TYPEDOBJ_LENGTH(obj) \
@ -42,6 +45,10 @@
#define HAS_PROPERTY(obj, prop) \
callFunction(std_Object_hasOwnProperty, obj, prop)
function TypedObjectTypeDescr(typedObj) {
return TYPROTO_DESCR(typedObj.__proto__);
}
///////////////////////////////////////////////////////////////////////////
// Getting values
//
@ -192,7 +199,7 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) {
// representation as the destination. In that case, we can just do a
// memcpy.
if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) {
if (!descr.variable && DescrsEquiv(descr, TYPEDOBJ_TYPE_DESCR(fromValue))) {
if (!descr.variable && DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) {
if (!TypedObjectIsAttached(fromValue))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
@ -386,7 +393,7 @@ function FillTypedArrayWithValue(destArray, fromValue) {
assert(IsObject(handle) && ObjectIsTypedObject(destArray),
"FillTypedArrayWithValue: not typed handle");
var descr = TYPEDOBJ_TYPE_DESCR(destArray);
var descr = TypedObjectTypeDescr(destArray);
var length = DESCR_SIZED_ARRAY_LENGTH(descr);
if (length === 0)
return;
@ -440,7 +447,7 @@ function TypedArrayRedimension(newArrayType) {
// Peel away the outermost array layers from the type of `this` to find
// the core element type. In the process, count the number of elements.
var oldArrayType = TYPEDOBJ_TYPE_DESCR(this);
var oldArrayType = TypedObjectTypeDescr(this);
var oldArrayReprKind = DESCR_KIND(oldArrayType);
var oldElementType = oldArrayType;
var oldElementCount = 1;
@ -507,7 +514,7 @@ function X4ToSource() {
if (!IsObject(this) || !ObjectIsTypedObject(this))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
var descr = TYPEDOBJ_TYPE_DESCR(this);
var descr = TypedObjectTypeDescr(this);
if (DESCR_KIND(descr) != JS_TYPEREPR_X4_KIND)
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
@ -570,7 +577,7 @@ function StorageOfTypedObject(obj) {
return null;
if (ObjectIsTransparentTypedObject(obj)) {
var descr = TYPEDOBJ_TYPE_DESCR(obj);
var descr = TypedObjectTypeDescr(obj);
var byteLength;
if (DESCR_KIND(descr) == JS_TYPEREPR_UNSIZED_ARRAY_KIND)
byteLength = DESCR_SIZE(descr.elementType) * obj.length;
@ -593,7 +600,7 @@ function StorageOfTypedObject(obj) {
// Warning: user exposed!
function TypeOfTypedObject(obj) {
if (IsObject(obj) && ObjectIsTypedObject(obj))
return TYPEDOBJ_TYPE_DESCR(obj);
return TypedObjectTypeDescr(obj);
// Note: Do not create bindings for `Any`, `String`, etc in
// Utilities.js, but rather access them through
@ -687,7 +694,7 @@ function TypedObjectArrayTypeFrom(a, b, c) {
function TypedArrayMap(a, b) {
if (!IsObject(this) || !ObjectIsTypedObject(this))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
var thisType = TYPEDOBJ_TYPE_DESCR(this);
var thisType = TypedObjectTypeDescr(this);
if (!TypeDescrIsArrayType(thisType))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
@ -707,7 +714,7 @@ function TypedArrayMapPar(a, b) {
// when not working with typed objects.
if (!IsObject(this) || !ObjectIsTypedObject(this))
return callFunction(TypedArrayMap, this, a, b);
var thisType = TYPEDOBJ_TYPE_DESCR(this);
var thisType = TypedObjectTypeDescr(this);
if (!TypeDescrIsArrayType(thisType))
return callFunction(TypedArrayMap, this, a, b);
@ -723,7 +730,7 @@ function TypedArrayReduce(a, b) {
// Arguments: func, [initial]
if (!IsObject(this) || !ObjectIsTypedObject(this))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
var thisType = TYPEDOBJ_TYPE_DESCR(this);
var thisType = TypedObjectTypeDescr(this);
if (!TypeDescrIsArrayType(thisType))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
@ -739,7 +746,7 @@ function TypedArrayScatter(a, b, c, d) {
// Arguments: outputArrayType, indices, defaultValue, conflictFunction
if (!IsObject(this) || !ObjectIsTypedObject(this))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
var thisType = TYPEDOBJ_TYPE_DESCR(this);
var thisType = TypedObjectTypeDescr(this);
if (!TypeDescrIsArrayType(thisType))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
@ -757,7 +764,7 @@ function TypedArrayFilter(func) {
// Arguments: predicate
if (!IsObject(this) || !ObjectIsTypedObject(this))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
var thisType = TYPEDOBJ_TYPE_DESCR(this);
var thisType = TypedObjectTypeDescr(this);
if (!TypeDescrIsArrayType(thisType))
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
@ -1140,7 +1147,7 @@ function RedirectPointer(typedObj, offset, outputIsScalar) {
// is an overapproximation: users can manually declare opaque
// types that nonetheless only contain scalar data.
typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj),
typedObj = NewDerivedTypedObject(TypedObjectTypeDescr(typedObj),
typedObj, 0);
}

View File

@ -108,9 +108,8 @@
#define JS_TYPEDARR_SLOTS 5 // Number of slots for typed arrays
// Specific to typed objects:
#define JS_TYPEDOBJ_SLOT_TYPE_DESCR 4 // A ScalarTypeDescr::Type constant
#define JS_TYPEDOBJ_SLOT_DATA 7
#define JS_TYPEDOBJ_SLOTS 5 // Number of slots for typed objs
#define JS_TYPEDOBJ_SLOTS 4 // Number of slots for typed objs
// (*) The interpretation of the JS_BUFVIEW_SLOT_LENGTH slot depends on
// the kind of view:

View File

@ -1,9 +1,12 @@
// API Surface Test: check that mutating prototypes
// of type descriptors has no effect.
// of type objects has no effect, and that mutating
// the prototypes of typed objects is an error.
if (!this.hasOwnProperty("TypedObject"))
quit();
load(libdir + "asserts.js");
var {StructType, uint32, Object, Any, storage, objectType} = TypedObject;
function main() { // once a C programmer, always a C programmer.
@ -12,6 +15,14 @@ function main() { // once a C programmer, always a C programmer.
Uints.prototype = {}; // no effect
assertEq(p, Uints.prototype);
var uints = new Uints();
assertEq(uints.__proto__, p);
assertThrowsInstanceOf(function() uints.__proto__ = {},
TypeError);
assertThrowsInstanceOf(function() Object.setPrototypeOf(uints, {}),
TypeError);
assertEq(uints.__proto__, p);
var Uintss = Uints.array(2);
var p = Uintss.prototype;
Uintss.prototype = {}; // no effect

View File

@ -4383,6 +4383,23 @@ CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
return true;
}
bool
CodeGenerator::visitTypedObjectProto(LTypedObjectProto *lir)
{
Register obj = ToRegister(lir->object());
JS_ASSERT(ToRegister(lir->output()) == ReturnReg);
// Eventually we ought to inline this helper function for
// efficiency, but it's mildly non-trivial since we must reach
// into the type object and so on.
const Register tempReg = ToRegister(lir->temp());
masm.setupUnalignedABICall(1, tempReg);
masm.passABIArg(obj);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TypedObjectProto));
return true;
}
bool
CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
{

View File

@ -174,6 +174,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitNeuterCheck(LNeuterCheck *lir);
bool visitTypedObjectElements(LTypedObjectElements *lir);
bool visitSetTypedObjectOffset(LSetTypedObjectOffset *lir);
bool visitTypedObjectProto(LTypedObjectProto *ins);
bool visitStringLength(LStringLength *lir);
bool visitInitializedLength(LInitializedLength *lir);
bool visitSetInitializedLength(LSetInitializedLength *lir);

View File

@ -10288,10 +10288,16 @@ IonBuilder::loadTypedObjectType(MDefinition *typedObj)
if (typedObj->isNewDerivedTypedObject())
return typedObj->toNewDerivedTypedObject()->type();
MInstruction *load = MLoadFixedSlot::New(alloc(), typedObj,
JS_TYPEDOBJ_SLOT_TYPE_DESCR);
MInstruction *proto = MTypedObjectProto::New(alloc(), typedObj);
current->add(proto);
MInstruction *load = MLoadFixedSlot::New(alloc(), proto, JS_TYPROTO_SLOT_DESCR);
current->add(load);
return load;
MInstruction *unbox = MUnbox::New(alloc(), load, MIRType_Object, MUnbox::Infallible);
current->add(unbox);
return unbox;
}
// Given a typed object `typedObj` and an offset `offset` into that

View File

@ -3676,6 +3676,25 @@ class LTypedArrayElements : public LInstructionHelper<1, 1, 0>
}
};
// Load a typed object's prototype, which is guaranteed to be a
// TypedProto object.
class LTypedObjectProto : public LCallInstructionHelper<1, 1, 1>
{
public:
LIR_HEADER(TypedObjectProto)
LTypedObjectProto(const LAllocation &object, const LDefinition &temp1) {
setOperand(0, object);
setTemp(0, temp1);
}
const LAllocation *object() {
return getOperand(0);
}
const LDefinition *temp() {
return getTemp(0);
}
};
// Load a typed array's elements vector.
class LTypedObjectElements : public LInstructionHelper<1, 1, 0>
{

View File

@ -249,6 +249,7 @@
_(SetArrayLength) \
_(TypedArrayLength) \
_(TypedArrayElements) \
_(TypedObjectProto) \
_(TypedObjectElements) \
_(SetTypedObjectOffset) \
_(StringLength) \

View File

@ -2384,6 +2384,16 @@ LIRGenerator::visitTypedArrayElements(MTypedArrayElements *ins)
return define(new(alloc()) LTypedArrayElements(useRegisterAtStart(ins->object())), ins);
}
bool
LIRGenerator::visitTypedObjectProto(MTypedObjectProto *ins)
{
JS_ASSERT(ins->type() == MIRType_Object);
return defineReturn(new(alloc()) LTypedObjectProto(
useFixed(ins->object(), CallTempReg0),
tempFixed(CallTempReg1)),
ins);
}
bool
LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins)
{

View File

@ -182,6 +182,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitNeuterCheck(MNeuterCheck *lir);
bool visitTypedObjectElements(MTypedObjectElements *ins);
bool visitSetTypedObjectOffset(MSetTypedObjectOffset *ins);
bool visitTypedObjectProto(MTypedObjectProto *ins);
bool visitInitializedLength(MInitializedLength *ins);
bool visitSetInitializedLength(MSetInitializedLength *ins);
bool visitNot(MNot *ins);

View File

@ -1697,6 +1697,39 @@ class MNewPar : public MUnaryInstruction
}
};
class MTypedObjectProto
: public MUnaryInstruction,
public SingleObjectPolicy
{
private:
MTypedObjectProto(MDefinition *object)
: MUnaryInstruction(object)
{
setResultType(MIRType_Object);
setMovable();
}
public:
INSTRUCTION_HEADER(TypedObjectProto)
static MTypedObjectProto *New(TempAllocator &alloc, MDefinition *object) {
return new(alloc) MTypedObjectProto(object);
}
TypePolicy *typePolicy() {
return this;
}
MDefinition *object() const {
return getOperand(0);
}
bool congruentTo(const MDefinition *ins) const {
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const {
return AliasSet::Load(AliasSet::ObjectFields);
}
};
// Creates a new derived type object. At runtime, this is just a call
// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
// compile to particularly optimized code. However, using a distinct

View File

@ -136,6 +136,7 @@ namespace jit {
_(SetArrayLength) \
_(TypedArrayLength) \
_(TypedArrayElements) \
_(TypedObjectProto) \
_(TypedObjectElements) \
_(SetTypedObjectOffset) \
_(InitializedLength) \

View File

@ -224,6 +224,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
WRITE_GUARDED_OP(SetArrayLength, elements)
SAFE_OP(TypedArrayLength)
SAFE_OP(TypedArrayElements)
SAFE_OP(TypedObjectProto)
SAFE_OP(TypedObjectElements)
SAFE_OP(SetTypedObjectOffset)
SAFE_OP(InitializedLength)

View File

@ -1155,5 +1155,14 @@ AssertValidValue(JSContext *cx, Value *v)
}
#endif
// Definition of the MTypedObjectProto MIR.
JSObject *
TypedObjectProto(JSObject *obj)
{
JS_ASSERT(obj->is<TypedObject>());
TypedObject &typedObj = obj->as<TypedObject>();
return &typedObj.typedProto();
}
} // namespace jit
} // namespace js

View File

@ -690,6 +690,8 @@ void AssertValidStringPtr(JSContext *cx, JSString *str);
void AssertValidValue(JSContext *cx, Value *v);
#endif
JSObject *TypedObjectProto(JSObject *obj);
} // namespace jit
} // namespace js