From 00a3b11d2e23dc78042c8544e161f8ba7a58a548 Mon Sep 17 00:00:00 2001 From: "Nicholas D. Matsakis" Date: Tue, 11 Mar 2014 12:50:32 -0400 Subject: [PATCH] Bug 973238 Part 4 -- Use prototype rather than reserved slot to uncover descriptor r=jandem --- CLOBBER | 3 +- js/src/builtin/TypedObject.cpp | 4 --- js/src/builtin/TypedObject.h | 6 +++- js/src/builtin/TypedObject.js | 35 +++++++++++-------- js/src/builtin/TypedObjectConstants.h | 3 +- .../jit-test/tests/TypedObject/prototypes.js | 13 ++++++- js/src/jit/CodeGenerator.cpp | 17 +++++++++ js/src/jit/CodeGenerator.h | 1 + js/src/jit/IonBuilder.cpp | 12 +++++-- js/src/jit/LIR-Common.h | 19 ++++++++++ js/src/jit/LOpcodes.h | 1 + js/src/jit/Lowering.cpp | 10 ++++++ js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 33 +++++++++++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/ParallelSafetyAnalysis.cpp | 1 + js/src/jit/VMFunctions.cpp | 9 +++++ js/src/jit/VMFunctions.h | 2 ++ 18 files changed, 144 insertions(+), 27 deletions(-) diff --git a/CLOBBER b/CLOBBER index fd9981b41ebc..96b8086e8bc3 100644 --- a/CLOBBER +++ b/CLOBBER @@ -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). diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 7a0a7fc7b252..ff786749f697 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -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()); diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 62503ad44c67..e7f76810e5ef 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -652,8 +652,12 @@ class TypedObject : public ArrayBufferViewObject return getReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObject().as(); } + TypedProto &typedProto() const { + return getProto()->as(); + } + TypeDescr &typeDescr() const { - return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as(); + return typedProto().typeDescr(); } uint8_t *typedMem() const { diff --git a/js/src/builtin/TypedObject.js b/js/src/builtin/TypedObject.js index 1763c9bc2818..3a29f99606a4 100644 --- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -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); } diff --git a/js/src/builtin/TypedObjectConstants.h b/js/src/builtin/TypedObjectConstants.h index 4bb1da0d55a7..3c2f65ba16f1 100644 --- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -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: diff --git a/js/src/jit-test/tests/TypedObject/prototypes.js b/js/src/jit-test/tests/TypedObject/prototypes.js index 452c30b6c153..3910ecf6c445 100644 --- a/js/src/jit-test/tests/TypedObject/prototypes.js +++ b/js/src/jit-test/tests/TypedObject/prototypes.js @@ -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 diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index ea530c98fe0e..4b366e6d8a8e 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -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) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index cb4440e28bc8..2a4077cd9a92 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -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); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 009116cc5066..f06c202edab6 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -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 diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 0d0340583c02..657f4b3b7c2c 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -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> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 20eadeea625c..1dc19b4144c4 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -249,6 +249,7 @@ _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ + _(TypedObjectProto) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \ _(StringLength) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index f0bb96295f65..439a03af6a2c 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -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) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index b333de0eac9e..217ba42d7526 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -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); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 5ab9b6960a34..771b5274838a 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -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 diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 2a5373eca639..097bdcff24be 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -136,6 +136,7 @@ namespace jit { _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ + _(TypedObjectProto) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \ _(InitializedLength) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 317351a2766d..91380ec58cf3 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -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) diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index d3fea53c18c6..3b65d9530867 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1155,5 +1155,14 @@ AssertValidValue(JSContext *cx, Value *v) } #endif +// Definition of the MTypedObjectProto MIR. +JSObject * +TypedObjectProto(JSObject *obj) +{ + JS_ASSERT(obj->is()); + TypedObject &typedObj = obj->as(); + return &typedObj.typedProto(); +} + } // namespace jit } // namespace js diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index dd51ddc3ce0a..3d0bd4b1e741 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -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