Bug 898359 - Implement reference types in typed objects r=sfink

This commit is contained in:
Nicholas D. Matsakis 2013-09-22 20:18:31 -04:00
Родитель bafb6b3389
Коммит c000e8b96a
18 изменённых файлов: 1306 добавлений и 392 удалений

Просмотреть файл

@ -687,16 +687,9 @@ CountHeap(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
jsval v;
int32_t traceKind;
JSString *str;
JSCountHeapTracer countTracer;
JSCountHeapNode *node;
size_t counter;
RootedValue startValue(cx, UndefinedValue());
if (args.length() > 0) {
v = args[0];
jsval v = args[0];
if (JSVAL_IS_TRACEABLE(v)) {
startValue = v;
} else if (!JSVAL_IS_NULL(v)) {
@ -707,28 +700,45 @@ CountHeap(JSContext *cx, unsigned argc, jsval *vp)
}
}
traceKind = -1;
RootedValue traceValue(cx);
int32_t traceKind = -1;
void *traceThing = NULL;
if (args.length() > 1) {
str = ToString(cx, args[0]);
JSString *str = ToString(cx, args[1]);
if (!str)
return false;
JSFlatString *flatStr = JS_FlattenString(cx, str);
if (!flatStr)
return false;
for (size_t i = 0; ;) {
if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
traceKind = traceKindNames[i].kind;
break;
}
if (++i == ArrayLength(traceKindNames)) {
JSAutoByteString bytes(cx, str);
if (!!bytes)
JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
if (JS_FlatStringEqualsAscii(flatStr, "specific")) {
if (args.length() < 3) {
JS_ReportError(cx, "tracing of specific value requested "
"but no value provided");
return false;
}
traceValue = args[2];
if (!JSVAL_IS_TRACEABLE(traceValue)){
JS_ReportError(cx, "cannot trace this kind of value");
return false;
}
traceThing = JSVAL_TO_TRACEABLE(traceValue);
} else {
for (size_t i = 0; ;) {
if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
traceKind = traceKindNames[i].kind;
break;
}
if (++i == ArrayLength(traceKindNames)) {
JSAutoByteString bytes(cx, str);
if (!!bytes)
JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
return false;
}
}
}
}
JSCountHeapTracer countTracer;
JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify);
if (!countTracer.visited.init()) {
JS_ReportOutOfMemory(cx);
@ -744,10 +754,18 @@ CountHeap(JSContext *cx, unsigned argc, jsval *vp)
JS_CallValueTracer(&countTracer.base, startValue.address(), "root");
}
counter = 0;
JSCountHeapNode *node;
size_t counter = 0;
while ((node = countTracer.traceList) != nullptr) {
if (traceKind == -1 || node->kind == traceKind)
counter++;
if (traceThing == nullptr) {
// We are looking for all nodes with a specific kind
if (traceKind == -1 || node->kind == traceKind)
counter++;
} else {
// We are looking for some specific thing
if (node->thing == traceThing)
counter++;
}
countTracer.traceList = node->next;
node->next = countTracer.recycleList;
countTracer.recycleList = node;
@ -1359,11 +1377,13 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
" was built with."),
JS_FN_HELP("countHeap", CountHeap, 0, 0,
"countHeap([start[, kind]])",
"countHeap([start[, kind[, thing]]])",
" Count the number of live GC things in the heap or things reachable from\n"
" start when it is given and is not null. kind is either 'all' (default) to\n"
" count all things or one of 'object', 'double', 'string', 'function'\n"
" to count only things of that kind."),
" to count only things of that kind. If kind is the string 'specific',\n"
" then you can provide an extra argument with some specific traceable\n"
" thing to count.\n"),
#ifdef DEBUG
JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,

Просмотреть файл

@ -59,6 +59,9 @@ TypeRepresentationHasher::match(TypeRepresentation *key1,
case TypeRepresentation::Scalar:
return matchScalars(key1->asScalar(), key2->asScalar());
case TypeRepresentation::Reference:
return matchReferences(key1->asReference(), key2->asReference());
case TypeRepresentation::Struct:
return matchStructs(key1->asStruct(), key2->asStruct());
@ -76,6 +79,13 @@ TypeRepresentationHasher::matchScalars(ScalarTypeRepresentation *key1,
return key1->type() == key2->type();
}
bool
TypeRepresentationHasher::matchReferences(ReferenceTypeRepresentation *key1,
ReferenceTypeRepresentation *key2)
{
return key1->type() == key2->type();
}
bool
TypeRepresentationHasher::matchStructs(StructTypeRepresentation *key1,
StructTypeRepresentation *key2)
@ -109,6 +119,9 @@ TypeRepresentationHasher::hash(TypeRepresentation *key) {
case TypeRepresentation::Scalar:
return hashScalar(key->asScalar());
case TypeRepresentation::Reference:
return hashReference(key->asReference());
case TypeRepresentation::Struct:
return hashStruct(key->asStruct());
@ -125,6 +138,12 @@ TypeRepresentationHasher::hashScalar(ScalarTypeRepresentation *key)
return HashGeneric(key->kind(), key->type());
}
HashNumber
TypeRepresentationHasher::hashReference(ReferenceTypeRepresentation *key)
{
return HashGeneric(key->kind(), key->type());
}
HashNumber
TypeRepresentationHasher::hashStruct(StructTypeRepresentation *key)
{
@ -145,14 +164,16 @@ TypeRepresentationHasher::hashArray(ArrayTypeRepresentation *key)
///////////////////////////////////////////////////////////////////////////
// Constructors
TypeRepresentation::TypeRepresentation(Kind kind, size_t size, size_t align)
TypeRepresentation::TypeRepresentation(Kind kind, size_t size,
size_t align, bool opaque)
: size_(size),
alignment_(align),
kind_(kind)
kind_(kind),
opaque_(opaque)
{}
ScalarTypeRepresentation::ScalarTypeRepresentation(Type type)
: TypeRepresentation(Scalar, 0, 1),
: TypeRepresentation(Scalar, 0, 1, false),
type_(type)
{
switch (type) {
@ -179,9 +200,28 @@ ScalarTypeRepresentation::ScalarTypeRepresentation(Type type)
}
}
ReferenceTypeRepresentation::ReferenceTypeRepresentation(Type type)
: TypeRepresentation(Reference, 0, 1, true),
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()),
: TypeRepresentation(Array, element->size() * length,
element->alignment(), element->opaque()),
element_(element),
length_(length)
{
@ -203,9 +243,10 @@ StructField::StructField(size_t index,
{}
StructTypeRepresentation::StructTypeRepresentation()
: TypeRepresentation(Struct, 0, 1),
: TypeRepresentation(Struct, 0, 1, false),
fieldCount_(0) // see ::init() below!
{
// note: size_, alignment_, and opaque_ are computed in ::init() below
}
bool
@ -222,9 +263,16 @@ StructTypeRepresentation::init(JSContext *cx,
// 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]);
if (fieldTypeRepr->opaque())
opaque_ = true;
uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment());
if (alignedSize < totalSize) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
@ -305,6 +353,11 @@ TypeRepresentation::addToTableOrFree(JSContext *cx,
Int32Value(asScalar()->type()));
break;
case Reference:
ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_TYPE,
Int32Value(asReference()->type()));
break;
case Struct:
break;
}
@ -336,6 +389,29 @@ ScalarTypeRepresentation::Create(JSContext *cx,
return ptr->addToTableOrFree(cx, p);
}
/*static*/
JSObject *
ReferenceTypeRepresentation::Create(JSContext *cx,
ReferenceTypeRepresentation::Type type)
{
JSCompartment *comp = cx->compartment();
ReferenceTypeRepresentation sample(type);
TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
if (p)
return (*p)->ownerObject();
// Note: cannot use cx->new_ because constructor is private.
ReferenceTypeRepresentation *ptr =
(ReferenceTypeRepresentation *) cx->malloc_(
sizeof(ReferenceTypeRepresentation));
if (!ptr)
return nullptr;
new(ptr) ReferenceTypeRepresentation(type);
return ptr->addToTableOrFree(cx, p);
}
/*static*/
JSObject *
ArrayTypeRepresentation::Create(JSContext *cx,
@ -422,6 +498,7 @@ TypeRepresentation::traceFields(JSTracer *trace)
switch (kind()) {
case Scalar:
case Reference:
break;
case Struct:
@ -472,6 +549,9 @@ TypeRepresentation::appendString(JSContext *cx, StringBuffer &contents)
case Scalar:
return asScalar()->appendStringScalar(cx, contents);
case Reference:
return asReference()->appendStringReference(cx, contents);
case Array:
return asArray()->appendStringArray(cx, contents);
@ -505,6 +585,28 @@ ScalarTypeRepresentation::appendStringScalar(JSContext *cx, StringBuffer &conten
MOZ_ASSUME_UNREACHABLE("Invalid type");
}
/*static*/ const char *
ReferenceTypeRepresentation::typeName(Type type)
{
switch (type) {
#define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
case constant_: return #name_;
JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
}
MOZ_ASSUME_UNREACHABLE("Invalid type");
}
bool
ReferenceTypeRepresentation::appendStringReference(JSContext *cx, StringBuffer &contents)
{
switch (type()) {
#define NUMERIC_TYPE_APPEND_STRING(constant_, type_, name_) \
case constant_: return contents.append(#name_);
JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_APPEND_STRING)
}
MOZ_ASSUME_UNREACHABLE("Invalid type");
}
bool
ArrayTypeRepresentation::appendStringArray(JSContext *cx, StringBuffer &contents)
{
@ -558,6 +660,162 @@ StructTypeRepresentation::appendStringStruct(JSContext *cx, StringBuffer &conten
return true;
}
///////////////////////////////////////////////////////////////////////////
// Walking memory
template<typename V>
static void
visitReferences(TypeRepresentation *repr, uint8_t *mem, V& visitor)
{
if (repr->transparent())
return;
switch (repr->kind()) {
case TypeRepresentation::Scalar:
return;
case TypeRepresentation::Reference:
visitor.visitReference(repr->asReference(), mem);
return;
case TypeRepresentation::Array:
{
ArrayTypeRepresentation *arrayRepr = repr->asArray();
TypeRepresentation *elementRepr = arrayRepr->element();
for (size_t i = 0; i < arrayRepr->length(); i++) {
visitReferences(elementRepr, mem, visitor);
mem += elementRepr->size();
}
return;
}
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;
}
}
MOZ_ASSUME_UNREACHABLE("Invalid type repr kind");
}
///////////////////////////////////////////////////////////////////////////
// Initializing instances
namespace js {
class MemoryInitVisitor {
const JSRuntime *rt_;
public:
MemoryInitVisitor(const JSRuntime *rt)
: rt_(rt)
{}
void visitReference(ReferenceTypeRepresentation *repr, uint8_t *mem);
};
} // namespace js
void
js::MemoryInitVisitor::visitReference(ReferenceTypeRepresentation *repr, uint8_t *mem)
{
switch (repr->type()) {
case ReferenceTypeRepresentation::TYPE_ANY:
{
js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
heapValue->init(UndefinedValue());
return;
}
case ReferenceTypeRepresentation::TYPE_OBJECT:
{
js::HeapPtrObject *objectPtr =
reinterpret_cast<js::HeapPtrObject *>(mem);
objectPtr->init(nullptr);
return;
}
case ReferenceTypeRepresentation::TYPE_STRING:
{
js::HeapPtrString *stringPtr =
reinterpret_cast<js::HeapPtrString *>(mem);
stringPtr->init(rt_->emptyString);
return;
}
}
MOZ_ASSUME_UNREACHABLE("Invalid kind");
}
void
TypeRepresentation::initInstance(const JSRuntime *rt, uint8_t *mem)
{
MemoryInitVisitor visitor(rt);
memset(mem, 0, size());
if (opaque())
visitReferences(this, mem, visitor);
}
///////////////////////////////////////////////////////////////////////////
// Tracing instances
namespace js {
class MemoryTracingVisitor {
JSTracer *trace_;
public:
MemoryTracingVisitor(JSTracer *trace)
: trace_(trace)
{}
void visitReference(ReferenceTypeRepresentation *repr, uint8_t *mem);
};
} // namespace js
void
js::MemoryTracingVisitor::visitReference(ReferenceTypeRepresentation *repr, uint8_t *mem)
{
switch (repr->type()) {
case ReferenceTypeRepresentation::TYPE_ANY:
{
js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
gc::MarkValue(trace_, heapValue, "reference-val");
return;
}
case ReferenceTypeRepresentation::TYPE_OBJECT:
{
js::HeapPtrObject *objectPtr =
reinterpret_cast<js::HeapPtrObject *>(mem);
if (*objectPtr)
gc::MarkObject(trace_, objectPtr, "reference-obj");
return;
}
case ReferenceTypeRepresentation::TYPE_STRING:
{
js::HeapPtrString *stringPtr =
reinterpret_cast<js::HeapPtrString *>(mem);
if (*stringPtr)
gc::MarkString(trace_, stringPtr, "reference-str");
return;
}
}
MOZ_ASSUME_UNREACHABLE("Invalid kind");
}
void
TypeRepresentation::traceInstance(JSTracer *trace, uint8_t *mem)
{
MemoryTracingVisitor visitor(trace);
visitReferences(this, mem, visitor);
}
///////////////////////////////////////////////////////////////////////////
// Misc

Просмотреть файл

@ -39,6 +39,14 @@
object, and the finalizer of the owner object removes the pointer
from the table and then frees the pointer.
# Opacity
A type representation is considered "opaque" if it contains
references (strings, objects, any). In those cases we have to be
more limited with respect to aliasing etc to preserve portability
across engines (for example, we don't want to expose sizeof(Any))
and memory safety.
# Future plans:
The owner object will eventually be exposed to self-hosted script
@ -59,6 +67,7 @@ namespace js {
class TypeRepresentation;
class ScalarTypeRepresentation;
class ReferenceTypeRepresentation;
class ArrayTypeRepresentation;
class StructTypeRepresentation;
@ -73,11 +82,14 @@ struct TypeRepresentationHasher
private:
static HashNumber hashScalar(ScalarTypeRepresentation *key);
static HashNumber hashReference(ReferenceTypeRepresentation *key);
static HashNumber hashStruct(StructTypeRepresentation *key);
static HashNumber hashArray(ArrayTypeRepresentation *key);
static bool matchScalars(ScalarTypeRepresentation *key1,
ScalarTypeRepresentation *key2);
static bool matchReferences(ReferenceTypeRepresentation *key1,
ReferenceTypeRepresentation *key2);
static bool matchStructs(StructTypeRepresentation *key1,
StructTypeRepresentation *key2);
static bool matchArrays(ArrayTypeRepresentation *key1,
@ -92,16 +104,18 @@ class TypeRepresentation {
public:
enum Kind {
Scalar = JS_TYPEREPR_SCALAR_KIND,
Reference = JS_TYPEREPR_REFERENCE_KIND,
Struct = JS_TYPEREPR_STRUCT_KIND,
Array = JS_TYPEREPR_ARRAY_KIND
};
protected:
TypeRepresentation(Kind kind, size_t size, size_t align);
TypeRepresentation(Kind kind, size_t size, size_t align, bool opaque);
size_t size_;
size_t alignment_;
Kind kind_;
bool opaque_;
JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p);
@ -117,12 +131,21 @@ class TypeRepresentation {
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);
@ -135,6 +158,15 @@ class TypeRepresentation {
return (ScalarTypeRepresentation*) this;
}
bool isReference() const {
return kind() == Reference;
}
ReferenceTypeRepresentation *asReference() {
JS_ASSERT(isReference());
return (ReferenceTypeRepresentation*) this;
}
bool isArray() const {
return kind() == Array;
}
@ -193,7 +225,9 @@ class ScalarTypeRepresentation : public TypeRepresentation {
return type_;
}
bool convertValue(JSContext *cx, HandleValue value, MutableHandleValue vp);
const char *typeName() const {
return typeName(type());
}
static const char *typeName(Type type);
static JSObject *Create(JSContext *cx, Type type);
@ -217,6 +251,45 @@ class ScalarTypeRepresentation : public TypeRepresentation {
JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \
macro_(ScalarTypeRepresentation::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped)
class ReferenceTypeRepresentation : public TypeRepresentation {
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;
private:
// so TypeRepresentation can call appendStringScalar() etc
friend class TypeRepresentation;
Type type_;
explicit ReferenceTypeRepresentation(Type type);
// See TypeRepresentation::appendString()
bool appendStringReference(JSContext *cx, StringBuffer &buffer);
public:
Type type() const {
return type_;
}
const char *typeName() const {
return typeName(type());
}
static const char *typeName(Type type);
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 ArrayTypeRepresentation : public TypeRepresentation {
private:
// so TypeRepresentation can call appendStringArray() etc

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -103,22 +103,32 @@ namespace js {
*/
extern const Class TypedObjectClass;
template <ScalarTypeRepresentation::Type type, typename T>
class NumericType
// Type for scalar type constructors like `uint8`. All such type
// constructors share a common js::Class and JSFunctionSpec. Scalar
// types are non-opaque (their storage is visible unless combined with
// an opaque reference type.)
class ScalarType
{
private:
static const Class * typeToClass();
public:
static bool reify(JSContext *cx, void *mem, MutableHandleValue vp);
static const Class class_;
static const JSFunctionSpec typeObjectMethods[];
typedef ScalarTypeRepresentation TypeRepr;
static bool call(JSContext *cx, unsigned argc, Value *vp);
};
/*
* These are the classes of the scalar type descriptors, like `uint8`,
* `uint16` etc. Each of these classes has exactly one instance that
* is pre-created.
*/
extern const Class NumericTypeClasses[ScalarTypeRepresentation::TYPE_MAX];
// Type for reference type constructors like `Any`, `String`, and
// `Object`. All such type constructors share a common js::Class and
// JSFunctionSpec. All these types are opaque.
class ReferenceType
{
public:
static const Class class_;
static const JSFunctionSpec typeObjectMethods[];
typedef ReferenceTypeRepresentation TypeRepr;
static bool call(JSContext *cx, unsigned argc, Value *vp);
};
/*
* Type descriptor created by `new ArrayType(...)`
@ -148,8 +158,6 @@ class ArrayType : public JSObject
static bool repeat(JSContext *cx, unsigned argc, Value *vp);
static bool subarray(JSContext *cx, unsigned argc, Value *vp);
static bool toSource(JSContext *cx, unsigned argc, Value *vp);
static JSObject *elementType(JSContext *cx, HandleObject obj);
};
@ -186,8 +194,6 @@ class StructType : public JSObject
// does `new StructType(...)`. It produces a struct type object.
static bool construct(JSContext *cx, unsigned argc, Value *vp);
static bool toSource(JSContext *cx, unsigned argc, Value *vp);
static bool convertAndCopyTo(JSContext *cx,
StructTypeRepresentation *typeRepr,
HandleValue from, uint8_t *mem);
@ -278,8 +284,12 @@ class TypedDatum : public JSObject
MutableHandleValue statep, MutableHandleId idp);
public:
// Returns the offset in bytes within the object where the `void*`
// pointer can be found.
// Each typed object contains a void* pointer pointing at the
// binary data that it represents. (That data may be owned by this
// 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,
@ -305,7 +315,7 @@ class TypedDatum : public JSObject
size_t offset);
// If `this` is the owner of the memory, use this.
void attach(void *mem);
void attach(uint8_t *mem);
// Otherwise, use this to attach to memory referenced by another datum.
void attach(JSObject &datum, uint32_t offset);
@ -417,12 +427,20 @@ bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo MemcpyJitInfo;
/*
* Usage: StoreScalar(targetDatum, targetOffset, value)
* Usage: Store_int8(targetDatum, targetOffset, value)
* ...
* Store_uint8(targetDatum, targetOffset, value)
* ...
* Store_float32(targetDatum, targetOffset, value)
* Store_float64(targetDatum, targetOffset, value)
*
* Intrinsic function. Stores value (which must be an int32 or uint32)
* by `scalarTypeRepr` (which must be a type repr obj) and stores the
* value at the memory for `targetDatum` at offset `targetOffset`.
* `targetDatum` must be attached.
* Intrinsic function. Stores `value` into the memory referenced by
* `targetDatum` at the offset `targetOffset`.
*
* Assumes (and asserts) that:
* - `targetDatum` is attached
* - `targetOffset` is a valid offset within the bounds of `targetDatum`
* - `value` is a number
*/
#define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \
class StoreScalar##T { \
@ -431,6 +449,29 @@ class StoreScalar##T { \
static const JSJitInfo JitInfo; \
};
/*
* Usage: Store_Any(targetDatum, targetOffset, value)
* Store_Object(targetDatum, targetOffset, value)
* Store_string(targetDatum, targetOffset, value)
*
* Intrinsic function. Stores `value` into the memory referenced by
* `targetDatum` at the offset `targetOffset`.
*
* Assumes (and asserts) that:
* - `targetDatum` is attached
* - `targetOffset` is a valid offset within the bounds of `targetDatum`
* - `value` is an object (`Store_Object`) or string (`Store_string`).
*/
#define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \
class StoreReference##T { \
private: \
static void store(T* heap, const Value &v); \
\
public: \
static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \
static const JSJitInfo JitInfo; \
};
/*
* Usage: LoadScalar(targetDatum, targetOffset, value)
*
@ -446,10 +487,30 @@ class LoadScalar##T { \
static const JSJitInfo JitInfo; \
};
/*
* Usage: LoadReference(targetDatum, targetOffset, value)
*
* Intrinsic function. Stores value (which must be an int32 or uint32)
* by `scalarTypeRepr` (which must be a type repr obj) and stores the
* value at the memory for `targetDatum` at offset `targetOffset`.
* `targetDatum` must be attached.
*/
#define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name) \
class LoadReference##T { \
private: \
static void load(T* heap, MutableHandleValue v); \
\
public: \
static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \
static const JSJitInfo JitInfo; \
};
// I was using templates for this stuff instead of macros, but ran
// into problems with the Unagi compiler.
JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN)
JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN)
JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN)
JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN)
} // namespace js

Просмотреть файл

@ -106,6 +106,7 @@ TypedObjectPointer.prototype.kind = function() {
TypedObjectPointer.prototype.moveTo = function(propName) {
switch (this.kind()) {
case JS_TYPEREPR_SCALAR_KIND:
case JS_TYPEREPR_REFERENCE_KIND:
break;
case JS_TYPEREPR_ARRAY_KIND:
@ -179,7 +180,6 @@ TypedObjectPointer.prototype.moveToField = function(propName) {
// by `this` and produce JS values. This process is called *reification*
// in the spec.
// Reifies the value referenced by the pointer, meaning that it
// returns a new object pointing at the value. If the value is
// a scalar, it will return a JS number, but otherwise the reified
@ -191,6 +191,9 @@ TypedObjectPointer.prototype.get = function() {
if (REPR_KIND(this.typeRepr) == JS_TYPEREPR_SCALAR_KIND)
return this.getScalar();
if (REPR_KIND(this.typeRepr) == JS_TYPEREPR_REFERENCE_KIND)
return this.getReference();
return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
}
@ -226,6 +229,22 @@ TypedObjectPointer.prototype.getScalar = function() {
assert(false, "Unhandled scalar type: " + type);
}
TypedObjectPointer.prototype.getReference = function() {
var type = REPR_TYPE(this.typeRepr);
switch (type) {
case JS_REFERENCETYPEREPR_ANY:
return Load_Any(this.datum, this.offset);
case JS_REFERENCETYPEREPR_OBJECT:
return Load_Object(this.datum, this.offset);
case JS_REFERENCETYPEREPR_STRING:
return Load_string(this.datum, this.offset);
}
assert(false, "Unhandled scalar type: " + type);
}
///////////////////////////////////////////////////////////////////////////
// Setting values
//
@ -258,6 +277,10 @@ TypedObjectPointer.prototype.set = function(fromValue) {
this.setScalar(fromValue);
return;
case JS_TYPEREPR_REFERENCE_KIND:
this.setReference(fromValue);
return;
case JS_TYPEREPR_ARRAY_KIND:
if (!IsObject(fromValue))
break;
@ -342,6 +365,23 @@ TypedObjectPointer.prototype.setScalar = function(fromValue) {
assert(false, "Unhandled scalar type: " + type);
}
TypedObjectPointer.prototype.setReference = function(fromValue) {
var type = REPR_TYPE(this.typeRepr);
switch (type) {
case JS_REFERENCETYPEREPR_ANY:
return Store_Any(this.datum, this.offset, fromValue);
case JS_REFERENCETYPEREPR_OBJECT:
var value = (fromValue === null ? fromValue : ToObject(fromValue));
return Store_Object(this.datum, this.offset, value);
case JS_REFERENCETYPEREPR_STRING:
return Store_string(this.datum, this.offset, ToString(fromValue));
}
assert(false, "Unhandled scalar type: " + type);
}
///////////////////////////////////////////////////////////////////////////
// C++ Wrappers
//

Просмотреть файл

@ -13,7 +13,7 @@
// Slots for type objects
//
// Some slots apply to all type objects and some are specific to
// particular kinds of type objects. Because all type objects, at
// particular kinds of type objects. Because all type objects, at
// least for now, have a distinct class, we can assign them distinct
// numbers of slots depending on their kind.
@ -23,6 +23,9 @@
// Slots on scalars
#define JS_TYPEOBJ_SCALAR_SLOTS 1 // Maximum number
// Slots on references
#define JS_TYPEOBJ_REFERENCE_SLOTS 1 // Maximum number
// Slots on arrays
#define JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE 1
#define JS_TYPEOBJ_ARRAY_SLOTS 2 // Maximum number
@ -36,7 +39,7 @@
// 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
// 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.
@ -49,21 +52,22 @@
// Slots on arrays:
#define JS_TYPEREPR_SLOT_LENGTH 3 // Length of the array
// Slots on scalars:
// Slots on scalars and references:
#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, since that allows you to
// 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_STRUCT_KIND 1
#define JS_TYPEREPR_ARRAY_KIND 2
#define JS_TYPEREPR_REFERENCE_KIND 1
#define JS_TYPEREPR_STRUCT_KIND 2
#define JS_TYPEREPR_ARRAY_KIND 3
// These constants are for use exclusively in JS code. In C++ code,
// prefer ScalarTypeRepresentation::TYPE_INT8 etc, since that allows
// 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
@ -76,6 +80,14 @@
#define JS_SCALARTYPEREPR_FLOAT64 7
#define JS_SCALARTYPEREPR_UINT8_CLAMPED 8
// These constants are for use exclusively in JS code. In C++ code,
// prefer ReferenceTypeRepresentation::TYPE_ANY etc, which allows
// you to write a switch which will receive a warning if you omit a
// case.
#define JS_REFERENCETYPEREPR_ANY 0
#define JS_REFERENCETYPEREPR_OBJECT 1
#define JS_REFERENCETYPEREPR_STRING 2
///////////////////////////////////////////////////////////////////////////
// Slots for typed objects (actually, any TypedContents objects)

Просмотреть файл

@ -6521,6 +6521,9 @@ IonBuilder::getElemTryTypedObject(bool *emitted, MDefinition *obj, MDefinition *
objTypeReprs,
elemTypeReprs,
elemSize);
case TypeRepresentation::Reference:
return true;
}
MOZ_ASSUME_UNREACHABLE("Bad kind");
@ -8174,6 +8177,9 @@ IonBuilder::getPropTryTypedObject(bool *emitted, PropertyName *name,
return true;
switch (fieldTypeReprs.kind()) {
case TypeRepresentation::Reference:
return true;
case TypeRepresentation::Struct:
case TypeRepresentation::Array:
return getPropTryComplexPropOfTypedObject(emitted,
@ -8712,6 +8718,7 @@ IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj,
return true;
switch (fieldTypeReprs.kind()) {
case TypeRepresentation::Reference:
case TypeRepresentation::Struct:
case TypeRepresentation::Array:
// For now, only optimize storing scalars.

Просмотреть файл

@ -14,6 +14,8 @@ var Point = new T.ArrayType(T.float32, 3);
var Line = new T.StructType({from: Point, to: Point});
var Lines = new T.ArrayType(Line, 3);
var Objects = new T.ArrayType(T.Object, 3);
function runTests() {
function testHandleGetSetWithScalarType() {
var lines = new Lines([
@ -30,6 +32,23 @@ function runTests() {
}
testHandleGetSetWithScalarType();
function testHandleGetSetWithObjectType() {
var one = {x: 1};
var two = {x: 2};
var three = {x: 3};
var objects = new Objects([one, two, three]);
var handle = T.Object.handle(objects, 0);
assertEq(T.Handle.get(handle), one);
T.Handle.set(handle, three);
assertEq(T.Handle.get(handle), three);
assertEq(objects[0], three);
T.Handle.move(handle, objects, 1);
assertEq(T.Handle.get(handle), two);
}
testHandleGetSetWithScalarType();
function testHandleGetSetWithComplexType() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},

Просмотреть файл

@ -89,7 +89,7 @@ function runTests()
check(function() type(+Infinity) === 0);
check(function() type(-Infinity) === 0);
check(function() type(NaN) === 0);
check(function() type.toString() === strings[i]);
check(function() type.toSource() === strings[i]);
check(function() type(null) == 0);
check(function() type(undefined) == 0);
check(function() type([]) == 0);
@ -110,7 +110,7 @@ function runTests()
check(function() type(+Infinity) === Infinity);
check(function() type(-Infinity) === -Infinity);
check(function() Number.isNaN(type(NaN)));
check(function() type.toString() === floatStrings[i]);
check(function() type.toSource() === floatStrings[i]);
check(function() type(null) == 0);
check(function() Number.isNaN(type(undefined)));
check(function() type([]) == 0);

Просмотреть файл

@ -0,0 +1,33 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
var BUGNUMBER = 898359;
var summary = 'TypedObjects reference type aliasing';
var actual = '';
var expect = '';
var ArrayType = TypedObject.ArrayType;
var StructType = TypedObject.StructType;
var Any = TypedObject.Any;
var Object = TypedObject.Object;
var string = TypedObject.string;
function runTests()
{
printBugNumber(BUGNUMBER);
printStatus(summary);
var MyType = new StructType({f: Object});
// Test aliasing
var myInstance = new MyType({f: {a: 22}});
var anotherInstance = new MyType({f: myInstance.f});
assertEq(myInstance.f.a, 22);
assertEq(myInstance.f.a, anotherInstance.f.a);
myInstance.f.a += 1;
assertEq(myInstance.f.a, 23);
assertEq(myInstance.f.a, anotherInstance.f.a);
reportCompare(true, true, "TypedObjects reference type aliasing tests");
}
runTests();

Просмотреть файл

@ -0,0 +1,70 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
var BUGNUMBER = 898359;
var summary = 'TypedObjects reference type coercions';
var actual = '';
var expect = '';
var ArrayType = TypedObject.ArrayType;
var StructType = TypedObject.StructType;
var Any = TypedObject.Any;
var Object = TypedObject.Object;
var string = TypedObject.string;
function TestValues(type, values) {
for (var i = 0; i < values.length; i++) {
compare(type(values[i].input), values[i]);
}
var Struct = new StructType({f: type});
for (var i = 0; i < values.length; i++) {
var struct = new Struct({f: values[i].input});
compare(struct.f, values[i]);
}
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);
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)
v = v.toSource();
assertEq(v, spec.output);
}
}
function runTests()
{
printBugNumber(BUGNUMBER);
printStatus(summary);
var x = {};
TestValues(Any, [{input: undefined, output: undefined},
{input: x, output: x},
{input: 22.22, output: 22.22},
{input: true, output: true}]);
TestValues(string, [{input: undefined, output: "undefined"},
{input: x, output: x.toString()},
{input: 22.22, output: "22.22"},
{input: true, output: "true"}]);
assertThrows(() => Object(undefined));
TestValues(Object, [{input: x, output: x},
{input: 22.22, source: true, output: "(new Number(22.22))"},
{input: true, source: true, output: "(new Boolean(true))"}]);
reportCompare(true, true, "TypedObjects reference type tests");
}
runTests();

Просмотреть файл

@ -0,0 +1,22 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
var BUGNUMBER = 898359;
var summary = 'TypedObjects reference type coercions';
var actual = '';
var expect = '';
var ArrayType = TypedObject.ArrayType;
var StructType = TypedObject.StructType;
var Any = TypedObject.Any;
var Object = TypedObject.Object;
var string = TypedObject.string;
function runTests()
{
var S = new StructType({f: Any, g: Any});
var s = new S({f: "Hello", g: "Hello"});
assertEq(s.f, s.g);
reportCompare(true, true, "TypedObjects trace tests");
}
runTests();

Просмотреть файл

@ -0,0 +1,88 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject")||!this.hasOwnProperty("countHeap"))
var BUGNUMBER = 898359;
var summary = 'TypedObjects reference type trace';
var actual = '';
var expect = '';
var ArrayType = TypedObject.ArrayType;
var StructType = TypedObject.StructType;
var Any = TypedObject.Any;
var Object = TypedObject.Object;
var string = TypedObject.string;
function assertCanReach(from, target, times) {
times = times || 1;
var count1 = countHeap(from, "specific", target);
print("canReach:", count1, times, from.toSource(), target.toSource());
assertEq(count1, times);
}
function assertCannotReach(from, target) {
var count1 = countHeap(from, "specific", target);
print("cannotReach:", count1, from.toSource(), target.toSource());
assertEq(count1, 0);
}
function TestStructFields(RefType) {
var rabbit = {};
var S1 = new StructType({f: 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 S1([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 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
// just use a constant string here, it's always reachable via the
// atoms table. Same is true of "Hello" + "1" (an earlier
// attempt) due to parser constant folding. So we have to make a
// rabbit that's not constant foldable. But don't just use
// Math.random(), since small integers are atoms already.
var rabbit = "Hello" + Math.random();
var S1 = new StructType({f: string});
var s1 = new S1({f: rabbit});
assertCanReach(s1, rabbit);
s1.f = "World";
assertCannotReach(s1, rabbit);
}
function runTests()
{
printBugNumber(BUGNUMBER);
printStatus(summary);
TestStructFields(Object);
TestStructFields(Any);
TestArrayElements(Object);
TestArrayElements(Any);
TestStructInArray(Object);
TestStructInArray(Any);
TestStringInStruct();
reportCompare(true, true, "TypedObjects trace tests");
}
runTests();

Просмотреть файл

@ -0,0 +1,30 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
var BUGNUMBER = 898359;
var summary = 'TypedObjects reference type default values';
var actual = '';
var expect = '';
var ArrayType = TypedObject.ArrayType;
var StructType = TypedObject.StructType;
var Any = TypedObject.Any;
var Object = TypedObject.Object;
var string = TypedObject.string;
function runTests()
{
printBugNumber(BUGNUMBER);
printStatus(summary);
var S = new StructType({any: Any,
object: Object,
string: string});
var s = new S();
assertEq(s.any, undefined);
assertEq(s.object, null);
assertEq(s.string, "");
reportCompare(true, true, "TypedObjects ref type uninit");
}
runTests();

Просмотреть файл

@ -2,30 +2,21 @@
var BUGNUMBER = 922216;
var summary = 'TypedObjects Equivalent Numeric Types';
var ArrayType = TypedObject.ArrayType;
var StructType = TypedObject.StructType;
var uint8 = TypedObject.uint8;
var uint16 = TypedObject.uint16;
var uint32 = TypedObject.uint32;
var uint8Clamped = TypedObject.uint8Clamped;
var int8 = TypedObject.int8;
var int16 = TypedObject.int16;
var int32 = TypedObject.int32;
var float32 = TypedObject.float32;
var float64 = TypedObject.float64;
var T = TypedObject;
function runTests() {
print(BUGNUMBER + ": " + summary);
var scalarTypes = [
int8, int16, int32,
uint8, uint16, uint32,
float32, float64
var simpleTypes = [
T.int8, T.int16, T.int32,
T.uint8, T.uint16, T.uint32,
T.float32, T.float64,
T.Object, T.Any, T.string
];
for (var i = 0; i < scalarTypes.length; i++)
for (var j = 0; j < scalarTypes.length; j++)
assertEq(i == j, scalarTypes[i].equivalent(scalarTypes[j]));
for (var i = 0; i < simpleTypes.length; i++)
for (var j = 0; j < simpleTypes.length; j++)
assertEq(i == j, simpleTypes[i].equivalent(simpleTypes[j]));
reportCompare(true, true);
print("Tests complete");

Просмотреть файл

@ -13,6 +13,7 @@
#define FOR_EACH_COMMON_PROPERTYNAME(macro) \
macro(anonymous, anonymous, "anonymous") \
macro(Any, Any, "Any") \
macro(apply, apply, "apply") \
macro(arguments, arguments, "arguments") \
macro(as, as, "as") \

Просмотреть файл

@ -659,14 +659,23 @@ static const JSFunctionSpec intrinsic_functions[] = {
JSNativeThreadSafeWrapper<js::Memcpy>,
&js::MemcpyJitInfo, 5, 0),
#define LOAD_AND_STORE_FN_DECLS(_constant, _type, _name) \
#define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name) \
JS_FNINFO("Store_" #_name, \
JSNativeThreadSafeWrapper<js::StoreScalar##_type::Func>, \
&js::StoreScalar##_type::JitInfo, 3, 0), \
JS_FNINFO("Load_" #_name, \
JSNativeThreadSafeWrapper<js::LoadScalar##_type::Func>, \
&js::LoadScalar##_type::JitInfo, 3, 0),
JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_FN_DECLS)
JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS)
#define LOAD_AND_STORE_REFERENCE_FN_DECLS(_constant, _type, _name) \
JS_FNINFO("Store_" #_name, \
JSNativeThreadSafeWrapper<js::StoreReference##_type::Func>, \
&js::StoreReference##_type::JitInfo, 3, 0), \
JS_FNINFO("Load_" #_name, \
JSNativeThreadSafeWrapper<js::LoadReference##_type::Func>, \
&js::LoadReference##_type::JitInfo, 3, 0),
JS_FOR_EACH_REFERENCE_TYPE_REPR(LOAD_AND_STORE_REFERENCE_FN_DECLS)
// See builtin/Intl.h for descriptions of the intl_* functions.
JS_FN("intl_availableCalendars", intl_availableCalendars, 1,0),