# HG changeset patch # Parent 8c8ead9e54253771723eb13c9956c6a3194267d2 # User Nikhil Marathe diff --git a/js/src/js.msg b/js/src/js.msg --- a/js/src/js.msg +++ b/js/src/js.msg @@ -396,8 +396,9 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 34 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 343, 1, JSEXN_TYPEERR, "asm.js link error: {0}") MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 344, 1, JSEXN_ERR, "successfully compiled asm.js code ({0})") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") MSG_DEF(JSMSG_YIELD_IN_ARROW, 346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield") MSG_DEF(JSMSG_WRONG_VALUE, 347, 2, JSEXN_ERR, "expected {0} but found {1}") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 348, 1, JSEXN_ERR, "target for index {0} is not an integer") MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME,349, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups") MSG_DEF(JSMSG_DEPRECATED_SOURCE_MAP, 350, 0, JSEXN_SYNTAXERR, "Using //@ to indicate source map URL pragmas is deprecated. Use //# instead") +MSG_DEF(JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS, 351, 0, JSEXN_ERR, "Invalid arguments") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1828,17 +1828,17 @@ static const JSStdName standard_class_at {js_InitIntlClass, EAGER_ATOM_AND_CLASP(Intl)}, #endif {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Type)}, {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Data)}, #define BINARYDATA_NUMERIC_NAMES(constant_, type_)\ {js_InitBinaryDataClasses, EAGER_CLASS_ATOM(type_), &NumericTypeClasses[constant_]}, BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_NAMES) #undef BINARYDATA_NUMERIC_NAMES - {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(ArrayType)}, + {js_InitBinaryDataClasses, EAGER_CLASS_ATOM(ArrayType), &js::ArrayType::class_}, {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(StructType)}, {NULL, 0, NULL} }; /* * Table of top-level function and constant names and their init functions. * If you add a "standard" global function or property, remember to update * this table. diff --git a/js/src/jsbinarydata.cpp b/js/src/jsbinarydata.cpp --- a/js/src/jsbinarydata.cpp +++ b/js/src/jsbinarydata.cpp @@ -4,24 +4,46 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jsbinarydata.h" #include "mozilla/FloatingPoint.h" #include "jscompartment.h" +#include "jsfun.h" #include "jsobj.h" +#include "vm/String.h" +#include "vm/StringBuffer.h" +#include "vm/GlobalObject.h" + #include "jsatominlines.h" #include "jsobjinlines.h" -#include "vm/GlobalObject.h" +using namespace js; -using namespace js; +/* + * Reify() takes a complex binary data object `owner` and an offset and tries to + * convert the value of type `type` at that offset to a JS Value stored in + * `vp`. + * + * NOTE: `type` is NOT the type of `owner`, but the type of `owner.elementType` in + * case of BinaryArray or `owner[field].type` in case of BinaryStruct. + */ +static bool Reify(JSContext *cx, HandleObject type, HandleObject owner, + size_t offset, MutableHandleValue vp); + +/* + * ConvertAndCopyTo() converts `from` to type `type` and stores the result in + * `mem`, which MUST be pre-allocated to the appropriate size for instances of + * `type`. + */ +static bool ConvertAndCopyTo(JSContext *cx, HandleObject type, + HandleValue from, uint8_t *mem); JSBool TypeThrowError(JSContext *cx, unsigned argc, Value *vp) { return ReportIsNotFunction(cx, *vp); } JSBool DataThrowError(JSContext *cx, unsigned argc, Value *vp) { @@ -41,22 +63,117 @@ static void ReportTypeError(JSContext *cx, Value fromValue, JSString *toType) { const char *fnName = JS_EncodeString(cx, toType); ReportTypeError(cx, fromValue, fnName); JS_free(cx, (void *) fnName); } static inline bool -IsNumericType(JSObject *type) +IsNumericType(HandleObject type) { return type && &NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() && type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]; } +static inline bool +IsArrayType(HandleObject type) +{ + return type && type->hasClass(&ArrayType::class_); +} + +static inline bool +IsStructType(HandleObject type) +{ + return type && type->hasClass(&StructTypeClass); +} + +static inline bool +IsComplexType(HandleObject type) +{ + return IsArrayType(type) || IsStructType(type); +} + +static inline bool +IsBinaryType(HandleObject type) +{ + return IsNumericType(type) || IsComplexType(type); +} + +static inline bool +IsBinaryArray(HandleObject type) +{ + return type && type->hasClass(&BinaryArray::class_); +} + +static inline bool +IsBlock(HandleObject type) +{ + return type->hasClass(&BinaryArray::class_); + /* TODO || type->hasClass(&BinaryStruct::class_); */ +} + +static inline JSObject * +GetType(HandleObject block) +{ + JS_ASSERT(IsBlock(block)); + return block->getFixedSlot(SLOT_DATATYPE).toObjectOrNull(); +} + +static size_t +GetMemSize(JSContext *cx, HandleObject type) +{ + if (IsComplexType(type)) + return type->getFixedSlot(SLOT_MEMSIZE).toInt32(); + + RootedObject typeObj(cx, type); + RootedValue val(cx); + JS_ASSERT(IsNumericType(type)); + JSObject::getProperty(cx, typeObj, typeObj, cx->names().bytes, &val); + return val.toInt32(); +} + +static size_t +GetAlign(JSContext *cx, HandleObject type) +{ + if (IsComplexType(type)) + return type->getFixedSlot(SLOT_ALIGN).toInt32(); + + RootedObject typeObj(cx, type); + RootedValue val(cx); + JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() && + type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]); + JSObject::getProperty(cx, typeObj, typeObj, cx->names().bytes, &val); + return val.toInt32(); +} + +static bool +IsSame(JSContext *cx, HandleObject type1, HandleObject type2) +{ + JS_ASSERT(IsBinaryType(type1)); + JS_ASSERT(IsBinaryType(type2)); + + if (IsNumericType(type1)) { + return type1->hasClass(type2->getClass()); + } else if (IsArrayType(type1) && IsArrayType(type2)) { + if (ArrayType::length(cx, type1) != ArrayType::length(cx, type2)) + return false; + + RootedObject elementType1(cx, ArrayType::elementType(cx, type1)); + RootedObject elementType2(cx, ArrayType::elementType(cx, type2)); + return IsSame(cx, elementType1, elementType2); + } else if (IsStructType(type1) && IsStructType(type2)) { + // according to the spec, don't check individual fields. + // they both need to be the *same* struct type + return type1 == type2; + } + + return false; +} + template bool InRange(Input x) { return std::numeric_limits::min() <= x && x <= std::numeric_limits::max(); } @@ -174,63 +291,990 @@ NumericType::call(JSContext *cx, unsi JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, fnName, "0", "s"); JS_free(cx, (void *) fnName); return false; } RootedValue arg(cx, args[0]); T answer; - if (!cast(cx, arg, &answer)) - { + if (!cast(cx, arg, &answer)) { ReportTypeError(cx, arg, args.callee().getClass()->name); return false; } - // TODO Once reify is implemented (in a later patch) this will be replaced - // by a call to reify. - args.rval().set(NumberValue(answer)); + RootedValue reified(cx); + if (!NumericType::reify(cx, &answer, &reified)) { + return false; + } + + args.rval().set(reified); return true; } template JSBool NumericTypeToString(JSContext *cx, unsigned int argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_ASSERT(NUMERICTYPE_UINT8 <= N && N <= NUMERICTYPE_FLOAT64); JSString *s = JS_NewStringCopyZ(cx, NumericTypeClasses[N].name); args.rval().set(StringValue(s)); return true; } +/* + * When creating: + * var A = new ArrayType(uint8, 10) + * or + * var S = new StructType({...}) + * + * A.prototype.__proto__ === ArrayType.prototype.prototype (and similar for + * StructType). + * + * This function takes a reference to either ArrayType or StructType and + * returns a JSObject which can be set as A.prototype. + */ +JSObject * +SetupAndGetPrototypeObjectForComplexTypeInstance(JSContext *cx, + HandleObject complexTypeGlobal) +{ + RootedObject global(cx, cx->compartment()->maybeGlobal()); + RootedValue complexTypePrototypeVal(cx); + RootedValue complexTypePrototypePrototypeVal(cx); + + if (!JSObject::getProperty(cx, complexTypeGlobal, complexTypeGlobal, + cx->names().classPrototype, &complexTypePrototypeVal)) + return NULL; + + RootedObject complexTypePrototypeObj(cx, + complexTypePrototypeVal.toObjectOrNull()); + + if (!JSObject::getProperty(cx, complexTypePrototypeObj, + complexTypePrototypeObj, + cx->names().classPrototype, + &complexTypePrototypePrototypeVal)) + return NULL; + + RootedObject prototypeObj(cx, + NewObjectWithGivenProto(cx, &ObjectClass, NULL, global)); + + if (!JS_SetPrototype(cx, prototypeObj, + complexTypePrototypePrototypeVal.toObjectOrNull())) + return NULL; + + return prototypeObj; +} + +Class ArrayType::class_ = { + "ArrayType", + JSCLASS_HAS_RESERVED_SLOTS(TYPE_RESERVED_SLOTS) | + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType), + JS_PropertyStub, + JS_DeletePropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, + NULL, + NULL, + NULL, + BinaryArray::construct, + NULL +}; + +Class BinaryArray::class_ = { + "BinaryArray", + Class::NON_NATIVE | + JSCLASS_HAS_RESERVED_SLOTS(BLOCK_RESERVED_SLOTS) | + JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType), + JS_PropertyStub, + JS_DeletePropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + BinaryArray::finalize, + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* hasInstance */ + BinaryArray::obj_trace, + JS_NULL_CLASS_EXT, + { + BinaryArray::obj_lookupGeneric, + BinaryArray::obj_lookupProperty, + BinaryArray::obj_lookupElement, + BinaryArray::obj_lookupSpecial, + BinaryArray::obj_defineGeneric, + BinaryArray::obj_defineProperty, + BinaryArray::obj_defineElement, + BinaryArray::obj_defineSpecial, + BinaryArray::obj_getGeneric, + BinaryArray::obj_getProperty, + BinaryArray::obj_getElement, + BinaryArray::obj_getElementIfPresent, + BinaryArray::obj_getSpecial, + BinaryArray::obj_setGeneric, + BinaryArray::obj_setProperty, + BinaryArray::obj_setElement, + BinaryArray::obj_setSpecial, + BinaryArray::obj_getGenericAttributes, + BinaryArray::obj_getPropertyAttributes, + BinaryArray::obj_getElementAttributes, + BinaryArray::obj_getSpecialAttributes, + BinaryArray::obj_setGenericAttributes, + BinaryArray::obj_setPropertyAttributes, + BinaryArray::obj_setElementAttributes, + BinaryArray::obj_setSpecialAttributes, + BinaryArray::obj_deleteProperty, + BinaryArray::obj_deleteElement, + BinaryArray::obj_deleteSpecial, + BinaryArray::obj_enumerate, + NULL, /* thisObject */ + } +}; + +inline uint32_t +ArrayType::length(JSContext *cx, HandleObject obj) +{ + JS_ASSERT(obj && IsArrayType(obj)); + Value vp; + if (!JS_GetProperty(cx, obj, "length", &vp)) + return -1; + JS_ASSERT(vp.isInt32()); + JS_ASSERT(vp.toInt32() >= 0); + return (uint32_t) vp.toInt32(); +} + +inline JSObject * +ArrayType::elementType(JSContext *cx, HandleObject array) +{ + JS_ASSERT(IsArrayType(array)); + RootedObject arr(cx, array); + RootedValue elementTypeVal(cx); + if (!JSObject::getProperty(cx, arr, arr, + cx->names().elementType, &elementTypeVal)) + + JS_ASSERT(elementTypeVal.isObject()); + return elementTypeVal.toObjectOrNull(); +} + +bool +ArrayType::convertAndCopyTo(JSContext *cx, HandleObject exemplar, + HandleValue from, uint8_t *mem) +{ + if (!from.isObject()) { + // TODO better type reporting once toString is implemented + ReportTypeError(cx, from, "binary array"); + return false; + } + + RootedObject val(cx, from.toObjectOrNull()); + if (IsBlock(val)) { + RootedObject type(cx, GetType(val)); + if (IsSame(cx, exemplar, type)) { + uint8_t *priv = (uint8_t*) val->getPrivate(); + memcpy(mem, priv, GetMemSize(cx, exemplar)); + return true; + } + ReportTypeError(cx, from, "incompatible types"); + return false; + } + + RootedObject valRooted(cx, val); + RootedValue fromLenVal(cx); + if (!JSObject::getProperty(cx, valRooted, valRooted, + cx->names().length, &fromLenVal) || + !fromLenVal.isInt32()) + { + ReportTypeError(cx, from, "doesn't have a valid length"); + return false; + } + + uint32_t fromLen = fromLenVal.toInt32(); + + if (ArrayType::length(cx, exemplar) != fromLen) { + ReportTypeError(cx, from, "length mismatch"); + return false; + } + + RootedObject elementType(cx, ArrayType::elementType(cx, exemplar)); + + uint32_t offsetMult = GetMemSize(cx, elementType); + + for (uint32_t i = 0; i < fromLen; i++) { + RootedValue fromElem(cx); + if (!JSObject::getElement(cx, valRooted, valRooted, i, &fromElem)) { + return false; + } + + if (!ConvertAndCopyTo(cx, elementType, fromElem, + (uint8_t *) mem + (offsetMult * i))) { + ReportTypeError(cx, fromElem, "conversion error"); + return false; + } + } + + return true; +} + +inline bool +ArrayType::reify(JSContext *cx, HandleObject type, + HandleObject owner, size_t offset, MutableHandleValue to) +{ + JSObject *obj = BinaryArray::create(cx, type, owner, offset); + if (!obj) + return false; + to.setObject(*obj); + return true; +} + +JSObject * +ArrayType::create(JSContext *cx, HandleObject arrayTypeGlobal, + HandleObject elementType, uint32_t length) +{ + JS_ASSERT(elementType); + JS_ASSERT(IsBinaryType(elementType)); + + RootedObject obj(cx, NewBuiltinClassInstance(cx, &ArrayType::class_)); + if (!obj) + return NULL; + + if (!JS_DefineProperty(cx, obj, "elementType", ObjectValue(*elementType), + NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) + return NULL; + + if (!JS_DefineProperty(cx, obj, "length", + Int32Value(length), NULL, NULL, + JSPROP_READONLY | JSPROP_PERMANENT)) + return NULL; + + Value elementTypeBytes; + if (!JS_GetProperty(cx, elementType, "bytes", &elementTypeBytes)) + return NULL; + + JS_ASSERT(elementTypeBytes.isInt32()); + + /* since this is the JS visible size and maybe not + * the actual size in terms of memory layout, it is + * always elementType.bytes * length */ + if (!JS_DefineProperty(cx, obj, "bytes", + NumberValue(elementTypeBytes.toInt32() * length), + NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) + return NULL; + + obj->setFixedSlot(SLOT_MEMSIZE, + Int32Value(::GetMemSize(cx, elementType) * length)); + + obj->setFixedSlot(SLOT_ALIGN, Int32Value(::GetAlign(cx, elementType))); + + RootedObject prototypeObj(cx, + SetupAndGetPrototypeObjectForComplexTypeInstance(cx, arrayTypeGlobal)); + + if (!prototypeObj) + return NULL; + + if (!LinkConstructorAndPrototype(cx, obj, prototypeObj)) + return NULL; + + if (!JS_DefineFunction(cx, prototypeObj, "fill", BinaryArray::fill, 1, 0)) + return NULL; + + RootedId id(cx, NON_INTEGER_ATOM_TO_JSID(cx->names().length)); + unsigned flags = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; + + RootedObject global(cx, cx->compartment()->maybeGlobal()); + JSObject *getter = + NewFunction(cx, NullPtr(), BinaryArray::lengthGetter, + 0, JSFunction::NATIVE_FUN, global, NullPtr()); + if (!getter) + return NULL; + + RootedValue value(cx, UndefinedValue()); + if (!DefineNativeProperty(cx, prototypeObj, id, value, + JS_DATA_TO_FUNC_PTR(PropertyOp, getter), NULL, + flags, 0, 0)) + return NULL; + return obj; +} + JSBool -createArrayType(JSContext *cx, unsigned argc, Value *vp) +ArrayType::construct(JSContext *cx, unsigned argc, Value *vp) { - return false; + if (!JS_IsConstructing(cx, vp)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NOT_FUNCTION, "ArrayType"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + + if (argc != 2 || + !args[0].isObject() || + !args[1].isInt32() || + args[1].toInt32() <= 0) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS); + return false; + } + + RootedObject arrayTypeGlobal(cx, &args.callee()); + RootedObject elementType(cx, args[0].toObjectOrNull()); + + if (!IsBinaryType(elementType)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS); + return false; + } + + JSObject *obj = create(cx, arrayTypeGlobal, elementType, args[1].toInt32()); + if (!obj) + return false; + args.rval().setObject(*obj); + return true; } JSBool createStructType(JSContext *cx, unsigned argc, Value *vp) { return false; } JSBool DataInstanceUpdate(JSContext *cx, unsigned argc, Value *vp) { return false; } JSBool -ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, Value *vp) +ArrayType::repeat(JSContext *cx, unsigned int argc, Value *vp) { return false; } +JSBool +ArrayType::toString(JSContext *cx, unsigned int argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedObject thisObj(cx, args.thisv().toObjectOrNull()); + if (!IsArrayType(thisObj)) + return false; + + StringBuffer contents(cx); + contents.append("ArrayType("); + + RootedValue elementTypeVal(cx, ObjectValue(*elementType(cx, thisObj))); + JSString *str = ToString(cx, elementTypeVal); + + contents.append(str); + contents.append(", "); + + Value len = NumberValue(length(cx, thisObj)); + contents.append(JS_ValueToString(cx, len)); + contents.append(")"); + args.rval().setString(contents.finishString()); + return true; +} + +JSObject * +BinaryArray::createEmpty(JSContext *cx, HandleObject type) +{ + JS_ASSERT(IsArrayType(type)); + RootedObject typeRooted(cx, type); + + RootedValue protoVal(cx); + if (!JSObject::getProperty(cx, typeRooted, typeRooted, + cx->names().classPrototype, &protoVal)) + return NULL; + + RootedObject obj(cx, + NewObjectWithClassProto(cx, &BinaryArray::class_, + protoVal.toObjectOrNull(), NULL)); + obj->setFixedSlot(SLOT_DATATYPE, ObjectValue(*type)); + obj->setFixedSlot(SLOT_BLOCKREFOWNER, NullValue()); + return obj; +} + +JSObject * +BinaryArray::create(JSContext *cx, HandleObject type) +{ + JSObject *obj = createEmpty(cx, type); + if (!obj) + return NULL; + + int32_t memsize = GetMemSize(cx, type); + void *memory = JS_malloc(cx, memsize); + memset(memory, 0, memsize); + if (!memory) + return NULL; + obj->setPrivate(memory); + return obj; +} + +JSObject * +BinaryArray::create(JSContext *cx, HandleObject type, HandleValue initial) +{ + JSObject *obj = create(cx, type); + if (!obj) + return NULL; + + uint8_t *memory = (uint8_t*) obj->getPrivate(); + if (!ConvertAndCopyTo(cx, type, initial, memory)) + return NULL; + + return obj; +} + +JSObject * +BinaryArray::create(JSContext *cx, HandleObject type, + HandleObject owner, size_t offset) +{ + JS_ASSERT(IsBlock(owner)); + JSObject *obj = createEmpty(cx, type); + if (!obj) + return NULL; + + obj->setPrivate(((uint8_t *) owner->getPrivate()) + offset); + obj->setFixedSlot(SLOT_BLOCKREFOWNER, ObjectValue(*owner)); + return obj; +} + +JSBool +BinaryArray::construct(JSContext *cx, unsigned int argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedObject callee(cx, &args.callee()); + + if (!IsArrayType(callee)) { + ReportTypeError(cx, args.calleev(), "is not an ArrayType"); + return false; + } + + JSObject *obj = NULL; + if (argc == 1) { + RootedValue v(cx, args[0]); + obj = create(cx, callee, v); + } else { + obj = create(cx, callee); + } + + if (obj) + args.rval().setObject(*obj); + + return obj != NULL; +} + +void +BinaryArray::finalize(js::FreeOp *op, JSObject *obj) +{ + if (obj->getFixedSlot(SLOT_BLOCKREFOWNER).isNull()) + op->free_(obj->getPrivate()); + //TODO: Should this also trace the ArrayType instance? +} + +void +BinaryArray::obj_trace(JSTracer *tracer, JSObject *obj) +{ + Value val = obj->getFixedSlot(SLOT_BLOCKREFOWNER); + if (val.isObject()) { + HeapPtrObject owner(val.toObjectOrNull()); + MarkObject(tracer, &owner, "binaryarray.blockRefOwner"); + } +} + +JSBool +BinaryArray::lengthGetter(JSContext *cx, unsigned int argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject thisObj(cx, args.thisv().toObjectOrNull()); + JS_ASSERT(IsBinaryArray(thisObj)); + + RootedObject type(cx, GetType(thisObj)); + vp->setInt32(ArrayType::length(cx, type)); + return true; +} + +JSBool +BinaryArray::forEach(JSContext *cx, unsigned int argc, Value *vp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::subarray(JSContext *cx, unsigned int argc, Value *vp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::fill(JSContext *cx, unsigned int argc, Value *vp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleObject objp, MutableHandleShape propp) +{ + JS_ASSERT(IsBinaryArray(obj)); + RootedObject type(cx, GetType(obj)); + + uint32_t index; + if (js_IdIsIndex(id, &index) && + index < ArrayType::length(cx, type)) { + MarkNonNativePropertyFound(propp); + objp.set(obj); + return true; + } + + if (JSID_IS_ATOM(id, cx->names().length)) { + MarkNonNativePropertyFound(propp); + objp.set(obj); + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + objp.set(NULL); + propp.set(NULL); + return true; + } + + return JSObject::lookupGeneric(cx, proto, id, objp, propp); +} + +JSBool +BinaryArray::obj_lookupProperty(JSContext *cx, + HandleObject obj, + HandlePropertyName name, + MutableHandleObject objp, + MutableHandleShape propp) +{ + RootedId id(cx, NameToId(name)); + return obj_lookupGeneric(cx, obj, id, objp, propp); +} + +JSBool +BinaryArray::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index, + MutableHandleObject objp, MutableHandleShape propp) +{ + JS_ASSERT(IsBinaryArray(obj)); + RootedObject type(cx, GetType(obj)); + + if (index < ArrayType::length(cx, type)) { + MarkNonNativePropertyFound(propp); + objp.set(obj); + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (proto) + return JSObject::lookupElement(cx, proto, index, objp, propp); + + objp.set(NULL); + propp.set(NULL); + return true; +} + +JSBool +BinaryArray::obj_lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, + MutableHandleObject objp, MutableHandleShape propp) +{ + RootedId id(cx, SPECIALID_TO_JSID(sid)); + return obj_lookupGeneric(cx, obj, id, objp, propp); +} + +JSBool +BinaryArray::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, + HandleValue v, PropertyOp getter, + StrictPropertyOp setter, unsigned attrs) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_defineProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, HandleValue v, + PropertyOp getter, StrictPropertyOp setter, + unsigned attrs) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, + HandleValue v, PropertyOp getter, + StrictPropertyOp setter, unsigned attrs) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_defineSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, HandleValue v, + PropertyOp getter, StrictPropertyOp setter, + unsigned attrs) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, + HandleId id, MutableHandleValue vp) +{ + uint32_t index; + if (js_IdIsIndex(id, &index)) { + return obj_getElement(cx, obj, receiver, index, vp); + } + + RootedValue idValue(cx, IdToValue(id)); + Rooted name(cx, ToAtom(cx, idValue)->asPropertyName()); + return obj_getProperty(cx, obj, receiver, name, vp); +} + +JSBool +BinaryArray::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, + HandlePropertyName name, MutableHandleValue vp) +{ + RootedObject proto(cx, obj->getProto()); + if (!proto) { + vp.setUndefined(); + return true; + } + + return JSObject::getProperty(cx, proto, receiver, name, vp); +} + +JSBool +BinaryArray::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver, + uint32_t index, MutableHandleValue vp) +{ + RootedObject type(cx, GetType(obj)); + + if (index < ArrayType::length(cx, type)) { + RootedObject elementType(cx, ArrayType::elementType(cx, type)); + size_t offset = GetMemSize(cx, elementType) * index; + return Reify(cx, elementType, obj, offset, vp); + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + vp.setUndefined(); + return true; + } + + return JSObject::getElement(cx, proto, receiver, index, vp); +} + +JSBool +BinaryArray::obj_getElementIfPresent(JSContext *cx, HandleObject obj, + HandleObject receiver, uint32_t index, + MutableHandleValue vp, bool *present) +{ + RootedObject type(cx, GetType(obj)); + + if (index < ArrayType::length(cx, type)) { + *present = true; + return obj_getElement(cx, obj, receiver, index, vp); + } + + *present = false; + vp.setUndefined(); + return true; +} + +JSBool +BinaryArray::obj_getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver, + HandleSpecialId sid, MutableHandleValue vp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, JSBool strict) +{ + uint32_t index; + if (js_IdIsIndex(id, &index)) { + return obj_setElement(cx, obj, index, vp, strict); + } + + if (JSID_IS_ATOM(id, cx->names().length)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, + NULL, JSMSG_CANT_REDEFINE_ARRAY_LENGTH); + return false; + } + + return true; +} + +JSBool +BinaryArray::obj_setProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, MutableHandleValue vp, + JSBool strict) +{ + RootedId id(cx, NameToId(name)); + return obj_setGeneric(cx, obj, id, vp, strict); +} + +JSBool +BinaryArray::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index, + MutableHandleValue vp, JSBool strict) +{ + RootedObject type(cx, GetType(obj)); + RootedValue elementTypeVal(cx); + if (!JSObject::getProperty(cx, type, type, cx->names().elementType, + &elementTypeVal)) + return false; + + RootedObject elementType(cx, elementTypeVal.toObjectOrNull()); + uint32_t offset = GetMemSize(cx, elementType) * index; + + bool result = + ConvertAndCopyTo(cx, elementType, vp, + ((uint8_t*) obj->getPrivate()) + offset ); + + if (!result) { + ReportTypeError(cx, vp, elementType->getClass()->name); + return false; + } + + return true; +} + +JSBool +BinaryArray::obj_setSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, MutableHandleValue vp, + JSBool strict) +{ + RootedId id(cx, SPECIALID_TO_JSID(sid)); + return obj_setGeneric(cx, obj, id, vp, strict); +} + +JSBool +BinaryArray::obj_getGenericAttributes(JSContext *cx, HandleObject obj, + HandleId id, unsigned *attrsp) +{ + uint32_t index; + RootedObject type(cx, GetType(obj)); + + if (js_IdIsIndex(id, &index) && + index < ArrayType::length(cx, type)) { + *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT; // should we report JSPROP_INDEX? + return true; + } + + if (JSID_IS_ATOM(id, cx->names().length)) { + *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; + return true; + } + + return false; +} + +JSBool +BinaryArray::obj_getPropertyAttributes(JSContext *cx, HandleObject obj, + HandlePropertyName name, unsigned *attrsp) +{ + RootedId id(cx, NameToId(name)); + return obj_getGenericAttributes(cx, obj, id, attrsp); +} + +JSBool +BinaryArray::obj_getElementAttributes(JSContext *cx, HandleObject obj, + uint32_t index, unsigned *attrsp) +{ + RootedId id(cx, INT_TO_JSID(index)); + return obj_getGenericAttributes(cx, obj, id, attrsp); +} + +JSBool +BinaryArray::obj_getSpecialAttributes(JSContext *cx, HandleObject obj, + HandleSpecialId sid, unsigned *attrsp) +{ + RootedId id(cx, SPECIALID_TO_JSID(sid)); + return obj_getGenericAttributes(cx, obj, id, attrsp); +} + +JSBool +BinaryArray::obj_setGenericAttributes(JSContext *cx, HandleObject obj, + HandleId id, unsigned *attrsp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_setPropertyAttributes(JSContext *cx, HandleObject obj, + HandlePropertyName name, unsigned *attrsp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_setElementAttributes(JSContext *cx, HandleObject obj, + uint32_t index, unsigned *attrsp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_setSpecialAttributes(JSContext *cx, HandleObject obj, + HandleSpecialId sid, unsigned *attrsp) +{ + JS_ASSERT(0); + return false; +} + +JSBool +BinaryArray::obj_deleteProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, JSBool *succeeded) +{ + JS_ASSERT(0); + *succeeded = false; + return false; +} + +JSBool +BinaryArray::obj_deleteElement(JSContext *cx, HandleObject obj, + uint32_t index, JSBool *succeeded) +{ + JS_ASSERT(0); + *succeeded = false; + return false; +} + +JSBool +BinaryArray::obj_deleteSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, JSBool *succeeded) +{ + JS_ASSERT(0); + *succeeded = false; + return false; +} + +JSBool +BinaryArray::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, + MutableHandleValue statep, MutableHandleId idp) +{ + JS_ASSERT(IsBinaryArray(obj)); + + RootedObject type(cx, GetType(obj)); + + uint32_t index; + switch (enum_op) { + case JSENUMERATE_INIT_ALL: + case JSENUMERATE_INIT: + statep.setInt32(0); + idp.set(INT_TO_JSID(ArrayType::length(cx, type))); + break; + + case JSENUMERATE_NEXT: + index = static_cast(statep.toInt32()); + + if (index < ArrayType::length(cx, type)) { + idp.set(INT_TO_JSID(index)); + statep.setInt32(index + 1); + } else { + JS_ASSERT(index == ArrayType::length(cx, type)); + statep.setNull(); + } + + break; + + case JSENUMERATE_DESTROY: + statep.setNull(); + break; + } + + return true; +} + +static bool +Reify(JSContext *cx, HandleObject type, + HandleObject owner, size_t offset, MutableHandleValue to) +{ + if (IsArrayType(type)) { + return ArrayType::reify(cx, type, owner, offset, to); + } + if (type->hasClass(&StructTypeClass)) + JS_ASSERT(0); + JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() && + type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]); + +#define REIFY_CASES(constant_, type_)\ + case constant_:\ + return NumericType::reify(cx,\ + ((uint8_t *) owner->getPrivate()) + offset, to); + + switch(type->getFixedSlot(0).toInt32()) { + BINARYDATA_FOR_EACH_NUMERIC_TYPES(REIFY_CASES); + default: + abort(); + } +#undef REIFY_CASES + return false; +} + +static bool +ConvertAndCopyTo(JSContext *cx, HandleObject type, HandleValue from, uint8_t *mem) +{ + if (IsComplexType(type)) { + uint8_t *block = NULL; + if (IsArrayType(type)) { + if (!ArrayType::convertAndCopyTo(cx, type, from, mem)) + return false; + } else if (type->hasClass(&StructTypeClass)) { + JS_ASSERT(0); + } else { + JS_ASSERT(0); + } + + return true; + } + + JS_ASSERT(&NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() && + type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]); + +#define CONVERT_CASES(constant_, type_)\ + case constant_:\ + {\ + type_##_t temp;\ + bool ok = NumericType::convert(cx, from, &temp);\ + if (!ok)\ + return false;\ + memcpy(mem, &temp, sizeof(type_##_t));\ + return true; } // TODO use bytes property + + switch(type->getFixedSlot(0).toInt32()) { + BINARYDATA_FOR_EACH_NUMERIC_TYPES(CONVERT_CASES); + default: + abort(); + } +#undef CONVERT_CASES + return false; +} + bool GlobalObject::initDataObject(JSContext *cx, Handle global) { RootedObject DataProto(cx); DataProto = NewObjectWithGivenProto(cx, &DataClass, global->getOrCreateObjectPrototype(cx), global, SingletonObject); if (!DataProto) @@ -279,19 +1323,21 @@ GlobalObject::initTypeObject(JSContext * TypeCtor, TypeProto)) return false; global->setReservedSlot(JSProto_Type, ObjectValue(*TypeCtor)); return true; } static JSObject * -SetupComplexHeirarchy(JSContext *cx, Handle global, - HandleObject complexObject) +SetupComplexHeirarchy(JSContext *cx, HandleObject obj, JSProtoKey protoKey, + HandleObject complexObject, MutableHandleObject proto, + MutableHandleObject protoProto) { + Rooted global(cx, &obj->as()); // get the 'Type' constructor RootedObject TypeObject(cx, global->getOrCreateTypeObject(cx)); if (!TypeObject) return NULL; // Set complexObject.__proto__ = Type if (!JS_SetPrototype(cx, complexObject, TypeObject)) return NULL; @@ -304,69 +1350,99 @@ SetupComplexHeirarchy(JSContext *cx, Han if (!JSObject::getProperty(cx, DataObject, DataObject, cx->names().classPrototype, &DataProtoVal)) return NULL; RootedObject DataProto(cx, DataProtoVal.toObjectOrNull()); if (!DataProto) return NULL; - // Set complexObject.prototype.__proto__ = Data - RootedObject prototypeObj(cx, JS_NewObject(cx, NULL, NULL, global)); + RootedObject prototypeObj(cx, + NewObjectWithGivenProto(cx, &ObjectClass, NULL, global)); + if (!prototypeObj) + return NULL; if (!LinkConstructorAndPrototype(cx, complexObject, prototypeObj)) return NULL; + if (!DefineConstructorAndPrototype(cx, global, protoKey, + complexObject, prototypeObj)) + return NULL; + // Set complexObject.prototype.__proto__ = Data if (!JS_SetPrototype(cx, prototypeObj, DataObject)) return NULL; + proto.set(prototypeObj); + // Set complexObject.prototype.prototype.__proto__ = Data.prototype // TODO does this have to actually be a Class so we can set accessor // properties etc? RootedObject prototypePrototypeObj(cx, JS_NewObject(cx, NULL, NULL, global)); if (!LinkConstructorAndPrototype(cx, prototypeObj, prototypePrototypeObj)) return NULL; if (!JS_SetPrototype(cx, prototypePrototypeObj, DataProto)) return NULL; + protoProto.set(prototypePrototypeObj); + return complexObject; } static JSObject * -InitComplexClasses(JSContext *cx, Handle global) +InitArrayType(JSContext *cx, HandleObject obj) { - RootedFunction ArrayTypeFun(cx, - JS_DefineFunction(cx, global, "ArrayType", createArrayType, 1, 0)); + JS_ASSERT(obj->isNative()); + Rooted global(cx, &obj->as()); + RootedFunction ctor(cx, + global->createConstructor(cx, ArrayType::construct, + cx->names().ArrayType, 2)); - if (!ArrayTypeFun) + if (!ctor) return NULL; - if (!SetupComplexHeirarchy(cx, global, ArrayTypeFun)) + RootedObject proto(cx); + RootedObject protoProto(cx); + if (!SetupComplexHeirarchy(cx, obj, JSProto_ArrayType, + ctor, &proto, &protoProto)) return NULL; - // ArrayType.prototype.repeat - RootedValue ArrayTypePrototypeVal(cx); - if (!JSObject::getProperty(cx, ArrayTypeFun, ArrayTypeFun, - cx->names().classPrototype, &ArrayTypePrototypeVal)) + if (!JS_DefineFunction(cx, proto, "repeat", ArrayType::repeat, 1, 0)) return NULL; - if (!JS_DefineFunction(cx, ArrayTypePrototypeVal.toObjectOrNull(), "repeat", - ArrayTypeObject::repeat, 1, 0)) + if (!JS_DefineFunction(cx, proto, "toString", ArrayType::toString, 0, 0)) return NULL; + if (!JS_DefineFunction(cx, protoProto, "forEach", BinaryArray::forEach, 1, 0)) + return NULL; + + if (!JS_DefineFunction(cx, protoProto, "subarray", + BinaryArray::subarray, 1, 0)) + return NULL; + + return proto; +} + +static JSObject * +InitStructType(JSContext *cx, HandleObject obj) +{ + JS_ASSERT(obj->isNative()); + Rooted global(cx, &obj->as()); RootedFunction StructTypeFun(cx, - JS_DefineFunction(cx, global, "StructType", createStructType, 1, 0)); - + global->createConstructor(cx, createStructType, + cx->names().StructType, 1)); if (!StructTypeFun) return NULL; - if (!SetupComplexHeirarchy(cx, global, StructTypeFun)) + RootedObject proto(cx); + RootedObject protoProto(cx); + if (!SetupComplexHeirarchy(cx, obj, JSProto_StructType, + StructTypeFun, &proto, &protoProto)) return NULL; - return global; + return proto; } JSObject * js_InitBinaryDataClasses(JSContext *cx, HandleObject obj) { JS_ASSERT(obj->is()); Rooted global(cx, &obj->as()); @@ -388,12 +1464,16 @@ js_InitBinaryDataClasses(JSContext *cx, \ if (!JS_DefineFunction(cx, numFun, "toString",\ NumericTypeToString, 0, 0))\ return NULL;\ } while(0); BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_DEFINE) #undef BINARYDATA_NUMERIC_DEFINE - if (!InitComplexClasses(cx, global)) + if (!InitArrayType(cx, obj)) return NULL; + + if (!InitStructType(cx, obj)) + return NULL; + return global; } diff --git a/js/src/jsbinarydata.h b/js/src/jsbinarydata.h --- a/js/src/jsbinarydata.h +++ b/js/src/jsbinarydata.h @@ -8,20 +8,16 @@ #define jsbinarydata_h #include "jsapi.h" #include "jsobj.h" #include "jsfriendapi.h" #include "gc/Heap.h" namespace js { -class Block : public gc::Cell -{ -}; - typedef float float32_t; typedef double float64_t; enum { NUMERICTYPE_UINT8 = 0, NUMERICTYPE_UINT16, NUMERICTYPE_UINT32, NUMERICTYPE_UINT64, @@ -37,16 +33,17 @@ enum { enum TypeCommonSlots { SLOT_MEMSIZE = 0, SLOT_ALIGN, TYPE_RESERVED_SLOTS }; enum BlockCommonSlots { SLOT_DATATYPE = 0, + SLOT_BLOCKREFOWNER, BLOCK_RESERVED_SLOTS }; static Class DataClass = { "Data", JSCLASS_HAS_CACHED_PROTO(JSProto_Data), JS_PropertyStub, JS_DeletePropertyStub, @@ -135,43 +132,211 @@ bool NumericType::reify(JSCon NULL,\ NULL\ }, static Class NumericTypeClasses[NUMERICTYPES] = { BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES) }; -static Class ArrayTypeClass = { - "ArrayType", - JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType), - JS_PropertyStub, - JS_DeletePropertyStub, - JS_PropertyStub, - JS_StrictPropertyStub, - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub +/* This represents the 'A' and it's [[Prototype]] chain + * in: + * A = new ArrayType(Type, N); + * a = new A(); + */ +class ArrayType : public JSObject +{ + private: + static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal, + HandleObject elementType, uint32_t length); + public: + static Class class_; + + static JSBool construct(JSContext *cx, unsigned int argc, jsval *vp); + static JSBool repeat(JSContext *cx, unsigned int argc, jsval *vp); + + static JSBool toString(JSContext *cx, unsigned int argc, jsval *vp); + + static uint32_t length(JSContext *cx, HandleObject obj); + static JSObject *elementType(JSContext *cx, HandleObject obj); + static bool convertAndCopyTo(JSContext *cx, HandleObject exemplar, + HandleValue from, uint8_t *mem); + static bool reify(JSContext *cx, HandleObject type, HandleObject owner, + size_t offset, MutableHandleValue to); +}; + +/* This represents the 'a' and it's [[Prototype]] chain */ +class BinaryArray +{ + private: + static JSObject *createEmpty(JSContext *cx, HandleObject type); + + // creates initialized memory of size of type + static JSObject *create(JSContext *cx, HandleObject type); + // attempts to [[Convert]] + static JSObject *create(JSContext *cx, HandleObject type, + HandleValue initial); + + public: + static Class class_; + + // uses passed block as memory + static JSObject *create(JSContext *cx, HandleObject type, + HandleObject owner, size_t offset); + static JSBool construct(JSContext *cx, unsigned int argc, jsval *vp); + + static void finalize(FreeOp *op, JSObject *obj); + static void obj_trace(JSTracer *tracer, JSObject *obj); + + static JSBool forEach(JSContext *cx, unsigned int argc, jsval *vp); + static JSBool subarray(JSContext *cx, unsigned int argc, jsval *vp); + static JSBool fill(JSContext *cx, unsigned int argc, jsval *vp); + + static JSBool obj_lookupGeneric(JSContext *cx, HandleObject obj, + HandleId id, MutableHandleObject objp, + MutableHandleShape propp); + + static JSBool obj_lookupProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, + MutableHandleObject objp, + MutableHandleShape propp); + + static JSBool obj_lookupElement(JSContext *cx, HandleObject obj, + uint32_t index, MutableHandleObject objp, + MutableHandleShape propp); + + static JSBool obj_lookupSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, + MutableHandleObject objp, + MutableHandleShape propp); + + static JSBool obj_defineGeneric(JSContext *cx, HandleObject obj, + HandleId id, HandleValue v, + PropertyOp getter, + StrictPropertyOp setter, + unsigned attrs); + + static JSBool obj_defineProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, HandleValue v, + PropertyOp getter, + StrictPropertyOp setter, + unsigned attrs); + + static JSBool obj_defineElement(JSContext *cx, HandleObject obj, + uint32_t index, HandleValue v, + PropertyOp getter, + StrictPropertyOp setter, + unsigned attrs); + + static JSBool obj_defineSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, HandleValue v, + PropertyOp getter, + StrictPropertyOp setter, + unsigned attrs); + + static JSBool obj_getGeneric(JSContext *cx, HandleObject obj, + HandleObject receiver, + HandleId id, + MutableHandleValue vp); + + static JSBool obj_getProperty(JSContext *cx, HandleObject obj, + HandleObject receiver, + HandlePropertyName name, + MutableHandleValue vp); + + static JSBool obj_getElement(JSContext *cx, HandleObject obj, + HandleObject receiver, + uint32_t index, + MutableHandleValue vp); + + static JSBool obj_getElementIfPresent(JSContext *cx, HandleObject obj, + HandleObject receiver, + uint32_t index, + MutableHandleValue vp, + bool *present); + + static JSBool obj_getSpecial(JSContext *cx, HandleObject obj, + HandleObject receiver, + HandleSpecialId sid, + MutableHandleValue vp); + + static JSBool obj_setGeneric(JSContext *cx, HandleObject obj, + HandleId id, MutableHandleValue vp, + JSBool strict); + + static JSBool obj_setProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, + MutableHandleValue vp, + JSBool strict); + + static JSBool obj_setElement(JSContext *cx, HandleObject obj, + uint32_t index, MutableHandleValue vp, + JSBool strict); + + static JSBool obj_setSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, + MutableHandleValue vp, + JSBool strict); + + static JSBool obj_getGenericAttributes(JSContext *cx, HandleObject obj, + HandleId id, unsigned *attrsp); + + static JSBool obj_getPropertyAttributes(JSContext *cx, HandleObject obj, + HandlePropertyName name, + unsigned *attrsp); + + static JSBool obj_getElementAttributes(JSContext *cx, HandleObject obj, + uint32_t index, unsigned *attrsp); + + static JSBool obj_getSpecialAttributes(JSContext *cx, HandleObject obj, + HandleSpecialId sid, + unsigned *attrsp); + + static JSBool obj_setGenericAttributes(JSContext *cx, HandleObject obj, + HandleId id, unsigned *attrsp); + + static JSBool obj_setPropertyAttributes(JSContext *cx, HandleObject obj, + HandlePropertyName name, + unsigned *attrsp); + + static JSBool obj_setElementAttributes(JSContext *cx, HandleObject obj, + uint32_t index, unsigned *attrsp); + + static JSBool obj_setSpecialAttributes(JSContext *cx, HandleObject obj, + HandleSpecialId sid, + unsigned *attrsp); + + static JSBool obj_deleteProperty(JSContext *cx, HandleObject obj, + HandlePropertyName name, + JSBool *succeeded); + + static JSBool obj_deleteElement(JSContext *cx, HandleObject obj, + uint32_t index, JSBool *succeeded); + + static JSBool obj_deleteSpecial(JSContext *cx, HandleObject obj, + HandleSpecialId sid, JSBool *succeeded); + + static JSBool obj_enumerate(JSContext *cx, HandleObject obj, + JSIterateOp enum_op, + MutableHandleValue statep, + MutableHandleId idp); + + static JSBool lengthGetter(JSContext *cx, unsigned int argc, jsval *vp); + }; static Class StructTypeClass = { "StructType", JSCLASS_HAS_CACHED_PROTO(JSProto_StructType), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub }; - -class ArrayTypeObject : public JSObject -{ - public: - static JSBool repeat(JSContext *cx, unsigned int argc, jsval *vp); -}; } extern JS_FRIEND_API(JSObject *) js_InitBinaryDataClasses(JSContext *cx, js::HandleObject obj); #endif /* jsbinarydata_h */ diff --git a/js/src/tests/ecma_6/BinaryData/arraytype.js b/js/src/tests/ecma_6/BinaryData/arraytype.js new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/BinaryData/arraytype.js @@ -0,0 +1,122 @@ +var BUGNUMBER = 578700; +var summary = 'ArrayType implementation'; + +print(BUGNUMBER + ": " + summary); + +function assertThrows(f) { + var ok = false; + try { + f(); + } catch (exc) { + ok = true; + } + if (!ok) + throw new TypeError("Assertion failed: " + f + " did not throw as expected"); +} + + +assertEq(typeof ArrayType.prototype.prototype.forEach == "function", true); +assertEq(typeof ArrayType.prototype.prototype.subarray == "function", true); + +assertThrows(function() ArrayType(uint8, 10)); +assertThrows(function() new ArrayType()); +assertThrows(function() new ArrayType("")); +assertThrows(function() new ArrayType(5)); +assertThrows(function() new ArrayType(uint8, -1)); +var A = new ArrayType(uint8, 10); +assertEq(A.__proto__, ArrayType.prototype); +assertEq(A.length, 10); +assertEq(A.elementType, uint8); +assertEq(A.bytes, 10); +assertEq(A.toString(), "ArrayType(uint8, 10)"); + +assertEq(A.prototype.__proto__, ArrayType.prototype.prototype); +assertEq(typeof A.prototype.fill, "function"); + +var a = new A(); +assertEq(a.__proto__, A.prototype); +assertEq(a.length, 10); + +assertThrows(function() a.length = 2); + +for (var i = 0; i < a.length; i++) + a[i] = i*2; + +for (var i = 0; i < a.length; i++) + assertEq(a[i], i*2); + + +assertThrows(function() a[i] = 257); + +assertEq(a[a.length], undefined); + +// constructor takes initial value +var b = new A(a); +for (var i = 0; i < a.length; i++) + assertEq(a[i], i*2); + +var b = new A([0, 1, 0, 1, 0, 1, 0, 1, 0, 1]); +for (var i = 0; i < b.length; i++) + assertEq(b[i], i%2); + + +assertThrows(function() new A(5)); +assertThrows(function() new A(/fail/)); +// Length different +assertThrows(function() new A([0, 1, 0, 1, 0, 1, 0, 1, 0])); + +var Vec3 = new ArrayType(float32, 3); +var Sprite = new ArrayType(Vec3, 3); // say for position, velocity, and direction +assertEq(Sprite.elementType, Vec3); +assertEq(Sprite.elementType.elementType, float32); + + +var mario = new Sprite(); +// setting using binary data +mario[0] = new Vec3([1, 0, 0]); +// setting using JS array conversion +mario[1] = [1, 1.414, 3.14]; + +assertEq(mario[0].length, 3); +assertEq(mario[0][0], 1); +assertEq(mario[0][1], 0); +assertEq(mario[0][2], 0); + +assertThrows(function() mario[1] = 5); +assertThrows(function() mario[1][1] = []); + + +// ok this is just for kicks +var AllSprites = new ArrayType(Sprite, 65536); +var as = new AllSprites(); +assertEq(as.length, 65536); + + +as.foo = "bar"; + +var indexPropDesc = Object.getOwnPropertyDescriptor(as, '0'); +assertEq(typeof indexPropDesc == "undefined", false); +assertEq(indexPropDesc.configurable, false); +assertEq(indexPropDesc.enumerable, true); +assertEq(indexPropDesc.writable, true); + + +var lengthPropDesc = Object.getOwnPropertyDescriptor(as, 'length'); +assertEq(typeof lengthPropDesc == "undefined", false); +assertEq(lengthPropDesc.configurable, false); +assertEq(lengthPropDesc.enumerable, false); +assertEq(lengthPropDesc.writable, false); + +assertThrows(function() Object.defineProperty(o, "foo", { value: "bar" })); + +// check if a reference acts the way it should +var AA = new ArrayType(new ArrayType(uint8, 5), 5); +var aa = new AA(); +var aa0 = aa[0]; +aa[0] = [0,1,2,3,4]; +for (var i = 0; i < aa0.length; i++) + assertEq(aa0[i], i); + +if (typeof reportCompare === "function") + reportCompare(true, true); +print("Tests complete"); diff --git a/js/src/tests/ecma_6/BinaryData/memory.js b/js/src/tests/ecma_6/BinaryData/memory.js new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/BinaryData/memory.js @@ -0,0 +1,41 @@ +var BUGNUMBER = 578700; +var summary = 'BinaryData memory check'; + +var spin = function() { + for (var i = 0; i < 10000; i++) + ; +} + +print(BUGNUMBER + ": " + summary); + +var AA = new ArrayType(new ArrayType(uint8, 5), 5); +var aa = new AA(); +var aa0 = aa[0]; +aa[0] = [0,1,2,3,4]; + +aa = null; + +gc(); +spin(); + +for (var i = 0; i < aa0.length; i++) + assertEq(aa0[i], i); + +var AAA = new ArrayType(AA, 5); +var aaa = new AAA(); +var a0 = aaa[0][0]; + +for (var i = 0; i < a0.length; i++) + assertEq(a0[i], 0); + +aaa[0] = [[0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4]]; + +aaa = null; +gc(); +spin(); +for (var i = 0; i < a0.length; i++) + assertEq(a0[i], i); + +if (typeof reportCompare === "function") + reportCompare(true, true); +print("Tests complete"); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -15,16 +15,17 @@ #define FOR_EACH_COMMON_PROPERTYNAME(macro) \ macro(anonymous, anonymous, "anonymous") \ macro(apply, apply, "apply") \ macro(arguments, arguments, "arguments") \ macro(buffer, buffer, "buffer") \ macro(builder, builder, "builder") \ macro(byteLength, byteLength, "byteLength") \ macro(byteOffset, byteOffset, "byteOffset") \ + macro(bytes, bytes, "bytes") \ macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \ macro(call, call, "call") \ macro(callee, callee, "callee") \ macro(caller, caller, "caller") \ macro(callFunction, callFunction, "callFunction") \ macro(caseFirst, caseFirst, "caseFirst") \ macro(classPrototype, classPrototype, "prototype") \ macro(Collator, Collator, "Collator") \ @@ -41,16 +42,17 @@ macro(decodeURI, decodeURI, "decodeURI") \ macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \ macro(defineProperty, defineProperty, "defineProperty") \ macro(defineGetter, defineGetter, "__defineGetter__") \ macro(defineSetter, defineSetter, "__defineSetter__") \ macro(delete, delete_, "delete") \ macro(deleteProperty, deleteProperty, "deleteProperty") \ macro(each, each, "each") \ + macro(elementType, elementType, "elementType") \ macro(empty, empty, "") \ macro(encodeURI, encodeURI, "encodeURI") \ macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \ macro(enumerable, enumerable, "enumerable") \ macro(enumerate, enumerate, "enumerate") \ macro(escape, escape, "escape") \ macro(eval, eval, "eval") \ macro(false, false_, "false") \