author | Nicholas D. Matsakis <nmatsakis@mozilla.com> |
Mon, 30 Sep 2013 10:19:09 -0400 | |
changeset 159317 | d58ca9a622c04d83f16077d8cfc5ab69e8b868bd |
parent 159316 | 957d85b31ff325d765c2b5bc09d85c201072362e |
child 159318 | c996211971a85633a6d2a60c58921566e765e90f |
push id | 37258 |
push user | nmatsakis@mozilla.com |
push date | Sat, 07 Dec 2013 01:44:00 +0000 |
treeherder | mozilla-inbound@c996211971a8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | sfink |
bugs | 922115 |
milestone | 28.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/TypeRepresentation.cpp +++ b/js/src/builtin/TypeRepresentation.cpp @@ -63,18 +63,23 @@ TypeRepresentationHasher::match(TypeRepr return matchReferences(key1->asReference(), key2->asReference()); case TypeRepresentation::X4: return matchX4s(key1->asX4(), key2->asX4()); case TypeRepresentation::Struct: return matchStructs(key1->asStruct(), key2->asStruct()); - case TypeRepresentation::Array: - return matchArrays(key1->asArray(), key2->asArray()); + case TypeRepresentation::SizedArray: + return matchSizedArrays(key1->asSizedArray(), + key2->asSizedArray()); + + case TypeRepresentation::UnsizedArray: + return matchUnsizedArrays(key1->asUnsizedArray(), + key2->asUnsizedArray()); } MOZ_ASSUME_UNREACHABLE("Invalid kind"); } bool TypeRepresentationHasher::matchScalars(ScalarTypeRepresentation *key1, ScalarTypeRepresentation *key2) @@ -110,41 +115,52 @@ TypeRepresentationHasher::matchStructs(S if (key1->field(i).typeRepr != key2->field(i).typeRepr) return false; } return true; } bool -TypeRepresentationHasher::matchArrays(ArrayTypeRepresentation *key1, - ArrayTypeRepresentation *key2) +TypeRepresentationHasher::matchSizedArrays(SizedArrayTypeRepresentation *key1, + SizedArrayTypeRepresentation *key2) { // We assume that these pointers have been canonicalized: return key1->element() == key2->element() && key1->length() == key2->length(); } +bool +TypeRepresentationHasher::matchUnsizedArrays(UnsizedArrayTypeRepresentation *key1, + UnsizedArrayTypeRepresentation *key2) +{ + // We assume that these pointers have been canonicalized: + return key1->element() == key2->element(); +} + HashNumber TypeRepresentationHasher::hash(TypeRepresentation *key) { switch (key->kind()) { case TypeRepresentation::Scalar: return hashScalar(key->asScalar()); case TypeRepresentation::Reference: return hashReference(key->asReference()); case TypeRepresentation::X4: return hashX4(key->asX4()); case TypeRepresentation::Struct: return hashStruct(key->asStruct()); - case TypeRepresentation::Array: - return hashArray(key->asArray()); + case TypeRepresentation::UnsizedArray: + return hashUnsizedArray(key->asUnsizedArray()); + + case TypeRepresentation::SizedArray: + return hashSizedArray(key->asSizedArray()); } MOZ_ASSUME_UNREACHABLE("Invalid kind"); } HashNumber TypeRepresentationHasher::hashScalar(ScalarTypeRepresentation *key) { @@ -170,102 +186,118 @@ TypeRepresentationHasher::hashStruct(Str for (HashNumber i = 0; i < key->fieldCount(); i++) { hash = AddToHash(hash, JSID_BITS(key->field(i).id.get())); hash = AddToHash(hash, key->field(i).typeRepr); } return hash; } HashNumber -TypeRepresentationHasher::hashArray(ArrayTypeRepresentation *key) +TypeRepresentationHasher::hashSizedArray(SizedArrayTypeRepresentation *key) { return HashGeneric(key->kind(), key->element(), key->length()); } +HashNumber +TypeRepresentationHasher::hashUnsizedArray(UnsizedArrayTypeRepresentation *key) +{ + return HashGeneric(key->kind(), key->element()); +} + /////////////////////////////////////////////////////////////////////////// // Constructors -TypeRepresentation::TypeRepresentation(Kind kind, size_t size, - size_t align, bool opaque) - : size_(size), - alignment_(align), - kind_(kind), +TypeRepresentation::TypeRepresentation(Kind kind, bool opaque) + : kind_(kind), opaque_(opaque) {} +SizedTypeRepresentation::SizedTypeRepresentation(Kind kind, bool opaque, + size_t size, size_t align) + : TypeRepresentation(kind, opaque), + size_(size), + alignment_(align) +{} + static size_t ScalarSizes[] = { #define SCALAR_SIZE(_kind, _type, _name) \ sizeof(_type), JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0 #undef SCALAR_SIZE }; ScalarTypeRepresentation::ScalarTypeRepresentation(Type type) - : TypeRepresentation(Scalar, ScalarSizes[type], ScalarSizes[type], false), + : SizedTypeRepresentation(Scalar, false, ScalarSizes[type], ScalarSizes[type]), type_(type) { } static size_t X4Sizes[] = { #define X4_SIZE(_kind, _type, _name) \ sizeof(_type) * 4, JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0 #undef X4_SIZE }; X4TypeRepresentation::X4TypeRepresentation(Type type) - : TypeRepresentation(X4, X4Sizes[type], X4Sizes[type], false), + : SizedTypeRepresentation(X4, false, X4Sizes[type], X4Sizes[type]), type_(type) { } ReferenceTypeRepresentation::ReferenceTypeRepresentation(Type type) - : TypeRepresentation(Reference, 0, 1, true), + : SizedTypeRepresentation(Reference, true, 0, 1), type_(type) { switch (type) { case TYPE_ANY: size_ = sizeof(js::HeapValue); alignment_ = MOZ_ALIGNOF(js::HeapValue); break; case TYPE_OBJECT: case TYPE_STRING: size_ = sizeof(js::HeapPtrObject); alignment_ = MOZ_ALIGNOF(js::HeapPtrObject); break; } } -ArrayTypeRepresentation::ArrayTypeRepresentation(TypeRepresentation *element, - size_t length) - : TypeRepresentation(Array, element->size() * length, - element->alignment(), element->opaque()), +SizedArrayTypeRepresentation::SizedArrayTypeRepresentation(SizedTypeRepresentation *element, + size_t length) + : SizedTypeRepresentation(SizedArray, element->opaque(), + element->size() * length, element->alignment()), element_(element), length_(length) { } +UnsizedArrayTypeRepresentation::UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element) + : TypeRepresentation(UnsizedArray, element->opaque()), + element_(element) +{ +} + static inline size_t alignTo(size_t address, size_t align) { JS_ASSERT(IsPowerOfTwo(align)); return (address + align - 1) & -align; } StructField::StructField(size_t index, jsid &id, - TypeRepresentation *typeRepr, + SizedTypeRepresentation *typeRepr, size_t offset) : index(index), id(id), typeRepr(typeRepr), offset(offset) {} StructTypeRepresentation::StructTypeRepresentation() - : TypeRepresentation(Struct, 0, 1, false), + : SizedTypeRepresentation(Struct, false, 0, 1), fieldCount_(0) // see ::init() below! { // note: size_, alignment_, and opaque_ are computed in ::init() below } bool StructTypeRepresentation::init(JSContext *cx, AutoIdVector &ids, @@ -280,17 +312,18 @@ StructTypeRepresentation::init(JSContext // consistency across build environments. uint32_t totalSize = 0; // These will be adjusted in the loop below: alignment_ = 1; opaque_ = false; for (size_t i = 0; i < ids.length(); i++) { - TypeRepresentation *fieldTypeRepr = fromOwnerObject(*typeReprOwners[i]); + SizedTypeRepresentation *fieldTypeRepr = + fromOwnerObject(*typeReprOwners[i])->asSized(); if (fieldTypeRepr->opaque()) opaque_ = true; uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment()); if (alignedSize < totalSize) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG); @@ -351,23 +384,31 @@ TypeRepresentation::addToTableOrFree(JSC js_free(this); return nullptr; } ownerObject->setPrivate(this); // Assign the various reserved slots: ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_KIND, Int32Value(kind())); - ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_SIZE, Int32Value(size())); - ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_ALIGNMENT, Int32Value(alignment())); + + if (isSized()) { + ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_SIZE, + Int32Value(asSized()->size())); + ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_ALIGNMENT, + Int32Value(asSized()->alignment())); + } switch (kind()) { - case Array: + case UnsizedArray: + break; + + case SizedArray: ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_LENGTH, - Int32Value(asArray()->length())); + Int32Value(asSizedArray()->length())); break; case Scalar: ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_TYPE, Int32Value(asScalar()->type())); break; case Reference: @@ -447,44 +488,68 @@ ReferenceTypeRepresentation::Create(JSCo return nullptr; new(ptr) ReferenceTypeRepresentation(type); return ptr->addToTableOrFree(cx, p); } /*static*/ JSObject * -ArrayTypeRepresentation::Create(JSContext *cx, - TypeRepresentation *element, - size_t length) +SizedArrayTypeRepresentation::Create(JSContext *cx, + SizedTypeRepresentation *element, + size_t length) { JSCompartment *comp = cx->compartment(); // Overly conservative, since we are using `size_t` to represent // size, but `SafeMul` operators on `int32_t` types. Still, it // should be good enough for now. int32_t temp; if (!SafeMul(element->size(), length, &temp)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG); return nullptr; } - ArrayTypeRepresentation sample(element, length); + SizedArrayTypeRepresentation sample(element, length); TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample); if (p) return (*p)->ownerObject(); // Note: cannot use cx->new_ because constructor is private. - ArrayTypeRepresentation *ptr = - (ArrayTypeRepresentation *) cx->malloc_( - sizeof(ArrayTypeRepresentation)); + SizedArrayTypeRepresentation *ptr = + (SizedArrayTypeRepresentation *) cx->malloc_( + sizeof(SizedArrayTypeRepresentation)); if (!ptr) return nullptr; - new(ptr) ArrayTypeRepresentation(element, length); + new(ptr) SizedArrayTypeRepresentation(element, length); + + return ptr->addToTableOrFree(cx, p); +} + + +/*static*/ +JSObject * +UnsizedArrayTypeRepresentation::Create(JSContext *cx, + SizedTypeRepresentation *element) +{ + JSCompartment *comp = cx->compartment(); + + UnsizedArrayTypeRepresentation sample(element); + TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample); + if (p) + return (*p)->ownerObject(); + + // Note: cannot use cx->new_ because constructor is private. + UnsizedArrayTypeRepresentation *ptr = + (UnsizedArrayTypeRepresentation *) cx->malloc_( + sizeof(UnsizedArrayTypeRepresentation)); + if (!ptr) + return nullptr; + new(ptr) UnsizedArrayTypeRepresentation(element); return ptr->addToTableOrFree(cx, p); } /*static*/ JSObject * StructTypeRepresentation::Create(JSContext *cx, AutoIdVector &ids, @@ -539,33 +604,44 @@ TypeRepresentation::traceFields(JSTracer case Reference: case X4: break; case Struct: asStruct()->traceStructFields(trace); break; - case Array: - asArray()->traceArrayFields(trace); + case SizedArray: + asSizedArray()->traceSizedArrayFields(trace); + break; + + case UnsizedArray: + asUnsizedArray()->traceUnsizedArrayFields(trace); break; } } void StructTypeRepresentation::traceStructFields(JSTracer *trace) { for (size_t i = 0; i < fieldCount(); i++) { gc::MarkId(trace, &fields()[i].id, "typerepr_field_id"); fields()[i].typeRepr->mark(trace); } } void -ArrayTypeRepresentation::traceArrayFields(JSTracer *trace) +SizedArrayTypeRepresentation::traceSizedArrayFields(JSTracer *trace) +{ + this->mark(trace); + element_->mark(trace); +} + +void +UnsizedArrayTypeRepresentation::traceUnsizedArrayFields(JSTracer *trace) { this->mark(trace); element_->mark(trace); } /////////////////////////////////////////////////////////////////////////// // Finalization @@ -589,18 +665,21 @@ TypeRepresentation::appendString(JSConte return asScalar()->appendStringScalar(cx, contents); case Reference: return asReference()->appendStringReference(cx, contents); case X4: return asX4()->appendStringX4(cx, contents); - case Array: - return asArray()->appendStringArray(cx, contents); + case SizedArray: + return asSizedArray()->appendStringSizedArray(cx, contents); + + case UnsizedArray: + return asUnsizedArray()->appendStringUnsizedArray(cx, contents); case Struct: return asStruct()->appendStringStruct(cx, contents); } MOZ_ASSUME_UNREACHABLE("Invalid kind"); return false; } @@ -657,31 +736,52 @@ X4TypeRepresentation::appendStringX4(JSC return contents.append("float32x4"); case TYPE_INT32: return contents.append("int32x4"); } MOZ_ASSUME_UNREACHABLE("Invalid type"); } bool -ArrayTypeRepresentation::appendStringArray(JSContext *cx, StringBuffer &contents) +SizedArrayTypeRepresentation::appendStringSizedArray(JSContext *cx, StringBuffer &contents) { - if (!contents.append("ArrayType(")) + SizedTypeRepresentation *elementType = element(); + while (elementType->isSizedArray()) + elementType = elementType->asSizedArray()->element(); + if (!elementType->appendString(cx, contents)) return false; + contents.append(".array("); + SizedArrayTypeRepresentation *arrayType = this; + while (arrayType != NULL) { + if (!NumberValueToStringBuffer(cx, NumberValue(length()), contents)) + return false; + + if (arrayType->element()->isSizedArray()) { + if (!contents.append(",")) + return false; + arrayType = arrayType->element()->asSizedArray(); + } else { + break; + } + } + + if (!contents.append(")")) + return false; + + return true; +} + +bool +UnsizedArrayTypeRepresentation::appendStringUnsizedArray(JSContext *cx, StringBuffer &contents) +{ if (!element()->appendString(cx, contents)) return false; - if (!contents.append(", ")) - return false; - - if (!NumberValueToStringBuffer(cx, NumberValue(length()), contents)) - return false; - - if (!contents.append(")")) + if (!contents.append(".array()")) return false; return true; } bool StructTypeRepresentation::appendStringStruct(JSContext *cx, StringBuffer &contents) { @@ -714,41 +814,48 @@ StructTypeRepresentation::appendStringSt return true; } /////////////////////////////////////////////////////////////////////////// // Walking memory template<typename V> static void -visitReferences(TypeRepresentation *repr, uint8_t *mem, V& visitor) +visitReferences(SizedTypeRepresentation *repr, + uint8_t *mem, + V& visitor) { if (repr->transparent()) return; switch (repr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::X4: return; case TypeRepresentation::Reference: visitor.visitReference(repr->asReference(), mem); return; - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: { - ArrayTypeRepresentation *arrayRepr = repr->asArray(); - TypeRepresentation *elementRepr = arrayRepr->element(); + SizedArrayTypeRepresentation *arrayRepr = repr->asSizedArray(); + SizedTypeRepresentation *elementRepr = arrayRepr->element(); for (size_t i = 0; i < arrayRepr->length(); i++) { visitReferences(elementRepr, mem, visitor); mem += elementRepr->size(); } return; } + case TypeRepresentation::UnsizedArray: + { + MOZ_ASSUME_UNREACHABLE("Only Sized Type representations"); + } + case TypeRepresentation::Struct: { StructTypeRepresentation *structRepr = repr->asStruct(); for (size_t i = 0; i < structRepr->fieldCount(); i++) { const StructField &f = structRepr->field(i); visitReferences(f.typeRepr, mem + f.offset, visitor); } return; @@ -801,22 +908,33 @@ js::MemoryInitVisitor::visitReference(Re return; } } MOZ_ASSUME_UNREACHABLE("Invalid kind"); } void -TypeRepresentation::initInstance(const JSRuntime *rt, uint8_t *mem) +SizedTypeRepresentation::initInstance(const JSRuntime *rt, + uint8_t *mem, + size_t length) { MemoryInitVisitor visitor(rt); + + // Initialize the 0th instance memset(mem, 0, size()); if (opaque()) visitReferences(this, mem, visitor); + + // Stamp out N copies of later instances + uint8_t *target = mem; + for (size_t i = 1; i < length; i++) { + target += size(); + memcpy(target, mem, size()); + } } /////////////////////////////////////////////////////////////////////////// // Tracing instances namespace js { class MemoryTracingVisitor { JSTracer *trace_; @@ -860,20 +978,26 @@ js::MemoryTracingVisitor::visitReference return; } } MOZ_ASSUME_UNREACHABLE("Invalid kind"); } void -TypeRepresentation::traceInstance(JSTracer *trace, uint8_t *mem) +SizedTypeRepresentation::traceInstance(JSTracer *trace, + uint8_t *mem, + size_t length) { MemoryTracingVisitor visitor(trace); - visitReferences(this, mem, visitor); + + for (size_t i = 0; i < length; i++) { + visitReferences(this, mem, visitor); + mem += size(); + } } /////////////////////////////////////////////////////////////////////////// // Misc const StructField * StructTypeRepresentation::fieldNamed(jsid id) const {
--- a/js/src/builtin/TypeRepresentation.h +++ b/js/src/builtin/TypeRepresentation.h @@ -61,158 +61,176 @@ #include "builtin/TypedObjectConstants.h" #include "gc/Barrier.h" #include "js/HashTable.h" namespace js { class TypeRepresentation; +class SizedTypeRepresentation; class ScalarTypeRepresentation; class ReferenceTypeRepresentation; class X4TypeRepresentation; -class ArrayTypeRepresentation; +class SizedArrayTypeRepresentation; +class UnsizedArrayTypeRepresentation; class StructTypeRepresentation; struct Class; class StringBuffer; struct TypeRepresentationHasher { typedef TypeRepresentation *Lookup; static HashNumber hash(TypeRepresentation *key); static bool match(TypeRepresentation *key1, TypeRepresentation *key2); private: static HashNumber hashScalar(ScalarTypeRepresentation *key); static HashNumber hashReference(ReferenceTypeRepresentation *key); static HashNumber hashX4(X4TypeRepresentation *key); static HashNumber hashStruct(StructTypeRepresentation *key); - static HashNumber hashArray(ArrayTypeRepresentation *key); + static HashNumber hashUnsizedArray(UnsizedArrayTypeRepresentation *key); + static HashNumber hashSizedArray(SizedArrayTypeRepresentation *key); static bool matchScalars(ScalarTypeRepresentation *key1, ScalarTypeRepresentation *key2); static bool matchReferences(ReferenceTypeRepresentation *key1, ReferenceTypeRepresentation *key2); static bool matchX4s(X4TypeRepresentation *key1, X4TypeRepresentation *key2); static bool matchStructs(StructTypeRepresentation *key1, StructTypeRepresentation *key2); - static bool matchArrays(ArrayTypeRepresentation *key1, - ArrayTypeRepresentation *key2); + static bool matchSizedArrays(SizedArrayTypeRepresentation *key1, + SizedArrayTypeRepresentation *key2); + static bool matchUnsizedArrays(UnsizedArrayTypeRepresentation *key1, + UnsizedArrayTypeRepresentation *key2); }; typedef js::HashSet<TypeRepresentation *, TypeRepresentationHasher, RuntimeAllocPolicy> TypeRepresentationHash; class TypeRepresentationHelper; class TypeRepresentation { public: enum Kind { Scalar = JS_TYPEREPR_SCALAR_KIND, Reference = JS_TYPEREPR_REFERENCE_KIND, X4 = JS_TYPEREPR_X4_KIND, Struct = JS_TYPEREPR_STRUCT_KIND, - Array = JS_TYPEREPR_ARRAY_KIND + SizedArray = JS_TYPEREPR_SIZED_ARRAY_KIND, + UnsizedArray = JS_TYPEREPR_UNSIZED_ARRAY_KIND, }; protected: - TypeRepresentation(Kind kind, size_t size, size_t align, bool opaque); + TypeRepresentation(Kind kind, bool opaque); // in order to call addToTableOrFree() friend class TypeRepresentationHelper; - size_t size_; - size_t alignment_; Kind kind_; bool opaque_; JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p); private: static const Class class_; static void obj_trace(JSTracer *trace, JSObject *object); static void obj_finalize(js::FreeOp *fop, JSObject *object); HeapPtrObject ownerObject_; void traceFields(JSTracer *tracer); public: - size_t size() const { return size_; } - size_t alignment() const { return alignment_; } Kind kind() const { return kind_; } bool opaque() const { return opaque_; } bool transparent() const { return !opaque_; } JSObject *ownerObject() const { return ownerObject_.get(); } // Appends a stringified form of this type representation onto // buffer, for use in error messages and the like. bool appendString(JSContext *cx, StringBuffer &buffer); - // Initializes memory that contains an instance of this type - // with appropriate default values (typically 0). - void initInstance(const JSRuntime *rt, uint8_t *mem); - - // Traces memory that contains an instance of this type. - void traceInstance(JSTracer *trace, uint8_t *mem); - static bool isOwnerObject(JSObject &obj); static TypeRepresentation *fromOwnerObject(JSObject &obj); + static bool isSized(Kind kind) { + return kind > JS_TYPEREPR_MAX_UNSIZED_KIND; + } + + bool isSized() const { + return isSized(kind()); + } + + inline SizedTypeRepresentation *asSized(); + bool isScalar() const { return kind() == Scalar; } - ScalarTypeRepresentation *asScalar() { - JS_ASSERT(isScalar()); - return (ScalarTypeRepresentation*) this; - } + inline ScalarTypeRepresentation *asScalar(); bool isReference() const { return kind() == Reference; } - ReferenceTypeRepresentation *asReference() { - JS_ASSERT(isReference()); - return (ReferenceTypeRepresentation*) this; - } + inline ReferenceTypeRepresentation *asReference(); bool isX4() const { return kind() == X4; } - X4TypeRepresentation *asX4() { - JS_ASSERT(isX4()); - return (X4TypeRepresentation*) this; + inline X4TypeRepresentation *asX4(); + + bool isSizedArray() const { + return kind() == SizedArray; } - bool isArray() const { - return kind() == Array; + inline SizedArrayTypeRepresentation *asSizedArray(); + + bool isUnsizedArray() const { + return kind() == UnsizedArray; } - ArrayTypeRepresentation *asArray() { - JS_ASSERT(isArray()); - return (ArrayTypeRepresentation*) this; + inline UnsizedArrayTypeRepresentation *asUnsizedArray(); + + bool isAnyArray() const { + return isSizedArray() || isUnsizedArray(); } bool isStruct() const { return kind() == Struct; } - StructTypeRepresentation *asStruct() { - JS_ASSERT(isStruct()); - return (StructTypeRepresentation*) this; - } + inline StructTypeRepresentation *asStruct(); void mark(JSTracer *tracer); }; -class ScalarTypeRepresentation : public TypeRepresentation { +class SizedTypeRepresentation : public TypeRepresentation { + protected: + SizedTypeRepresentation(Kind kind, bool opaque, size_t size, size_t align); + + size_t size_; + size_t alignment_; + + public: + size_t size() const { return size_; } + size_t alignment() const { return alignment_; } + + // Initializes memory that contains `count` instances of this type + void initInstance(const JSRuntime *rt, uint8_t *mem, size_t count); + + // Traces memory that contains `count` instances of this type. + void traceInstance(JSTracer *trace, uint8_t *mem, size_t count); +}; + +class ScalarTypeRepresentation : public SizedTypeRepresentation { public: // Must match order of JS_FOR_EACH_SCALAR_TYPE_REPR below enum Type { TYPE_INT8 = JS_SCALARTYPEREPR_INT8, TYPE_UINT8 = JS_SCALARTYPEREPR_UINT8, TYPE_INT16 = JS_SCALARTYPEREPR_INT16, TYPE_UINT16 = JS_SCALARTYPEREPR_UINT16, TYPE_INT32 = JS_SCALARTYPEREPR_INT32, @@ -268,17 +286,17 @@ class ScalarTypeRepresentation : public macro_(ScalarTypeRepresentation::TYPE_FLOAT32, float, float32) \ macro_(ScalarTypeRepresentation::TYPE_FLOAT64, double, float64) // Must be in same order as the enum ScalarTypeRepresentation::Type: #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_) \ JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \ macro_(ScalarTypeRepresentation::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped) -class ReferenceTypeRepresentation : public TypeRepresentation { +class ReferenceTypeRepresentation : public SizedTypeRepresentation { public: // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below enum Type { TYPE_ANY = JS_REFERENCETYPEREPR_ANY, TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT, TYPE_STRING = JS_REFERENCETYPEREPR_STRING, }; static const int32_t TYPE_MAX = TYPE_STRING + 1; @@ -307,17 +325,17 @@ class ReferenceTypeRepresentation : publ static JSObject *Create(JSContext *cx, Type type); }; #define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_) \ macro_(ReferenceTypeRepresentation::TYPE_ANY, HeapValue, Any) \ macro_(ReferenceTypeRepresentation::TYPE_OBJECT, HeapPtrObject, Object) \ macro_(ReferenceTypeRepresentation::TYPE_STRING, HeapPtrString, string) -class X4TypeRepresentation : public TypeRepresentation { +class X4TypeRepresentation : public SizedTypeRepresentation { public: enum Type { TYPE_INT32 = JS_X4TYPEREPR_INT32, TYPE_FLOAT32 = JS_X4TYPEREPR_FLOAT32, }; private: // so TypeRepresentation can call appendStringScalar() etc @@ -341,60 +359,84 @@ class X4TypeRepresentation : public Type static JSObject *Create(JSContext *cx, Type type); }; // Must be in same order as the enum ScalarTypeRepresentation::Type: #define JS_FOR_EACH_X4_TYPE_REPR(macro_) \ macro_(X4TypeRepresentation::TYPE_INT32, int32_t, int32) \ macro_(X4TypeRepresentation::TYPE_FLOAT32, float, float32) -class ArrayTypeRepresentation : public TypeRepresentation { +class UnsizedArrayTypeRepresentation : public TypeRepresentation { private: // so TypeRepresentation can call appendStringArray() etc friend class TypeRepresentation; - TypeRepresentation *element_; - size_t length_; + SizedTypeRepresentation *element_; - ArrayTypeRepresentation(TypeRepresentation *element, - size_t length); + UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element); // See TypeRepresentation::traceFields() - void traceArrayFields(JSTracer *trace); + void traceUnsizedArrayFields(JSTracer *trace); // See TypeRepresentation::appendString() - bool appendStringArray(JSContext *cx, StringBuffer &buffer); + bool appendStringUnsizedArray(JSContext *cx, StringBuffer &buffer); public: - TypeRepresentation *element() { + SizedTypeRepresentation *element() { + return element_; + } + + static JSObject *Create(JSContext *cx, + SizedTypeRepresentation *elementTypeRepr); +}; + +class SizedArrayTypeRepresentation : public SizedTypeRepresentation { + private: + // so TypeRepresentation can call appendStringSizedArray() etc + friend class TypeRepresentation; + + SizedTypeRepresentation *element_; + size_t length_; + + SizedArrayTypeRepresentation(SizedTypeRepresentation *element, + size_t length); + + // See TypeRepresentation::traceFields() + void traceSizedArrayFields(JSTracer *trace); + + // See TypeRepresentation::appendString() + bool appendStringSizedArray(JSContext *cx, StringBuffer &buffer); + + public: + SizedTypeRepresentation *element() { return element_; } size_t length() { return length_; } static JSObject *Create(JSContext *cx, - TypeRepresentation *elementTypeRepr, + SizedTypeRepresentation *elementTypeRepr, size_t length); }; struct StructField { size_t index; HeapId id; - TypeRepresentation *typeRepr; + SizedTypeRepresentation *typeRepr; size_t offset; explicit StructField(size_t index, jsid &id, - TypeRepresentation *typeRepr, + SizedTypeRepresentation *typeRepr, size_t offset); }; -class StructTypeRepresentation : public TypeRepresentation { +class StructTypeRepresentation : public SizedTypeRepresentation { private: // so TypeRepresentation can call appendStringStruct() etc friend class TypeRepresentation; size_t fieldCount_; // StructTypeRepresentations are allocated with extra space to // store the contents of the fields array. @@ -423,16 +465,65 @@ class StructTypeRepresentation : public const StructField &field(size_t i) const { JS_ASSERT(i < fieldCount()); return fields()[i]; } const StructField *fieldNamed(jsid id) const; + // Creates a struct type containing fields with names from `ids` + // and types from `typeReprOwners`. The latter should be the owner + // objects of a set of sized type representations. static JSObject *Create(JSContext *cx, AutoIdVector &ids, AutoObjectVector &typeReprOwners); }; +// Definitions of the casting methods. These are pulled out of the +// main class definition because both the super- and subtypes must be +// defined for C++ to permit the static_cast. + +SizedTypeRepresentation * +TypeRepresentation::asSized() { + JS_ASSERT(isSized()); + return static_cast<SizedTypeRepresentation*>(this); +} + +ScalarTypeRepresentation * +TypeRepresentation::asScalar() { + JS_ASSERT(isScalar()); + return static_cast<ScalarTypeRepresentation*>(this); +} + +ReferenceTypeRepresentation * +TypeRepresentation::asReference() { + JS_ASSERT(isReference()); + return static_cast<ReferenceTypeRepresentation*>(this); +} + +X4TypeRepresentation * +TypeRepresentation::asX4() { + JS_ASSERT(isX4()); + return static_cast<X4TypeRepresentation*>(this); +} + +SizedArrayTypeRepresentation * +TypeRepresentation::asSizedArray() { + JS_ASSERT(isSizedArray()); + return static_cast<SizedArrayTypeRepresentation*>(this); +} + +UnsizedArrayTypeRepresentation * +TypeRepresentation::asUnsizedArray() { + JS_ASSERT(isUnsizedArray()); + return static_cast<UnsizedArrayTypeRepresentation*>(this); +} + +StructTypeRepresentation * +TypeRepresentation::asStruct() { + JS_ASSERT(isStruct()); + return static_cast<StructTypeRepresentation*>(this); +} + } // namespace js #endif
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -48,68 +48,64 @@ static const JSFunctionSpec TypedObjectM static void ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, InformalValueTypeName(fromValue), toType); } -static void -ReportCannotConvertTo(JSContext *cx, HandleObject fromObject, const char *toType) -{ - RootedValue fromValue(cx, ObjectValue(*fromObject)); - ReportCannotConvertTo(cx, fromValue, toType); -} - -static void -ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, HandleString toType) -{ - const char *fnName = JS_EncodeString(cx, toType); - if (!fnName) - return; - ReportCannotConvertTo(cx, fromValue, fnName); - JS_free(cx, (void *) fnName); -} - -static void -ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, TypeRepresentation *toType) -{ - StringBuffer contents(cx); - if (!toType->appendString(cx, contents)) - return; - - RootedString result(cx, contents.finishString()); - if (!result) - return; - - ReportCannotConvertTo(cx, fromValue, result); -} - -static int32_t -Clamp(int32_t value, int32_t min, int32_t max) -{ - JS_ASSERT(min < max); - if (value < min) - return min; - if (value > max) - return max; - return value; -} - static inline JSObject * ToObjectIfObject(HandleValue value) { if (!value.isObject()) return nullptr; return &value.toObject(); } static inline bool +IsTypedDatum(JSObject &obj) +{ + return obj.is<TypedObject>() || obj.is<TypedHandle>(); +} + +static inline uint8_t * +TypedMem(JSObject &val) +{ + JS_ASSERT(IsTypedDatum(val)); + return (uint8_t*) val.getPrivate(); +} + +static inline bool IsTypeObject(JSObject &type); + +/* + * Given a user-visible type descriptor object, returns the + * owner object for the TypeRepresentation* that we use internally. + */ +static JSObject * +typeRepresentationOwnerObj(JSObject &typeObj) +{ + JS_ASSERT(IsTypeObject(typeObj)); + return &typeObj.getReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR).toObject(); +} + +/* + * Given a user-visible type descriptor object, returns the + * TypeRepresentation* that we use internally. + * + * Note: this pointer is valid only so long as `typeObj` remains rooted. + */ +static TypeRepresentation * +typeRepresentation(JSObject &typeObj) +{ + return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj)); +} + +static inline bool IsScalarTypeObject(JSObject &type) { return type.hasClass(&ScalarType::class_); } static inline bool IsReferenceTypeObject(JSObject &type) { @@ -151,49 +147,31 @@ IsTypeObject(JSObject &type) { return IsScalarTypeObject(type) || IsReferenceTypeObject(type) || IsX4TypeObject(type) || IsComplexTypeObject(type); } static inline bool -IsTypedDatum(JSObject &obj) +IsTypeObjectOfKind(JSObject &type, TypeRepresentation::Kind kind) { - return obj.is<TypedObject>() || obj.is<TypedHandle>(); -} - -static inline uint8_t * -TypedMem(JSObject &val) -{ - JS_ASSERT(IsTypedDatum(val)); - return (uint8_t*) val.getPrivate(); + if (!IsTypeObject(type)) + return false; + + return typeRepresentation(type)->kind() == kind; } -/* - * Given a user-visible type descriptor object, returns the - * owner object for the TypeRepresentation* that we use internally. - */ -static JSObject * -typeRepresentationOwnerObj(JSObject &typeObj) +static inline bool +IsSizedTypeObject(JSObject &type) { - JS_ASSERT(IsTypeObject(typeObj)); - return &typeObj.getReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR).toObject(); -} - -/* - * Given a user-visible type descriptor object, returns the - * TypeRepresentation* that we use internally. - * - * Note: this pointer is valid only so long as `typeObj` remains rooted. - */ -static TypeRepresentation * -typeRepresentation(JSObject &typeObj) -{ - return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj)); + if (!IsTypeObject(type)) + return false; + + return typeRepresentation(type)->isSized(); } static inline JSObject * GetType(JSObject &datum) { JS_ASSERT(IsTypedDatum(datum)); return &datum.getReservedSlot(JS_DATUM_SLOT_TYPE_OBJ).toObject(); } @@ -318,34 +296,83 @@ Reify(JSContext *cx, to.set(args.rval()); return true; } static inline size_t TypeSize(JSObject &type) { - return typeRepresentation(type)->size(); + return typeRepresentation(type)->asSized()->size(); +} + +static inline size_t +DatumLength(JSObject &val) +{ + JS_ASSERT(IsTypedDatum(val)); + JS_ASSERT(typeRepresentation(*GetType(val))->isAnyArray()); + return val.getReservedSlot(JS_DATUM_SLOT_LENGTH).toInt32(); } static inline size_t DatumSize(JSObject &val) { - return TypeSize(*GetType(val)); + JSObject *typeObj = GetType(val); + TypeRepresentation *typeRepr = typeRepresentation(*typeObj); + switch (typeRepr->kind()) { + case TypeRepresentation::Scalar: + case TypeRepresentation::X4: + case TypeRepresentation::Reference: + case TypeRepresentation::Struct: + case TypeRepresentation::SizedArray: + return typeRepr->asSized()->size(); + + case TypeRepresentation::UnsizedArray: + return typeRepr->asUnsizedArray()->element()->size() * DatumLength(val); + } + MOZ_ASSUME_UNREACHABLE("unhandled typerepresentation kind"); } static inline bool IsTypedDatumOfKind(JSObject &obj, TypeRepresentation::Kind kind) { if (!IsTypedDatum(obj)) return false; TypeRepresentation *repr = typeRepresentation(*GetType(obj)); return repr->kind() == kind; } +static inline bool +IsArrayTypedDatum(JSObject &obj) +{ + if (!IsTypedDatum(obj)) + return false; + TypeRepresentation *repr = typeRepresentation(*GetType(obj)); + return repr->isAnyArray(); +} + +// Extracts the `prototype` property from `obj`, throwing if it is +// missing or not an object. +static JSObject * +GetPrototype(JSContext *cx, HandleObject obj) +{ + RootedValue prototypeVal(cx); + if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype, + &prototypeVal)) + { + return nullptr; + } + if (!prototypeVal.isObject()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, + JSMSG_INVALID_PROTOTYPE); + return nullptr; + } + return &prototypeVal.toObject(); +} + /*************************************************************************** * Scalar type objects * * Scalar type objects like `uint8`, `uint16`, are all instances of * the ScalarType class. Like all type objects, they have a reserved * slot pointing to a TypeRepresentation object, which is used to * distinguish which scalar type object this actually is. */ @@ -366,16 +393,17 @@ const Class js::ScalarType::class_ = { nullptr, nullptr, nullptr }; const JSFunctionSpec js::ScalarType::typeObjectMethods[] = { JS_FN("toSource", TypeObjectToSource, 0, 0), {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"}, + {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"}, JS_FS_END }; template <typename T> static T ConvertScalar(double d) { if (TypeIsFloatingPoint<T>()) { @@ -450,16 +478,17 @@ const Class js::ReferenceType::class_ = nullptr, nullptr, nullptr }; const JSFunctionSpec js::ReferenceType::typeObjectMethods[] = { JS_FN("toSource", TypeObjectToSource, 0, 0), {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"}, + {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"}, JS_FS_END }; bool js::ReferenceType::call(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -537,41 +566,25 @@ js::ReferenceType::call(JSContext *cx, u * (And, again, same with respect to s and S.) * * This function creates the A.prototype/S.prototype object. It takes * as an argument either the TypedObject.ArrayType or the * TypedObject.StructType constructor function, then returns an empty * object with the .prototype.prototype object as its [[Prototype]]. */ static JSObject * -CreateComplexTypeInstancePrototype(JSContext *cx, - HandleObject typeObjectCtor) +CreatePrototypeObjectForComplexTypeInstance(JSContext *cx, + HandleObject ctorPrototype) { - RootedValue ctorPrototypeVal(cx); - if (!JSObject::getProperty(cx, typeObjectCtor, typeObjectCtor, - cx->names().prototype, - &ctorPrototypeVal)) - { + RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype)); + if (!ctorPrototypePrototype) return nullptr; - } - JS_ASSERT(ctorPrototypeVal.isObject()); // immutable binding - RootedObject ctorPrototypeObj(cx, &ctorPrototypeVal.toObject()); - - RootedValue ctorPrototypePrototypeVal(cx); - if (!JSObject::getProperty(cx, ctorPrototypeObj, ctorPrototypeObj, - cx->names().prototype, - &ctorPrototypePrototypeVal)) - { - return nullptr; - } - - JS_ASSERT(ctorPrototypePrototypeVal.isObject()); // immutable binding - RootedObject proto(cx, &ctorPrototypePrototypeVal.toObject()); - return NewObjectWithGivenProto(cx, &JSObject::class_, proto, - &typeObjectCtor->global()); + + return NewObjectWithGivenProto(cx, &JSObject::class_, + &*ctorPrototypePrototype, nullptr); } const Class ArrayType::class_ = { "ArrayType", JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEOBJ_ARRAY_SLOTS), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, @@ -588,384 +601,254 @@ const Class ArrayType::class_ = { }; const JSPropertySpec ArrayType::typeObjectProperties[] = { JS_PS_END }; const JSFunctionSpec ArrayType::typeObjectMethods[] = { {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"}, - JS_FN("repeat", ArrayType::repeat, 1, 0), + {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, + JS_FN("dimension", ArrayType::dimension, 1, 0), JS_FN("toSource", TypeObjectToSource, 0, 0), {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"}, JS_FS_END }; const JSPropertySpec ArrayType::typedObjectProperties[] = { JS_PS_END }; const JSFunctionSpec ArrayType::typedObjectMethods[] = { - JS_FN("subarray", ArrayType::subarray, 2, 0), {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"}, {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"}, JS_FS_END }; static JSObject * -ArrayElementType(HandleObject array) -{ - JS_ASSERT(IsArrayTypeObject(*array)); - return &array->getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject(); -} - -static bool -FillTypedArrayWithValue(JSContext *cx, HandleObject array, HandleValue val) -{ - JS_ASSERT(IsTypedDatumOfKind(*array, TypeRepresentation::Array)); - - RootedFunction func( - cx, SelfHostedFunction(cx, cx->names().FillTypedArrayWithValue)); - if (!func) - return false; - - InvokeArgs args(cx); - if (!args.init(2)) - return false; - - args.setCallee(ObjectValue(*func)); - args[0].setObject(*array); - args[1].set(val); - return Invoke(cx, args); -} - -bool -ArrayType::repeat(JSContext *cx, unsigned int argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, - nullptr, JSMSG_MORE_ARGS_NEEDED, - "repeat()", "0", "s"); - return false; - } - - RootedObject thisObj(cx, ToObjectIfObject(args.thisv())); - if (!thisObj || !IsArrayTypeObject(*thisObj)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_INCOMPATIBLE_PROTO, - "ArrayType", "repeat", - InformalValueTypeName(args.thisv())); - return false; - } - - RootedObject binaryArray(cx, TypedObject::createZeroed(cx, thisObj)); - if (!binaryArray) - return false; - - RootedValue val(cx, args[0]); - if (!FillTypedArrayWithValue(cx, binaryArray, val)) - return false; - - args.rval().setObject(*binaryArray); - return true; -} - -/** - * The subarray function first creates an ArrayType instance - * which will act as the elementType for the subarray. - * - * var MA = new ArrayType(elementType, 10); - * var mb = MA.repeat(val); - * - * mb.subarray(begin, end=mb.length) => (Only for +ve) - * var internalSA = new ArrayType(elementType, end-begin); - * var ret = new internalSA() - * for (var i = begin; i < end; i++) - * ret[i-begin] = ret[i] - * return ret - * - * The range specified by the begin and end values is clamped to the valid - * index range for the current array. If the computed length of the new - * TypedArray would be negative, it is clamped to zero. - * see: http://www.khronos.org/registry/typedarray/specs/latest/#7 - * - */ -/* static */ bool -ArrayType::subarray(JSContext *cx, unsigned int argc, Value *vp) +ArrayElementType(JSObject &array) { - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, - nullptr, JSMSG_MORE_ARGS_NEEDED, - "subarray()", "0", "s"); - return false; - } - - if (!args[0].isInt32()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "1"); - return false; - } - - RootedObject thisObj(cx, &args.thisv().toObject()); - if (!IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) { - ReportCannotConvertTo(cx, thisObj, "binary array"); - return false; - } - - RootedObject type(cx, GetType(*thisObj)); - ArrayTypeRepresentation *typeRepr = typeRepresentation(*type)->asArray(); - size_t length = typeRepr->length(); - - int32_t begin = args[0].toInt32(); - int32_t end = length; - - if (args.length() >= 2) { - if (!args[1].isInt32()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "2"); - return false; - } - - end = args[1].toInt32(); - } - - if (begin < 0) - begin = length + begin; - if (end < 0) - end = length + end; - - begin = Clamp(begin, 0, length); - end = Clamp(end, 0, length); - - int32_t sublength = end - begin; // end exclusive - sublength = Clamp(sublength, 0, length); - - RootedObject globalObj(cx, cx->compartment()->maybeGlobal()); - JS_ASSERT(globalObj); - Rooted<GlobalObject*> global(cx, &globalObj->as<GlobalObject>()); - RootedObject arrayTypeGlobal(cx, global->getArrayType(cx)); - - RootedObject elementType(cx, ArrayElementType(type)); - RootedObject subArrayType(cx, ArrayType::create(cx, arrayTypeGlobal, - elementType, sublength)); - if (!subArrayType) - return false; - - int32_t elementSize = typeRepr->element()->size(); - size_t offset = elementSize * begin; - - RootedObject subarray( - cx, TypedDatum::createDerived(cx, subArrayType, thisObj, offset)); - if (!subarray) - return false; - - args.rval().setObject(*subarray); - return true; -} - -static bool -ArrayFillSubarray(JSContext *cx, unsigned int argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if (args.length() < 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, - nullptr, JSMSG_MORE_ARGS_NEEDED, - "fill()", "0", "s"); - return false; - } - - RootedObject thisObj(cx, ToObjectIfObject(args.thisv())); - if (!thisObj || !IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, - nullptr, JSMSG_INCOMPATIBLE_PROTO, - "ArrayType", "fill", - InformalValueTypeName(args.thisv())); - return false; - } - - Value funArrayTypeVal = GetFunctionNativeReserved(&args.callee(), 0); - JS_ASSERT(funArrayTypeVal.isObject()); - - RootedObject type(cx, GetType(*thisObj)); - TypeRepresentation *typeRepr = typeRepresentation(*type); - RootedObject funArrayType(cx, &funArrayTypeVal.toObject()); - TypeRepresentation *funArrayTypeRepr = typeRepresentation(*funArrayType); - if (typeRepr != funArrayTypeRepr) { - RootedValue thisObjValue(cx, ObjectValue(*thisObj)); - ReportCannotConvertTo(cx, thisObjValue, funArrayTypeRepr); - return false; - } - - args.rval().setUndefined(); - RootedValue val(cx, args[0]); - return FillTypedArrayWithValue(cx, thisObj, val); + JS_ASSERT(IsArrayTypeObject(array)); + return &array.getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject(); } static bool InitializeCommonTypeDescriptorProperties(JSContext *cx, HandleObject obj, HandleObject typeReprOwnerObj) { TypeRepresentation *typeRepr = TypeRepresentation::fromOwnerObject(*typeReprOwnerObj); - if (typeRepr->transparent()) { + if (typeRepr->transparent() && typeRepr->isSized()) { + SizedTypeRepresentation *sizedTypeRepr = typeRepr->asSized(); + // byteLength - RootedValue typeByteLength(cx, NumberValue(typeRepr->size())); + RootedValue typeByteLength(cx, NumberValue(sizedTypeRepr->size())); if (!JSObject::defineProperty(cx, obj, cx->names().byteLength, typeByteLength, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) { return false; } // byteAlignment - RootedValue typeByteAlignment(cx, NumberValue(typeRepr->alignment())); + RootedValue typeByteAlignment( + cx, NumberValue(sizedTypeRepr->alignment())); if (!JSObject::defineProperty(cx, obj, cx->names().byteAlignment, typeByteAlignment, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) { return false; } + } else { + RootedValue undef(cx, UndefinedValue()); + + // byteLength + if (!JSObject::defineProperty(cx, obj, cx->names().byteLength, + undef, + nullptr, nullptr, + JSPROP_READONLY | JSPROP_PERMANENT)) + { + return false; + } + + // byteAlignment + if (!JSObject::defineProperty(cx, obj, cx->names().byteAlignment, + undef, + nullptr, nullptr, + JSPROP_READONLY | JSPROP_PERMANENT)) + { + return false; + } } - // variable -- always false since we do not yet support variable-size types - RootedValue variable(cx, JSVAL_FALSE); + // variable -- true for unsized arrays + RootedValue variable(cx, BooleanValue(!typeRepr->isSized())); if (!JSObject::defineProperty(cx, obj, cx->names().variable, variable, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) { return false; } return true; } JSObject * ArrayType::create(JSContext *cx, - HandleObject metaTypeObject, - HandleObject elementType, - size_t length) + HandleObject arrayTypePrototype, + HandleObject arrayTypeReprObj, + HandleObject elementType) { - JS_ASSERT(elementType); + JS_ASSERT(TypeRepresentation::isOwnerObject(*arrayTypeReprObj)); JS_ASSERT(IsTypeObject(*elementType)); - TypeRepresentation *elementTypeRepr = typeRepresentation(*elementType); - RootedObject typeReprObj( - cx, ArrayTypeRepresentation::Create(cx, elementTypeRepr, length)); - if (!typeReprObj) - return nullptr; - - RootedValue prototypeVal(cx); - if (!JSObject::getProperty(cx, metaTypeObject, metaTypeObject, - cx->names().prototype, - &prototypeVal)) - { - return nullptr; - } - JS_ASSERT(prototypeVal.isObject()); // immutable binding - RootedObject obj( - cx, NewObjectWithGivenProto(cx, &ArrayType::class_, - &prototypeVal.toObject(), cx->global())); + cx, NewObjectWithClassProto(cx, &ArrayType::class_, + arrayTypePrototype, nullptr)); if (!obj) return nullptr; - obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, ObjectValue(*typeReprObj)); + obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, + ObjectValue(*arrayTypeReprObj)); RootedValue elementTypeVal(cx, ObjectValue(*elementType)); if (!JSObject::defineProperty(cx, obj, cx->names().elementType, elementTypeVal, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) return nullptr; obj->initReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE, elementTypeVal); - RootedValue lengthVal(cx, Int32Value(length)); - if (!JSObject::defineProperty(cx, obj, cx->names().length, - lengthVal, nullptr, nullptr, - JSPROP_READONLY | JSPROP_PERMANENT)) - return nullptr; - - if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj)) + if (!InitializeCommonTypeDescriptorProperties(cx, obj, arrayTypeReprObj)) return nullptr; RootedObject prototypeObj( - cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject)); + cx, CreatePrototypeObjectForComplexTypeInstance(cx, arrayTypePrototype)); if (!prototypeObj) return nullptr; if (!LinkConstructorAndPrototype(cx, obj, prototypeObj)) return nullptr; - JSFunction *fillFun = DefineFunctionWithReserved(cx, prototypeObj, "fill", ArrayFillSubarray, 1, 0); - if (!fillFun) - return nullptr; - - // This is important - // so that A.prototype.fill.call(b, val) - // where b.type != A raises an error - SetFunctionNativeReserved(fillFun, 0, ObjectValue(*obj)); - return obj; } bool ArrayType::construct(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "ArrayType"); return false; } - if (args.length() != 2 || - !args[0].isObject() || - !args[1].isNumber() || - args[1].toNumber() < 0) + RootedObject arrayTypeGlobal(cx, &args.callee()); + + // Expect one argument which is a sized type object + if (args.length() < 1) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "ArrayType", "0", ""); + return false; + } + + if (!args[0].isObject() || !IsSizedTypeObject(args[0].toObject())) { + ReportCannotConvertTo(cx, args[0], "ArrayType element specifier"); + return false; + } + + RootedObject elementType(cx, &args[0].toObject()); + SizedTypeRepresentation *elementTypeRepr = + typeRepresentation(*elementType)->asSized(); + + // construct the type repr + RootedObject arrayTypeReprObj( + cx, UnsizedArrayTypeRepresentation::Create(cx, elementTypeRepr)); + if (!arrayTypeReprObj) + return false; + + // Extract ArrayType.prototype + RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal)); + if (!arrayTypePrototype) + return nullptr; + + // Create the instance of ArrayType + RootedObject obj( + cx, create(cx, arrayTypePrototype, arrayTypeReprObj, elementType)); + if (!obj) + return false; + + // Add `length` property, which is undefined for an unsized array. + RootedValue lengthVal(cx, UndefinedValue()); + if (!JSObject::defineProperty(cx, obj, cx->names().length, + lengthVal, nullptr, nullptr, + JSPROP_READONLY | JSPROP_PERMANENT)) + return nullptr; + + args.rval().setObject(*obj); + return true; +} + +/*static*/ bool +ArrayType::dimension(JSContext *cx, unsigned int argc, jsval *vp) +{ + // Expect that the `this` pointer is an unsized array type + // and the first argument is an integer size. + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() != 1 || + !args.thisv().isObject() || + !IsTypeObjectOfKind(args.thisv().toObject(), TypeRepresentation::UnsizedArray) || + !args[0].isInt32() || + args[0].toInt32() < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); return false; } - RootedObject arrayTypeGlobal(cx, &args.callee()); - RootedObject elementType(cx, &args[0].toObject()); - - if (!IsTypeObject(*elementType)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); + // Extract arguments. + RootedObject unsizedTypeObj(cx, &args.thisv().toObject()); + UnsizedArrayTypeRepresentation *unsizedTypeRepr = + typeRepresentation(*unsizedTypeObj)->asUnsizedArray(); + int32_t length = args[0].toInt32(); + + // Create sized type representation. + RootedObject sizedTypeReprObj( + cx, SizedArrayTypeRepresentation::Create( + cx, unsizedTypeRepr->element(), length)); + if (!sizedTypeReprObj) return false; - } - - if (!args[1].isInt32()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); - return false; - } - - int32_t length = args[1].toInt32(); - if (length < 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); - return false; - } - - RootedObject obj(cx, create(cx, arrayTypeGlobal, elementType, length)); + + // Create the sized type object. + RootedObject elementType(cx, ArrayElementType(*unsizedTypeObj)); + RootedObject obj( + cx, create(cx, unsizedTypeObj, sizedTypeReprObj, elementType)); if (!obj) return false; + + // Add `length` property. + RootedValue lengthVal(cx, Int32Value(length)); + if (!JSObject::defineProperty(cx, obj, cx->names().length, + lengthVal, nullptr, nullptr, + JSPROP_READONLY | JSPROP_PERMANENT)) + return nullptr; + + // Add `unsized` property, which is a link from the sized + // array to the unsized array. + RootedValue unsizedTypeObjValue(cx, ObjectValue(*unsizedTypeObj)); + if (!JSObject::defineProperty(cx, obj, cx->names().unsized, + unsizedTypeObjValue, nullptr, nullptr, + JSPROP_READONLY | JSPROP_PERMANENT)) + return nullptr; + args.rval().setObject(*obj); return true; } /********************************* * StructType class */ @@ -989,16 +872,17 @@ const Class StructType::class_ = { }; const JSPropertySpec StructType::typeObjectProperties[] = { JS_PS_END }; const JSFunctionSpec StructType::typeObjectMethods[] = { {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"}, + {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, JS_FN("toSource", TypeObjectToSource, 0, 0), {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"}, JS_FS_END }; const JSPropertySpec StructType::typedObjectProperties[] = { JS_PS_END }; @@ -1033,17 +917,17 @@ StructType::layout(JSContext *cx, Handle uint32_t index; if (js_IdIsIndex(id, &index)) { RootedValue idValue(cx, IdToJsval(id)); ReportCannotConvertTo(cx, idValue, "StructType field name"); return false; } RootedObject fieldType(cx, ToObjectIfObject(fieldTypeVal)); - if (!fieldType || !IsTypeObject(*fieldType)) { + if (!fieldType || !IsSizedTypeObject(*fieldType)) { ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier"); return false; } if (!fieldTypeObjs.append(fieldTypeVal)) { js_ReportOutOfMemory(cx); return false; } @@ -1129,45 +1013,43 @@ StructType::layout(JSContext *cx, Handle fieldTypesValue, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) return false; return true; } JSObject * -StructType::create(JSContext *cx, HandleObject metaTypeObject, +StructType::create(JSContext *cx, + HandleObject metaTypeObject, HandleObject fields) { - RootedValue prototypeVal(cx); - if (!JSObject::getProperty(cx, metaTypeObject, metaTypeObject, - cx->names().prototype, - &prototypeVal)) + RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeObject)); + if (!structTypePrototype) return nullptr; - JS_ASSERT(prototypeVal.isObject()); // immutable binding RootedObject obj( - cx, NewObjectWithGivenProto(cx, &StructType::class_, - &prototypeVal.toObject(), cx->global())); + cx, NewObjectWithClassProto(cx, &StructType::class_, + structTypePrototype, nullptr)); if (!obj) return nullptr; if (!StructType::layout(cx, obj, fields)) return nullptr; RootedObject fieldsProto(cx); if (!JSObject::getProto(cx, fields, &fieldsProto)) return nullptr; RootedObject typeReprObj(cx, typeRepresentationOwnerObj(*obj)); if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj)) return nullptr; RootedObject prototypeObj( - cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject)); + cx, CreatePrototypeObjectForComplexTypeInstance(cx, structTypePrototype)); if (!prototypeObj) return nullptr; if (!LinkConstructorAndPrototype(cx, obj, prototypeObj)) return nullptr; return obj; } @@ -1305,40 +1187,58 @@ const Class X4Type::class_ = { nullptr }; // These classes just exist to group together various properties and so on. namespace js { class Int32x4Defn { public: static const X4TypeRepresentation::Type type = X4TypeRepresentation::TYPE_INT32; + static const JSFunctionSpec TypeDescriptorMethods[]; static const JSPropertySpec TypedObjectProperties[]; static const JSFunctionSpec TypedObjectMethods[]; }; class Float32x4Defn { public: static const X4TypeRepresentation::Type type = X4TypeRepresentation::TYPE_FLOAT32; + static const JSFunctionSpec TypeDescriptorMethods[]; static const JSPropertySpec TypedObjectProperties[]; static const JSFunctionSpec TypedObjectMethods[]; }; } // namespace js +const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = { + JS_FN("toSource", TypeObjectToSource, 0, 0), + {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"}, + {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, + {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"}, + JS_FS_END +}; + const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = { JS_SELF_HOSTED_GET("x", "Int32x4Lane0", JSPROP_PERMANENT), JS_SELF_HOSTED_GET("y", "Int32x4Lane1", JSPROP_PERMANENT), JS_SELF_HOSTED_GET("z", "Int32x4Lane2", JSPROP_PERMANENT), JS_SELF_HOSTED_GET("w", "Int32x4Lane3", JSPROP_PERMANENT), JS_PS_END }; const JSFunctionSpec js::Int32x4Defn::TypedObjectMethods[] = { JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0), JS_FS_END }; +const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = { + JS_FN("toSource", TypeObjectToSource, 0, 0), + {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"}, + {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, + {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"}, + JS_FS_END +}; + const JSPropertySpec js::Float32x4Defn::TypedObjectProperties[] = { JS_SELF_HOSTED_GET("x", "Float32x4Lane0", JSPROP_PERMANENT), JS_SELF_HOSTED_GET("y", "Float32x4Lane1", JSPROP_PERMANENT), JS_SELF_HOSTED_GET("z", "Float32x4Lane2", JSPROP_PERMANENT), JS_SELF_HOSTED_GET("w", "Float32x4Lane3", JSPROP_PERMANENT), JS_PS_END }; @@ -1384,16 +1284,19 @@ CreateX4Class(JSContext *cx, Handle<Glob } // Link type constructor to the type representation x4->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, ObjectValue(*typeReprObj)); // Link constructor to prototype and install properties + if (!JS_DefineFunctions(cx, x4, T::TypeDescriptorMethods)) + return nullptr; + if (!LinkConstructorAndPrototype(cx, x4, proto) || !DefinePropertiesAndBrand(cx, proto, T::TypedObjectProperties, T::TypedObjectMethods)) { return nullptr; } return x4; @@ -1413,17 +1316,17 @@ X4Type::call(JSContext *cx, unsigned arg double values[LANES]; for (uint32_t i = 0; i < LANES; i++) { if (!ToNumber(cx, args[i], &values[i])) return false; } RootedObject typeObj(cx, &args.callee()); - RootedObject result(cx, TypedObject::createZeroed(cx, typeObj)); + RootedObject result(cx, TypedObject::createZeroed(cx, typeObj, 0)); if (!result) return false; X4TypeRepresentation *typeRepr = typeRepresentation(*typeObj)->asX4(); switch (typeRepr->type()) { #define STORE_LANES(_constant, _type, _name) \ case _constant: \ { \ @@ -1480,19 +1383,18 @@ DefineMetaTypeObject(JSContext *cx, protoProtoValue, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) return nullptr; // Create ctor itself const int constructorLength = 2; - RootedFunction ctor(cx, - global->createConstructor(cx, T::construct, - className, constructorLength)); + RootedFunction ctor(cx); + ctor = global->createConstructor(cx, T::construct, className, constructorLength); if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) || !DefinePropertiesAndBrand(cx, proto, T::typeObjectProperties, T::typeObjectMethods) || !DefinePropertiesAndBrand(cx, protoProto, T::typedObjectProperties, T::typedObjectMethods)) @@ -1654,32 +1556,35 @@ js_InitTypedObjectDummy(JSContext *cx, H * * Datums represent either typed objects or handles. See comment in * TypedObject.h. */ template<class T> /*static*/ T * TypedDatum::createUnattached(JSContext *cx, - HandleObject type) + HandleObject type, + int32_t length) { JS_STATIC_ASSERT(T::IsTypedDatumClass); JS_ASSERT(IsTypeObject(*type)); - RootedObject obj(cx, createUnattachedWithClass(cx, &T::class_, type)); + RootedObject obj( + cx, createUnattachedWithClass(cx, &T::class_, type, length)); if (!obj) return nullptr; return &obj->as<T>(); } /*static*/ TypedDatum * TypedDatum::createUnattachedWithClass(JSContext *cx, const Class *clasp, - HandleObject type) + HandleObject type, + int32_t length) { JS_ASSERT(IsTypeObject(*type)); JS_ASSERT(clasp == &TypedObject::class_ || clasp == &TypedHandle::class_); JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_DATUM_SLOTS); JS_ASSERT(clasp->hasPrivate()); RootedObject proto(cx); if (IsSimpleTypeObject(*type)) { @@ -1698,16 +1603,17 @@ TypedDatum::createUnattachedWithClass(JS RootedObject obj( cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr)); if (!obj) return nullptr; obj->setPrivate(nullptr); obj->initReservedSlot(JS_DATUM_SLOT_TYPE_OBJ, ObjectValue(*type)); obj->initReservedSlot(JS_DATUM_SLOT_OWNER, NullValue()); + obj->initReservedSlot(JS_DATUM_SLOT_LENGTH, Int32Value(length)); // Tag the type object for this instance with the type // representation, if that has not been done already. if (cx->typeInferenceEnabled() && !IsSimpleTypeObject(*type)) { // FIXME Bug 929651 ^~~~~~~~~~~~~~~~~~~~~~~~~~~ RootedTypeObject typeObj(cx, obj->getType(cx)); if (typeObj) { TypeRepresentation *typeRepr = typeRepresentation(*type); @@ -1737,26 +1643,52 @@ TypedDatum::attach(JSObject &datum, uint // find the owner, which is often but not always `datum` JSObject &owner = datum.getReservedSlot(JS_DATUM_SLOT_OWNER).toObject(); setPrivate(mem); setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(owner)); } +// Returns a suitable JS_DATUM_SLOT_LENGTH value for an instance of +// the type `type`. `type` must not be an unsized array. +static int32_t +DatumLengthFromType(JSObject &type) +{ + TypeRepresentation *typeRepr = typeRepresentation(type); + switch (typeRepr->kind()) { + case TypeRepresentation::Scalar: + case TypeRepresentation::Reference: + case TypeRepresentation::Struct: + case TypeRepresentation::X4: + return 0; + + case TypeRepresentation::SizedArray: + return typeRepr->asSizedArray()->length(); + + case TypeRepresentation::UnsizedArray: + MOZ_ASSUME_UNREACHABLE("DatumLengthFromType() invoked on unsized type"); + } + MOZ_ASSUME_UNREACHABLE("Invalid kind"); +} + /*static*/ TypedDatum * TypedDatum::createDerived(JSContext *cx, HandleObject type, HandleObject datum, size_t offset) { + JS_ASSERT(IsTypeObject(*type)); JS_ASSERT(IsTypedDatum(*datum)); JS_ASSERT(offset <= DatumSize(*datum)); JS_ASSERT(offset + TypeSize(*type) <= DatumSize(*datum)); + int32_t length = DatumLengthFromType(*type); + const js::Class *clasp = datum->getClass(); - Rooted<TypedDatum*> obj(cx, createUnattachedWithClass(cx, clasp, type)); + Rooted<TypedDatum*> obj( + cx, createUnattachedWithClass(cx, clasp, type, length)); if (!obj) return nullptr; obj->attach(*datum, offset); return obj; } static bool @@ -1791,20 +1723,34 @@ ReportDatumTypeError(JSContext *cx, /*static*/ void TypedDatum::obj_trace(JSTracer *trace, JSObject *object) { JS_ASSERT(IsTypedDatum(*object)); for (size_t i = 0; i < JS_DATUM_SLOTS; i++) gc::MarkSlot(trace, &object->getReservedSlotRef(i), "TypedObjectSlot"); - uint8_t *mem = TypedMem(*object); TypeRepresentation *repr = typeRepresentation(*GetType(*object)); - if (repr->opaque()) - repr->traceInstance(trace, mem); + if (repr->opaque()) { + uint8_t *mem = TypedMem(*object); + switch (repr->kind()) { + case TypeRepresentation::Scalar: + case TypeRepresentation::Reference: + case TypeRepresentation::Struct: + case TypeRepresentation::SizedArray: + case TypeRepresentation::X4: + repr->asSized()->traceInstance(trace, mem, 1); + break; + + case TypeRepresentation::UnsizedArray: + repr->asUnsizedArray()->element()->traceInstance( + trace, mem, DatumLength(*object)); + break; + } + } } /*static*/ void TypedDatum::obj_finalize(js::FreeOp *op, JSObject *obj) { // if the obj owns the memory, indicating by the owner slot being // set to itself, then we must free it when finalized. @@ -1829,17 +1775,18 @@ TypedDatum::obj_lookupGeneric(JSContext TypeRepresentation *typeRepr = typeRepresentation(*type); switch (typeRepr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::Reference: case TypeRepresentation::X4: break; - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: { uint32_t index; if (js_IdIsIndex(id, &index)) return obj_lookupElement(cx, obj, index, objp, propp); if (JSID_IS_ATOM(id, cx->names().length)) { MarkNonNativePropertyFound(propp); objp.set(obj); @@ -2008,19 +1955,27 @@ TypedDatum::obj_getGeneric(JSContext *cx switch (typeRepr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::Reference: break; case TypeRepresentation::X4: break; - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: if (JSID_IS_ATOM(id, cx->names().length)) { - vp.setInt32(typeRepr->asArray()->length()); + if (!TypedMem(*obj)) { // unattached + JS_ReportErrorNumber( + cx, js_GetErrorMessage, + nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); + return false; + } + + vp.setInt32(DatumLength(*obj)); return true; } break; case TypeRepresentation::Struct: { StructTypeRepresentation *structTypeRepr = typeRepr->asStruct(); const StructField *field = structTypeRepr->fieldNamed(id); if (!field) @@ -2069,28 +2024,33 @@ TypedDatum::obj_getElementIfPresent(JSCo switch (typeRepr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::Reference: case TypeRepresentation::X4: case TypeRepresentation::Struct: break; - case TypeRepresentation::Array: { + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: + { + JS_ASSERT(IsArrayTypedDatum(*obj)); + *present = true; - ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray(); - - if (index >= arrayTypeRepr->length()) { + + if (index >= DatumLength(*obj)) { vp.setUndefined(); return true; } - RootedObject elementType(cx, ArrayElementType(type)); - size_t offset = arrayTypeRepr->element()->size() * index; - return Reify(cx, arrayTypeRepr->element(), elementType, obj, offset, vp); + RootedObject elementType(cx, ArrayElementType(*GetType(*obj))); + SizedTypeRepresentation *elementTypeRepr = + typeRepresentation(*elementType)->asSized(); + size_t offset = elementTypeRepr->size() * index; + return Reify(cx, elementTypeRepr, elementType, obj, offset, vp); } } RootedObject proto(cx, obj->getProto()); if (!proto) { *present = false; vp.setUndefined(); return true; @@ -2122,17 +2082,18 @@ TypedDatum::obj_setGeneric(JSContext *cx switch (typeRepr->kind()) { case ScalarTypeRepresentation::Scalar: case TypeRepresentation::Reference: break; case ScalarTypeRepresentation::X4: break; - case ScalarTypeRepresentation::Array: + case ScalarTypeRepresentation::SizedArray: + case ScalarTypeRepresentation::UnsizedArray: if (JSID_IS_ATOM(id, cx->names().length)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH); return false; } break; case ScalarTypeRepresentation::Struct: { @@ -2157,39 +2118,41 @@ TypedDatum::obj_setProperty(JSContext *c bool strict) { RootedId id(cx, NameToId(name)); return obj_setGeneric(cx, obj, id, vp, strict); } bool TypedDatum::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, bool strict) { RootedObject type(cx, GetType(*obj)); TypeRepresentation *typeRepr = typeRepresentation(*type); switch (typeRepr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::Reference: case TypeRepresentation::X4: case TypeRepresentation::Struct: break; - case TypeRepresentation::Array: { - ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray(); - - if (index >= arrayTypeRepr->length()) { + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: + { + if (index >= DatumLength(*obj)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX); return false; } - RootedObject elementType(cx, ArrayElementType(type)); - size_t offset = arrayTypeRepr->element()->size() * index; + RootedObject elementType(cx, ArrayElementType(*GetType(*obj))); + SizedTypeRepresentation *elementTypeRepr = + typeRepresentation(*elementType)->asSized(); + size_t offset = elementTypeRepr->size() * index; return ConvertAndCopyTo(cx, elementType, obj, offset, vp); } } return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj); } bool @@ -2212,17 +2175,18 @@ TypedDatum::obj_getGenericAttributes(JSC switch (typeRepr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::Reference: break; case TypeRepresentation::X4: break; - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: if (js_IdIsIndex(id, &index)) { *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT; return true; } if (JSID_IS_ATOM(id, cx->names().length)) { *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; return true; } @@ -2253,17 +2217,18 @@ IsOwnId(JSContext *cx, HandleObject obj, TypeRepresentation *typeRepr = typeRepresentation(*type); switch (typeRepr->kind()) { case TypeRepresentation::Scalar: case TypeRepresentation::Reference: case TypeRepresentation::X4: return false; - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length); case TypeRepresentation::Struct: return typeRepr->asStruct()->fieldNamed(id) != nullptr; } return false; } @@ -2357,32 +2322,33 @@ TypedDatum::obj_enumerate(JSContext *cx, case JSENUMERATE_NEXT: case JSENUMERATE_DESTROY: statep.setNull(); break; } break; - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: switch (enum_op) { case JSENUMERATE_INIT_ALL: case JSENUMERATE_INIT: statep.setInt32(0); - idp.set(INT_TO_JSID(typeRepr->asArray()->length())); + idp.set(INT_TO_JSID(DatumLength(*obj))); break; case JSENUMERATE_NEXT: index = static_cast<int32_t>(statep.toInt32()); - if (index < typeRepr->asArray()->length()) { + if (index < DatumLength(*obj)) { idp.set(INT_TO_JSID(index)); statep.setInt32(index + 1); } else { - JS_ASSERT(index == typeRepr->asArray()->length()); + JS_ASSERT(index == DatumLength(*obj)); statep.setNull(); } break; case JSENUMERATE_DESTROY: statep.setNull(); break; @@ -2417,17 +2383,17 @@ TypedDatum::obj_enumerate(JSContext *cx, } return true; } /* static */ size_t TypedDatum::dataOffset() { - return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS + 1); + return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS); } /****************************************************************************** * Typed Objects */ const Class TypedObject::class_ = { "TypedObject", @@ -2473,47 +2439,122 @@ const Class TypedObject::class_ = { TypedDatum::obj_deleteElement, TypedDatum::obj_deleteSpecial, nullptr, nullptr, // watch/unwatch TypedDatum::obj_enumerate, nullptr, /* thisObject */ } }; -/*static*/ JSObject * -TypedObject::createZeroed(JSContext *cx, HandleObject type) +/*static*/ TypedObject * +TypedObject::createZeroed(JSContext *cx, + HandleObject typeObj, + int32_t length) { - Rooted<TypedObject*> obj(cx, createUnattached<TypedObject>(cx, type)); + // Create unattached wrapper object. + Rooted<TypedObject*> obj(cx); + obj = createUnattached<TypedObject>(cx, typeObj, length); if (!obj) return nullptr; - TypeRepresentation *typeRepr = typeRepresentation(*type); - size_t memsize = typeRepr->size(); - uint8_t *memory = (uint8_t*) cx->malloc_(memsize); - if (!memory) - return nullptr; - typeRepr->initInstance(cx->runtime(), memory); - obj->attach(memory); - return obj; + // Allocate and initialize the memory for this instance. + // Also initialize the JS_DATUM_SLOT_LENGTH slot. + TypeRepresentation *typeRepr = typeRepresentation(*typeObj); + switch (typeRepr->kind()) { + case TypeRepresentation::Scalar: + case TypeRepresentation::Reference: + case TypeRepresentation::Struct: + case TypeRepresentation::X4: + { + uint8_t *memory = (uint8_t*) cx->malloc_(typeRepr->asSized()->size()); + if (!memory) + return nullptr; + typeRepr->asSized()->initInstance(cx->runtime(), memory, 1); + obj->attach(memory); + return obj; + } + + case TypeRepresentation::SizedArray: + { + uint8_t *memory = (uint8_t*) cx->malloc_(typeRepr->asSizedArray()->size()); + if (!memory) + return nullptr; + typeRepr->asSizedArray()->initInstance(cx->runtime(), memory, 1); + obj->attach(memory); + return obj; + } + + case TypeRepresentation::UnsizedArray: + { + SizedTypeRepresentation *elementTypeRepr = + typeRepr->asUnsizedArray()->element(); + + int32_t totalSize; + if (!SafeMul(elementTypeRepr->size(), length, &totalSize)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, + JSMSG_TYPEDOBJECT_TOO_BIG); + return nullptr; + } + + uint8_t *memory = (uint8_t*) JS_malloc(cx, totalSize); + if (!memory) + return nullptr; + + elementTypeRepr->initInstance(cx->runtime(), memory, length); + obj->attach(memory); + return obj; + } + } + + MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind"); } /*static*/ bool TypedObject::construct(JSContext *cx, unsigned int argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedObject callee(cx, &args.callee()); JS_ASSERT(IsTypeObject(*callee)); - - RootedObject obj(cx, createZeroed(cx, callee)); + TypeRepresentation *typeRepr = typeRepresentation(*callee); + + // Determine the length based on the target type. + uint32_t nextArg = 0; + int32_t length = 0; + switch (typeRepr->kind()) { + case TypeRepresentation::Scalar: + case TypeRepresentation::Reference: + case TypeRepresentation::Struct: + case TypeRepresentation::X4: + length = 0; + break; + + case TypeRepresentation::SizedArray: + length = typeRepr->asSizedArray()->length(); + break; + + case TypeRepresentation::UnsizedArray: + // First argument is a length. + if (nextArg >= argc || !args[nextArg].isInt32()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, + nullptr, JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, + "1", "array length"); + return false; + } + length = args[nextArg++].toInt32(); + break; + } + + // Create zeroed wrapper object. + Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length)); if (!obj) - return false; - - if (argc == 1) { - RootedValue initial(cx, args[0]); + return nullptr; + + if (nextArg < argc) { + RootedValue initial(cx, args[nextArg++]); if (!ConvertAndCopyTo(cx, obj, initial)) return false; } args.rval().setObject(*obj); return true; } @@ -2582,21 +2623,22 @@ const JSFunctionSpec TypedHandle::handle * Intrinsics */ bool js::NewTypedHandle(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_ASSERT(argc == 1); - JS_ASSERT(args[0].isObject() && IsTypeObject(args[0].toObject())); + JS_ASSERT(args[0].isObject() && IsSizedTypeObject(args[0].toObject())); RootedObject typeObj(cx, &args[0].toObject()); - Rooted<TypedHandle*> obj( - cx, TypedDatum::createUnattached<TypedHandle>(cx, typeObj)); + int32_t length = DatumLengthFromType(*typeObj); + Rooted<TypedHandle*> obj(cx); + obj = TypedDatum::createUnattached<TypedHandle>(cx, typeObj, length); if (!obj) return false; args.rval().setObject(*obj); return true; } bool js::NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -143,16 +143,28 @@ class X4Type : public JSObject }; /* * Type descriptor created by `new ArrayType(...)` */ class ArrayType : public JSObject { private: + // Helper for creating a new ArrayType object, either sized or unsized. + // - `arrayTypePrototype` - prototype for the new object to be created, + // either ArrayType.prototype or + // unsizedArrayType.__proto__ depending on + // whether this is a sized or unsized array + // - `arrayTypeReprObj` - a type representation object for the array + // - `elementType` - type object for the elements in the array + static JSObject *create(JSContext *cx, + HandleObject arrayTypePrototype, + HandleObject arrayTypeReprObj, + HandleObject elementType); + public: static const Class class_; // Properties and methods to be installed on ArrayType.prototype, // and hence inherited by all array type objects: static const JSPropertySpec typeObjectProperties[]; static const JSFunctionSpec typeObjectMethods[]; @@ -160,20 +172,19 @@ class ArrayType : public JSObject // and hence inherited by all array *typed* objects: static const JSPropertySpec typedObjectProperties[]; static const JSFunctionSpec typedObjectMethods[]; // This is the function that gets called when the user // does `new ArrayType(elem)`. It produces an array type object. static bool construct(JSContext *cx, unsigned argc, Value *vp); - static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal, - HandleObject elementType, size_t length); - static bool repeat(JSContext *cx, unsigned argc, Value *vp); - static bool subarray(JSContext *cx, unsigned argc, Value *vp); + // This is the sized method on unsized array type objects. It + // produces a sized variant. + static bool dimension(JSContext *cx, unsigned int argc, jsval *vp); static JSObject *elementType(JSContext *cx, HandleObject obj); }; /* * Type descriptor created by `new StructType(...)` */ class StructType : public JSObject @@ -301,50 +312,56 @@ class TypedDatum : public JSObject // object or this object may alias data owned by someone else.) // This function returns the offset in bytes within the object // where the `void*` pointer can be found. It is intended for use // by the JIT. static size_t dataOffset(); static TypedDatum *createUnattachedWithClass(JSContext *cx, const Class *clasp, - HandleObject type); + HandleObject type, + int32_t length); // Creates an unattached typed object or handle (depending on the // type parameter T). Note that it is only legal for unattached // handles to escape to the end user; for non-handles, the caller // should always invoke one of the `attach()` methods below. // // Arguments: // - type: type object for resulting object template<class T> - static T *createUnattached(JSContext *cx, HandleObject type); + static T *createUnattached(JSContext *cx, HandleObject type, int32_t length); // Creates a datum that aliases the memory pointed at by `owner` // at the given offset. The datum will be a handle iff type is a // handle and a typed object otherwise. static TypedDatum *createDerived(JSContext *cx, HandleObject type, HandleObject typedContents, size_t offset); + // If `this` is the owner of the memory, use this. void attach(uint8_t *mem); // Otherwise, use this to attach to memory referenced by another datum. void attach(JSObject &datum, uint32_t offset); }; class TypedObject : public TypedDatum { public: static const Class class_; - // creates zeroed memory of size of type - static JSObject *createZeroed(JSContext *cx, HandleObject type); + // Creates a new typed object whose memory is freshly allocated + // and initialized with zeroes (or, in the case of references, an + // appropriate default value). + static TypedObject *createZeroed(JSContext *cx, + HandleObject typeObj, + int32_t length); // user-accessible constructor (`new TypeDescriptor(...)`) static bool construct(JSContext *cx, unsigned argc, Value *vp); }; class TypedHandle : public TypedDatum { public: @@ -355,16 +372,23 @@ class TypedHandle : public TypedDatum /* * Usage: NewTypedHandle(typeObj) * * Constructs a new, unattached instance of `Handle`. */ bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp); /* + * Usage: NewTypedHandle(typeObj) + * + * Constructs a new, unattached instance of `Handle`. + */ +bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp); + +/* * Usage: NewDerivedTypedDatum(typeObj, owner, offset) * * Constructs a new, unattached instance of `Handle`. */ bool NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp); /* * Usage: AttachHandle(handle, newOwner, newOffset)
--- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -9,16 +9,18 @@ UnsafeGetReservedSlot(obj, JS_TYPEOBJ_SLOT_TYPE_REPR) // Typed object slots #define DATUM_TYPE_OBJ(obj) \ UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_TYPE_OBJ) #define DATUM_OWNER(obj) \ UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_OWNER) +#define DATUM_LENGTH(obj) \ + TO_INT32(UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_LENGTH)) // Type repr slots #define REPR_KIND(obj) \ TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_KIND)) #define REPR_SIZE(obj) \ TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_SIZE)) #define REPR_ALIGNMENT(obj) \ @@ -90,38 +92,51 @@ TypedObjectPointer.prototype.reset = fun this.offset = inPtr.offset; return this; }; TypedObjectPointer.prototype.kind = function() { return REPR_KIND(this.typeRepr); } +TypedObjectPointer.prototype.length = function() { + switch (this.kind()) { + case JS_TYPEREPR_SIZED_ARRAY_KIND: + return REPR_LENGTH(this.typeRepr); + + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: + return DATUM_LENGTH(this.datum); + } + assert(false, "length() invoked on non-array-type"); + return 0; +} + /////////////////////////////////////////////////////////////////////////// // Moving the pointer // // The methods in this section adjust `this` in place to point at // subelements or subproperties. // Adjusts `this` in place so that it points at the property // `propName`. Throws if there is no such property. Returns `this`. TypedObjectPointer.prototype.moveTo = function(propName) { switch (this.kind()) { case JS_TYPEREPR_SCALAR_KIND: case JS_TYPEREPR_REFERENCE_KIND: case JS_TYPEREPR_X4_KIND: break; - case JS_TYPEREPR_ARRAY_KIND: + case JS_TYPEREPR_SIZED_ARRAY_KIND: + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: // For an array, property must be an element. Note that we use the // length as loaded from the type *representation* as opposed to // the type *object*; this is because some type objects represent // unsized arrays and hence do not have a length. var index = TO_INT32(propName); - if (index === propName && index >= 0 && index < REPR_LENGTH(this.typeRepr)) + if (index === propName && index >= 0 && index < this.length()) return this.moveToElem(index); break; case JS_TYPEREPR_STRUCT_KIND: if (HAS_PROPERTY(this.typeObj.fieldTypes, propName)) return this.moveToField(propName); break; } @@ -129,21 +144,22 @@ TypedObjectPointer.prototype.moveTo = fu ThrowError(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, propName); return undefined; }; // Adjust `this` in place to point at the element `index`. `this` // must be a array type and `index` must be within bounds. Returns // `this`. TypedObjectPointer.prototype.moveToElem = function(index) { - assert(this.kind() == JS_TYPEREPR_ARRAY_KIND, + assert(this.kind() == JS_TYPEREPR_SIZED_ARRAY_KIND || + this.kind() == JS_TYPEREPR_UNSIZED_ARRAY_KIND, "moveToElem invoked on non-array"); assert(TO_INT32(index) === index, "moveToElem invoked with non-integer index"); - assert(index >= 0 && index < REPR_LENGTH(this.typeRepr), + assert(index >= 0 && index < this.length(), "moveToElem invoked with out-of-bounds index: " + index); var elementTypeObj = this.typeObj.elementType; var elementTypeRepr = TYPE_TYPE_REPR(elementTypeObj); this.typeRepr = elementTypeRepr; this.typeObj = elementTypeObj; var elementSize = REPR_SIZE(elementTypeRepr); @@ -195,19 +211,24 @@ TypedObjectPointer.prototype.get = funct return this.getScalar(); case JS_TYPEREPR_REFERENCE_KIND: return this.getReference(); case JS_TYPEREPR_X4_KIND: return this.getX4(); - case JS_TYPEREPR_ARRAY_KIND: + case JS_TYPEREPR_SIZED_ARRAY_KIND: + return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset); + case JS_TYPEREPR_STRUCT_KIND: return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset); + + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: + assert(false, "Unhandled repr kind: " + REPR_KIND(this.typeRepr)); } assert(false, "Unhandled kind: " + REPR_KIND(this.typeRepr)); return undefined; } TypedObjectPointer.prototype.getScalar = function() { var type = REPR_TYPE(this.typeRepr); @@ -293,18 +314,18 @@ TypedObjectPointer.prototype.getX4 = fun TypedObjectPointer.prototype.set = function(fromValue) { assert(ObjectIsAttached(this.datum), "set() called with unattached datum"); var typeRepr = this.typeRepr; // Fast path: `fromValue` is a typed object with same type // representation as the destination. In that case, we can just do a // memcpy. - if (IsObject(fromValue) && HaveSameClass(fromValue, this.datum)) { - if (DATUM_TYPE_REPR(fromValue) === typeRepr) { + if (IsObject(fromValue) && ObjectIsTypedDatum(fromValue)) { + if (!typeRepr.variable && DATUM_TYPE_REPR(fromValue) === typeRepr) { if (!ObjectIsAttached(fromValue)) ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); var size = REPR_SIZE(typeRepr); Memcpy(this.datum, this.offset, fromValue, 0, size); return; } } @@ -317,22 +338,23 @@ TypedObjectPointer.prototype.set = funct case JS_TYPEREPR_REFERENCE_KIND: this.setReference(fromValue); return; case JS_TYPEREPR_X4_KIND: this.setX4(fromValue); return; - case JS_TYPEREPR_ARRAY_KIND: + case JS_TYPEREPR_SIZED_ARRAY_KIND: + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: if (!IsObject(fromValue)) break; // Check that "array-like" fromValue has an appropriate length. - var length = REPR_LENGTH(typeRepr); + var length = this.length(); if (fromValue.length !== length) break; // Adapt each element. if (length > 0) { var tempPtr = this.copy().moveToElem(0); var size = REPR_SIZE(tempPtr.typeRepr); for (var i = 0; i < length; i++) { @@ -536,28 +558,41 @@ function TypedArrayRedimension(newArrayT ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array"); if (!IsObject(newArrayType) || !ObjectIsTypeObject(newArrayType)) ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "type object"); // Peel away the outermost array layers from the type of `this` to find // the core element type. In the process, count the number of elements. var oldArrayType = DATUM_TYPE_OBJ(this); + var oldArrayReprKind = REPR_KIND(TYPE_TYPE_REPR(oldArrayType)); var oldElementType = oldArrayType; var oldElementCount = 1; - while (REPR_KIND(TYPE_TYPE_REPR(oldElementType)) == JS_TYPEREPR_ARRAY_KIND) { + switch (oldArrayReprKind) { + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: + oldElementCount *= this.length; + oldElementType = oldElementType.elementType; + break; + + case JS_TYPEREPR_SIZED_ARRAY_KIND: + break; + + default: + ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array"); + } + while (REPR_KIND(TYPE_TYPE_REPR(oldElementType)) === JS_TYPEREPR_SIZED_ARRAY_KIND) { oldElementCount *= oldElementType.length; oldElementType = oldElementType.elementType; } // Peel away the outermost array layers from `newArrayType`. In the // process, count the number of elements. var newElementType = newArrayType; var newElementCount = 1; - while (REPR_KIND(TYPE_TYPE_REPR(newElementType)) == JS_TYPEREPR_ARRAY_KIND) { + while (REPR_KIND(TYPE_TYPE_REPR(newElementType)) == JS_TYPEREPR_SIZED_ARRAY_KIND) { newElementCount *= newElementType.length; newElementType = newElementType.elementType; } // Check that the total number of elements does not change. if (oldElementCount !== newElementCount) { ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "New number of elements does not match old number of elements"); @@ -587,16 +622,28 @@ function TypedArrayRedimension(newArrayT // This is the `handle([obj, [...path]])` method on type objects. // User exposed! // // FIXME bug 929656 -- label algorithms with steps from the spec function HandleCreate(obj, ...path) { if (!IsObject(this) || !ObjectIsTypeObject(this)) ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "handle", "value"); + switch (REPR_KIND(TYPE_TYPE_REPR(this))) { + case JS_TYPEREPR_SCALAR_KIND: + case JS_TYPEREPR_REFERENCE_KIND: + case JS_TYPEREPR_X4_KIND: + case JS_TYPEREPR_SIZED_ARRAY_KIND: + case JS_TYPEREPR_STRUCT_KIND: + break; + + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: + ThrowError(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED); + } + var handle = NewTypedHandle(this); if (obj !== undefined) HandleMoveInternal(handle, obj, path) return handle; } @@ -724,16 +771,33 @@ function X4ToSource() { /////////////////////////////////////////////////////////////////////////// // Miscellaneous // This is the `objectType()` function defined in the spec. // It returns the type of its argument. // // Warning: user exposed! +function ArrayShorthand(...dims) { + if (!IsObject(this) || !ObjectIsTypeObject(this)) + ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, + "this", "typed object"); + + var T = StandardTypeObjectDescriptors(); + + if (dims.length == 0) + return new T.ArrayType(this); + + var accum = this; + for (var i = dims.length - 1; i >= 0; i--) + accum = new T.ArrayType(accum).dimension(dims[i]); + return accum; +} + +// Warning: user exposed! function TypeOfTypedDatum(obj) { if (IsObject(obj) && ObjectIsTypedDatum(obj)) return DATUM_TYPE_OBJ(obj); // Note: Do not create bindings for `Any`, `String`, etc in // Utilities.js, but rather access them through // `StandardTypeObjectDescriptors()`. The reason is that bindings // you create in Utilities.js are part of the self-hosted global,
--- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -24,55 +24,57 @@ #define JS_TYPEOBJ_SCALAR_SLOTS 1 // Maximum number // Slots on references #define JS_TYPEOBJ_REFERENCE_SLOTS 1 // Maximum number // Slots on x4s #define JS_TYPEOBJ_X4_SLOTS 1 // Maximum number -// Slots on arrays +// Slots on array type objects #define JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE 1 -#define JS_TYPEOBJ_ARRAY_SLOTS 2 // Maximum number +#define JS_TYPEOBJ_ARRAY_SLOTS 3 // Maximum number // Slots on structs #define JS_TYPEOBJ_SLOT_STRUCT_FIELD_TYPES 1 #define JS_TYPEOBJ_STRUCT_SLOTS 2 // Maximum number /////////////////////////////////////////////////////////////////////////// // Slots for type representation objects // // Some slots apply to all type representations and some are specific // to particular kinds of type representations. Because all type // representations share the same class, however, they always have the // same number of slots, though not all of them will be initialized or // used in the same way. -// Slots on *all* type objects: +// Slots on *all* type representations: #define JS_TYPEREPR_SLOT_KIND 0 // One of the `kind` constants below #define JS_TYPEREPR_SLOT_SIZE 1 // Size in bytes. #define JS_TYPEREPR_SLOT_ALIGNMENT 2 // Alignment in bytes. -// Slots on arrays: +// Slots on sized arrays: #define JS_TYPEREPR_SLOT_LENGTH 3 // Length of the array // Slots on scalars, references, and X4s: #define JS_TYPEREPR_SLOT_TYPE 3 // One of the constants below // Maximum number of slots for any type representation #define JS_TYPEREPR_SLOTS 4 // These constants are for use exclusively in JS code. In C++ code, // prefer TypeRepresentation::Scalar etc, which allows you to // write a switch which will receive a warning if you omit a case. -#define JS_TYPEREPR_SCALAR_KIND 0 -#define JS_TYPEREPR_REFERENCE_KIND 1 -#define JS_TYPEREPR_X4_KIND 2 -#define JS_TYPEREPR_STRUCT_KIND 3 -#define JS_TYPEREPR_ARRAY_KIND 4 +#define JS_TYPEREPR_UNSIZED_ARRAY_KIND 0 +#define JS_TYPEREPR_MAX_UNSIZED_KIND 0 // Unsized kinds go above here +#define JS_TYPEREPR_SCALAR_KIND 1 +#define JS_TYPEREPR_REFERENCE_KIND 2 +#define JS_TYPEREPR_STRUCT_KIND 3 +#define JS_TYPEREPR_SIZED_ARRAY_KIND 4 +#define JS_TYPEREPR_X4_KIND 5 // These constants are for use exclusively in JS code. In C++ code, // prefer ScalarTypeRepresentation::TYPE_INT8 etc, which allows // you to write a switch which will receive a warning if you omit a // case. #define JS_SCALARTYPEREPR_INT8 0 #define JS_SCALARTYPEREPR_UINT8 1 #define JS_SCALARTYPEREPR_INT16 2 @@ -98,11 +100,17 @@ #define JS_X4TYPEREPR_INT32 0 #define JS_X4TYPEREPR_FLOAT32 1 /////////////////////////////////////////////////////////////////////////// // Slots for typed objects (actually, any TypedContents objects) #define JS_DATUM_SLOT_TYPE_OBJ 0 // Type object for a given typed object #define JS_DATUM_SLOT_OWNER 1 // Owner of data (if null, this is owner) -#define JS_DATUM_SLOTS 2 // Number of slots for typed objs +#define JS_DATUM_SLOT_LENGTH 2 // Length of array (see (*) below) +#define JS_DATUM_SLOTS 3 // Number of slots for typed objs + +// (*) The JS_DATUM_SLOT_LENGTH slot stores the length for datums of +// sized and unsized array type. The slot contains 0 for non-arrays. +// The slot also contains 0 for *unattached* datums, no matter what +// type they have. #endif
--- a/js/src/jit-test/tests/TypedObject/fuzz10.js +++ b/js/src/jit-test/tests/TypedObject/fuzz10.js @@ -1,8 +1,8 @@ // |jit-test| error:Error if (!this.hasOwnProperty("TypedObject")) throw new Error("type too large"); -var AA = new TypedObject.ArrayType(new ArrayType(TypedObject.uint8, 2147483647), 5); +var AA = TypedObject.uint8.array(2147483647).array(5); var aa = new AA(); var aa0 = aa[0];
--- a/js/src/jit-test/tests/TypedObject/fuzz11.js +++ b/js/src/jit-test/tests/TypedObject/fuzz11.js @@ -1,13 +1,13 @@ // |jit-test| error:Error if (!this.hasOwnProperty("TypedObject")) throw new Error("type too large"); -var A = new TypedObject.ArrayType(TypedObject.uint8, 2147483647); +var A = TypedObject.uint8.array(2147483647); var S = new TypedObject.StructType({a: A, b: A, c: A, d: A, e: A}); var aa = new S(); var aa0 = aa.a;
--- a/js/src/jit-test/tests/TypedObject/fuzz4.js +++ b/js/src/jit-test/tests/TypedObject/fuzz4.js @@ -1,10 +1,10 @@ // |jit-test| error:Error; if (!this.hasOwnProperty("TypedObject")) throw new Error(); -var A = new TypedObject.ArrayType(TypedObject.uint8, 10); +var A = TypedObject.uint8.array(10); var a = new A(); a.forEach(function(val, i) { assertEq(arguments[5], a); });
--- a/js/src/jit-test/tests/TypedObject/fuzz8.js +++ b/js/src/jit-test/tests/TypedObject/fuzz8.js @@ -1,6 +1,6 @@ // |jit-test| error:Error if (!this.hasOwnProperty("TypedObject")) throw new Error(); -new TypedObject.ArrayType(TypedObject.uint8, .0000000009); +TypedObject.uint8.array(.0000000009);
--- a/js/src/jit-test/tests/TypedObject/fuzz9.js +++ b/js/src/jit-test/tests/TypedObject/fuzz9.js @@ -1,10 +1,10 @@ // |jit-test| error: TypeError if (!this.hasOwnProperty("TypedObject")) throw new TypeError(); -var Vec3 = new TypedObject.ArrayType(TypedObject.float32, 3); -var Sprite = new TypedObject.ArrayType(Vec3, 3); +var Vec3 = TypedObject.float32.array(3); +var Sprite = Vec3.array(3); var mario = new Sprite(); mario[/\u00ee[]/] = new Vec3([1, 0, 0]);
--- a/js/src/jit-test/tests/TypedObject/jit-complex.js +++ b/js/src/jit-test/tests/TypedObject/jit-complex.js @@ -1,30 +1,33 @@ // Test that we can optimize stuff like line.target.x without // creating an intermediate object. if (!this.hasOwnProperty("TypedObject")) quit(); +setJitCompilerOption("ion.usecount.trigger", 30); + var PointType = new TypedObject.StructType({x: TypedObject.float64, y: TypedObject.float64}); var LineType = new TypedObject.StructType({source: PointType, target: PointType}); -function manhattenDistance(line) { +function manhattanDistance(line) { return (Math.abs(line.target.x - line.source.x) + Math.abs(line.target.y - line.source.y)); } function foo() { - var N = 30000; + var N = 100; var points = []; var obj; var s; var fromAToB = new LineType({source: {x: 22, y: 44}, target: {x: 66, y: 88}}); - for (var i = 0; i < N; i++) - assertEq(manhattenDistance(fromAToB), 88); + for (var i = 0; i < N; i++) { + assertEq(manhattanDistance(fromAToB), 88); + } } foo();
--- a/js/src/jit-test/tests/TypedObject/jit-prefix.js +++ b/js/src/jit-test/tests/TypedObject/jit-prefix.js @@ -1,11 +1,13 @@ if (!this.hasOwnProperty("TypedObject")) quit(); +setJitCompilerOption("ion.usecount.trigger", 30); + var PointType2 = new TypedObject.StructType({ x: TypedObject.float64, y: TypedObject.float64}); var PointType3 = new TypedObject.StructType({ x: TypedObject.float64, @@ -17,17 +19,17 @@ function xPlusY(p) { } function xPlusYTweak(p) { p.x = 22; return xPlusY(p); } function foo() { - var N = 30000; + var N = 100; var points = []; var obj; var s; for (var i = 0; i < N; i++) { if ((i % 2) == 0 || true) obj = new PointType2({x: i, y: i+1}); else
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/TypedObject/jit-read-many.js @@ -0,0 +1,62 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +if (!this.hasOwnProperty("TypedObject")) + quit(); + +// Test a single function (`bar`) being used with arrays that are all +// of known length, but not the same length. + +setJitCompilerOption("ion.usecount.trigger", 30); + +var N0 = 50; +var N1 = 100; +var N2 = 150; +var T = TypedObject; +var Array0 = T.uint32.array(N0); +var Array1 = T.uint32.array(N1); +var Array2 = T.uint32.array(N2); + +function bar(array, i, v) { + assertEq(array[i], v); +} + +function foo() { + var array0 = new Array0(); + var array1 = new Array1(); + var array2 = new Array2(); + + for (var i = 0; i < N0; i++) + array0[i] = i + 0; + + for (var i = 0; i < N1; i++) + array1[i] = i + 1; + + for (var i = 0; i < N2; i++) + array2[i] = i + 2; + + // get it primed up.. + for (var i = 0; i < N0; i++) { + bar(array0, i, i); + bar(array1, i, i + 1); + bar(array2, i, i + 2); + } + + // ...do some OOB accesses... + for (var i = N0; i < N1; i++) { + bar(array0, i, undefined); + bar(array1, i, i + 1); + bar(array2, i, i + 2); + } + + // ...and some more. + for (var i = N1; i < N2; i++) { + bar(array0, i, undefined); + bar(array1, i, undefined); + bar(array2, i, i + 2); + } +} + +foo();
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js @@ -1,18 +1,19 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ if (!this.hasOwnProperty("TypedObject")) quit(); -var PointType = new TypedObject.ArrayType(TypedObject.uint16, 3); -var VecPointType = new TypedObject.ArrayType(PointType, 3); +var T = TypedObject; +var PointType = T.uint16.array(3); +var VecPointType = PointType.array(3); function foo() { for (var i = 0; i < 10000; i += 9) { var vec = new VecPointType([ [i, i+1, i+2], [i+3, i+4, i+5], [i+6, i+7, i+8]]); var sum = vec[0][0] + vec[0][1] + vec[0][2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js @@ -5,17 +5,17 @@ if (!this.hasOwnProperty("TypedObject")) quit(); var PointType = new TypedObject.StructType({x: TypedObject.uint16, y: TypedObject.uint16, z: TypedObject.uint16}); -var VecPointType = new TypedObject.ArrayType(PointType, 3); +var VecPointType = PointType.array(3); var PairVecType = new TypedObject.StructType({fst: VecPointType, snd: VecPointType}); function foo() { for (var i = 0; i < 5000; i += 9) { var p = new PairVecType({fst: [{x: i, y: i+1, z:i+2}, {x: i+3, y: i+4, z:i+5},
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js @@ -5,17 +5,17 @@ if (!this.hasOwnProperty("TypedObject")) quit(); var PointType = new TypedObject.StructType({x: TypedObject.uint16, y: TypedObject.uint16, z: TypedObject.uint16}); -var VecPointType = new TypedObject.ArrayType(PointType, 3); +var VecPointType = PointType.array(3); function foo() { for (var i = 0; i < 10000; i += 9) { var vec = new VecPointType([ {x: i, y:i+1, z:i+2}, {x: i+3, y:i+4, z:i+5}, {x: i+6, y:i+7, z:i+8}]); var sum = vec[0].x + vec[0].y + vec[0].z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js @@ -1,17 +1,17 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ if (!this.hasOwnProperty("TypedObject")) quit(); -var Vec3u16Type = new TypedObject.ArrayType(TypedObject.uint16, 3); +var Vec3u16Type = TypedObject.uint16.array(3); var PairVec3u16Type = new TypedObject.StructType({fst: Vec3u16Type, snd: Vec3u16Type}); function foo_u16() { for (var i = 0; i < 15000; i += 6) { var p = new PairVec3u16Type({fst: [i, i+1, i+2], snd: [i+3,i+4,i+5]}); var sum = p.fst[0] + p.fst[1] + p.fst[2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js @@ -1,17 +1,17 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ if (!this.hasOwnProperty("TypedObject")) quit(); -var Vec3u16Type = new TypedObject.ArrayType(TypedObject.uint16, 3); +var Vec3u16Type = TypedObject.uint16.array(3); function foo_u16() { for (var i = 0; i < 30000; i += 3) { var vec = new Vec3u16Type([i, i+1, i+2]); var sum = vec[0] + vec[1] + vec[2]; assertEq(sum, 3*i + 3); } }
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js @@ -1,18 +1,18 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ if (!this.hasOwnProperty("TypedObject")) quit(); -var PointType = new TypedObject.ArrayType(TypedObject.uint32, 3); -var VecPointType = new TypedObject.ArrayType(PointType, 3); +var PointType = TypedObject.uint32.array(3); +var VecPointType = PointType.array(3); function foo() { for (var i = 0; i < 10000; i += 9) { var vec = new VecPointType([ [i, i+1, i+2], [i+3, i+4, i+5], [i+6, i+7, i+8]]); var sum = vec[0][0] + vec[0][1] + vec[0][2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js @@ -5,17 +5,17 @@ if (!this.hasOwnProperty("TypedObject")) quit(); var PointType = new TypedObject.StructType({x: TypedObject.uint32, y: TypedObject.uint32, z: TypedObject.uint32}); -var VecPointType = new TypedObject.ArrayType(PointType, 3); +var VecPointType = PointType.array(3); var PairVecType = new TypedObject.StructType({fst: VecPointType, snd: VecPointType}); function foo() { for (var i = 0; i < 5000; i += 9) { var p = new PairVecType({fst: [{x: i, y: i+1, z:i+2}, {x: i+3, y: i+4, z:i+5},
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js @@ -5,17 +5,17 @@ if (!this.hasOwnProperty("TypedObject")) quit(); var PointType = new TypedObject.StructType({x: TypedObject.uint32, y: TypedObject.uint32, z: TypedObject.uint32}); -var VecPointType = new TypedObject.ArrayType(PointType, 3); +var VecPointType = PointType.array(3); function foo() { for (var i = 0; i < 10000; i += 9) { var vec = new VecPointType([ {x: i, y:i+1, z:i+2}, {x: i+3, y:i+4, z:i+5}, {x: i+6, y:i+7, z:i+8}]); var sum = vec[0].x + vec[0].y + vec[0].z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js @@ -1,17 +1,17 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ if (!this.hasOwnProperty("TypedObject")) quit(); -var Vec3u32Type = new TypedObject.ArrayType(TypedObject.uint32, 3); +var Vec3u32Type = TypedObject.uint32.array(3); var PairVec3u32Type = new TypedObject.StructType({fst: Vec3u32Type, snd: Vec3u32Type}); function foo_u32() { for (var i = 0; i < 15000; i += 6) { var p = new PairVec3u32Type({fst: [i, i+1, i+2], snd: [i+3,i+4,i+5]}); var sum = p.fst[0] + p.fst[1] + p.fst[2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js +++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js @@ -1,17 +1,17 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ if (!this.hasOwnProperty("TypedObject")) quit(); -var Vec3u32Type = new TypedObject.ArrayType(TypedObject.uint32, 3); +var Vec3u32Type = TypedObject.uint32.array(3); function foo_u32() { for (var i = 0; i < 30000; i += 3) { var vec = new Vec3u32Type([i, i+1, i+2]); var sum = vec[0] + vec[1] + vec[2]; assertEq(sum, 3*i + 3); } }
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/TypedObject/jit-read-unsized.js @@ -0,0 +1,47 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +if (!this.hasOwnProperty("TypedObject")) + quit(); + +setJitCompilerOption("ion.usecount.trigger", 30); + +var N = 100; +var T = TypedObject; +var Point = new T.StructType({x: T.uint32, y: T.uint32, z: T.uint32}); +var PointArray = Point.array(); + +function bar(array, i, x, y, z) { + assertEq(array[i].x, x); + assertEq(array[i].y, y); + assertEq(array[i].z, z); +} + +function foo() { + var array = new PointArray(N); + for (var i = 0; i < N; i++) { + array[i].x = i + 0; + array[i].y = i + 1; + array[i].z = i + 2; + } + + // get it primed up.. + for (var i = 0; i < N; i++) + bar(array, i, i, i + 1, i + 2); + + // ...do some OOB accesses... + for (var i = 0; i < N; i++) { + try { + bar(array, N, undefined, undefined, undefined); + assertEq(false, true); + } catch(e) { } + } + + // ...test again. + for (var i = 0; i < N; i++) + bar(array, i, i, i + 1, i + 2); +} + +foo();
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6590,95 +6590,124 @@ IonBuilder::getElemTryTypedObject(bool * return false; if (!objTypeReprs.allOfArrayKind()) return true; TypeRepresentationSet elemTypeReprs; if (!objTypeReprs.arrayElementType(*this, &elemTypeReprs)) return false; + if (elemTypeReprs.empty()) + return true; + + JS_ASSERT(TypeRepresentation::isSized(elemTypeReprs.kind())); size_t elemSize; if (!elemTypeReprs.allHaveSameSize(&elemSize)) return true; switch (elemTypeReprs.kind()) { case TypeRepresentation::X4: // FIXME (bug 894104): load into a MIRType_float32x4 etc return true; case TypeRepresentation::Struct: - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: return getElemTryComplexElemOfTypedObject(emitted, obj, index, objTypeReprs, elemTypeReprs, elemSize); case TypeRepresentation::Scalar: return getElemTryScalarElemOfTypedObject(emitted, obj, index, objTypeReprs, elemTypeReprs, elemSize); case TypeRepresentation::Reference: return true; + + case TypeRepresentation::UnsizedArray: + MOZ_ASSUME_UNREACHABLE("Unsized arrays cannot be element types"); } MOZ_ASSUME_UNREACHABLE("Bad kind"); } static MIRType MIRTypeForTypedArrayRead(ScalarTypeRepresentation::Type arrayType, bool observedDouble); bool +IonBuilder::checkTypedObjectIndexInBounds(size_t elemSize, + MDefinition *obj, + MDefinition *index, + MDefinition **indexAsByteOffset, + TypeRepresentationSet objTypeReprs) +{ + // Ensure index is an integer. + MInstruction *idInt32 = MToInt32::New(alloc(), index); + current->add(idInt32); + + // If we know the length statically from the type, just embed it. + // Otherwise, load it from the appropriate reserved slot on the + // typed object. We know it's an int32, so we can convert from + // Value to int32 using truncation. + size_t lenOfAll; + MDefinition *length; + if (objTypeReprs.hasKnownArrayLength(&lenOfAll)) { + length = constantInt(lenOfAll); + } else { + MInstruction *lengthValue = MLoadFixedSlot::New(alloc(), obj, JS_DATUM_SLOT_LENGTH); + current->add(lengthValue); + + MInstruction *length32 = MTruncateToInt32::New(alloc(), lengthValue); + current->add(length32); + + length = length32; + } + + index = addBoundsCheck(idInt32, length); + + // Since we passed the bounds check, it is impossible for the + // result of multiplication to overflow; so enable imul path. + MMul *mul = MMul::New(alloc(), index, constantInt(elemSize), + MIRType_Int32, MMul::Integer); + current->add(mul); + + *indexAsByteOffset = mul; + return true; +} + +bool IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted, MDefinition *obj, MDefinition *index, TypeRepresentationSet objTypeReprs, TypeRepresentationSet elemTypeReprs, size_t elemSize) { - JS_ASSERT(objTypeReprs.allOfArrayKind()); + JS_ASSERT(objTypeReprs.kind() == TypeRepresentation::SizedArray); // Must always be loading the same scalar type if (!elemTypeReprs.singleton()) return true; ScalarTypeRepresentation *elemTypeRepr = elemTypeReprs.getTypeRepresentation()->asScalar(); - - // Get the length. - size_t lenOfAll = objTypeReprs.arrayLength(); - if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound - return true; - MInstruction *length = MConstant::New(alloc(), Int32Value(int32_t(lenOfAll))); - - *emitted = true; - current->add(length); - - // Ensure index is an integer. - MInstruction *idInt32 = MToInt32::New(alloc(), index); - current->add(idInt32); - index = idInt32; - - // Typed-object accesses usually in bounds (bail out otherwise). - index = addBoundsCheck(index, length); - - // Since we passed the bounds check, it is impossible for the - // result of multiplication to overflow; so enable imul path. - const int32_t alignment = elemTypeRepr->alignment(); - MMul *indexAsByteOffset = MMul::New(alloc(), index, constantInt(alignment), - MIRType_Int32, MMul::Integer); - current->add(indexAsByteOffset); + JS_ASSERT(elemSize == elemTypeRepr->alignment()); + + MDefinition *indexAsByteOffset; + if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeReprs)) + return false; // Find location within the owner object. MDefinition *elements, *scaledOffset; - loadTypedObjectElements(obj, indexAsByteOffset, alignment, &elements, &scaledOffset); + loadTypedObjectElements(obj, indexAsByteOffset, elemSize, &elements, &scaledOffset); // Load the element. MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset, elemTypeRepr->type()); current->add(load); current->push(load); // If we are reading in-bounds elements, we can use knowledge about // the array type to determine the result type, even if the opcode has @@ -6687,75 +6716,53 @@ IonBuilder::getElemTryScalarElemOfTypedO types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc); bool allowDouble = resultTypes->hasType(types::Type::DoubleType()); MIRType knownType = MIRTypeForTypedArrayRead(elemTypeRepr->type(), allowDouble); // Note: we can ignore the type barrier here, we know the type must // be valid and unbarriered. load->setResultType(knownType); load->setResultTypeSet(resultTypes); + *emitted = true; return true; } bool IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted, MDefinition *obj, MDefinition *index, TypeRepresentationSet objTypeReprs, TypeRepresentationSet elemTypeReprs, size_t elemSize) { JS_ASSERT(objTypeReprs.allOfArrayKind()); MDefinition *type = loadTypedObjectType(obj); MDefinition *elemType = typeObjectForElementFromArrayStructType(type); - // Get the length. - size_t lenOfAll = objTypeReprs.arrayLength(); - if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound - return true; - MInstruction *length = MConstant::New(alloc(), Int32Value(int32_t(lenOfAll))); - - *emitted = true; - current->add(length); - - // Ensure index is an integer. - MInstruction *idInt32 = MToInt32::New(alloc(), index); - current->add(idInt32); - index = idInt32; - - // Typed-object accesses usually in bounds (bail out otherwise). - index = addBoundsCheck(index, length); - - // Convert array index to element data offset. - MConstant *alignment = MConstant::New(alloc(), Int32Value(elemSize)); - current->add(alignment); - - // Since we passed the bounds check, it is impossible for the - // result of multiplication to overflow; so enable imul path. - MMul *indexAsByteOffset = MMul::New(alloc(), index, alignment, MIRType_Int32, - MMul::Integer); - current->add(indexAsByteOffset); + MDefinition *indexAsByteOffset; + if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeReprs)) + return false; // Find location within the owner object. MDefinition *owner, *ownerOffset; loadTypedObjectData(obj, indexAsByteOffset, &owner, &ownerOffset); // Create the derived type object. MInstruction *derived = MNewDerivedTypedObject::New(alloc(), elemTypeReprs, elemType, owner, ownerOffset); types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc); derived->setResultTypeSet(resultTypes); current->add(derived); current->push(derived); - + *emitted = true; return true; } bool IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index) { JS_ASSERT(*emitted == false); @@ -8291,28 +8298,31 @@ IonBuilder::getPropTryTypedObject(bool * case TypeRepresentation::Reference: return true; case TypeRepresentation::X4: // FIXME (bug 894104): load into a MIRType_float32x4 etc return true; case TypeRepresentation::Struct: - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: return getPropTryComplexPropOfTypedObject(emitted, fieldOffset, fieldTypeReprs, fieldIndex, resultTypes); case TypeRepresentation::Scalar: return getPropTryScalarPropOfTypedObject(emitted, fieldOffset, fieldTypeReprs, resultTypes); + + case TypeRepresentation::UnsizedArray: + MOZ_ASSUME_UNREACHABLE("Field of unsized array type"); } MOZ_ASSUME_UNREACHABLE("Bad kind"); } bool IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, int32_t fieldOffset, @@ -8858,17 +8868,18 @@ IonBuilder::setPropTryTypedObject(bool * switch (fieldTypeReprs.kind()) { case TypeRepresentation::X4: // FIXME (bug 894104): store into a MIRType_float32x4 etc return true; case TypeRepresentation::Reference: case TypeRepresentation::Struct: - case TypeRepresentation::Array: + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: // For now, only optimize storing scalars. return true; case TypeRepresentation::Scalar: return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset, value, fieldTypeReprs); }
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -448,16 +448,21 @@ class IonBuilder : public MIRGenerator MDefinition **ownerScaledOffset); MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj); MDefinition *typeObjectForFieldFromStructType(MDefinition *type, size_t fieldIndex); bool storeScalarTypedObjectValue(MDefinition *typedObj, MDefinition *offset, ScalarTypeRepresentation *typeRepr, MDefinition *value); + bool checkTypedObjectIndexInBounds(size_t elemSize, + MDefinition *obj, + MDefinition *index, + MDefinition **indexAsByteOffset, + TypeRepresentationSet objTypeReprs); // jsop_setelem() helpers. bool setElemTryTyped(bool *emitted, MDefinition *object, MDefinition *index, MDefinition *value); bool setElemTryTypedStatic(bool *emitted, MDefinition *object, MDefinition *index, MDefinition *value); bool setElemTryDense(bool *emitted, MDefinition *object, MDefinition *index, MDefinition *value);
--- a/js/src/jit/TypeRepresentationSet.cpp +++ b/js/src/jit/TypeRepresentationSet.cpp @@ -179,17 +179,29 @@ TypeRepresentationSet::getTypeRepresenta } bool TypeRepresentationSet::allOfArrayKind() { if (empty()) return false; - return kind() == TypeRepresentation::Array; + switch (kind()) { + case TypeRepresentation::SizedArray: + case TypeRepresentation::UnsizedArray: + return true; + + case TypeRepresentation::X4: + case TypeRepresentation::Reference: + case TypeRepresentation::Scalar: + case TypeRepresentation::Struct: + return false; + } + + MOZ_ASSUME_UNREACHABLE("Invalid kind() in TypeRepresentationSet"); } bool TypeRepresentationSet::allOfKind(TypeRepresentation::Kind aKind) { if (empty()) return false; @@ -197,55 +209,78 @@ TypeRepresentationSet::allOfKind(TypeRep } bool TypeRepresentationSet::allHaveSameSize(size_t *out) { if (empty()) return false; - size_t size = get(0)->size(); + JS_ASSERT(TypeRepresentation::isSized(kind())); + + size_t size = get(0)->asSized()->size(); for (size_t i = 1; i < length(); i++) { - if (get(i)->size() != size) + if (get(i)->asSized()->size() != size) return false; } *out = size; return true; } TypeRepresentation::Kind TypeRepresentationSet::kind() { JS_ASSERT(!empty()); return get(0)->kind(); } -size_t -TypeRepresentationSet::arrayLength() +bool +TypeRepresentationSet::hasKnownArrayLength(size_t *l) { - JS_ASSERT(kind() == TypeRepresentation::Array); - const size_t result = get(0)->asArray()->length(); - for (size_t i = 1; i < length(); i++) { - if (get(i)->asArray()->length() != result) - return SIZE_MAX; + switch (kind()) { + case TypeRepresentation::UnsizedArray: + return false; + + case TypeRepresentation::SizedArray: + { + const size_t result = get(0)->asSizedArray()->length(); + for (size_t i = 1; i < length(); i++) { + if (get(i)->asSizedArray()->length() != result) + return false; + } + *l = result; + return true; + } + + default: + MOZ_ASSUME_UNREACHABLE("Invalid array size for call to arrayLength()"); } - return result; } bool TypeRepresentationSet::arrayElementType(IonBuilder &builder, TypeRepresentationSet *out) { - JS_ASSERT(kind() == TypeRepresentation::Array); - TypeRepresentationSetBuilder elementTypes; for (size_t i = 0; i < length(); i++) { - if (!elementTypes.insert(get(i)->asArray()->element())) - return false; + switch (kind()) { + case TypeRepresentation::UnsizedArray: + if (!elementTypes.insert(get(i)->asUnsizedArray()->element())) + return false; + break; + + case TypeRepresentation::SizedArray: + if (!elementTypes.insert(get(i)->asSizedArray()->element())) + return false; + break; + + default: + MOZ_ASSUME_UNREACHABLE("Invalid kind for arrayElementType()"); + } } return elementTypes.build(builder, out); } bool TypeRepresentationSet::fieldNamed(IonBuilder &builder, jsid id, size_t *offset,
--- a/js/src/jit/TypeRepresentationSet.h +++ b/js/src/jit/TypeRepresentationSet.h @@ -108,23 +108,24 @@ class TypeRepresentationSet { TypeRepresentation::Kind kind(); ////////////////////////////////////////////////////////////////////// // The following operations are only valid on a singleton set: TypeRepresentation *getTypeRepresentation(); ////////////////////////////////////////////////////////////////////// - // Array operations + // SizedArray operations // - // Only valid when `kind() == TypeRepresentation::Array` + // Only valid when `kind() == TypeRepresentation::SizedArray` - // Returns the length of the arrays in this set, or SIZE_MAX - // if they are not all the same. - size_t arrayLength(); + // Determines whether all arrays in this set have the same, + // statically known, array length and return that length + // (via `*length`) if so. Otherwise returns false. + bool hasKnownArrayLength(size_t *length); // Returns a `TypeRepresentationSet` representing the element // types of the various array types in this set. The returned set // may be the empty set. bool arrayElementType(IonBuilder &builder, TypeRepresentationSet *out); ////////////////////////////////////////////////////////////////////// // Struct operations
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -426,8 +426,10 @@ MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 372, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set") MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT, 373, 0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword") MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, 374, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword") MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED, 375, 0, JSEXN_SYNTAXERR, "modules are not implemented yet") MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL, 376, 0, JSEXN_SYNTAXERR, "export declarations may only appear at top level") MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 377, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list") MSG_DEF(JSMSG_NO_EXPORT_NAME, 378, 0, JSEXN_SYNTAXERR, "missing export name") MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT, 379, 0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword") +MSG_DEF(JSMSG_INVALID_PROTOTYPE, 380, 0, JSEXN_TYPEERR, "prototype field is not an object") +MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED, 381, 0, JSEXN_TYPEERR, "cannot create a handle to an unsized type")
--- a/js/src/tests/ecma_6/TypedObject/arrayequiv.js +++ b/js/src/tests/ecma_6/TypedObject/arrayequiv.js @@ -24,36 +24,36 @@ function assertNotEquivalent(t1, t2) { assertEq(false, t2.equivalent(t1)); } function runTests() { print(BUGNUMBER + ": " + summary); // Create a line: var PixelType1 = new StructType({x: uint8, y: uint8}); - var PixelsType1 = new ArrayType(PixelType1, 22); + var PixelsType1 = PixelType1.array(22); // Sanity checks about type equivalence: assertEquivalent(PixelType1, PixelType1); assertEquivalent(PixelsType1, PixelsType1); assertNotEquivalent(PixelType1, PixelsType1); // Define the same two types again. Equivalent. var PixelType2 = new StructType({x: uint8, y: uint8}); - var PixelsType2 = new ArrayType(PixelType2, 22); + var PixelsType2 = PixelType2.array(22); assertEquivalent(PixelType1, PixelType2); assertEquivalent(PixelsType1, PixelsType2); // Define the pixel type with field order reversed. Not equivalent. var PixelType3 = new StructType({y: uint8, x: uint8}); - var PixelsType3 = new ArrayType(PixelType3, 22); + var PixelsType3 = PixelType3.array(22); assertNotEquivalent(PixelType1, PixelType3); assertNotEquivalent(PixelsType1, PixelsType3); // Define the pixels type with different number of elements. Not equivalent. - var PixelsType3 = new ArrayType(PixelType1, 23); + var PixelsType3 = PixelType1.array(23); assertNotEquivalent(PixelsType1, PixelsType3); reportCompare(true, true); print("Tests complete"); } runTests();
--- a/js/src/tests/ecma_6/TypedObject/arrayofstructs.js +++ b/js/src/tests/ecma_6/TypedObject/arrayofstructs.js @@ -2,19 +2,19 @@ var BUGNUMBER = 578700; var summary = 'TypedObjects StructType prototype chains'; var ArrayType = TypedObject.ArrayType; var StructType = TypedObject.StructType; var float32 = TypedObject.float32; function runTests() { - var Point = new ArrayType(float32, 3); + var Point = new ArrayType(float32).dimension(3); var Line = new StructType({from: Point, to: Point}); - var Lines = new ArrayType(Line, 3); + var Lines = new ArrayType(Line).dimension(3); var lines = new Lines([ {from: [1, 2, 3], to: [4, 5, 6]}, {from: [7, 8, 9], to: [10, 11, 12]}, {from: [13, 14, 15], to: [16, 17, 18]} ]); assertEq(lines[1].to[1], 11);
--- a/js/src/tests/ecma_6/TypedObject/arraytype.js +++ b/js/src/tests/ecma_6/TypedObject/arraytype.js @@ -17,35 +17,30 @@ var ArrayType = TypedObject.ArrayType; var uint8 = TypedObject.uint8; var float32 = TypedObject.float32; var uint32 = TypedObject.uint32; function runTests() { print(BUGNUMBER + ": " + summary); 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); + assertThrows(function() new ArrayType(uint8).dimension(-1)); + var A = new ArrayType(uint8).dimension(10); + assertEq(A.__proto__.__proto__, ArrayType.prototype); assertEq(A.length, 10); assertEq(A.elementType, uint8); assertEq(A.byteLength, 10); - assertEq(A.toSource(), "ArrayType(uint8, 10)"); + assertEq(A.toSource(), "uint8.array(10)"); - assertEq(A.prototype.__proto__, ArrayType.prototype.prototype); - assertEq(typeof A.prototype.fill, "function"); - - var X = { __proto__: A }; - assertThrows(function() X.repeat(42)); + assertEq(A.prototype.__proto__.__proto__, ArrayType.prototype.prototype); 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++) @@ -74,18 +69,18 @@ function runTests() { 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 + var Vec3 = new ArrayType(float32).dimension(3); + var Sprite = new ArrayType(Vec3).dimension(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 @@ -96,77 +91,20 @@ function runTests() { assertEq(mario[0][1], 0); assertEq(mario[0][2], 0); assertThrows(function() mario[1] = 5); mario[1][1] = {}; assertEq(Number.isNaN(mario[1][1]), true); // ok this is just for kicks - var AllSprites = new ArrayType(Sprite, 65536); + var AllSprites = new ArrayType(Sprite).dimension(65536); var as = new AllSprites(); assertEq(as.length, 65536); - // test methods - var c = new A(); - c.fill(3); - for (var i = 0; i < c.length; i++) - assertEq(c[i], 3); - - assertThrows(function() Vec3.prototype.fill.call(c, 2)); - - var d = A.repeat(10); - for (var i = 0; i < d.length; i++) - assertEq(d[i], 10); - - assertThrows(function() ArrayType.prototype.repeat.call(d, 2)); - - var MA = new ArrayType(uint32, 5); - var ma = new MA([1, 2, 3, 4, 5]); - - var mb = ma.subarray(2); - assertEq(mb.length, 3); - assertEq(mb[0], 3); - assertEq(mb[1], 4); - assertEq(mb[2], 5); - - assertThrows(function() ma.subarray()); - assertThrows(function() ma.subarray(2.14)); - assertThrows(function() ma.subarray({})); - assertThrows(function() ma.subarray(2, [])); - - var range = ma.subarray(0, 3); - assertEq(range.length, 3); - assertEq(range[0], 1); - assertEq(range[1], 2); - assertEq(range[2], 3); - - assertEq(ma.subarray(ma.length).length, 0); - assertEq(ma.subarray(ma.length, ma.length-1).length, 0); - - var rangeNeg = ma.subarray(-2); - assertEq(rangeNeg.length, 2); - assertEq(rangeNeg[0], 4); - assertEq(rangeNeg[1], 5); - - var rangeNeg = ma.subarray(-5, -3); - assertEq(rangeNeg.length, 2); - assertEq(rangeNeg[0], 1); - assertEq(rangeNeg[1], 2); - - assertEq(ma.subarray(-2, -3).length, 0); - assertEq(ma.subarray(-6).length, ma.length); - - var modifyOriginal = ma.subarray(2); - modifyOriginal[0] = 42; - assertEq(ma[2], 42); - - ma[4] = 97; - assertEq(modifyOriginal[2], 97); - 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); @@ -178,17 +116,17 @@ function runTests() { for (var nm in as) { assertEq(+nm, counter++); } assertEq(counter, as.length); 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 = uint8.array(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);
--- a/js/src/tests/ecma_6/TypedObject/arrayzerolen.js +++ b/js/src/tests/ecma_6/TypedObject/arrayzerolen.js @@ -2,16 +2,16 @@ var BUGNUMBER = 926401; var summary = 'TypedObjects ArrayType implementation'; // Test creation of zero-length array function runTest() { var T = TypedObject; var Color = new T.StructType({'r': T.uint8, 'g': T.uint8, 'b': T.uint8}); - var Rainbow = new T.ArrayType(Color, 0); + var Rainbow = Color.array(0); var theOneISawWasJustBlack = new Rainbow([]); if (typeof reportCompare === "function") reportCompare(true, true); print("Tests complete"); } runTest();
--- a/js/src/tests/ecma_6/TypedObject/handle.js +++ b/js/src/tests/ecma_6/TypedObject/handle.js @@ -6,19 +6,19 @@ */ var BUGNUMBER = 898342; var summary = 'Handles'; var T = TypedObject; function runTests() { - var Point = new T.ArrayType(T.float32, 3); + var Point = T.float32.array(3); var Line = new T.StructType({from: Point, to: Point}); - var Lines = new T.ArrayType(Line, 3); + var Lines = Line.array(3); var lines = new Lines([ {from: [1, 2, 3], to: [4, 5, 6]}, {from: [7, 8, 9], to: [10, 11, 12]}, {from: [13, 14, 15], to: [16, 17, 18]} ]); var handle = Lines.handle(lines);
--- a/js/src/tests/ecma_6/TypedObject/handle_get_set.js +++ b/js/src/tests/ecma_6/TypedObject/handle_get_set.js @@ -5,21 +5,21 @@ * http://creativecommons.org/licenses/publicdomain/ */ var BUGNUMBER = 898342; var summary = 'Handle Move'; var T = TypedObject; -var Point = new T.ArrayType(T.float32, 3); +var Point = T.float32.array(3); var Line = new T.StructType({from: Point, to: Point}); -var Lines = new T.ArrayType(Line, 3); +var Lines = Line.array(3); -var Objects = new T.ArrayType(T.Object, 3); +var Objects = T.Object.array(3); function runTests() { function testHandleGetSetWithScalarType() { var lines = new Lines([ {from: [1, 2, 3], to: [4, 5, 6]}, {from: [7, 8, 9], to: [10, 11, 12]}, {from: [13, 14, 15], to: [16, 17, 18]} ]);
--- a/js/src/tests/ecma_6/TypedObject/handle_move.js +++ b/js/src/tests/ecma_6/TypedObject/handle_move.js @@ -5,19 +5,19 @@ * http://creativecommons.org/licenses/publicdomain/ */ var BUGNUMBER = 898342; var summary = 'Handle Move'; var T = TypedObject; -var Point = new T.ArrayType(T.float32, 3); +var Point = T.float32.array(3); var Line = new T.StructType({from: Point, to: Point}); -var Lines = new T.ArrayType(Line, 3); +var Lines = Line.array(3); function runTests() { function testHandleToPoint() { var lines = new Lines([ {from: [1, 2, 3], to: [4, 5, 6]}, {from: [7, 8, 9], to: [10, 11, 12]}, {from: [13, 14, 15], to: [16, 17, 18]} ]); @@ -85,17 +85,17 @@ function runTests() { // Spot check the results assertEq(lines[0].from[0], 101); assertEq(lines[1].to[1], 111); assertEq(lines[2].to[2], 118); } testHandleToFloat(); function testHandleToEquivalentType() { - var Point2 = new T.ArrayType(T.float32, 3); + var Point2 = T.float32.array(3); assertEq(Point.equivalent(Point2), true); var lines = new Lines([ {from: [1, 2, 3], to: [4, 5, 6]}, {from: [7, 8, 9], to: [10, 11, 12]}, {from: [13, 14, 15], to: [16, 17, 18]} ]);
--- a/js/src/tests/ecma_6/TypedObject/handle_unattached.js +++ b/js/src/tests/ecma_6/TypedObject/handle_unattached.js @@ -7,22 +7,26 @@ var BUGNUMBER = 898342; var summary = 'Unattached handles'; var T = TypedObject; function runTests() { var Line = new T.StructType({from: T.uint8, to: T.uint8}); - var Lines = new T.ArrayType(Line, 3); + var Lines = Line.array(3); // Create unattached handle to array, struct: var handle = Lines.handle(); var handle0 = Line.handle(); + // Accessing length throws: + assertThrowsInstanceOf(function() handle.length, TypeError, + "handle.length did not yield error"); + // Accessing properties throws: assertThrowsInstanceOf(function() handle[0], TypeError, "Unattached handle did not yield error"); assertThrowsInstanceOf(function() handle0.from, TypeError, "Unattached handle did not yield error"); // Handle.get() throws: assertThrowsInstanceOf(function() T.Handle.get(handle), TypeError,
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/TypedObject/handle_unsized.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty("TypedObject")) + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var BUGNUMBER = 922115; +var summary = 'check we cannot create handles to unsized arrays'; + +var T = TypedObject; + +function runTests() { + var Line = new T.StructType({from: T.uint8, to: T.uint8}); + var Lines = Line.array(); + assertThrowsInstanceOf(function() Lines.handle(), TypeError, + "was able to create handle to unsized array"); + + reportCompare(true, true); + print("Tests complete"); +} + +runTests(); + +
--- a/js/src/tests/ecma_6/TypedObject/memory.js +++ b/js/src/tests/ecma_6/TypedObject/memory.js @@ -18,49 +18,56 @@ var int16 = TypedObject.int16; var int32 = TypedObject.int32; var float32 = TypedObject.float32; var float64 = TypedObject.float64; function runTests() { print(BUGNUMBER + ": " + summary); - var AA = new ArrayType(new ArrayType(uint8, 5), 5); + var AA = uint8.array(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 = AA.array(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); var Color = new StructType({'r': uint8, 'g': uint8, 'b': uint8}); - var Rainbow = new ArrayType(Color, 7); + var Rainbow = Color.array(7); - var theOneISawWasJustBlack = Rainbow.repeat({'r': 0, 'g': 0, 'b': 0}); + var theOneISawWasJustBlack = new Rainbow([ + {'r': 0, 'g': 0, 'b': 0}, + {'r': 0, 'g': 0, 'b': 0}, + {'r': 0, 'g': 0, 'b': 0}, + {'r': 0, 'g': 0, 'b': 0}, + {'r': 0, 'g': 0, 'b': 0}, + {'r': 0, 'g': 0, 'b': 0}, + {'r': 0, 'g': 0, 'b': 0}]); var middleBand = theOneISawWasJustBlack[3]; theOneISawWasJustBlack = null; gc(); spin(); assertEq(middleBand['r'] == 0 && middleBand['g'] == 0 && middleBand['b'] == 0, true); middleBand.r = 255; middleBand.g = 207;
--- a/js/src/tests/ecma_6/TypedObject/objecttype.js +++ b/js/src/tests/ecma_6/TypedObject/objecttype.js @@ -5,19 +5,19 @@ var summary = 'objecttype'; /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ var T = TypedObject; function runTests() { - var Point = new T.ArrayType(T.float32, 3); + var Point = T.float32.array(3); var Line = new T.StructType({from: Point, to: Point}); - var Lines = new T.ArrayType(Line, 3); + var Lines = Line.array(3); var lines = new Lines([ {from: [1, 2, 3], to: [4, 5, 6]}, {from: [7, 8, 9], to: [10, 11, 12]}, {from: [13, 14, 15], to: [16, 17, 18]} ]); assertEq(T.objectType(lines), Lines);
--- a/js/src/tests/ecma_6/TypedObject/redimension.js +++ b/js/src/tests/ecma_6/TypedObject/redimension.js @@ -8,54 +8,50 @@ var summary = 'redimension method'; */ var T = TypedObject; function runTests() { var counter; // create an array of 40 bytes with some initial data - var Bytes40 = new T.ArrayType(T.uint8, 40); + var Bytes40 = T.uint8.array(40); var bytes40 = new Bytes40(); for (var i = 0, counter = 0; i < 40; i++) bytes40[i] = counter++; // redimension to an array of 10x4 bytes, check data is unchanged - var Bytes10times4 = new T.ArrayType(new T.ArrayType(T.uint8, 4), 10); + var Bytes10times4 = T.uint8.array(10, 4); var bytes10times4 = bytes40.redimension(Bytes10times4); counter = 0; for (var i = 0; i < 10; i++) for (var j = 0; j < 4; j++) assertEq(counter++, bytes10times4[i][j]); // redimension to an array of 2x5x2x2, check data is unchanged - var Bytes2times5times2times2 = - new T.ArrayType( - new T.ArrayType( - new T.ArrayType( - new T.ArrayType(T.uint8, 2), 2), 5), 2); + var Bytes2times5times2times2 = T.uint8.array(2, 5, 2, 2); var bytes2times5times2times2 = bytes10times4.redimension(Bytes2times5times2times2); counter = 0; for (var i = 0; i < 2; i++) for (var j = 0; j < 5; j++) for (var k = 0; k < 2; k++) for (var l = 0; l < 2; l++) assertEq(counter++, bytes2times5times2times2[i][j][k][l]); // test what happens if number of elements does not match assertThrowsInstanceOf(() => { - var Bytes10times5 = new T.ArrayType(new T.ArrayType(T.uint8, 5), 10); + var Bytes10times5 = T.uint8.array(10, 5); bytes40.redimension(Bytes10times5); - }, TypeError, "New number of elements does not match old number of elements"); + }, TypeError); // test what happens if inner type does not match, even if size is the same assertThrowsInstanceOf(() => { - var Words40 = new T.ArrayType(T.uint8Clamped, 40); + var Words40 = T.uint8Clamped.array(40); bytes40.redimension(Words40); - }, TypeError, "New element type is not equivalent to old element type"); + }, TypeError); reportCompare(true, true); print("Tests complete"); } runTests();
--- a/js/src/tests/ecma_6/TypedObject/referencetypecoercions.js +++ b/js/src/tests/ecma_6/TypedObject/referencetypecoercions.js @@ -22,17 +22,17 @@ function TestValues(type, values) { } for (var i = 0; i < values.length; i++) { var struct = new Struct(); struct.f = values[i].input; compare(struct.f, values[i]); } - var Array = new ArrayType(type, 1); + var Array = new ArrayType(type).dimension(1); for (var i = 0; i < values.length; i++) { var array = new Array(); array[0] = values[i].input; compare(array[0], values[i]); } function compare(v, spec) { if (spec.source)
--- a/js/src/tests/ecma_6/TypedObject/referencetypetrace.js +++ b/js/src/tests/ecma_6/TypedObject/referencetypetrace.js @@ -34,27 +34,36 @@ function TestStructFields(RefType) { var s1 = new S1({f: rabbit}); assertCanReach(s1, rabbit); s1.f = null; assertCannotReach(s1, rabbit); } function TestArrayElements(RefType) { var rabbit = {}; - var S1 = new ArrayType(RefType, 1); + var S1 = new ArrayType(RefType).dimension(1); var s1 = new S1([rabbit]); assertCanReach(s1, rabbit); s1[0] = null; assertCannotReach(s1, rabbit); } +function TestUnsizedArrayElements(RefType) { + var rabbit = {}; + var S1 = new ArrayType(RefType); + var s1 = new S1(1, [rabbit]); + assertCanReach(s1, rabbit); + s1[0] = null; + assertCannotReach(s1, rabbit); +} + function TestStructInArray(RefType) { var rabbit = {}; var S2 = new StructType({f: RefType, g: RefType}); - var S1 = new ArrayType(S2, 1); + var S1 = new ArrayType(S2).dimension(1); var s1 = new S1([{f: rabbit, g: {}}]); assertCanReach(s1, rabbit); s1[0].f = null; assertCannotReach(s1, rabbit); } function TestStringInStruct() { // Rather subtle hair-pullingly maddening testing phenomena: If you @@ -77,16 +86,19 @@ function runTests() printStatus(summary); TestStructFields(Object); TestStructFields(Any); TestArrayElements(Object); TestArrayElements(Any); + TestUnsizedArrayElements(Object); + TestUnsizedArrayElements(Any); + TestStructInArray(Object); TestStructInArray(Any); TestStringInStruct(); reportCompare(true, true, "TypedObjects trace tests"); }
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js +++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js @@ -10,17 +10,17 @@ var summary = 'float32x4 handles'; var ArrayType = TypedObject.ArrayType; var float32x4 = TypedObject.float32x4; var float32 = TypedObject.float32; var Handle = TypedObject.Handle; function test() { print(BUGNUMBER + ": " + summary); - var Array = new ArrayType(float32x4, 3); + var Array = float32x4.array(3); var array = new Array([float32x4(1, 2, 3, 4), float32x4(5, 6, 7, 8), float32x4(9, 10, 11, 12)]); // Test that trying to create handle into the interior of a // float32x4 fails. assertThrowsInstanceOf(function() {
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js +++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js @@ -8,17 +8,17 @@ var summary = 'float32x4 reify'; */ var ArrayType = TypedObject.ArrayType; var float32x4 = TypedObject.float32x4; function test() { print(BUGNUMBER + ": " + summary); - var Array = new ArrayType(float32x4, 3); + var Array = float32x4.array(3); var array = new Array([float32x4(1, 2, 3, 4), float32x4(5, 6, 7, 8), float32x4(9, 10, 11, 12)]); // Test that reading array[1] produces a *copy* of float32x4, not an // alias into the array. var f = array[1];
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js +++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js @@ -8,17 +8,17 @@ var summary = 'float32x4 setting'; */ var ArrayType = TypedObject.ArrayType; var float32x4 = TypedObject.float32x4; function test() { print(BUGNUMBER + ": " + summary); - var Array = new ArrayType(float32x4, 3); + var Array = float32x4.array(3); var array = new Array([float32x4(1, 2, 3, 4), float32x4(5, 6, 7, 8), float32x4(9, 10, 11, 12)]); assertEq(array[1].w, 8); // Test that we are allowed to write float32x4 values into array, // but not other things.
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js @@ -10,17 +10,17 @@ var summary = 'int32x4 handles'; var ArrayType = TypedObject.ArrayType; var int32x4 = TypedObject.int32x4; var int32 = TypedObject.int32; var Handle = TypedObject.Handle; function test() { print(BUGNUMBER + ": " + summary); - var Array = new ArrayType(int32x4, 3); + var Array = int32x4.array(3); var array = new Array([int32x4(1, 2, 3, 4), int32x4(5, 6, 7, 8), int32x4(9, 10, 11, 12)]); // Test that trying to create handle into the interior of a // int32x4 fails. assertThrowsInstanceOf(function() {
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js @@ -8,17 +8,17 @@ var summary = 'int32x4 reify'; */ var ArrayType = TypedObject.ArrayType; var int32x4 = TypedObject.int32x4; function test() { print(BUGNUMBER + ": " + summary); - var Array = new ArrayType(int32x4, 3); + var Array = int32x4.array(3); var array = new Array([int32x4(1, 2, 3, 4), int32x4(5, 6, 7, 8), int32x4(9, 10, 11, 12)]); // Test that reading array[1] produces a *copy* of int32x4, not an // alias into the array. var f = array[1];
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js @@ -8,17 +8,17 @@ var summary = 'int32x4 setting'; */ var ArrayType = TypedObject.ArrayType; var int32x4 = TypedObject.int32x4; function test() { print(BUGNUMBER + ": " + summary); - var Array = new ArrayType(int32x4, 3); + var Array = int32x4.array(3); var array = new Array([int32x4(1, 2, 3, 4), int32x4(5, 6, 7, 8), int32x4(9, 10, 11, 12)]); assertEq(array[1].w, 8); // Test that we are allowed to write int32x4 values into array, // but not other things.
--- a/js/src/tests/ecma_6/TypedObject/size_and_alignment.js +++ b/js/src/tests/ecma_6/TypedObject/size_and_alignment.js @@ -34,19 +34,19 @@ function runTests() { {type: float32, size: 4, alignment: 4}, {type: float64, size: 8, alignment: 8}, {type: new StructType({a: uint8, b: uint16, c: uint8}), size: 6, alignment: 2}, {type: new StructType({a: uint8, b: uint8, c: uint16}), size: 4, alignment: 2}, - {type: new ArrayType(uint8, 32), size: 32, alignment: 1}, - {type: new ArrayType(uint16, 16), size: 32, alignment: 2}, - {type: new ArrayType(uint32, 8), size: 32, alignment: 4}, + {type: new ArrayType(uint8).dimension(32), size: 32, alignment: 1}, + {type: new ArrayType(uint16).dimension(16), size: 32, alignment: 2}, + {type: new ArrayType(uint32).dimension(8), size: 32, alignment: 4}, ]; for (var i = 0; i < typesAndAlignments.length; i++) { var test = typesAndAlignments[i]; print("Type:", test.type.toSource(), "Size:", test.type.byteLength, "Alignment:", test.type.byteAlignment); assertEq(test.type.byteLength, test.size);
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/TypedObject/unsizedarrays.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty("TypedObject")) +var BUGNUMBER = 922115; +var summary = 'TypedObjects ArrayType implementation'; + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var { ArrayType, StructType, uint8, float32, uint32 } = TypedObject; +var ObjectType = TypedObject.Object; + +function runTests() { + print(BUGNUMBER + ": " + summary); + + (function SimpleArrayOfTwoObjects() { + var Objects = new ArrayType(ObjectType); + var objects2 = new Objects(2, [{f: "Hello"}, + {f: "World"}]); + assertEq(objects2[0].f, "Hello"); + assertEq(objects2[1].f, "World"); + assertEq(objects2.length, 2); + })(); + + (function EmbedUnsizedArraysBad() { + var Objects = new ArrayType(ObjectType); + assertThrows(() => new ArrayType(Objects)); + assertThrows(() => new StructType({f: Objects})); + })(); + + (function MultipleSizes() { + var Uints = new ArrayType(uint32); + var Point = new StructType({values: new ArrayType(uint32).dimension(3)}); + + var uints = new Uints(3, [0, 1, 2]); + var point = new Point({values: uints}); + + assertEq(uints.length, point.values.length); + for (var i = 0; i < uints.length; i++) { + assertEq(uints[i], i); + assertEq(uints[i], point.values[i]); + } + })(); + + if (typeof reportCompare === "function") + reportCompare(true, true); + print("Tests complete"); +} + +runTests(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/TypedObject/unsizedarraysanddim.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty("TypedObject")) +var BUGNUMBER = 922115; +var summary = 'TypedObjects ArrayType implementation'; + +var ArrayType = TypedObject.ArrayType; +var StructType = TypedObject.StructType; +var uint8 = TypedObject.uint8; +var float32 = TypedObject.float32; +var uint32 = TypedObject.uint32; +var ObjectType = TypedObject.Object; + +function runTests() { + (function DimensionLinkedToUndimension() { + var UintsA = uint32.array(); + var FiveUintsA = UintsA.dimension(5); + var FiveUintsB = uint32.array(5); + + assertEq(true, FiveUintsA.equivalent(FiveUintsB)); + assertEq(true, FiveUintsA.unsized === UintsA); + assertEq(true, FiveUintsB.unsized !== UintsA); + })(); + + (function PrototypeHierarchy() { + var Uint8s = uint8.array(); + assertEq(Uint8s.prototype.__proto__, ArrayType.prototype.prototype); + Uint8s.prototype.sum = function() { + var r = 0; + for (var i = 0; i < this.length; i++) + r = uint8(r + this[i]); + return r; + }; + + var FiveUint8s = Uint8s.dimension(5); + assertEq(FiveUint8s.__proto__, Uint8s); + + var fiveUint8s = new FiveUint8s([128, 128, 128, 128, 128]); + assertEq(128, fiveUint8s.sum()); + })(); + + if (typeof reportCompare === "function") + reportCompare(true, true); + + print("Tests complete"); +} + +runTests(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/TypedObject/unsizedarraysembedded.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty("TypedObject")) +var BUGNUMBER = 922115; +var summary = 'cannot embed unsized array in a struct'; + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var T = TypedObject; + +function runTests() { + print(BUGNUMBER + ": " + summary); + + var Uints = T.uint32.array(); + assertThrowsInstanceOf(() => { new T.StructType({f: Uints}) }, TypeError); + assertThrowsInstanceOf(() => { Uints.array() }, TypeError); + + reportCompare(true, true); + print("Tests complete"); +} + +runTests();
--- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -162,16 +162,17 @@ macro(toUTCString, toUTCString, "toUTCString") \ macro(true, true_, "true") \ macro(unescape, unescape, "unescape") \ macro(uneval, uneval, "uneval") \ macro(uint8, uint8, "uint8") \ macro(uint8Clamped, uint8Clamped, "uint8Clamped") \ macro(uint16, uint16, "uint16") \ macro(uint32, uint32, "uint32") \ + macro(unsized, unsized, "unsized") \ macro(unwatch, unwatch, "unwatch") \ macro(url, url, "url") \ macro(usage, usage, "usage") \ macro(useGrouping, useGrouping, "useGrouping") \ macro(useAsm, useAsm, "use asm") \ macro(useStrict, useStrict, "use strict") \ macro(value, value, "value") \ macro(valueOf, valueOf, "valueOf") \