michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: michael@0: #include "mozilla/CheckedInt.h" michael@0: michael@0: #include "jscompartment.h" michael@0: #include "jsfun.h" michael@0: #include "jsobj.h" michael@0: #include "jsutil.h" michael@0: michael@0: #include "gc/Marking.h" michael@0: #include "js/Vector.h" michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/ObjectImpl.h" michael@0: #include "vm/String.h" michael@0: #include "vm/StringBuffer.h" michael@0: #include "vm/TypedArrayObject.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "vm/Shape-inl.h" michael@0: michael@0: using mozilla::CheckedInt32; michael@0: using mozilla::DebugOnly; michael@0: michael@0: using namespace js; michael@0: michael@0: const Class js::TypedObjectModuleObject::class_ = { michael@0: "TypedObject", michael@0: JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub michael@0: }; michael@0: michael@0: static const JSFunctionSpec TypedObjectMethods[] = { michael@0: JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0), michael@0: JS_SELF_HOSTED_FN("storage", "StorageOfTypedObject", 1, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static void michael@0: ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, michael@0: InformalValueTypeName(fromValue), toType); michael@0: } michael@0: michael@0: template michael@0: static inline T* michael@0: ToObjectIf(HandleValue value) michael@0: { michael@0: if (!value.isObject()) michael@0: return nullptr; michael@0: michael@0: if (!value.toObject().is()) michael@0: return nullptr; michael@0: michael@0: return &value.toObject().as(); michael@0: } michael@0: michael@0: static inline CheckedInt32 roundUpToAlignment(CheckedInt32 address, int32_t align) michael@0: { michael@0: JS_ASSERT(IsPowerOfTwo(align)); michael@0: michael@0: // Note: Be careful to order operators such that we first make the michael@0: // value smaller and then larger, so that we don't get false michael@0: // overflow errors due to (e.g.) adding `align` and then michael@0: // subtracting `1` afterwards when merely adding `align-1` would michael@0: // not have overflowed. Note that due to the nature of two's michael@0: // complement representation, if `address` is already aligned, michael@0: // then adding `align-1` cannot itself cause an overflow. michael@0: michael@0: return ((address + (align - 1)) / align) * align; michael@0: } michael@0: michael@0: /* michael@0: * Overwrites the contents of `typedObj` at offset `offset` with `val` michael@0: * converted to the type `typeObj`. This is done by delegating to michael@0: * self-hosted code. This is used for assignments and initializations. michael@0: * michael@0: * For example, consider the final assignment in this snippet: michael@0: * michael@0: * var Point = new StructType({x: float32, y: float32}); michael@0: * var Line = new StructType({from: Point, to: Point}); michael@0: * var line = new Line(); michael@0: * line.to = {x: 22, y: 44}; michael@0: * michael@0: * This would result in a call to `ConvertAndCopyTo` michael@0: * where: michael@0: * - typeObj = Point michael@0: * - typedObj = line michael@0: * - offset = sizeof(Point) == 8 michael@0: * - val = {x: 22, y: 44} michael@0: * This would result in loading the value of `x`, converting michael@0: * it to a float32, and hen storing it at the appropriate offset, michael@0: * and then doing the same for `y`. michael@0: * michael@0: * Note that the type of `typeObj` may not be the michael@0: * type of `typedObj` but rather some subcomponent of `typedObj`. michael@0: */ michael@0: static bool michael@0: ConvertAndCopyTo(JSContext *cx, michael@0: HandleTypeDescr typeObj, michael@0: HandleTypedObject typedObj, michael@0: int32_t offset, michael@0: HandleValue val) michael@0: { michael@0: RootedFunction func( michael@0: cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo)); michael@0: if (!func) michael@0: return false; michael@0: michael@0: InvokeArgs args(cx); michael@0: if (!args.init(4)) michael@0: return false; michael@0: michael@0: args.setCallee(ObjectValue(*func)); michael@0: args[0].setObject(*typeObj); michael@0: args[1].setObject(*typedObj); michael@0: args[2].setInt32(offset); michael@0: args[3].set(val); michael@0: michael@0: return Invoke(cx, args); michael@0: } michael@0: michael@0: static bool michael@0: ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val) michael@0: { michael@0: Rooted type(cx, &typedObj->typeDescr()); michael@0: return ConvertAndCopyTo(cx, type, typedObj, 0, val); michael@0: } michael@0: michael@0: /* michael@0: * Overwrites the contents of `typedObj` at offset `offset` with `val` michael@0: * converted to the type `typeObj` michael@0: */ michael@0: static bool michael@0: Reify(JSContext *cx, michael@0: HandleTypeDescr type, michael@0: HandleTypedObject typedObj, michael@0: size_t offset, michael@0: MutableHandleValue to) michael@0: { michael@0: RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify)); michael@0: if (!func) michael@0: return false; michael@0: michael@0: InvokeArgs args(cx); michael@0: if (!args.init(3)) michael@0: return false; michael@0: michael@0: args.setCallee(ObjectValue(*func)); michael@0: args[0].setObject(*type); michael@0: args[1].setObject(*typedObj); michael@0: args[2].setInt32(offset); michael@0: michael@0: if (!Invoke(cx, args)) michael@0: return false; michael@0: michael@0: to.set(args.rval()); michael@0: return true; michael@0: } michael@0: michael@0: // Extracts the `prototype` property from `obj`, throwing if it is michael@0: // missing or not an object. michael@0: static JSObject * michael@0: GetPrototype(JSContext *cx, HandleObject obj) michael@0: { michael@0: RootedValue prototypeVal(cx); michael@0: if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype, michael@0: &prototypeVal)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: if (!prototypeVal.isObject()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_INVALID_PROTOTYPE); michael@0: return nullptr; michael@0: } michael@0: return &prototypeVal.toObject(); michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: * Typed Prototypes michael@0: * michael@0: * Every type descriptor has an associated prototype. Instances of michael@0: * that type descriptor use this as their prototype. Per the spec, michael@0: * typed object prototypes cannot be mutated. michael@0: */ michael@0: michael@0: const Class js::TypedProto::class_ = { michael@0: "TypedProto", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_TYPROTO_SLOTS), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr michael@0: }; michael@0: michael@0: /*************************************************************************** michael@0: * Scalar type objects michael@0: * michael@0: * Scalar type objects like `uint8`, `uint16`, are all instances of michael@0: * the ScalarTypeDescr class. Like all type objects, they have a reserved michael@0: * slot pointing to a TypeRepresentation object, which is used to michael@0: * distinguish which scalar type object this actually is. michael@0: */ michael@0: michael@0: const Class js::ScalarTypeDescr::class_ = { michael@0: "Scalar", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, michael@0: ScalarTypeDescr::call, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr michael@0: }; michael@0: michael@0: const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = { michael@0: JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), michael@0: {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, michael@0: {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"}, michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static int32_t ScalarSizes[] = { michael@0: #define SCALAR_SIZE(_kind, _type, _name) \ michael@0: sizeof(_type), michael@0: JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0 michael@0: #undef SCALAR_SIZE michael@0: }; michael@0: michael@0: int32_t michael@0: ScalarTypeDescr::size(Type t) michael@0: { michael@0: return ScalarSizes[t]; michael@0: } michael@0: michael@0: int32_t michael@0: ScalarTypeDescr::alignment(Type t) michael@0: { michael@0: return ScalarSizes[t]; michael@0: } michael@0: michael@0: /*static*/ const char * michael@0: ScalarTypeDescr::typeName(Type type) michael@0: { michael@0: switch (type) { michael@0: #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \ michael@0: case constant_: return #name_; michael@0: JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING) michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Invalid type"); michael@0: } michael@0: michael@0: bool michael@0: ScalarTypeDescr::call(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() < 1) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, michael@0: args.callee().getClass()->name, "0", "s"); michael@0: return false; michael@0: } michael@0: michael@0: Rooted descr(cx, &args.callee().as()); michael@0: ScalarTypeDescr::Type type = descr->type(); michael@0: michael@0: double number; michael@0: if (!ToNumber(cx, args[0], &number)) michael@0: return false; michael@0: michael@0: if (type == ScalarTypeDescr::TYPE_UINT8_CLAMPED) michael@0: number = ClampDoubleToUint8(number); michael@0: michael@0: switch (type) { michael@0: #define SCALARTYPE_CALL(constant_, type_, name_) \ michael@0: case constant_: { \ michael@0: type_ converted = ConvertScalar(number); \ michael@0: args.rval().setNumber((double) converted); \ michael@0: return true; \ michael@0: } michael@0: michael@0: JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL) michael@0: #undef SCALARTYPE_CALL michael@0: michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: * Reference type objects michael@0: * michael@0: * Reference type objects like `Any` or `Object` basically work the michael@0: * same way that the scalar type objects do. There is one class with michael@0: * many instances, and each instance has a reserved slot with a michael@0: * TypeRepresentation object, which is used to distinguish which michael@0: * reference type object this actually is. michael@0: */ michael@0: michael@0: const Class js::ReferenceTypeDescr::class_ = { michael@0: "Reference", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, michael@0: ReferenceTypeDescr::call, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr michael@0: }; michael@0: michael@0: const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = { michael@0: JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), michael@0: {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, michael@0: {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"}, michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static int32_t ReferenceSizes[] = { michael@0: #define REFERENCE_SIZE(_kind, _type, _name) \ michael@0: sizeof(_type), michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(REFERENCE_SIZE) 0 michael@0: #undef REFERENCE_SIZE michael@0: }; michael@0: michael@0: int32_t michael@0: ReferenceTypeDescr::size(Type t) michael@0: { michael@0: return ReferenceSizes[t]; michael@0: } michael@0: michael@0: int32_t michael@0: ReferenceTypeDescr::alignment(Type t) michael@0: { michael@0: return ReferenceSizes[t]; michael@0: } michael@0: michael@0: /*static*/ const char * michael@0: ReferenceTypeDescr::typeName(Type type) michael@0: { michael@0: switch (type) { michael@0: #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \ michael@0: case constant_: return #name_; michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING) michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Invalid type"); michael@0: } michael@0: michael@0: bool michael@0: js::ReferenceTypeDescr::call(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: JS_ASSERT(args.callee().is()); michael@0: Rooted descr(cx, &args.callee().as()); michael@0: michael@0: if (args.length() < 1) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_MORE_ARGS_NEEDED, michael@0: descr->typeName(), "0", "s"); michael@0: return false; michael@0: } michael@0: michael@0: switch (descr->type()) { michael@0: case ReferenceTypeDescr::TYPE_ANY: michael@0: args.rval().set(args[0]); michael@0: return true; michael@0: michael@0: case ReferenceTypeDescr::TYPE_OBJECT: michael@0: { michael@0: RootedObject obj(cx, ToObject(cx, args[0])); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: case ReferenceTypeDescr::TYPE_STRING: michael@0: { michael@0: RootedString obj(cx, ToString(cx, args[0])); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setString(&*obj); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unhandled Reference type"); michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: * X4 type objects michael@0: * michael@0: * Note: these are partially defined in SIMD.cpp michael@0: */ michael@0: michael@0: static int32_t X4Sizes[] = { michael@0: #define X4_SIZE(_kind, _type, _name) \ michael@0: sizeof(_type) * 4, michael@0: JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0 michael@0: #undef X4_SIZE michael@0: }; michael@0: michael@0: int32_t michael@0: X4TypeDescr::size(Type t) michael@0: { michael@0: return X4Sizes[t]; michael@0: } michael@0: michael@0: int32_t michael@0: X4TypeDescr::alignment(Type t) michael@0: { michael@0: return X4Sizes[t]; michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: * ArrayMetaTypeDescr class michael@0: */ michael@0: michael@0: /* michael@0: * For code like: michael@0: * michael@0: * var A = new TypedObject.ArrayType(uint8, 10); michael@0: * var S = new TypedObject.StructType({...}); michael@0: * michael@0: * As usual, the [[Prototype]] of A is michael@0: * TypedObject.ArrayType.prototype. This permits adding methods to michael@0: * all ArrayType types, by setting michael@0: * TypedObject.ArrayType.prototype.methodName = function() { ... }. michael@0: * The same holds for S with respect to TypedObject.StructType. michael@0: * michael@0: * We may also want to add methods to *instances* of an ArrayType: michael@0: * michael@0: * var a = new A(); michael@0: * var s = new S(); michael@0: * michael@0: * As usual, the [[Prototype]] of a is A.prototype. What's michael@0: * A.prototype? It's an empty object, and you can set michael@0: * A.prototype.methodName = function() { ... } to add a method to all michael@0: * A instances. (And the same with respect to s and S.) michael@0: * michael@0: * But what if you want to add a method to all ArrayType instances, michael@0: * not just all A instances? (Or to all StructType instances.) The michael@0: * [[Prototype]] of the A.prototype empty object is michael@0: * TypedObject.ArrayType.prototype.prototype (two .prototype levels!). michael@0: * So just set TypedObject.ArrayType.prototype.prototype.methodName = michael@0: * function() { ... } to add a method to all ArrayType instances. michael@0: * (And, again, same with respect to s and S.) michael@0: * michael@0: * This function creates the A.prototype/S.prototype object. It takes michael@0: * as an argument either the TypedObject.ArrayType or the michael@0: * TypedObject.StructType constructor function, then returns an empty michael@0: * object with the .prototype.prototype object as its [[Prototype]]. michael@0: */ michael@0: static TypedProto * michael@0: CreatePrototypeObjectForComplexTypeInstance(JSContext *cx, michael@0: Handle descr, michael@0: HandleObject ctorPrototype) michael@0: { michael@0: RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype)); michael@0: if (!ctorPrototypePrototype) michael@0: return nullptr; michael@0: michael@0: Rooted result(cx); michael@0: result = NewObjectWithProto(cx, michael@0: &*ctorPrototypePrototype, michael@0: nullptr, michael@0: TenuredObject); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: result->initTypeDescrSlot(*descr); michael@0: return result; michael@0: } michael@0: michael@0: const Class UnsizedArrayTypeDescr::class_ = { michael@0: "ArrayType", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS), michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: TypedObject::constructUnsized, michael@0: nullptr michael@0: }; michael@0: michael@0: const Class SizedArrayTypeDescr::class_ = { michael@0: "ArrayType", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS), michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: TypedObject::constructSized, michael@0: nullptr michael@0: }; michael@0: michael@0: const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = { michael@0: JS_PS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = { michael@0: {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, michael@0: JS_FN("dimension", UnsizedArrayTypeDescr::dimension, 1, 0), michael@0: JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), michael@0: {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"}, michael@0: JS_SELF_HOSTED_FN("build", "TypedObjectArrayTypeBuild", 3, 0), michael@0: JS_SELF_HOSTED_FN("buildPar", "TypedObjectArrayTypeBuildPar", 3, 0), michael@0: JS_SELF_HOSTED_FN("from", "TypedObjectArrayTypeFrom", 3, 0), michael@0: JS_SELF_HOSTED_FN("fromPar", "TypedObjectArrayTypeFromPar", 3, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: const JSPropertySpec ArrayMetaTypeDescr::typedObjectProperties[] = { michael@0: JS_PS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec ArrayMetaTypeDescr::typedObjectMethods[] = { michael@0: {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"}, michael@0: {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"}, michael@0: JS_SELF_HOSTED_FN("map", "TypedArrayMap", 2, 0), michael@0: JS_SELF_HOSTED_FN("mapPar", "TypedArrayMapPar", 2, 0), michael@0: JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 2, 0), michael@0: JS_SELF_HOSTED_FN("reducePar", "TypedArrayReducePar", 2, 0), michael@0: JS_SELF_HOSTED_FN("scatter", "TypedArrayScatter", 4, 0), michael@0: JS_SELF_HOSTED_FN("scatterPar", "TypedArrayScatterPar", 4, 0), michael@0: JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0), michael@0: JS_SELF_HOSTED_FN("filterPar", "TypedArrayFilterPar", 1, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: bool michael@0: js::CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr descr) michael@0: { michael@0: // If data is transparent, also store the public slots. michael@0: if (descr->transparent() && descr->is()) { michael@0: Rooted sizedDescr(cx, &descr->as()); michael@0: michael@0: // byteLength michael@0: RootedValue typeByteLength(cx, Int32Value(sizedDescr->size())); michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().byteLength, michael@0: typeByteLength, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: // byteAlignment michael@0: RootedValue typeByteAlignment(cx, Int32Value(sizedDescr->alignment())); michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment, michael@0: typeByteAlignment, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return false; michael@0: } michael@0: } else { michael@0: // byteLength michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().byteLength, michael@0: UndefinedHandleValue, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: // byteAlignment michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment, michael@0: UndefinedHandleValue, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // variable -- true for unsized arrays michael@0: RootedValue variable(cx, BooleanValue(!descr->is())); michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().variable, michael@0: variable, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: T * michael@0: ArrayMetaTypeDescr::create(JSContext *cx, michael@0: HandleObject arrayTypePrototype, michael@0: HandleSizedTypeDescr elementType, michael@0: HandleAtom stringRepr, michael@0: int32_t size) michael@0: { michael@0: Rooted obj(cx); michael@0: obj = NewObjectWithProto(cx, arrayTypePrototype, nullptr, TenuredObject); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind)); michael@0: obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); michael@0: obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment())); michael@0: obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size)); michael@0: obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque())); michael@0: obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType)); michael@0: michael@0: RootedValue elementTypeVal(cx, ObjectValue(*elementType)); michael@0: if (!JSObject::defineProperty(cx, obj, cx->names().elementType, michael@0: elementTypeVal, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: if (!CreateUserSizeAndAlignmentProperties(cx, obj)) michael@0: return nullptr; michael@0: michael@0: Rooted prototypeObj(cx); michael@0: prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, obj, arrayTypePrototype); michael@0: if (!prototypeObj) michael@0: return nullptr; michael@0: michael@0: obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj)); michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, obj, prototypeObj)) michael@0: return nullptr; michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: bool michael@0: ArrayMetaTypeDescr::construct(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (!args.isConstructing()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_NOT_FUNCTION, "ArrayType"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject arrayTypeGlobal(cx, &args.callee()); michael@0: michael@0: // Expect one argument which is a sized type object michael@0: if (args.length() < 1) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, michael@0: "ArrayType", "0", ""); michael@0: return false; michael@0: } michael@0: michael@0: if (!args[0].isObject() || !args[0].toObject().is()) { michael@0: ReportCannotConvertTo(cx, args[0], "ArrayType element specifier"); michael@0: return false; michael@0: } michael@0: michael@0: Rooted elementType(cx); michael@0: elementType = &args[0].toObject().as(); michael@0: michael@0: // Construct a canonical string `new ArrayType()`: michael@0: StringBuffer contents(cx); michael@0: contents.append("new ArrayType("); michael@0: contents.append(&elementType->stringRepr()); michael@0: contents.append(")"); michael@0: RootedAtom stringRepr(cx, contents.finishAtom()); michael@0: if (!stringRepr) michael@0: return nullptr; michael@0: michael@0: // Extract ArrayType.prototype michael@0: RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal)); michael@0: if (!arrayTypePrototype) michael@0: return nullptr; michael@0: michael@0: // Create the instance of ArrayType michael@0: Rooted obj(cx); michael@0: obj = create(cx, arrayTypePrototype, elementType, michael@0: stringRepr, 0); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: // Add `length` property, which is undefined for an unsized array. michael@0: if (!JSObject::defineProperty(cx, obj, cx->names().length, michael@0: UndefinedHandleValue, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: /*static*/ bool michael@0: UnsizedArrayTypeDescr::dimension(JSContext *cx, unsigned int argc, jsval *vp) michael@0: { michael@0: // Expect that the `this` pointer is an unsized array type michael@0: // and the first argument is an integer size. michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || michael@0: !args.thisv().isObject() || michael@0: !args.thisv().toObject().is() || michael@0: !args[0].isInt32() || michael@0: args[0].toInt32() < 0) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: // Extract arguments. michael@0: Rooted unsizedTypeDescr(cx); michael@0: unsizedTypeDescr = &args.thisv().toObject().as(); michael@0: int32_t length = args[0].toInt32(); michael@0: Rooted elementType(cx, &unsizedTypeDescr->elementType()); michael@0: michael@0: // Compute the size. michael@0: CheckedInt32 size = CheckedInt32(elementType->size()) * length; michael@0: if (!size.isValid()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_TOO_BIG); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Construct a canonical string `new ArrayType().dimension(N)`: michael@0: StringBuffer contents(cx); michael@0: contents.append("new ArrayType("); michael@0: contents.append(&elementType->stringRepr()); michael@0: contents.append(").dimension("); michael@0: if (!NumberValueToStringBuffer(cx, NumberValue(length), contents)) michael@0: return false; michael@0: contents.append(")"); michael@0: RootedAtom stringRepr(cx, contents.finishAtom()); michael@0: if (!stringRepr) michael@0: return nullptr; michael@0: michael@0: // Create the sized type object. michael@0: Rooted obj(cx); michael@0: obj = ArrayMetaTypeDescr::create(cx, unsizedTypeDescr, michael@0: elementType, michael@0: stringRepr, size.value()); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: obj->initReservedSlot(JS_DESCR_SLOT_SIZED_ARRAY_LENGTH, michael@0: Int32Value(length)); michael@0: michael@0: // Add `length` property. michael@0: RootedValue lengthVal(cx, Int32Value(length)); michael@0: if (!JSObject::defineProperty(cx, obj, cx->names().length, michael@0: lengthVal, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Add `unsized` property, which is a link from the sized michael@0: // array to the unsized array. michael@0: RootedValue unsizedTypeDescrValue(cx, ObjectValue(*unsizedTypeDescr)); michael@0: if (!JSObject::defineProperty(cx, obj, cx->names().unsized, michael@0: unsizedTypeDescrValue, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::IsTypedObjectArray(JSObject &obj) michael@0: { michael@0: if (!obj.is()) michael@0: return false; michael@0: TypeDescr& d = obj.as().typeDescr(); michael@0: return d.is() || d.is(); michael@0: } michael@0: michael@0: /********************************* michael@0: * StructType class michael@0: */ michael@0: michael@0: const Class StructTypeDescr::class_ = { michael@0: "StructType", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | michael@0: JSCLASS_HAS_PRIVATE, // used to store FieldList michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: TypedObject::constructSized, michael@0: nullptr /* trace */ michael@0: }; michael@0: michael@0: const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = { michael@0: JS_PS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = { michael@0: {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, michael@0: JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), michael@0: {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"}, michael@0: JS_FS_END michael@0: }; michael@0: michael@0: const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = { michael@0: JS_PS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = { michael@0: JS_FS_END michael@0: }; michael@0: michael@0: JSObject * michael@0: StructMetaTypeDescr::create(JSContext *cx, michael@0: HandleObject metaTypeDescr, michael@0: HandleObject fields) michael@0: { michael@0: // Obtain names of fields, which are the own properties of `fields` michael@0: AutoIdVector ids(cx); michael@0: if (!GetPropertyNames(cx, fields, JSITER_OWNONLY, &ids)) michael@0: return nullptr; michael@0: michael@0: // Iterate through each field. Collect values for the various michael@0: // vectors below and also track total size and alignment. Be wary michael@0: // of overflow! michael@0: StringBuffer stringBuffer(cx); // Canonical string repr michael@0: AutoValueVector fieldNames(cx); // Name of each field. michael@0: AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field. michael@0: AutoValueVector fieldOffsets(cx); // Offset of each field field. michael@0: RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object michael@0: RootedObject userFieldTypes(cx); // User-exposed {f:descr} object. michael@0: CheckedInt32 sizeSoFar(0); // Size of struct thus far. michael@0: int32_t alignment = 1; // Alignment of struct. michael@0: bool opaque = false; // Opacity of struct. michael@0: michael@0: userFieldOffsets = NewObjectWithProto(cx, nullptr, nullptr, TenuredObject); michael@0: if (!userFieldOffsets) michael@0: return nullptr; michael@0: michael@0: userFieldTypes = NewObjectWithProto(cx, nullptr, nullptr, TenuredObject); michael@0: if (!userFieldTypes) michael@0: return nullptr; michael@0: michael@0: if (!stringBuffer.append("new StructType({")) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedValue fieldTypeVal(cx); michael@0: RootedId id(cx); michael@0: Rooted fieldType(cx); michael@0: for (unsigned int i = 0; i < ids.length(); i++) { michael@0: id = ids[i]; michael@0: michael@0: // Check that all the property names are non-numeric strings. michael@0: uint32_t unused; michael@0: if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) { michael@0: RootedValue idValue(cx, IdToValue(id)); michael@0: ReportCannotConvertTo(cx, idValue, "StructType field name"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Load the value for the current field from the `fields` object. michael@0: // The value should be a type descriptor. michael@0: if (!JSObject::getGeneric(cx, fields, fields, id, &fieldTypeVal)) michael@0: return nullptr; michael@0: fieldType = ToObjectIf(fieldTypeVal); michael@0: if (!fieldType) { michael@0: ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Collect field name and type object michael@0: RootedValue fieldName(cx, IdToValue(id)); michael@0: if (!fieldNames.append(fieldName)) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: if (!fieldTypeObjs.append(ObjectValue(*fieldType))) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: // userFieldTypes[id] = typeObj michael@0: if (!JSObject::defineGeneric(cx, userFieldTypes, id, michael@0: fieldTypeObjs.handleAt(i), nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Append "f:Type" to the string repr michael@0: if (i > 0 && !stringBuffer.append(", ")) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: if (!stringBuffer.append(JSID_TO_ATOM(id))) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: if (!stringBuffer.append(": ")) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: if (!stringBuffer.append(&fieldType->stringRepr())) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Offset of this field is the current total size adjusted for michael@0: // the field's alignment. michael@0: CheckedInt32 offset = roundUpToAlignment(sizeSoFar, fieldType->alignment()); michael@0: if (!offset.isValid()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_TOO_BIG); michael@0: return nullptr; michael@0: } michael@0: if (!fieldOffsets.append(Int32Value(offset.value()))) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: // userFieldOffsets[id] = offset michael@0: RootedValue offsetValue(cx, Int32Value(offset.value())); michael@0: if (!JSObject::defineGeneric(cx, userFieldOffsets, id, michael@0: offsetValue, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Add space for this field to the total struct size. michael@0: sizeSoFar = offset + fieldType->size(); michael@0: if (!sizeSoFar.isValid()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_TOO_BIG); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Struct is opaque if any field is opaque michael@0: if (fieldType->opaque()) michael@0: opaque = true; michael@0: michael@0: // Alignment of the struct is the max of the alignment of its fields. michael@0: alignment = js::Max(alignment, fieldType->alignment()); michael@0: } michael@0: michael@0: // Complete string representation. michael@0: if (!stringBuffer.append("})")) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: RootedAtom stringRepr(cx, stringBuffer.finishAtom()); michael@0: if (!stringRepr) michael@0: return nullptr; michael@0: michael@0: // Adjust the total size to be a multiple of the final alignment. michael@0: CheckedInt32 totalSize = roundUpToAlignment(sizeSoFar, alignment); michael@0: if (!totalSize.isValid()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_TOO_BIG); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Now create the resulting type descriptor. michael@0: RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr)); michael@0: if (!structTypePrototype) michael@0: return nullptr; michael@0: michael@0: Rooted descr(cx); michael@0: descr = NewObjectWithProto(cx, structTypePrototype, nullptr, michael@0: TenuredObject); michael@0: if (!descr) michael@0: return nullptr; michael@0: michael@0: descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(TypeDescr::Struct)); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(alignment)); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value())); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque)); michael@0: michael@0: // Construct for internal use an array with the name for each field. michael@0: { michael@0: RootedObject fieldNamesVec(cx); michael@0: fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(), michael@0: fieldNames.begin(), nullptr, michael@0: TenuredObject); michael@0: if (!fieldNamesVec) michael@0: return nullptr; michael@0: descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES, michael@0: ObjectValue(*fieldNamesVec)); michael@0: } michael@0: michael@0: // Construct for internal use an array with the type object for each field. michael@0: { michael@0: RootedObject fieldTypeVec(cx); michael@0: fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(), michael@0: fieldTypeObjs.begin(), nullptr, michael@0: TenuredObject); michael@0: if (!fieldTypeVec) michael@0: return nullptr; michael@0: descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES, michael@0: ObjectValue(*fieldTypeVec)); michael@0: } michael@0: michael@0: // Construct for internal use an array with the offset for each field. michael@0: { michael@0: RootedObject fieldOffsetsVec(cx); michael@0: fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(), michael@0: fieldOffsets.begin(), nullptr, michael@0: TenuredObject); michael@0: if (!fieldOffsetsVec) michael@0: return nullptr; michael@0: descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS, michael@0: ObjectValue(*fieldOffsetsVec)); michael@0: } michael@0: michael@0: // Create data properties fieldOffsets and fieldTypes michael@0: if (!JSObject::freeze(cx, userFieldOffsets)) michael@0: return nullptr; michael@0: if (!JSObject::freeze(cx, userFieldTypes)) michael@0: return nullptr; michael@0: RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets)); michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().fieldOffsets, michael@0: userFieldOffsetsValue, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes)); michael@0: if (!JSObject::defineProperty(cx, descr, cx->names().fieldTypes, michael@0: userFieldTypesValue, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!CreateUserSizeAndAlignmentProperties(cx, descr)) michael@0: return nullptr; michael@0: michael@0: Rooted prototypeObj(cx); michael@0: prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, descr, structTypePrototype); michael@0: if (!prototypeObj) michael@0: return nullptr; michael@0: michael@0: descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj)); michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, descr, prototypeObj)) michael@0: return nullptr; michael@0: michael@0: return descr; michael@0: } michael@0: michael@0: bool michael@0: StructMetaTypeDescr::construct(JSContext *cx, unsigned int argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (!args.isConstructing()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_NOT_FUNCTION, "StructType"); michael@0: return false; michael@0: } michael@0: michael@0: if (args.length() >= 1 && args[0].isObject()) { michael@0: RootedObject metaTypeDescr(cx, &args.callee()); michael@0: RootedObject fields(cx, &args[0].toObject()); michael@0: RootedObject obj(cx, create(cx, metaTypeDescr, fields)); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: size_t michael@0: StructTypeDescr::fieldCount() michael@0: { michael@0: return getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject().getDenseInitializedLength(); michael@0: } michael@0: michael@0: bool michael@0: StructTypeDescr::fieldIndex(jsid id, size_t *out) michael@0: { michael@0: JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject(); michael@0: size_t l = fieldNames.getDenseInitializedLength(); michael@0: for (size_t i = 0; i < l; i++) { michael@0: JSAtom &a = fieldNames.getDenseElement(i).toString()->asAtom(); michael@0: if (JSID_IS_ATOM(id, &a)) { michael@0: *out = i; michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: JSAtom & michael@0: StructTypeDescr::fieldName(size_t index) michael@0: { michael@0: JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject(); michael@0: return fieldNames.getDenseElement(index).toString()->asAtom(); michael@0: } michael@0: michael@0: int32_t michael@0: StructTypeDescr::fieldOffset(size_t index) michael@0: { michael@0: JSObject &fieldOffsets = michael@0: getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject(); michael@0: JS_ASSERT(index < fieldOffsets.getDenseInitializedLength()); michael@0: return fieldOffsets.getDenseElement(index).toInt32(); michael@0: } michael@0: michael@0: SizedTypeDescr& michael@0: StructTypeDescr::fieldDescr(size_t index) michael@0: { michael@0: JSObject &fieldDescrs = michael@0: getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject(); michael@0: JS_ASSERT(index < fieldDescrs.getDenseInitializedLength()); michael@0: return fieldDescrs.getDenseElement(index).toObject().as(); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * Creating the TypedObject "module" michael@0: * michael@0: * We create one global, `TypedObject`, which contains the following michael@0: * members: michael@0: * michael@0: * 1. uint8, uint16, etc michael@0: * 2. ArrayType michael@0: * 3. StructType michael@0: * michael@0: * Each of these is a function and hence their prototype is michael@0: * `Function.__proto__` (in terms of the JS Engine, they are not michael@0: * JSFunctions but rather instances of their own respective JSClasses michael@0: * which override the call and construct operations). michael@0: * michael@0: * Each type object also has its own `prototype` field. Therefore, michael@0: * using `StructType` as an example, the basic setup is: michael@0: * michael@0: * StructType --__proto__--> Function.__proto__ michael@0: * | michael@0: * prototype -- prototype --> { } michael@0: * | michael@0: * v michael@0: * { } -----__proto__--> Function.__proto__ michael@0: * michael@0: * When a new type object (e.g., an instance of StructType) is created, michael@0: * it will look as follows: michael@0: * michael@0: * MyStruct -__proto__-> StructType.prototype -__proto__-> Function.__proto__ michael@0: * | | michael@0: * | prototype michael@0: * | | michael@0: * | v michael@0: * prototype -----__proto__----> { } michael@0: * | michael@0: * v michael@0: * { } --__proto__-> Object.prototype michael@0: * michael@0: * Finally, when an instance of `MyStruct` is created, its michael@0: * structure is as follows: michael@0: * michael@0: * object -__proto__-> michael@0: * MyStruct.prototype -__proto__-> michael@0: * StructType.prototype.prototype -__proto__-> michael@0: * Object.prototype michael@0: */ michael@0: michael@0: // Here `T` is either `ScalarTypeDescr` or `ReferenceTypeDescr` michael@0: template michael@0: static bool michael@0: DefineSimpleTypeDescr(JSContext *cx, michael@0: Handle global, michael@0: HandleObject module, michael@0: typename T::Type type, michael@0: HandlePropertyName className) michael@0: { michael@0: RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); michael@0: if (!objProto) michael@0: return false; michael@0: michael@0: RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); michael@0: if (!funcProto) michael@0: return false; michael@0: michael@0: Rooted descr(cx); michael@0: descr = NewObjectWithProto(cx, funcProto, global, TenuredObject); michael@0: if (!descr) michael@0: return false; michael@0: michael@0: descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind)); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className)); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(type))); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(T::size(type))); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque)); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(type)); michael@0: michael@0: if (!CreateUserSizeAndAlignmentProperties(cx, descr)) michael@0: return false; michael@0: michael@0: if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods)) michael@0: return false; michael@0: michael@0: // Create the typed prototype for the scalar type. This winds up michael@0: // not being user accessible, but we still create one for consistency. michael@0: Rooted proto(cx); michael@0: proto = NewObjectWithProto(cx, objProto, nullptr, TenuredObject); michael@0: if (!proto) michael@0: return nullptr; michael@0: proto->initTypeDescrSlot(*descr); michael@0: descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto)); michael@0: michael@0: RootedValue descrValue(cx, ObjectValue(*descr)); michael@0: if (!JSObject::defineProperty(cx, module, className, michael@0: descrValue, nullptr, nullptr, 0)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: michael@0: template michael@0: static JSObject * michael@0: DefineMetaTypeDescr(JSContext *cx, michael@0: Handle global, michael@0: HandleObject module, michael@0: TypedObjectModuleObject::Slot protoSlot) michael@0: { michael@0: RootedAtom className(cx, Atomize(cx, T::class_.name, michael@0: strlen(T::class_.name))); michael@0: if (!className) michael@0: return nullptr; michael@0: michael@0: RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); michael@0: if (!funcProto) michael@0: return nullptr; michael@0: michael@0: // Create ctor.prototype, which inherits from Function.__proto__ michael@0: michael@0: RootedObject proto(cx, NewObjectWithProto(cx, funcProto, global, michael@0: SingletonObject)); michael@0: if (!proto) michael@0: return nullptr; michael@0: michael@0: // Create ctor.prototype.prototype, which inherits from Object.__proto__ michael@0: michael@0: RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); michael@0: if (!objProto) michael@0: return nullptr; michael@0: RootedObject protoProto(cx); michael@0: protoProto = NewObjectWithProto(cx, objProto, michael@0: global, SingletonObject); michael@0: if (!protoProto) michael@0: return nullptr; michael@0: michael@0: RootedValue protoProtoValue(cx, ObjectValue(*protoProto)); michael@0: if (!JSObject::defineProperty(cx, proto, cx->names().prototype, michael@0: protoProtoValue, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Create ctor itself michael@0: michael@0: const int constructorLength = 2; michael@0: RootedFunction ctor(cx); michael@0: ctor = global->createConstructor(cx, T::construct, className, constructorLength); michael@0: if (!ctor || michael@0: !LinkConstructorAndPrototype(cx, ctor, proto) || michael@0: !DefinePropertiesAndBrand(cx, proto, michael@0: T::typeObjectProperties, michael@0: T::typeObjectMethods) || michael@0: !DefinePropertiesAndBrand(cx, protoProto, michael@0: T::typedObjectProperties, michael@0: T::typedObjectMethods)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: module->initReservedSlot(protoSlot, ObjectValue(*proto)); michael@0: michael@0: return ctor; michael@0: } michael@0: michael@0: /* The initialization strategy for TypedObjects is mildly unusual michael@0: * compared to other classes. Because all of the types are members michael@0: * of a single global, `TypedObject`, we basically make the michael@0: * initializer for the `TypedObject` class populate the michael@0: * `TypedObject` global (which is referred to as "module" herein). michael@0: */ michael@0: bool michael@0: GlobalObject::initTypedObjectModule(JSContext *cx, Handle global) michael@0: { michael@0: RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); michael@0: if (!objProto) michael@0: return false; michael@0: michael@0: Rooted module(cx); michael@0: module = NewObjectWithProto(cx, objProto, global); michael@0: if (!module) michael@0: return false; michael@0: michael@0: if (!JS_DefineFunctions(cx, module, TypedObjectMethods)) michael@0: return false; michael@0: michael@0: // uint8, uint16, any, etc michael@0: michael@0: #define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_) \ michael@0: if (!DefineSimpleTypeDescr(cx, global, module, constant_, \ michael@0: cx->names().name_)) \ michael@0: return nullptr; michael@0: JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE) michael@0: #undef BINARYDATA_SCALAR_DEFINE michael@0: michael@0: #define BINARYDATA_REFERENCE_DEFINE(constant_, type_, name_) \ michael@0: if (!DefineSimpleTypeDescr(cx, global, module, constant_, \ michael@0: cx->names().name_)) \ michael@0: return nullptr; michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE) michael@0: #undef BINARYDATA_REFERENCE_DEFINE michael@0: michael@0: // ArrayType. michael@0: michael@0: RootedObject arrayType(cx); michael@0: arrayType = DefineMetaTypeDescr( michael@0: cx, global, module, TypedObjectModuleObject::ArrayTypePrototype); michael@0: if (!arrayType) michael@0: return nullptr; michael@0: michael@0: RootedValue arrayTypeValue(cx, ObjectValue(*arrayType)); michael@0: if (!JSObject::defineProperty(cx, module, cx->names().ArrayType, michael@0: arrayTypeValue, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // StructType. michael@0: michael@0: RootedObject structType(cx); michael@0: structType = DefineMetaTypeDescr( michael@0: cx, global, module, TypedObjectModuleObject::StructTypePrototype); michael@0: if (!structType) michael@0: return nullptr; michael@0: michael@0: RootedValue structTypeValue(cx, ObjectValue(*structType)); michael@0: if (!JSObject::defineProperty(cx, module, cx->names().StructType, michael@0: structTypeValue, michael@0: nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Everything is setup, install module on the global object: michael@0: RootedValue moduleValue(cx, ObjectValue(*module)); michael@0: global->setConstructor(JSProto_TypedObject, moduleValue); michael@0: if (!JSObject::defineProperty(cx, global, cx->names().TypedObject, michael@0: moduleValue, michael@0: nullptr, nullptr, michael@0: 0)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: return module; michael@0: } michael@0: michael@0: JSObject * michael@0: js_InitTypedObjectModuleObject(JSContext *cx, HandleObject obj) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: Rooted global(cx, &obj->as()); michael@0: return global->getOrCreateTypedObjectModule(cx); michael@0: } michael@0: michael@0: JSObject * michael@0: js_InitTypedObjectDummy(JSContext *cx, HandleObject obj) michael@0: { michael@0: /* michael@0: * This function is entered into the jsprototypes.h table michael@0: * as the initializer for `TypedObject`. It should not michael@0: * be executed via the `standard_class_atoms` mechanism. michael@0: */ michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("shouldn't be initializing TypedObject via the JSProtoKey initializer mechanism"); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * Typed objects michael@0: */ michael@0: michael@0: /*static*/ TypedObject * michael@0: TypedObject::createUnattached(JSContext *cx, michael@0: HandleTypeDescr descr, michael@0: int32_t length) michael@0: { michael@0: if (descr->opaque()) michael@0: return createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length); michael@0: else michael@0: return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length); michael@0: } michael@0: michael@0: michael@0: /*static*/ TypedObject * michael@0: TypedObject::createUnattachedWithClass(JSContext *cx, michael@0: const Class *clasp, michael@0: HandleTypeDescr type, michael@0: int32_t length) michael@0: { michael@0: JS_ASSERT(clasp == &TransparentTypedObject::class_ || michael@0: clasp == &OpaqueTypedObject::class_); michael@0: JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_TYPEDOBJ_SLOTS); michael@0: JS_ASSERT(clasp->hasPrivate()); michael@0: michael@0: RootedObject proto(cx); michael@0: if (type->is()) { michael@0: // FIXME Bug 929651 -- What prototype to use? michael@0: proto = type->global().getOrCreateObjectPrototype(cx); michael@0: } else { michael@0: RootedValue protoVal(cx); michael@0: if (!JSObject::getProperty(cx, type, type, michael@0: cx->names().prototype, &protoVal)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: proto = &protoVal.toObject(); michael@0: } michael@0: michael@0: RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr)); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->setPrivate(nullptr); michael@0: obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0)); michael@0: obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0)); michael@0: obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue()); michael@0: obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr)); michael@0: obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length)); michael@0: obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type)); michael@0: michael@0: // Tag the type object for this instance with the type michael@0: // representation, if that has not been done already. michael@0: if (!type->is()) { // FIXME Bug 929651 michael@0: RootedTypeObject typeObj(cx, obj->getType(cx)); michael@0: if (typeObj) { michael@0: if (!typeObj->addTypedObjectAddendum(cx, type)) michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: return static_cast(&*obj); michael@0: } michael@0: michael@0: void michael@0: TypedObject::attach(ArrayBufferObject &buffer, int32_t offset) michael@0: { michael@0: JS_ASSERT(offset >= 0); michael@0: JS_ASSERT((size_t) (offset + size()) <= buffer.byteLength()); michael@0: michael@0: buffer.addView(this); michael@0: InitArrayBufferViewDataPointer(this, &buffer, offset); michael@0: setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset)); michael@0: setReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, ObjectValue(buffer)); michael@0: } michael@0: michael@0: void michael@0: TypedObject::attach(TypedObject &typedObj, int32_t offset) michael@0: { michael@0: JS_ASSERT(!typedObj.owner().isNeutered()); michael@0: JS_ASSERT(typedObj.typedMem() != NULL); michael@0: michael@0: attach(typedObj.owner(), typedObj.offset() + offset); michael@0: } michael@0: michael@0: // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of michael@0: // the type `type`. `type` must not be an unsized array. michael@0: static int32_t michael@0: TypedObjLengthFromType(TypeDescr &descr) michael@0: { michael@0: switch (descr.kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::Struct: michael@0: case TypeDescr::X4: michael@0: return 0; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: return descr.as().length(); michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: MOZ_ASSUME_UNREACHABLE("TypedObjLengthFromType() invoked on unsized type"); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: michael@0: /*static*/ TypedObject * michael@0: TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type, michael@0: HandleTypedObject typedObj, int32_t offset) michael@0: { michael@0: JS_ASSERT(!typedObj->owner().isNeutered()); michael@0: JS_ASSERT(typedObj->typedMem() != NULL); michael@0: JS_ASSERT(offset <= typedObj->size()); michael@0: JS_ASSERT(offset + type->size() <= typedObj->size()); michael@0: michael@0: int32_t length = TypedObjLengthFromType(*type); michael@0: michael@0: const js::Class *clasp = typedObj->getClass(); michael@0: Rooted obj(cx); michael@0: obj = createUnattachedWithClass(cx, clasp, type, length); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->attach(*typedObj, offset); michael@0: return obj; michael@0: } michael@0: michael@0: /*static*/ TypedObject * michael@0: TypedObject::createZeroed(JSContext *cx, michael@0: HandleTypeDescr descr, michael@0: int32_t length) michael@0: { michael@0: // Create unattached wrapper object. michael@0: Rooted obj(cx, createUnattached(cx, descr, length)); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: // Allocate and initialize the memory for this instance. michael@0: // Also initialize the JS_TYPEDOBJ_SLOT_LENGTH slot. michael@0: switch (descr->kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::Struct: michael@0: case TypeDescr::X4: michael@0: case TypeDescr::SizedArray: michael@0: { michael@0: size_t totalSize = descr->as().size(); michael@0: Rooted buffer(cx); michael@0: buffer = ArrayBufferObject::create(cx, totalSize); michael@0: if (!buffer) michael@0: return nullptr; michael@0: descr->as().initInstances(cx->runtime(), buffer->dataPointer(), 1); michael@0: obj->attach(*buffer, 0); michael@0: return obj; michael@0: } michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: { michael@0: Rooted elementTypeRepr(cx); michael@0: elementTypeRepr = &descr->as().elementType(); michael@0: michael@0: CheckedInt32 totalSize = CheckedInt32(elementTypeRepr->size()) * length; michael@0: if (!totalSize.isValid()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPEDOBJECT_TOO_BIG); michael@0: return nullptr; michael@0: } michael@0: michael@0: Rooted buffer(cx); michael@0: buffer = ArrayBufferObject::create(cx, totalSize.value()); michael@0: if (!buffer) michael@0: return nullptr; michael@0: michael@0: if (length) michael@0: elementTypeRepr->initInstances(cx->runtime(), buffer->dataPointer(), length); michael@0: obj->attach(*buffer, 0); michael@0: return obj; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind"); michael@0: } michael@0: michael@0: static bool michael@0: ReportTypedObjTypeError(JSContext *cx, michael@0: const unsigned errorNumber, michael@0: HandleTypedObject obj) michael@0: { michael@0: // Serialize type string of obj michael@0: char *typeReprStr = JS_EncodeString(cx, &obj->typeDescr().stringRepr()); michael@0: if (!typeReprStr) michael@0: return false; michael@0: michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: errorNumber, typeReprStr); michael@0: michael@0: JS_free(cx, (void *) typeReprStr); michael@0: return false; michael@0: } michael@0: michael@0: /*static*/ void michael@0: TypedObject::obj_trace(JSTracer *trace, JSObject *object) michael@0: { michael@0: gc::MarkSlot(trace, &object->getReservedSlotRef(JS_TYPEDOBJ_SLOT_TYPE_DESCR), michael@0: "TypedObjectTypeDescr"); michael@0: michael@0: ArrayBufferViewObject::trace(trace, object); michael@0: michael@0: JS_ASSERT(object->is()); michael@0: TypedObject &typedObj = object->as(); michael@0: TypeDescr &descr = typedObj.typeDescr(); michael@0: if (descr.opaque()) { michael@0: uint8_t *mem = typedObj.typedMem(); michael@0: if (!mem) michael@0: return; // partially constructed michael@0: michael@0: if (typedObj.owner().isNeutered()) michael@0: return; michael@0: michael@0: switch (descr.kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::Struct: michael@0: case TypeDescr::SizedArray: michael@0: case TypeDescr::X4: michael@0: descr.as().traceInstances(trace, mem, 1); michael@0: break; michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: descr.as().elementType().traceInstances(trace, mem, typedObj.length()); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleObject objp, MutableHandleShape propp) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: michael@0: Rooted descr(cx, &obj->as().typeDescr()); michael@0: switch (descr->kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::X4: michael@0: break; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: case TypeDescr::UnsizedArray: michael@0: { michael@0: uint32_t index; michael@0: if (js_IdIsIndex(id, &index)) michael@0: return obj_lookupElement(cx, obj, index, objp, propp); michael@0: michael@0: if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: MarkNonNativePropertyFound(propp); michael@0: objp.set(obj); michael@0: return true; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case TypeDescr::Struct: michael@0: { michael@0: StructTypeDescr &structDescr = descr->as(); michael@0: size_t index; michael@0: if (structDescr.fieldIndex(id, &index)) { michael@0: MarkNonNativePropertyFound(propp); michael@0: objp.set(obj); michael@0: return true; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: objp.set(nullptr); michael@0: propp.set(nullptr); michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::lookupGeneric(cx, proto, id, objp, propp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_lookupProperty(JSContext *cx, michael@0: HandleObject obj, michael@0: HandlePropertyName name, michael@0: MutableHandleObject objp, michael@0: MutableHandleShape propp) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: return obj_lookupGeneric(cx, obj, id, objp, propp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index, michael@0: MutableHandleObject objp, MutableHandleShape propp) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: MarkNonNativePropertyFound(propp); michael@0: objp.set(obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ReportPropertyError(JSContext *cx, michael@0: const unsigned errorNumber, michael@0: HandleId id) michael@0: { michael@0: RootedString str(cx, IdToString(cx, id)); michael@0: if (!str) michael@0: return false; michael@0: michael@0: char *propName = JS_EncodeString(cx, str); michael@0: if (!propName) michael@0: return false; michael@0: michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: errorNumber, propName); michael@0: michael@0: JS_free(cx, propName); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs) michael@0: { michael@0: return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj, michael@0: HandlePropertyName name, HandleValue v, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs) michael@0: { michael@0: Rooted id(cx, NameToId(name)); michael@0: return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs) michael@0: { michael@0: AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); michael@0: Rooted id(cx); michael@0: if (!IndexToId(cx, index, &id)) michael@0: return false; michael@0: return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: HandleId id, MutableHandleValue vp) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: Rooted typedObj(cx, &obj->as()); michael@0: michael@0: // Dispatch elements to obj_getElement: michael@0: uint32_t index; michael@0: if (js_IdIsIndex(id, &index)) michael@0: return obj_getElement(cx, obj, receiver, index, vp); michael@0: michael@0: // Handle everything else here: michael@0: michael@0: switch (typedObj->typeDescr().kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: break; michael@0: michael@0: case TypeDescr::X4: michael@0: break; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: case TypeDescr::UnsizedArray: michael@0: if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: if (typedObj->owner().isNeutered() || !typedObj->typedMem()) { // unattached michael@0: JS_ReportErrorNumber( michael@0: cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); michael@0: return false; michael@0: } michael@0: michael@0: vp.setInt32(typedObj->length()); michael@0: return true; michael@0: } michael@0: break; michael@0: michael@0: case TypeDescr::Struct: { michael@0: Rooted descr(cx, &typedObj->typeDescr().as()); michael@0: michael@0: size_t fieldIndex; michael@0: if (!descr->fieldIndex(id, &fieldIndex)) michael@0: break; michael@0: michael@0: size_t offset = descr->fieldOffset(fieldIndex); michael@0: Rooted fieldType(cx, &descr->fieldDescr(fieldIndex)); michael@0: return Reify(cx, fieldType, typedObj, offset, vp); michael@0: } michael@0: } michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: vp.setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::getGeneric(cx, proto, receiver, id, vp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: HandlePropertyName name, MutableHandleValue vp) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: return obj_getGeneric(cx, obj, receiver, id, vp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: uint32_t index, MutableHandleValue vp) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: Rooted typedObj(cx, &obj->as()); michael@0: Rooted descr(cx, &typedObj->typeDescr()); michael@0: michael@0: switch (descr->kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::X4: michael@0: case TypeDescr::Struct: michael@0: break; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: return obj_getArrayElement(cx, typedObj, descr, michael@0: index, vp); michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: return obj_getArrayElement(cx, typedObj, descr, michael@0: index, vp); michael@0: } michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: vp.setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::getElement(cx, proto, receiver, index, vp); michael@0: } michael@0: michael@0: template michael@0: /*static*/ bool michael@0: TypedObject::obj_getArrayElement(JSContext *cx, michael@0: Handle typedObj, michael@0: Handle typeDescr, michael@0: uint32_t index, michael@0: MutableHandleValue vp) michael@0: { michael@0: JS_ASSERT(typeDescr->is()); michael@0: michael@0: if (index >= (size_t) typedObj->length()) { michael@0: vp.setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: Rooted elementType(cx, &typeDescr->as().elementType()); michael@0: size_t offset = elementType->size() * index; michael@0: return Reify(cx, elementType, typedObj, offset, vp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleValue vp, bool strict) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: Rooted typedObj(cx, &obj->as()); michael@0: michael@0: uint32_t index; michael@0: if (js_IdIsIndex(id, &index)) michael@0: return obj_setElement(cx, obj, index, vp, strict); michael@0: michael@0: switch (typedObj->typeDescr().kind()) { michael@0: case ScalarTypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: break; michael@0: michael@0: case ScalarTypeDescr::X4: michael@0: break; michael@0: michael@0: case ScalarTypeDescr::SizedArray: michael@0: case ScalarTypeDescr::UnsizedArray: michael@0: if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH); michael@0: return false; michael@0: } michael@0: break; michael@0: michael@0: case ScalarTypeDescr::Struct: { michael@0: Rooted descr(cx, &typedObj->typeDescr().as()); michael@0: michael@0: size_t fieldIndex; michael@0: if (!descr->fieldIndex(id, &fieldIndex)) michael@0: break; michael@0: michael@0: size_t offset = descr->fieldOffset(fieldIndex); michael@0: Rooted fieldType(cx, &descr->fieldDescr(fieldIndex)); michael@0: return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp); michael@0: } michael@0: } michael@0: michael@0: return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, michael@0: HandlePropertyName name, MutableHandleValue vp, michael@0: bool strict) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: return obj_setGeneric(cx, obj, id, vp, strict); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index, michael@0: MutableHandleValue vp, bool strict) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: Rooted typedObj(cx, &obj->as()); michael@0: Rooted descr(cx, &typedObj->typeDescr()); michael@0: michael@0: switch (descr->kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::X4: michael@0: case TypeDescr::Struct: michael@0: break; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: return obj_setArrayElement(cx, typedObj, descr, index, vp); michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: return obj_setArrayElement(cx, typedObj, descr, index, vp); michael@0: } michael@0: michael@0: return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj); michael@0: } michael@0: michael@0: template michael@0: /*static*/ bool michael@0: TypedObject::obj_setArrayElement(JSContext *cx, michael@0: Handle typedObj, michael@0: Handle descr, michael@0: uint32_t index, michael@0: MutableHandleValue vp) michael@0: { michael@0: JS_ASSERT(descr->is()); michael@0: michael@0: if (index >= (size_t) typedObj->length()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX); michael@0: return false; michael@0: } michael@0: michael@0: Rooted elementType(cx); michael@0: elementType = &descr->as().elementType(); michael@0: size_t offset = elementType->size() * index; michael@0: return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj, michael@0: HandleId id, unsigned *attrsp) michael@0: { michael@0: uint32_t index; michael@0: Rooted typedObj(cx, &obj->as()); michael@0: switch (typedObj->typeDescr().kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: break; michael@0: michael@0: case TypeDescr::X4: michael@0: break; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: case TypeDescr::UnsizedArray: michael@0: if (js_IdIsIndex(id, &index)) { michael@0: *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT; michael@0: return true; michael@0: } michael@0: if (JSID_IS_ATOM(id, cx->names().length)) { michael@0: *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; michael@0: return true; michael@0: } michael@0: break; michael@0: michael@0: case TypeDescr::Struct: michael@0: size_t index; michael@0: if (typedObj->typeDescr().as().fieldIndex(id, &index)) { michael@0: *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT; michael@0: return true; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: *attrsp = 0; michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::getGenericAttributes(cx, proto, id, attrsp); michael@0: } michael@0: michael@0: static bool michael@0: IsOwnId(JSContext *cx, HandleObject obj, HandleId id) michael@0: { michael@0: uint32_t index; michael@0: Rooted typedObj(cx, &obj->as()); michael@0: switch (typedObj->typeDescr().kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::X4: michael@0: return false; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: case TypeDescr::UnsizedArray: michael@0: return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length); michael@0: michael@0: case TypeDescr::Struct: michael@0: size_t index; michael@0: if (typedObj->typeDescr().as().fieldIndex(id, &index)) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj, michael@0: HandleId id, unsigned *attrsp) michael@0: { michael@0: if (IsOwnId(cx, obj, id)) michael@0: return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id); michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: *attrsp = 0; michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::setGenericAttributes(cx, proto, id, attrsp); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj, michael@0: HandlePropertyName name, bool *succeeded) michael@0: { michael@0: Rooted id(cx, NameToId(name)); michael@0: if (IsOwnId(cx, obj, id)) michael@0: return ReportPropertyError(cx, JSMSG_CANT_DELETE, id); michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: *succeeded = false; michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::deleteProperty(cx, proto, name, succeeded); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index, michael@0: bool *succeeded) michael@0: { michael@0: RootedId id(cx); michael@0: if (!IndexToId(cx, index, &id)) michael@0: return false; michael@0: michael@0: if (IsOwnId(cx, obj, id)) michael@0: return ReportPropertyError(cx, JSMSG_CANT_DELETE, id); michael@0: michael@0: RootedObject proto(cx, obj->getProto()); michael@0: if (!proto) { michael@0: *succeeded = false; michael@0: return true; michael@0: } michael@0: michael@0: return JSObject::deleteElement(cx, proto, index, succeeded); michael@0: } michael@0: michael@0: bool michael@0: TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, michael@0: MutableHandleValue statep, MutableHandleId idp) michael@0: { michael@0: int32_t index; michael@0: michael@0: JS_ASSERT(obj->is()); michael@0: Rooted typedObj(cx, &obj->as()); michael@0: Rooted descr(cx, &typedObj->typeDescr()); michael@0: switch (descr->kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::X4: michael@0: switch (enum_op) { michael@0: case JSENUMERATE_INIT_ALL: michael@0: case JSENUMERATE_INIT: michael@0: statep.setInt32(0); michael@0: idp.set(INT_TO_JSID(0)); michael@0: michael@0: case JSENUMERATE_NEXT: michael@0: case JSENUMERATE_DESTROY: michael@0: statep.setNull(); michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: case TypeDescr::UnsizedArray: michael@0: switch (enum_op) { michael@0: case JSENUMERATE_INIT_ALL: michael@0: case JSENUMERATE_INIT: michael@0: statep.setInt32(0); michael@0: idp.set(INT_TO_JSID(typedObj->length())); michael@0: break; michael@0: michael@0: case JSENUMERATE_NEXT: michael@0: index = static_cast(statep.toInt32()); michael@0: michael@0: if (index < typedObj->length()) { michael@0: idp.set(INT_TO_JSID(index)); michael@0: statep.setInt32(index + 1); michael@0: } else { michael@0: JS_ASSERT(index == typedObj->length()); michael@0: statep.setNull(); michael@0: } michael@0: michael@0: break; michael@0: michael@0: case JSENUMERATE_DESTROY: michael@0: statep.setNull(); michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case TypeDescr::Struct: michael@0: switch (enum_op) { michael@0: case JSENUMERATE_INIT_ALL: michael@0: case JSENUMERATE_INIT: michael@0: statep.setInt32(0); michael@0: idp.set(INT_TO_JSID(descr->as().fieldCount())); michael@0: break; michael@0: michael@0: case JSENUMERATE_NEXT: michael@0: index = static_cast(statep.toInt32()); michael@0: michael@0: if ((size_t) index < descr->as().fieldCount()) { michael@0: idp.set(AtomToId(&descr->as().fieldName(index))); michael@0: statep.setInt32(index + 1); michael@0: } else { michael@0: statep.setNull(); michael@0: } michael@0: michael@0: break; michael@0: michael@0: case JSENUMERATE_DESTROY: michael@0: statep.setNull(); michael@0: break; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ size_t michael@0: TypedObject::offsetOfOwnerSlot() michael@0: { michael@0: return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_OWNER); michael@0: } michael@0: michael@0: /* static */ size_t michael@0: TypedObject::offsetOfDataSlot() michael@0: { michael@0: // the offset of 7 is based on the alloc kind michael@0: return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA); michael@0: } michael@0: michael@0: /* static */ size_t michael@0: TypedObject::offsetOfByteOffsetSlot() michael@0: { michael@0: return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_BYTEOFFSET); michael@0: } michael@0: michael@0: void michael@0: TypedObject::neuter(void *newData) michael@0: { michael@0: setSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(0)); michael@0: setSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0)); michael@0: setSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0)); michael@0: setPrivate(newData); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * Typed Objects michael@0: */ michael@0: michael@0: const Class TransparentTypedObject::class_ = { michael@0: "TypedObject", michael@0: Class::NON_NATIVE | michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) | michael@0: JSCLASS_HAS_PRIVATE | michael@0: JSCLASS_IMPLEMENTS_BARRIERS, michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: TypedObject::obj_trace, michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: { michael@0: TypedObject::obj_lookupGeneric, michael@0: TypedObject::obj_lookupProperty, michael@0: TypedObject::obj_lookupElement, michael@0: TypedObject::obj_defineGeneric, michael@0: TypedObject::obj_defineProperty, michael@0: TypedObject::obj_defineElement, michael@0: TypedObject::obj_getGeneric, michael@0: TypedObject::obj_getProperty, michael@0: TypedObject::obj_getElement, michael@0: TypedObject::obj_setGeneric, michael@0: TypedObject::obj_setProperty, michael@0: TypedObject::obj_setElement, michael@0: TypedObject::obj_getGenericAttributes, michael@0: TypedObject::obj_setGenericAttributes, michael@0: TypedObject::obj_deleteProperty, michael@0: TypedObject::obj_deleteElement, michael@0: nullptr, nullptr, // watch/unwatch michael@0: nullptr, /* slice */ michael@0: TypedObject::obj_enumerate, michael@0: nullptr, /* thisObject */ michael@0: } michael@0: }; michael@0: michael@0: static int32_t michael@0: LengthForType(TypeDescr &descr) michael@0: { michael@0: switch (descr.kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::Struct: michael@0: case TypeDescr::X4: michael@0: return 0; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: return descr.as().length(); michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: return 0; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: michael@0: static bool michael@0: CheckOffset(int32_t offset, michael@0: int32_t size, michael@0: int32_t alignment, michael@0: int32_t bufferLength) michael@0: { michael@0: JS_ASSERT(size >= 0); michael@0: JS_ASSERT(alignment >= 0); michael@0: michael@0: // No negative offsets. michael@0: if (offset < 0) michael@0: return false; michael@0: michael@0: // Offset (plus size) must be fully contained within the buffer. michael@0: if (offset > bufferLength) michael@0: return false; michael@0: if (offset + size < offset) michael@0: return false; michael@0: if (offset + size > bufferLength) michael@0: return false; michael@0: michael@0: // Offset must be aligned. michael@0: if ((offset % alignment) != 0) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /*static*/ bool michael@0: TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: JS_ASSERT(args.callee().is()); michael@0: Rooted callee(cx, &args.callee().as()); michael@0: michael@0: // Typed object constructors for sized types are overloaded in michael@0: // three ways, in order of precedence: michael@0: // michael@0: // new TypeObj() michael@0: // new TypeObj(buffer, [offset]) michael@0: // new TypeObj(data) michael@0: michael@0: // Zero argument constructor: michael@0: if (args.length() == 0) { michael@0: int32_t length = LengthForType(*callee); michael@0: Rooted obj(cx, createZeroed(cx, callee, length)); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: // Buffer constructor. michael@0: if (args[0].isObject() && args[0].toObject().is()) { michael@0: Rooted buffer(cx); michael@0: buffer = &args[0].toObject().as(); michael@0: michael@0: if (callee->opaque() || buffer->isNeutered()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: int32_t offset; michael@0: if (args.length() >= 2 && !args[1].isUndefined()) { michael@0: if (!args[1].isInt32()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: offset = args[1].toInt32(); michael@0: } else { michael@0: offset = 0; michael@0: } michael@0: michael@0: if (args.length() >= 3 && !args[2].isUndefined()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: if (!CheckOffset(offset, callee->size(), callee->alignment(), michael@0: buffer->byteLength())) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Rooted obj(cx); michael@0: obj = TypedObject::createUnattached(cx, callee, LengthForType(*callee)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: obj->attach(*buffer, offset); michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: // Data constructor. michael@0: if (args[0].isObject()) { michael@0: // Create the typed object. michael@0: int32_t length = LengthForType(*callee); michael@0: Rooted obj(cx, createZeroed(cx, callee, length)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: // Initialize from `arg`. michael@0: if (!ConvertAndCopyTo(cx, obj, args[0])) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: // Something bogus. michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: /*static*/ bool michael@0: TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: JS_ASSERT(args.callee().is()); michael@0: Rooted callee(cx); michael@0: callee = &args.callee().as(); michael@0: michael@0: // Typed object constructors for unsized arrays are overloaded in michael@0: // four ways, in order of precedence: michael@0: // michael@0: // new TypeObj(buffer, [offset, [length]]) // [1] michael@0: // new TypeObj(length) // [1] michael@0: // new TypeObj(data) michael@0: // new TypeObj() michael@0: // michael@0: // [1] Explicit lengths only available for unsized arrays. michael@0: michael@0: // Zero argument constructor: michael@0: if (args.length() == 0) { michael@0: Rooted obj(cx, createZeroed(cx, callee, 0)); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: // Length constructor. michael@0: if (args[0].isInt32()) { michael@0: int32_t length = args[0].toInt32(); michael@0: if (length < 0) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return nullptr; michael@0: } michael@0: Rooted obj(cx, createZeroed(cx, callee, length)); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: // Buffer constructor. michael@0: if (args[0].isObject() && args[0].toObject().is()) { michael@0: Rooted buffer(cx); michael@0: buffer = &args[0].toObject().as(); michael@0: michael@0: if (callee->opaque()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: int32_t offset; michael@0: if (args.length() >= 2 && !args[1].isUndefined()) { michael@0: if (!args[1].isInt32()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: offset = args[1].toInt32(); michael@0: } else { michael@0: offset = 0; michael@0: } michael@0: michael@0: if (!CheckOffset(offset, 0, callee->alignment(), buffer->byteLength())) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: int32_t elemSize = callee->elementType().size(); michael@0: int32_t bytesRemaining = buffer->byteLength() - offset; michael@0: int32_t maximumLength = bytesRemaining / elemSize; michael@0: michael@0: int32_t length; michael@0: if (args.length() >= 3 && !args[2].isUndefined()) { michael@0: if (!args[2].isInt32()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: length = args[2].toInt32(); michael@0: michael@0: if (length < 0 || length > maximumLength) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: } else { michael@0: if ((bytesRemaining % elemSize) != 0) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: length = maximumLength; michael@0: } michael@0: michael@0: if (buffer->isNeutered()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Rooted obj(cx); michael@0: obj = TypedObject::createUnattached(cx, callee, length); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: obj->attach(args[0].toObject().as(), offset); michael@0: } michael@0: michael@0: // Data constructor for unsized values michael@0: if (args[0].isObject()) { michael@0: // Read length out of the object. michael@0: RootedObject arg(cx, &args[0].toObject()); michael@0: RootedValue lengthVal(cx); michael@0: if (!JSObject::getProperty(cx, arg, arg, cx->names().length, &lengthVal)) michael@0: return false; michael@0: if (!lengthVal.isInt32()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: int32_t length = lengthVal.toInt32(); michael@0: michael@0: // Check that length * elementSize does not overflow. michael@0: int32_t elementSize = callee->elementType().size(); michael@0: int32_t byteLength; michael@0: if (!SafeMul(elementSize, length, &byteLength)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: // Create the unsized array. michael@0: Rooted obj(cx, createZeroed(cx, callee, length)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: // Initialize from `arg` michael@0: if (!ConvertAndCopyTo(cx, obj, args[0])) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: // Something bogus. michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, michael@0: nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * Handles michael@0: */ michael@0: michael@0: const Class OpaqueTypedObject::class_ = { michael@0: "Handle", michael@0: Class::NON_NATIVE | michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) | michael@0: JSCLASS_HAS_PRIVATE | michael@0: JSCLASS_IMPLEMENTS_BARRIERS, michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: nullptr, /* call */ michael@0: nullptr, /* construct */ michael@0: nullptr, /* hasInstance */ michael@0: TypedObject::obj_trace, michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: { michael@0: TypedObject::obj_lookupGeneric, michael@0: TypedObject::obj_lookupProperty, michael@0: TypedObject::obj_lookupElement, michael@0: TypedObject::obj_defineGeneric, michael@0: TypedObject::obj_defineProperty, michael@0: TypedObject::obj_defineElement, michael@0: TypedObject::obj_getGeneric, michael@0: TypedObject::obj_getProperty, michael@0: TypedObject::obj_getElement, michael@0: TypedObject::obj_setGeneric, michael@0: TypedObject::obj_setProperty, michael@0: TypedObject::obj_setElement, michael@0: TypedObject::obj_getGenericAttributes, michael@0: TypedObject::obj_setGenericAttributes, michael@0: TypedObject::obj_deleteProperty, michael@0: TypedObject::obj_deleteElement, michael@0: nullptr, nullptr, // watch/unwatch michael@0: nullptr, // slice michael@0: TypedObject::obj_enumerate, michael@0: nullptr, /* thisObject */ michael@0: } michael@0: }; michael@0: michael@0: const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = { michael@0: {"move", {nullptr, nullptr}, 3, 0, "HandleMove"}, michael@0: {"get", {nullptr, nullptr}, 1, 0, "HandleGet"}, michael@0: {"set", {nullptr, nullptr}, 2, 0, "HandleSet"}, michael@0: {"isHandle", {nullptr, nullptr}, 1, 0, "HandleTest"}, michael@0: JS_FS_END michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * Intrinsics michael@0: */ michael@0: michael@0: bool michael@0: js::NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: michael@0: Rooted descr(cx, &args[0].toObject().as()); michael@0: int32_t length = TypedObjLengthFromType(*descr); michael@0: Rooted obj(cx); michael@0: obj = TypedObject::createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 3); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: JS_ASSERT(args[1].isObject() && args[1].toObject().is()); michael@0: JS_ASSERT(args[2].isInt32()); michael@0: michael@0: Rooted descr(cx, &args[0].toObject().as()); michael@0: Rooted typedObj(cx, &args[1].toObject().as()); michael@0: int32_t offset = args[2].toInt32(); michael@0: michael@0: Rooted obj(cx); michael@0: obj = TypedObject::createDerived(cx, descr, typedObj, offset); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::AttachTypedObject(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 3); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: JS_ASSERT(args[1].isObject() && args[1].toObject().is()); michael@0: JS_ASSERT(args[2].isInt32()); michael@0: michael@0: TypedObject &handle = args[0].toObject().as(); michael@0: TypedObject &target = args[1].toObject().as(); michael@0: JS_ASSERT(handle.typedMem() == nullptr); // must not be attached already michael@0: size_t offset = args[2].toInt32(); michael@0: handle.attach(target, offset); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachTypedObjectJitInfo, michael@0: AttachTypedObjectJitInfo, michael@0: js::AttachTypedObject); michael@0: michael@0: bool michael@0: js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: JS_ASSERT(args[1].isInt32()); michael@0: michael@0: TypedObject &typedObj = args[0].toObject().as(); michael@0: int32_t offset = args[1].toInt32(); michael@0: michael@0: JS_ASSERT(!typedObj.owner().isNeutered()); michael@0: JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already michael@0: michael@0: typedObj.setPrivate(typedObj.owner().dataPointer() + offset); michael@0: typedObj.setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset)); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: // Do not use JSNativeThreadSafeWrapper<> so that ion can reference michael@0: // this function more easily when inlining. michael@0: return SetTypedObjectOffset(cx, argc, vp); michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::intrinsic_SetTypedObjectOffsetJitInfo, michael@0: SetTypedObjectJitInfo, michael@0: SetTypedObjectOffset); michael@0: michael@0: bool michael@0: js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypeDescrJitInfo, ObjectIsTypeDescrJitInfo, michael@0: js::ObjectIsTypeDescr); michael@0: michael@0: bool michael@0: js::ObjectIsTypedObject(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypedObjectJitInfo, michael@0: ObjectIsTypedObjectJitInfo, michael@0: js::ObjectIsTypedObject); michael@0: michael@0: bool michael@0: js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsOpaqueTypedObjectJitInfo, michael@0: ObjectIsOpaqueTypedObjectJitInfo, michael@0: js::ObjectIsOpaqueTypedObject); michael@0: michael@0: bool michael@0: js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTransparentTypedObjectJitInfo, michael@0: ObjectIsTransparentTypedObjectJitInfo, michael@0: js::ObjectIsTransparentTypedObject); michael@0: michael@0: bool michael@0: js::TypeDescrIsSimpleType(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[0].toObject().is()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSimpleTypeJitInfo, michael@0: TypeDescrIsSimpleTypeJitInfo, michael@0: js::TypeDescrIsSimpleType); michael@0: michael@0: bool michael@0: js::TypeDescrIsArrayType(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[0].toObject().is()); michael@0: JSObject& obj = args[0].toObject(); michael@0: args.rval().setBoolean(obj.is() || michael@0: obj.is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsArrayTypeJitInfo, michael@0: TypeDescrIsArrayTypeJitInfo, michael@0: js::TypeDescrIsArrayType); michael@0: michael@0: bool michael@0: js::TypeDescrIsSizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[0].toObject().is()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSizedArrayTypeJitInfo, michael@0: TypeDescrIsSizedArrayTypeJitInfo, michael@0: js::TypeDescrIsSizedArrayType); michael@0: michael@0: bool michael@0: js::TypeDescrIsUnsizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isObject()); michael@0: JS_ASSERT(args[0].toObject().is()); michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsUnsizedArrayTypeJitInfo, michael@0: TypeDescrIsUnsizedArrayTypeJitInfo, michael@0: js::TypeDescrIsUnsizedArrayType); michael@0: michael@0: bool michael@0: js::TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: TypedObject &typedObj = args[0].toObject().as(); michael@0: args.rval().setBoolean(!typedObj.owner().isNeutered() && typedObj.typedMem() != nullptr); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypedObjectIsAttachedJitInfo, michael@0: TypedObjectIsAttachedJitInfo, michael@0: js::TypedObjectIsAttached); michael@0: michael@0: bool michael@0: js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 1); michael@0: JS_ASSERT(args[0].isNumber()); michael@0: args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber())); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo, michael@0: js::ClampToUint8); michael@0: michael@0: bool michael@0: js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 5); michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); michael@0: JS_ASSERT(args[1].isInt32()); michael@0: JS_ASSERT(args[2].isObject() && args[2].toObject().is()); michael@0: JS_ASSERT(args[3].isInt32()); michael@0: JS_ASSERT(args[4].isInt32()); michael@0: michael@0: TypedObject &targetTypedObj = args[0].toObject().as(); michael@0: int32_t targetOffset = args[1].toInt32(); michael@0: TypedObject &sourceTypedObj = args[2].toObject().as(); michael@0: int32_t sourceOffset = args[3].toInt32(); michael@0: int32_t size = args[4].toInt32(); michael@0: michael@0: JS_ASSERT(targetOffset >= 0); michael@0: JS_ASSERT(sourceOffset >= 0); michael@0: JS_ASSERT(size >= 0); michael@0: JS_ASSERT(size + targetOffset <= targetTypedObj.size()); michael@0: JS_ASSERT(size + sourceOffset <= sourceTypedObj.size()); michael@0: michael@0: uint8_t *target = targetTypedObj.typedMem(targetOffset); michael@0: uint8_t *source = sourceTypedObj.typedMem(sourceOffset); michael@0: memcpy(target, source, size); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy); michael@0: michael@0: bool michael@0: js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: Rooted global(cx, cx->global()); michael@0: JS_ASSERT(global); michael@0: args.rval().setObject(global->getTypedObjectModule()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: Rooted global(cx, cx->global()); michael@0: JS_ASSERT(global); michael@0: args.rval().setObject(global->float32x4TypeDescr()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: Rooted global(cx, cx->global()); michael@0: JS_ASSERT(global); michael@0: args.rval().setObject(global->int32x4TypeDescr()); michael@0: return true; michael@0: } michael@0: michael@0: #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name) \ michael@0: bool \ michael@0: js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \ michael@0: { \ michael@0: CallArgs args = CallArgsFromVp(argc, vp); \ michael@0: JS_ASSERT(args.length() == 3); \ michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); \ michael@0: JS_ASSERT(args[1].isInt32()); \ michael@0: JS_ASSERT(args[2].isNumber()); \ michael@0: \ michael@0: TypedObject &typedObj = args[0].toObject().as(); \ michael@0: int32_t offset = args[1].toInt32(); \ michael@0: \ michael@0: /* Should be guaranteed by the typed objects API: */ \ michael@0: JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ michael@0: \ michael@0: T *target = reinterpret_cast(typedObj.typedMem(offset)); \ michael@0: double d = args[2].toNumber(); \ michael@0: *target = ConvertScalar(d); \ michael@0: args.rval().setUndefined(); \ michael@0: return true; \ michael@0: } \ michael@0: \ michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo, \ michael@0: StoreScalar##T, \ michael@0: js::StoreScalar##T::Func); michael@0: michael@0: #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name) \ michael@0: bool \ michael@0: js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \ michael@0: { \ michael@0: CallArgs args = CallArgsFromVp(argc, vp); \ michael@0: JS_ASSERT(args.length() == 3); \ michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); \ michael@0: JS_ASSERT(args[1].isInt32()); \ michael@0: \ michael@0: TypedObject &typedObj = args[0].toObject().as(); \ michael@0: int32_t offset = args[1].toInt32(); \ michael@0: \ michael@0: /* Should be guaranteed by the typed objects API: */ \ michael@0: JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ michael@0: \ michael@0: T *target = reinterpret_cast(typedObj.typedMem(offset)); \ michael@0: store(target, args[2]); \ michael@0: args.rval().setUndefined(); \ michael@0: return true; \ michael@0: } \ michael@0: \ michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreReference##T::JitInfo, \ michael@0: StoreReference##T, \ michael@0: js::StoreReference##T::Func); michael@0: michael@0: #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name) \ michael@0: bool \ michael@0: js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \ michael@0: { \ michael@0: CallArgs args = CallArgsFromVp(argc, vp); \ michael@0: JS_ASSERT(args.length() == 2); \ michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); \ michael@0: JS_ASSERT(args[1].isInt32()); \ michael@0: \ michael@0: TypedObject &typedObj = args[0].toObject().as(); \ michael@0: int32_t offset = args[1].toInt32(); \ michael@0: \ michael@0: /* Should be guaranteed by the typed objects API: */ \ michael@0: JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ michael@0: \ michael@0: T *target = reinterpret_cast(typedObj.typedMem(offset)); \ michael@0: args.rval().setNumber((double) *target); \ michael@0: return true; \ michael@0: } \ michael@0: \ michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadScalar##T::JitInfo, LoadScalar##T, \ michael@0: js::LoadScalar##T::Func); michael@0: michael@0: #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name) \ michael@0: bool \ michael@0: js::LoadReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \ michael@0: { \ michael@0: CallArgs args = CallArgsFromVp(argc, vp); \ michael@0: JS_ASSERT(args.length() == 2); \ michael@0: JS_ASSERT(args[0].isObject() && args[0].toObject().is()); \ michael@0: JS_ASSERT(args[1].isInt32()); \ michael@0: \ michael@0: TypedObject &typedObj = args[0].toObject().as(); \ michael@0: int32_t offset = args[1].toInt32(); \ michael@0: \ michael@0: /* Should be guaranteed by the typed objects API: */ \ michael@0: JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \ michael@0: \ michael@0: T *target = reinterpret_cast(typedObj.typedMem(offset)); \ michael@0: load(target, args.rval()); \ michael@0: return true; \ michael@0: } \ michael@0: \ michael@0: JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo, \ michael@0: LoadReference##T, \ michael@0: js::LoadReference##T::Func); michael@0: michael@0: // Because the precise syntax for storing values/objects/strings michael@0: // differs, we abstract it away using specialized variants of the michael@0: // private methods `store()` and `load()`. michael@0: michael@0: void michael@0: StoreReferenceHeapValue::store(HeapValue *heap, const Value &v) michael@0: { michael@0: *heap = v; michael@0: } michael@0: michael@0: void michael@0: StoreReferenceHeapPtrObject::store(HeapPtrObject *heap, const Value &v) michael@0: { michael@0: JS_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused michael@0: *heap = v.toObjectOrNull(); michael@0: } michael@0: michael@0: void michael@0: StoreReferenceHeapPtrString::store(HeapPtrString *heap, const Value &v) michael@0: { michael@0: JS_ASSERT(v.isString()); // or else Store_string is being misused michael@0: *heap = v.toString(); michael@0: } michael@0: michael@0: void michael@0: LoadReferenceHeapValue::load(HeapValue *heap, michael@0: MutableHandleValue v) michael@0: { michael@0: v.set(*heap); michael@0: } michael@0: michael@0: void michael@0: LoadReferenceHeapPtrObject::load(HeapPtrObject *heap, michael@0: MutableHandleValue v) michael@0: { michael@0: if (*heap) michael@0: v.setObject(**heap); michael@0: else michael@0: v.setNull(); michael@0: } michael@0: michael@0: void michael@0: LoadReferenceHeapPtrString::load(HeapPtrString *heap, michael@0: MutableHandleValue v) michael@0: { michael@0: v.setString(*heap); michael@0: } michael@0: michael@0: // I was using templates for this stuff instead of macros, but ran michael@0: // into problems with the Unagi compiler. michael@0: JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL) michael@0: JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL) michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_IMPL) michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_IMPL) michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Walking memory michael@0: michael@0: template michael@0: static void michael@0: visitReferences(SizedTypeDescr &descr, michael@0: uint8_t *mem, michael@0: V& visitor) michael@0: { michael@0: if (descr.transparent()) michael@0: return; michael@0: michael@0: switch (descr.kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::X4: michael@0: return; michael@0: michael@0: case TypeDescr::Reference: michael@0: visitor.visitReference(descr.as(), mem); michael@0: return; michael@0: michael@0: case TypeDescr::SizedArray: michael@0: { michael@0: SizedArrayTypeDescr &arrayDescr = descr.as(); michael@0: SizedTypeDescr &elementDescr = arrayDescr.elementType(); michael@0: for (int32_t i = 0; i < arrayDescr.length(); i++) { michael@0: visitReferences(elementDescr, mem, visitor); michael@0: mem += elementDescr.size(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: case TypeDescr::UnsizedArray: michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("Only Sized Type representations"); michael@0: } michael@0: michael@0: case TypeDescr::Struct: michael@0: { michael@0: StructTypeDescr &structDescr = descr.as(); michael@0: for (size_t i = 0; i < structDescr.fieldCount(); i++) { michael@0: SizedTypeDescr &descr = structDescr.fieldDescr(i); michael@0: size_t offset = structDescr.fieldOffset(i); michael@0: visitReferences(descr, mem + offset, visitor); michael@0: } michael@0: return; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid type repr kind"); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Initializing instances michael@0: michael@0: namespace js { michael@0: class MemoryInitVisitor { michael@0: const JSRuntime *rt_; michael@0: michael@0: public: michael@0: MemoryInitVisitor(const JSRuntime *rt) michael@0: : rt_(rt) michael@0: {} michael@0: michael@0: void visitReference(ReferenceTypeDescr &descr, uint8_t *mem); michael@0: }; michael@0: } // namespace js michael@0: michael@0: void michael@0: js::MemoryInitVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem) michael@0: { michael@0: switch (descr.type()) { michael@0: case ReferenceTypeDescr::TYPE_ANY: michael@0: { michael@0: js::HeapValue *heapValue = reinterpret_cast(mem); michael@0: heapValue->init(UndefinedValue()); michael@0: return; michael@0: } michael@0: michael@0: case ReferenceTypeDescr::TYPE_OBJECT: michael@0: { michael@0: js::HeapPtrObject *objectPtr = michael@0: reinterpret_cast(mem); michael@0: objectPtr->init(nullptr); michael@0: return; michael@0: } michael@0: michael@0: case ReferenceTypeDescr::TYPE_STRING: michael@0: { michael@0: js::HeapPtrString *stringPtr = michael@0: reinterpret_cast(mem); michael@0: stringPtr->init(rt_->emptyString); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: michael@0: void michael@0: SizedTypeDescr::initInstances(const JSRuntime *rt, uint8_t *mem, size_t length) michael@0: { michael@0: JS_ASSERT(length >= 1); michael@0: michael@0: MemoryInitVisitor visitor(rt); michael@0: michael@0: // Initialize the 0th instance michael@0: memset(mem, 0, size()); michael@0: if (opaque()) michael@0: visitReferences(*this, mem, visitor); michael@0: michael@0: // Stamp out N copies of later instances michael@0: uint8_t *target = mem; michael@0: for (size_t i = 1; i < length; i++) { michael@0: target += size(); michael@0: memcpy(target, mem, size()); michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Tracing instances michael@0: michael@0: namespace js { michael@0: class MemoryTracingVisitor { michael@0: JSTracer *trace_; michael@0: michael@0: public: michael@0: michael@0: MemoryTracingVisitor(JSTracer *trace) michael@0: : trace_(trace) michael@0: {} michael@0: michael@0: void visitReference(ReferenceTypeDescr &descr, uint8_t *mem); michael@0: }; michael@0: } // namespace js michael@0: michael@0: void michael@0: js::MemoryTracingVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem) michael@0: { michael@0: switch (descr.type()) { michael@0: case ReferenceTypeDescr::TYPE_ANY: michael@0: { michael@0: js::HeapValue *heapValue = reinterpret_cast(mem); michael@0: gc::MarkValue(trace_, heapValue, "reference-val"); michael@0: return; michael@0: } michael@0: michael@0: case ReferenceTypeDescr::TYPE_OBJECT: michael@0: { michael@0: js::HeapPtrObject *objectPtr = michael@0: reinterpret_cast(mem); michael@0: if (*objectPtr) michael@0: gc::MarkObject(trace_, objectPtr, "reference-obj"); michael@0: return; michael@0: } michael@0: michael@0: case ReferenceTypeDescr::TYPE_STRING: michael@0: { michael@0: js::HeapPtrString *stringPtr = michael@0: reinterpret_cast(mem); michael@0: if (*stringPtr) michael@0: gc::MarkString(trace_, stringPtr, "reference-str"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: michael@0: void michael@0: SizedTypeDescr::traceInstances(JSTracer *trace, uint8_t *mem, size_t length) michael@0: { michael@0: MemoryTracingVisitor visitor(trace); michael@0: michael@0: for (size_t i = 0; i < length; i++) { michael@0: visitReferences(*this, mem, visitor); michael@0: mem += size(); michael@0: } michael@0: } michael@0: