diff --git a/js/public/TypeDecls.h b/js/public/TypeDecls.h --- a/js/public/TypeDecls.h +++ b/js/public/TypeDecls.h @@ -29,16 +29,17 @@ class JSScript; class JSString; struct jsid; typedef char16_t jschar; namespace JS { +class Symbol; class Value; template class Handle; template class MutableHandle; template class Rooted; template class PersistentRooted; typedef Handle HandleFunction; typedef Handle HandleId; diff --git a/js/public/Value.h b/js/public/Value.h --- a/js/public/Value.h +++ b/js/public/Value.h @@ -89,16 +89,17 @@ JS_STATIC_ASSERT(sizeof(JSValueType) == /* Remember to propagate changes to the C defines below. */ JS_ENUM_HEADER(JSValueTag, uint32_t) { JSVAL_TAG_CLEAR = 0xFFFFFF80, JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32, JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED, JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING, + JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL, JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN, JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC, JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL, JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT } JS_ENUM_FOOTER(JSValueTag); JS_STATIC_ASSERT(sizeof(JSValueTag) == 4); @@ -106,30 +107,32 @@ JS_STATIC_ASSERT(sizeof(JSValueTag) == 4 /* Remember to propagate changes to the C defines below. */ JS_ENUM_HEADER(JSValueTag, uint32_t) { JSVAL_TAG_MAX_DOUBLE = 0x1FFF0, JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32, JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED, JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING, + JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL, JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN, JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC, JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL, JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT } JS_ENUM_FOOTER(JSValueTag); JS_STATIC_ASSERT(sizeof(JSValueTag) == sizeof(uint32_t)); JS_ENUM_HEADER(JSValueShiftedTag, uint64_t) { JSVAL_SHIFTED_TAG_MAX_DOUBLE = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF), JSVAL_SHIFTED_TAG_INT32 = (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT), JSVAL_SHIFTED_TAG_UNDEFINED = (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT), JSVAL_SHIFTED_TAG_STRING = (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT), + JSVAL_SHIFTED_TAG_SYMBOL = (((uint64_t)JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT), JSVAL_SHIFTED_TAG_BOOLEAN = (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT), JSVAL_SHIFTED_TAG_MAGIC = (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT), JSVAL_SHIFTED_TAG_NULL = (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT), JSVAL_SHIFTED_TAG_OBJECT = (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) } JS_ENUM_FOOTER(JSValueShiftedTag); JS_STATIC_ASSERT(sizeof(JSValueShiftedTag) == sizeof(uint64_t)); @@ -139,49 +142,53 @@ JS_STATIC_ASSERT(sizeof(JSValueShiftedTa typedef uint8_t JSValueType; #define JSVAL_TYPE_DOUBLE ((uint8_t)0x00) #define JSVAL_TYPE_INT32 ((uint8_t)0x01) #define JSVAL_TYPE_UNDEFINED ((uint8_t)0x02) #define JSVAL_TYPE_BOOLEAN ((uint8_t)0x03) #define JSVAL_TYPE_MAGIC ((uint8_t)0x04) #define JSVAL_TYPE_STRING ((uint8_t)0x05) -#define JSVAL_TYPE_NULL ((uint8_t)0x06) -#define JSVAL_TYPE_OBJECT ((uint8_t)0x07) +#define JSVAL_TYPE_SYMBOL ((uint8_t)0x06) +#define JSVAL_TYPE_NULL ((uint8_t)0x07) +#define JSVAL_TYPE_OBJECT ((uint8_t)0x08) #define JSVAL_TYPE_UNKNOWN ((uint8_t)0x20) #if JS_BITS_PER_WORD == 32 typedef uint32_t JSValueTag; #define JSVAL_TAG_CLEAR ((uint32_t)(0xFFFFFF80)) #define JSVAL_TAG_INT32 ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32)) #define JSVAL_TAG_UNDEFINED ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED)) #define JSVAL_TAG_STRING ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING)) +#define JSVAL_TAG_SYMBOL ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL)) #define JSVAL_TAG_BOOLEAN ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN)) #define JSVAL_TAG_MAGIC ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC)) #define JSVAL_TAG_NULL ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL)) #define JSVAL_TAG_OBJECT ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT)) #elif JS_BITS_PER_WORD == 64 typedef uint32_t JSValueTag; #define JSVAL_TAG_MAX_DOUBLE ((uint32_t)(0x1FFF0)) #define JSVAL_TAG_INT32 (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) #define JSVAL_TAG_UNDEFINED (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED) #define JSVAL_TAG_STRING (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING) +#define JSVAL_TAG_SYMBOL (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL) #define JSVAL_TAG_BOOLEAN (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) #define JSVAL_TAG_MAGIC (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC) #define JSVAL_TAG_NULL (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) #define JSVAL_TAG_OBJECT (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT) typedef uint64_t JSValueShiftedTag; #define JSVAL_SHIFTED_TAG_MAX_DOUBLE ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF) #define JSVAL_SHIFTED_TAG_INT32 (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT) #define JSVAL_SHIFTED_TAG_UNDEFINED (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT) #define JSVAL_SHIFTED_TAG_STRING (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT) +#define JSVAL_SHIFTED_TAG_SYMBOL (((uint64_t)JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT) #define JSVAL_SHIFTED_TAG_BOOLEAN (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT) #define JSVAL_SHIFTED_TAG_MAGIC (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT) #define JSVAL_SHIFTED_TAG_NULL (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT) #define JSVAL_SHIFTED_TAG_OBJECT (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) #endif /* JS_BITS_PER_WORD */ #endif /* !defined(__SUNPRO_CC) && !defined(__xlC__) */ @@ -465,16 +472,38 @@ STRING_TO_JSVAL_IMPL(JSString *str) static inline JSString * JSVAL_TO_STRING_IMPL(jsval_layout l) { return l.s.payload.str; } static inline bool +JSVAL_IS_SYMBOL_IMPL(jsval_layout l) +{ + return l.s.tag == JSVAL_TAG_SYMBOL; +} + +static inline jsval_layout +SYMBOL_TO_JSVAL_IMPL(JS::Symbol *sym) +{ + jsval_layout l; + MOZ_ASSERT(uintptr_t(sym) > 0x1000); + l.s.tag = JSVAL_TAG_SYMBOL; + l.s.payload.sym = sym; + return l; +} + +static inline JS::Symbol * +JSVAL_TO_SYMBOL_IMPL(jsval_layout l) +{ + return l.s.payload.sym; +} + +static inline bool JSVAL_IS_BOOLEAN_IMPL(jsval_layout l) { return l.s.tag == JSVAL_TAG_BOOLEAN; } static inline bool JSVAL_TO_BOOLEAN_IMPL(jsval_layout l) { @@ -565,17 +594,19 @@ static inline void * JSVAL_TO_GCTHING_IMPL(jsval_layout l) { return l.s.payload.ptr; } static inline bool JSVAL_IS_TRACEABLE_IMPL(jsval_layout l) { - return l.s.tag == JSVAL_TAG_STRING || l.s.tag == JSVAL_TAG_OBJECT; + return l.s.tag == JSVAL_TAG_STRING || + l.s.tag == JSVAL_TAG_SYMBOL || + l.s.tag == JSVAL_TAG_OBJECT; } static inline uint32_t JSVAL_TRACE_KIND_IMPL(jsval_layout l) { return (uint32_t)(bool)JSVAL_IS_STRING_IMPL(l); } @@ -696,16 +727,39 @@ STRING_TO_JSVAL_IMPL(JSString *str) static inline JSString * JSVAL_TO_STRING_IMPL(jsval_layout l) { return (JSString *)(l.asBits & JSVAL_PAYLOAD_MASK); } static inline bool +JSVAL_IS_SYMBOL_IMPL(jsval_layout l) +{ + return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_SYMBOL; +} + +static inline jsval_layout +SYMBOL_TO_JSVAL_IMPL(JS::Symbol *sym) +{ + jsval_layout l; + uint64_t symBits = (uint64_t)sym; + MOZ_ASSERT(uintptr_t(sym) > 0x1000); + MOZ_ASSERT((symBits >> JSVAL_TAG_SHIFT) == 0); + l.asBits = symBits | JSVAL_SHIFTED_TAG_SYMBOL; + return l; +} + +static inline JS::Symbol * +JSVAL_TO_SYMBOL_IMPL(jsval_layout l) +{ + return (JS::Symbol *)(l.asBits & JSVAL_PAYLOAD_MASK); +} + +static inline bool JSVAL_IS_BOOLEAN_IMPL(jsval_layout l) { return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN; } static inline bool JSVAL_TO_BOOLEAN_IMPL(jsval_layout l) { @@ -896,17 +950,17 @@ CanonicalizeNaN(double d) #endif /* * JS::Value is the interface for a single JavaScript Engine value. A few * general notes on JS::Value: * * - JS::Value has setX() and isX() members for X in * - * { Int32, Double, String, Boolean, Undefined, Null, Object, Magic } + * { Int32, Double, String, Symbol, Boolean, Undefined, Null, Object, Magic } * * JS::Value also contains toX() for each of the non-singleton types. * * - Magic is a singleton type whose payload contains either a JSWhyMagic "reason" for * the magic value or a uint32_t value. By providing JSWhyMagic values when * creating and checking for magic values, it is possible to assert, at * runtime, that only magic values with the expected reason flow through a * particular value. For example, if cx->exception has a magic value, the @@ -970,16 +1024,21 @@ class Value return data.asDouble; } void setString(JSString *str) { MOZ_ASSERT(!IsPoisonedPtr(str)); data = STRING_TO_JSVAL_IMPL(str); } + void setSymbol(JS::Symbol *sym) { + MOZ_ASSERT(!IsPoisonedPtr(sym)); + data = SYMBOL_TO_JSVAL_IMPL(sym); + } + void setObject(JSObject &obj) { MOZ_ASSERT(!IsPoisonedPtr(&obj)); data = OBJECT_TO_JSVAL_IMPL(&obj); } void setBoolean(bool b) { data = BOOLEAN_TO_JSVAL_IMPL(b); } @@ -1055,16 +1114,20 @@ class Value bool isNumber() const { return JSVAL_IS_NUMBER_IMPL(data); } bool isString() const { return JSVAL_IS_STRING_IMPL(data); } + bool isSymbol() const { + return JSVAL_IS_SYMBOL_IMPL(data); + } + bool isObject() const { return JSVAL_IS_OBJECT_IMPL(data); } bool isPrimitive() const { return JSVAL_IS_PRIMITIVE_IMPL(data); } @@ -1145,16 +1208,21 @@ class Value return isDouble() ? toDouble() : double(toInt32()); } JSString *toString() const { MOZ_ASSERT(isString()); return JSVAL_TO_STRING_IMPL(data); } + JS::Symbol *toSymbol() const { + MOZ_ASSERT(isSymbol()); + return JSVAL_TO_SYMBOL_IMPL(data); + } + JSObject &toObject() const { MOZ_ASSERT(isObject()); return *JSVAL_TO_OBJECT_IMPL(data); } JSObject *toObjectOrNull() const { MOZ_ASSERT(isObjectOrNull()); return JSVAL_TO_OBJECT_IMPL(data); @@ -1266,16 +1334,18 @@ class Value friend Value JS_VALUE_CONSTEXPR (JS::UndefinedValue)(); }; inline bool IsPoisonedValue(const Value &v) { if (v.isString()) return IsPoisonedPtr(v.toString()); + if (v.isSymbol()) + return IsPoisonedPtr(v.toSymbol()); if (v.isObject()) return IsPoisonedPtr(&v.toObject()); return false; } inline bool IsOptimizedPlaceholderMagicValue(const Value &v) { @@ -1344,16 +1414,24 @@ static inline Value StringValue(JSString *str) { Value v; v.setString(str); return v; } static inline Value +SymbolValue(JS::Symbol *sym) +{ + Value v; + v.setSymbol(sym); + return v; +} + +static inline Value BooleanValue(bool boo) { Value v; v.setBoolean(boo); return v; } static inline Value @@ -1586,31 +1664,33 @@ class ValueOperations bool isNull() const { return value()->isNull(); } bool isBoolean() const { return value()->isBoolean(); } bool isTrue() const { return value()->isTrue(); } bool isFalse() const { return value()->isFalse(); } bool isNumber() const { return value()->isNumber(); } bool isInt32() const { return value()->isInt32(); } bool isDouble() const { return value()->isDouble(); } bool isString() const { return value()->isString(); } + bool isSymbol() const { return value()->isSymbol(); } bool isObject() const { return value()->isObject(); } bool isMagic() const { return value()->isMagic(); } bool isMagic(JSWhyMagic why) const { return value()->isMagic(why); } bool isMarkable() const { return value()->isMarkable(); } bool isPrimitive() const { return value()->isPrimitive(); } bool isGCThing() const { return value()->isGCThing(); } bool isNullOrUndefined() const { return value()->isNullOrUndefined(); } bool isObjectOrNull() const { return value()->isObjectOrNull(); } bool toBoolean() const { return value()->toBoolean(); } double toNumber() const { return value()->toNumber(); } int32_t toInt32() const { return value()->toInt32(); } double toDouble() const { return value()->toDouble(); } JSString *toString() const { return value()->toString(); } + JS::Symbol *toSymbol() const { return value()->toSymbol(); } JSObject &toObject() const { return value()->toObject(); } JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } void *toGCThing() const { return value()->toGCThing(); } uint64_t asRawBits() const { return value()->asRawBits(); } JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); } uint32_t toPrivateUint32() const { return value()->toPrivateUint32(); } @@ -1635,16 +1715,17 @@ class MutableValueOperations : public Va void setInt32(int32_t i) { value()->setInt32(i); } void setDouble(double d) { value()->setDouble(d); } void setNaN() { setDouble(JS::GenericNaN()); } void setBoolean(bool b) { value()->setBoolean(b); } void setMagic(JSWhyMagic why) { value()->setMagic(why); } bool setNumber(uint32_t ui) { return value()->setNumber(ui); } bool setNumber(double d) { return value()->setNumber(d); } void setString(JSString *str) { this->value()->setString(str); } + void setSymbol(JS::Symbol *sym) { this->value()->setSymbol(sym); } void setObject(JSObject &obj) { this->value()->setObject(obj); } void setObjectOrNull(JSObject *arg) { this->value()->setObjectOrNull(arg); } }; /* * Augment the generic Heap interface when T = Value with * type-querying, value-extracting, and mutating operations. */ @@ -1665,16 +1746,17 @@ class HeapBase : public Value void setNull() { setBarriered(JS::NullValue()); } void setUndefined() { setBarriered(JS::UndefinedValue()); } void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); } void setDouble(double d) { setBarriered(JS::DoubleValue(d)); } void setNaN() { setDouble(JS::GenericNaN()); } void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); } void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); } void setString(JSString *str) { setBarriered(JS::StringValue(str)); } + void setSymbol(JS::Symbol *sym) { setBarriered(JS::SymbolValue(sym)); } void setObject(JSObject &obj) { setBarriered(JS::ObjectValue(obj)); } bool setNumber(uint32_t ui) { if (ui > JSVAL_INT_MAX) { setDouble((double)ui); return false; } else { setInt32((int32_t)ui); diff --git a/js/src/jsapi.h b/js/src/jsapi.h --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -7,16 +7,17 @@ /* JavaScript API. */ #ifndef jsapi_h #define jsapi_h #include "mozilla/FloatingPoint.h" #include "mozilla/MemoryReporting.h" #include "mozilla/RangedPtr.h" +#include "mozilla/TypedEnum.h" #include #include #include #include #include "jsalloc.h" #include "jspubtd.h" @@ -4339,16 +4340,52 @@ class JSAutoByteString /* Copy and assignment are not supported. */ JSAutoByteString(const JSAutoByteString &another); JSAutoByteString &operator=(const JSAutoByteString &another); }; /************************************************************************/ /* + * Symbols + */ + +namespace JS { + +/* + * Create a new Symbol with the given description. This function never returns + * a Symbol that is in the Runtime-wide symbol registry. + * + * If description is null, the new Symbol's [[Description]] attribute is + * undefined. + */ +JS_PUBLIC_API(bool) +NewSymbol(JSContext *cx, HandleString description, MutableHandle result); + +/* + * Symbol.for as specified in ES6. + * + * Get a Symbol with the description 'key' from the Runtime-wide symbol registry. + * If there is not already a Symbol with that description in the registry, a new + * Symbol is created and registered. 'key' must not be null. + */ +JS_PUBLIC_API(bool) +GetSymbolFor(JSContext *cx, HandleString key, MutableHandle result); + +/* + * Get the [[Description]] attribute of the given symbol. If the symbol's + * [[Description]] attribute is undefined, set result to null. + */ +JS_PUBLIC_API(bool) +GetSymbolDescription(JSContext *cx, Handle symbol, MutableHandleString result); + +} /* namespace JS */ + +/************************************************************************/ +/* * JSON functions */ typedef bool (* JSONWriteCallback)(const jschar *buf, uint32_t len, void *data); /* * JSON.stringify as specified by ES5. */ JS_PUBLIC_API(bool)