--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -389,16 +389,19 @@ GCThingIsMarkedGray(GCCellPtr thing)
{
if (js::gc::IsInsideNursery(thing.asCell()))
return false;
if (thing.mayBeOwnedByOtherRuntime())
return false;
return js::gc::detail::CellIsMarkedGray(thing.asCell());
}
+extern JS_PUBLIC_API(JS::TraceKind)
+GCThingTraceKind(void* thing);
+
} /* namespace JS */
namespace js {
namespace gc {
static MOZ_ALWAYS_INLINE bool
IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime* rt, const JS::GCCellPtr thing)
{
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -71,18 +71,19 @@ JS_ENUM_HEADER(JSValueType, uint8_t)
{
JSVAL_TYPE_DOUBLE = 0x00,
JSVAL_TYPE_INT32 = 0x01,
JSVAL_TYPE_UNDEFINED = 0x02,
JSVAL_TYPE_BOOLEAN = 0x03,
JSVAL_TYPE_MAGIC = 0x04,
JSVAL_TYPE_STRING = 0x05,
JSVAL_TYPE_SYMBOL = 0x06,
- JSVAL_TYPE_NULL = 0x07,
- JSVAL_TYPE_OBJECT = 0x08,
+ JSVAL_TYPE_PRIVATE_GCTHING = 0x07,
+ JSVAL_TYPE_NULL = 0x08,
+ JSVAL_TYPE_OBJECT = 0x0c,
/* These never appear in a jsval; they are only provided as an out-of-band value. */
JSVAL_TYPE_UNKNOWN = 0x20,
JSVAL_TYPE_MISSING = 0x21
} JS_ENUM_FOOTER(JSValueType);
static_assert(sizeof(JSValueType) == 1,
"compiler typed enum support is apparently buggy");
@@ -95,17 +96,18 @@ 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
+ JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT,
+ JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING
} JS_ENUM_FOOTER(JSValueTag);
static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
"compiler typed enum support is apparently buggy");
#elif defined(JS_PUNBOX64)
/* Remember to propagate changes to the C defines below. */
@@ -114,33 +116,35 @@ 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
+ JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT,
+ JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING
} JS_ENUM_FOOTER(JSValueTag);
static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
"compiler typed enum support is apparently buggy");
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)
+ 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),
+ JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT)
} JS_ENUM_FOOTER(JSValueShiftedTag);
static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
"compiler typed enum support is apparently buggy");
#endif
/*
@@ -158,56 +162,60 @@ static_assert(sizeof(JSValueShiftedTag)
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_SYMBOL ((uint8_t)0x06)
-#define JSVAL_TYPE_NULL ((uint8_t)0x07)
-#define JSVAL_TYPE_OBJECT ((uint8_t)0x08)
+#define JSVAL_TYPE_PRIVATE_GCTHING ((uint8_t)0x07)
+#define JSVAL_TYPE_NULL ((uint8_t)0x08)
+#define JSVAL_TYPE_OBJECT ((uint8_t)0x0c)
#define JSVAL_TYPE_UNKNOWN ((uint8_t)0x20)
#if defined(JS_NUNBOX32)
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))
+#define JSVAL_TAG_PRIVATE_GCTHING ((uint32_t)(JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING))
#elif defined(JS_PUNBOX64)
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)
+#define JSVAL_TAG_PRIVATE_GCTHING (uint32_t)(JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING)
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)
+#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)
+#define JSVAL_SHIFTED_TAG_PRIVATE_GCTHING (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT)
#endif /* JS_PUNBOX64 */
#endif /* !defined(__SUNPRO_CC) && !defined(__xlC__) */
#if defined(JS_NUNBOX32)
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_CLEAR | (type)))
@@ -627,16 +635,39 @@ PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
}
static inline void*
JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
{
return l.s.payload.ptr;
}
+static inline jsval_layout
+PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell)
+{
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
+ "Private GC thing Values must not be strings. Make a StringValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
+ "Private GC thing Values must not be symbols. Make a SymbolValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
+ "Private GC thing Values must not be objects. Make an ObjectValue instead.");
+
+ jsval_layout l;
+ MOZ_ASSERT(uintptr_t(cell) > 0x1000);
+ l.s.tag = JSVAL_TAG_PRIVATE_GCTHING;
+ l.s.payload.cell = cell;
+ return l;
+}
+
+static inline bool
+JSVAL_IS_PRIVATE_GCTHING_IMPL(jsval_layout l)
+{
+ return l.s.tag == JSVAL_TAG_PRIVATE_GCTHING;
+}
+
static inline bool
JSVAL_IS_GCTHING_IMPL(jsval_layout l)
{
/* gcc sometimes generates signed < without explicit casts. */
return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET;
}
static inline js::gc::Cell*
@@ -649,16 +680,18 @@ static inline uint32_t
JSVAL_TRACE_KIND_IMPL(jsval_layout l)
{
static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
"Value type tags must correspond with JS::TraceKinds.");
+ if (MOZ_UNLIKELY(JSVAL_IS_PRIVATE_GCTHING_IMPL(l)))
+ return (uint32_t)JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(l));
return l.s.tag & 0x03;
}
static inline bool
JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
{
return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32;
}
@@ -868,16 +901,22 @@ OBJECT_TO_JSVAL_IMPL(JSObject* obj)
static inline bool
JSVAL_IS_NULL_IMPL(jsval_layout l)
{
return l.asBits == JSVAL_SHIFTED_TAG_NULL;
}
static inline bool
+JSVAL_IS_PRIVATE_GCTHING_IMPL(jsval_layout l)
+{
+ return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_PRIVATE_GCTHING;
+}
+
+static inline bool
JSVAL_IS_GCTHING_IMPL(jsval_layout l)
{
return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
}
static inline js::gc::Cell*
JSVAL_TO_GCTHING_IMPL(jsval_layout l)
{
@@ -890,16 +929,18 @@ static inline uint32_t
JSVAL_TRACE_KIND_IMPL(jsval_layout l)
{
static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
"Value type tags must correspond with JS::TraceKinds.");
+ if (MOZ_UNLIKELY(JSVAL_IS_PRIVATE_GCTHING_IMPL(l)))
+ return (uint32_t)JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(l));
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03;
}
static inline jsval_layout
PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
{
jsval_layout l;
uint64_t ptrBits = (uint64_t)ptr;
@@ -911,16 +952,34 @@ PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
static inline void*
JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
{
MOZ_ASSERT((l.asBits & 0x8000000000000000LL) == 0);
return (void*)(l.asBits << 1);
}
+static inline jsval_layout
+PRIVATE_GCTHING_TO_JSVAL_IMPL(js::gc::Cell* cell)
+{
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
+ "Private GC thing Values must not be strings. Make a StringValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
+ "Private GC thing Values must not be symbols. Make a SymbolValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
+ "Private GC thing Values must not be objects. Make an ObjectValue instead.");
+
+ jsval_layout l;
+ uint64_t cellBits = (uint64_t)cell;
+ MOZ_ASSERT(uintptr_t(cellBits) > 0x1000);
+ MOZ_ASSERT((cellBits >> JSVAL_TAG_SHIFT) == 0);
+ l.asBits = cellBits | JSVAL_SHIFTED_TAG_PRIVATE_GCTHING;
+ return l;
+}
+
static inline bool
JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
{
return l.asBits == (((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
}
static inline bool
JSVAL_IS_SPECIFIC_BOOLEAN_IMPL(jsval_layout l, bool b)
@@ -1337,16 +1396,33 @@ class Value
setInt32(int32_t(ui));
}
uint32_t toPrivateUint32() const {
return uint32_t(toInt32());
}
/*
+ * Private GC Thing API
+ *
+ * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
+ * payload as private GC things. Such Values are considered isMarkable()
+ * and isGCThing(), and as such, automatically marked. Their traceKind()
+ * is gotten via their cells.
+ */
+
+ void setPrivateGCThing(js::gc::Cell* cell) {
+ data = PRIVATE_GCTHING_TO_JSVAL_IMPL(cell);
+ }
+
+ bool isPrivateGCThing() const {
+ return JSVAL_IS_PRIVATE_GCTHING_IMPL(data);
+ }
+
+ /*
* An unmarked value is just a void* cast as a Value. Thus, the Value is
* not safe for GC and must not be marked. This API avoids raw casts
* and the ensuing strict-aliasing warnings.
*/
void setUnmarkedPtr(void* ptr) {
data.asPtr = ptr;
}
@@ -1678,16 +1754,24 @@ PrivateValue(void* ptr)
static inline Value
PrivateUint32Value(uint32_t ui)
{
Value v;
v.setPrivateUint32(ui);
return v;
}
+static inline Value
+PrivateGCThingValue(js::gc::Cell* cell)
+{
+ Value v;
+ v.setPrivateGCThing(cell);
+ return v;
+}
+
inline bool
SameType(const Value& lhs, const Value& rhs)
{
return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data);
}
} // namespace JS
@@ -1796,16 +1880,17 @@ class MutableValueOperations : public Va
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); }
+ void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
};
/*
* Augment the generic Heap<T> interface when T = Value with
* type-querying, value-extracting, and mutating operations.
*/
template <>
class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
@@ -1824,16 +1909,17 @@ class HeapBase<JS::Value> : public Value
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)); }
+ void setPrivateGCThing(js::gc::Cell* cell) { setBarriered(JS::PrivateGCThingValue(cell)); }
bool setNumber(uint32_t ui) {
if (ui > JSVAL_INT_MAX) {
setDouble((double)ui);
return false;
} else {
setInt32((int32_t)ui);
return true;
@@ -1885,16 +1971,18 @@ DispatchTyped(F f, const JS::Value& val,
-> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
{
if (val.isString())
return f(val.toString(), mozilla::Forward<Args>(args)...);
if (val.isObject())
return f(&val.toObject(), mozilla::Forward<Args>(args)...);
if (val.isSymbol())
return f(val.toSymbol(), mozilla::Forward<Args>(args)...);
+ if (MOZ_UNLIKELY(val.isPrivateGCThing()))
+ return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...);
MOZ_ASSERT(!val.isMarkable());
return F::defaultValue(val);
}
template <class S> struct VoidDefaultAdaptor { static void defaultValue(S) {} };
template <class S> struct IdentityDefaultAdaptor { static S defaultValue(const S& v) {return v;} };
template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(S) { return v; } };
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -75,30 +75,39 @@ CurrentThreadIsHandlingInitFailure()
template <typename S>
template <typename T>
void
ReadBarrierFunctor<S>::operator()(T* t)
{
InternalBarrierMethods<T*>::readBarrier(t);
}
-template void ReadBarrierFunctor<JS::Value>::operator()<JS::Symbol>(JS::Symbol*);
-template void ReadBarrierFunctor<JS::Value>::operator()<JSObject>(JSObject*);
-template void ReadBarrierFunctor<JS::Value>::operator()<JSString>(JSString*);
+
+// All GC things may be held in a Value, either publicly or as a private GC
+// thing.
+#define JS_EXPAND_DEF(name, type, _) \
+template void ReadBarrierFunctor<JS::Value>::operator()<type>(type*);
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
template <typename S>
template <typename T>
void
PreBarrierFunctor<S>::operator()(T* t)
{
InternalBarrierMethods<T*>::preBarrier(t);
}
-template void PreBarrierFunctor<JS::Value>::operator()<JS::Symbol>(JS::Symbol*);
-template void PreBarrierFunctor<JS::Value>::operator()<JSObject>(JSObject*);
-template void PreBarrierFunctor<JS::Value>::operator()<JSString>(JSString*);
+
+// All GC things may be held in a Value, either publicly or as a private GC
+// thing.
+#define JS_EXPAND_DEF(name, type, _) \
+template void PreBarrierFunctor<JS::Value>::operator()<type>(type*);
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+
template void PreBarrierFunctor<jsid>::operator()<JS::Symbol>(JS::Symbol*);
template void PreBarrierFunctor<jsid>::operator()<JSString>(JSString*);
template <typename T>
/* static */ HashNumber
MovableCellHasher<T>::hash(const Lookup& l)
{
if (!l)
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -256,17 +256,17 @@ struct InternalBarrierMethods<T*>
static void preBarrier(T* v) { T::writeBarrierPre(v); }
static void postBarrier(T** vp, T* prev, T* next) { T::writeBarrierPost(vp, prev, next); }
static void readBarrier(T* v) { T::readBarrier(v); }
};
-template <typename S> struct PreBarrierFunctor : VoidDefaultAdaptor<S> {
+template <typename S> struct PreBarrierFunctor : public VoidDefaultAdaptor<S> {
template <typename T> void operator()(T* t);
};
template <typename S> struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
template <typename T> void operator()(T* t);
};
template <>
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2016,17 +2016,17 @@ TenuringTracer::traverse(JSObject** objp
if (IsInsideNursery(*objp) && !nursery().getForwardedPointer(objp))
*objp = moveToTenured(*objp);
}
template <typename S>
struct TenuringTraversalFunctor : public IdentityDefaultAdaptor<S> {
template <typename T> S operator()(T* t, TenuringTracer* trc) {
trc->traverse(&t);
- return js::gc::RewrapTaggedPointer<S, T*>::wrap(t);
+ return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
}
};
template <typename T>
void
TenuringTracer::traverse(T* thingp)
{
*thingp = DispatchTyped(TenuringTraversalFunctor<T>(), *thingp, this);
@@ -2422,17 +2422,17 @@ IsMarkedInternal(JSObject** thingp)
}
return IsMarkedInternalCommon(thingp);
}
template <typename S>
struct IsMarkedFunctor : public IdentityDefaultAdaptor<S> {
template <typename T> S operator()(T* t, bool* rv) {
*rv = IsMarkedInternal(&t);
- return js::gc::RewrapTaggedPointer<S, T*>::wrap(t);
+ return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
}
};
template <typename T>
static bool
IsMarkedInternal(T* thingp)
{
bool rv = true;
@@ -2481,17 +2481,17 @@ IsAboutToBeFinalizedInternal(T** thingp)
return false;
}
template <typename S>
struct IsAboutToBeFinalizedFunctor : public IdentityDefaultAdaptor<S> {
template <typename T> S operator()(T* t, bool* rv) {
*rv = IsAboutToBeFinalizedInternal(&t);
- return js::gc::RewrapTaggedPointer<S, T*>::wrap(t);
+ return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
}
};
template <typename T>
static bool
IsAboutToBeFinalizedInternal(T* thingp)
{
bool rv = false;
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -415,24 +415,44 @@ IsNullTaggedPointer(void* p)
}
// Wrap a GC thing pointer into a new Value or jsid. The type system enforces
// that the thing pointer is a wrappable type.
template <typename S, typename T>
struct RewrapTaggedPointer{};
#define DECLARE_REWRAP(S, T, method, prefix) \
template <> struct RewrapTaggedPointer<S, T> { \
- static S wrap(T thing) { return method ( prefix thing ); } \
+ static S wrap(T* thing) { return method ( prefix thing ); } \
}
-DECLARE_REWRAP(JS::Value, JSObject*, JS::ObjectOrNullValue, );
-DECLARE_REWRAP(JS::Value, JSString*, JS::StringValue, );
-DECLARE_REWRAP(JS::Value, JS::Symbol*, JS::SymbolValue, );
-DECLARE_REWRAP(jsid, JSString*, NON_INTEGER_ATOM_TO_JSID, (JSAtom*));
-DECLARE_REWRAP(jsid, JS::Symbol*, SYMBOL_TO_JSID, );
-DECLARE_REWRAP(js::TaggedProto, JSObject*, js::TaggedProto, );
+DECLARE_REWRAP(JS::Value, JSObject, JS::ObjectOrNullValue, );
+DECLARE_REWRAP(JS::Value, JSString, JS::StringValue, );
+DECLARE_REWRAP(JS::Value, JS::Symbol, JS::SymbolValue, );
+DECLARE_REWRAP(jsid, JSString, NON_INTEGER_ATOM_TO_JSID, (JSAtom*));
+DECLARE_REWRAP(jsid, JS::Symbol, SYMBOL_TO_JSID, );
+DECLARE_REWRAP(js::TaggedProto, JSObject, js::TaggedProto, );
+#undef DECLARE_REWRAP
+
+template <typename T>
+struct IsPrivateGCThingInValue
+ : public mozilla::EnableIf<mozilla::IsBaseOf<Cell, T>::value &&
+ !mozilla::IsBaseOf<JSObject, T>::value &&
+ !mozilla::IsBaseOf<JSString, T>::value &&
+ !mozilla::IsBaseOf<JS::Symbol, T>::value, T>
+{
+ static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
+ "T must not be Cell or TenuredCell");
+};
+
+template <typename T>
+struct RewrapTaggedPointer<Value, T>
+{
+ static Value wrap(typename IsPrivateGCThingInValue<T>::Type* thing) {
+ return JS::PrivateGCThingValue(thing);
+ }
+};
} /* namespace gc */
bool
UnmarkGrayShapeRecursively(Shape* shape);
template<typename T>
void
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -54,17 +54,17 @@ DoCallback(JS::CallbackTracer* trc, T* t
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
template type* DoCallback<type*>(JS::CallbackTracer*, type**, const char*);
JS_FOR_EACH_TRACEKIND(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
template <typename S>
struct DoCallbackFunctor : public IdentityDefaultAdaptor<S> {
template <typename T> S operator()(T* t, JS::CallbackTracer* trc, const char* name) {
- return js::gc::RewrapTaggedPointer<S, T*>::wrap(DoCallback(trc, &t, name));
+ return js::gc::RewrapTaggedPointer<S, T>::wrap(DoCallback(trc, &t, name));
}
};
template <>
Value
DoCallback<Value>(JS::CallbackTracer* trc, Value* vp, const char* name)
{
*vp = DispatchTyped(DoCallbackFunctor<Value>(), *vp, trc, name);
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -728,17 +728,17 @@ class ICGetElemNativeCompiler : public I
bool emitCheckKey(MacroAssembler& masm, Label& failure);
bool emitCallNative(MacroAssembler& masm, Register objReg);
bool emitCallScripted(MacroAssembler& masm, Register objReg);
bool generateStubCode(MacroAssembler& masm);
protected:
virtual int32_t getKey() const {
MOZ_ASSERT(static_cast<int32_t>(acctype_) <= 7);
- MOZ_ASSERT(static_cast<int32_t>(unboxedType_) <= 8);
+ MOZ_ASSERT(static_cast<int32_t>(unboxedType_) <= 15);
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(needsAtomize_) << 17) |
(static_cast<int32_t>(acctype_) << 18) |
(static_cast<int32_t>(unboxedType_) << 21) |
(static_cast<int32_t>(mozilla::IsSame<JS::Symbol*, T>::value) << 25) |
(HeapReceiverGuard::keyBits(obj_) << 26);
}
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -63,16 +63,17 @@ UNIFIED_SOURCES += [
'testNewObject.cpp',
'testNewTargetInvokeConstructor.cpp',
'testNullRoot.cpp',
'testObjectEmulatingUndefined.cpp',
'testOOM.cpp',
'testParseJSON.cpp',
'testPersistentRooted.cpp',
'testPreserveJitCode.cpp',
+ 'testPrivateGCThingValue.cpp',
'testProfileStrings.cpp',
'testPropCache.cpp',
'testRegExp.cpp',
'testResolveRecursion.cpp',
'tests.cpp',
'testSameValue.cpp',
'testSavedStacks.cpp',
'testScriptInfo.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testPrivateGCThingValue.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* vim: set ts=8 sts=4 et sw=4 tw=99:
+*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsapi.h"
+
+#include "js/HeapAPI.h"
+#include "jsapi-tests/tests.h"
+
+class TestTracer : public JS::CallbackTracer
+{
+ void onChild(const JS::GCCellPtr& thing) override {
+ if (thing.asCell() == expectedCell && thing.kind() == expectedKind)
+ found = true;
+ }
+
+ public:
+ js::gc::Cell* expectedCell;
+ JS::TraceKind expectedKind;
+ bool found;
+
+ explicit TestTracer(JSContext* cx)
+ : JS::CallbackTracer(JS_GetRuntime(cx)),
+ found(false)
+ { }
+};
+
+static const JSClass TestClass = {
+ "TestClass",
+ JSCLASS_HAS_RESERVED_SLOTS(1)
+};
+
+BEGIN_TEST(testPrivateGCThingValue)
+{
+ JS::RootedObject obj(cx, JS_NewObject(cx, &TestClass));
+ CHECK(obj);
+
+ // Make a JSScript to stick into a PrivateGCThingValue.
+ JS::RootedScript script(cx);
+ const char code[] = "'objet petit a'";
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(__FILE__, __LINE__);
+ CHECK(JS_CompileScript(cx, code, sizeof(code) - 1, options, &script));
+ JS_SetReservedSlot(obj, 0, PrivateGCThingValue(script));
+
+ TestTracer trc(cx);
+ trc.expectedCell = script;
+ trc.expectedKind = JS::TraceKind::Script;
+ JS::TraceChildren(&trc, JS::GCCellPtr(obj, JS::TraceKind::Object));
+ CHECK(trc.found);
+
+ return true;
+}
+END_TEST(testPrivateGCThingValue)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6537,8 +6537,15 @@ JS::CopyAsyncStack(JSContext* cx, JS::Ha
return true;
}
JS_PUBLIC_API(Zone*)
JS::GetObjectZone(JSObject* obj)
{
return obj->zone();
}
+
+JS_PUBLIC_API(JS::TraceKind)
+JS::GCThingTraceKind(void* thing)
+{
+ MOZ_ASSERT(thing);
+ return static_cast<js::gc::Cell*>(thing)->getTraceKind();
+}
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -582,23 +582,16 @@ js::ZoneGlobalsAreAllGray(JS::Zone* zone
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
JSObject* obj = comp->maybeGlobal();
if (!obj || !JS::ObjectIsMarkedGray(obj))
return false;
}
return true;
}
-JS_FRIEND_API(JS::TraceKind)
-js::GCThingTraceKind(void* thing)
-{
- MOZ_ASSERT(thing);
- return static_cast<js::gc::Cell*>(thing)->getTraceKind();
-}
-
JS_FRIEND_API(void)
js::VisitGrayWrapperTargets(Zone* zone, GCThingCallback callback, void* closure)
{
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
gc::Cell* thing = e.front().key().wrapped;
if (thing->isTenured() && thing->asTenured().isMarked(gc::GRAY))
callback(closure, JS::GCCellPtr(thing, thing->asTenured().getTraceKind()));
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -505,19 +505,16 @@ typedef void
(*GCThingCallback)(void* closure, JS::GCCellPtr thing);
extern JS_FRIEND_API(void)
VisitGrayWrapperTargets(JS::Zone* zone, GCThingCallback callback, void* closure);
extern JS_FRIEND_API(JSObject*)
GetWeakmapKeyDelegate(JSObject* key);
-JS_FRIEND_API(JS::TraceKind)
-GCThingTraceKind(void* thing);
-
/**
* Invoke cellCallback on every gray JS_OBJECT in the given zone.
*/
extern JS_FRIEND_API(void)
IterateGrayObjects(JS::Zone* zone, GCThingCallback cellCallback, void* data);
#ifdef JS_HAS_CTYPES
extern JS_FRIEND_API(size_t)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -7412,16 +7412,18 @@ JS::GCCellPtr::GCCellPtr(const Value& v)
: ptr(0)
{
if (v.isString())
ptr = checkedCast(v.toString(), JS::TraceKind::String);
else if (v.isObject())
ptr = checkedCast(&v.toObject(), JS::TraceKind::Object);
else if (v.isSymbol())
ptr = checkedCast(v.toSymbol(), JS::TraceKind::Symbol);
+ else if (v.isPrivateGCThing())
+ ptr = checkedCast(v.toGCThing(), v.toGCThing()->getTraceKind());
else
ptr = checkedCast(nullptr, JS::TraceKind::Null);
}
JS::TraceKind
JS::GCCellPtr::outOfLineKind() const
{
MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1140,17 +1140,17 @@ Forwarded(T* t)
{
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
MOZ_ASSERT(overlay->isForwarded());
return reinterpret_cast<T*>(overlay->forwardingAddress());
}
struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
template <typename T> inline Value operator()(T* t) {
- return js::gc::RewrapTaggedPointer<Value, T*>::wrap(Forwarded(t));
+ return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
}
};
inline Value
Forwarded(const JS::Value& value)
{
return DispatchTyped(ForwardedFunctor(), value);
}
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -294,17 +294,17 @@ CheckParticipatesInCycleCollection(JS::G
NS_IMETHODIMP
JSGCThingParticipant::Traverse(void* aPtr,
nsCycleCollectionTraversalCallback& aCb)
{
auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
mGCThingCycleCollectorGlobal));
- JS::GCCellPtr cellPtr(aPtr, js::GCThingTraceKind(aPtr));
+ JS::GCCellPtr cellPtr(aPtr, JS::GCThingTraceKind(aPtr));
runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL, cellPtr, aCb);
return NS_OK;
}
// NB: This is only used to initialize the participant in
// CycleCollectedJSRuntime. It should never be used directly.
static JSGCThingParticipant sGCThingCycleCollectorGlobal;
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3071,17 +3071,17 @@ nsCycleCollector::ScanIncrementalRoots()
// If a GCed object was added to the graph with a refcount of zero, and is
// now marked black by the GC, it was probably gray before and was exposed
// to active JS, so it may have been stored somewhere, so it needs to be
// treated as live.
if (pi->IsGrayJS() && MOZ_LIKELY(hasJSRuntime)) {
// If the object is still marked gray by the GC, nothing could have gotten
// hold of it, so it isn't an incremental root.
if (pi->mParticipant == jsParticipant) {
- JS::GCCellPtr ptr(pi->mPointer, js::GCThingTraceKind(pi->mPointer));
+ JS::GCCellPtr ptr(pi->mPointer, JS::GCThingTraceKind(pi->mPointer));
if (GCThingIsGrayCCThing(ptr)) {
continue;
}
} else if (pi->mParticipant == zoneParticipant) {
JS::Zone* zone = static_cast<JS::Zone*>(pi->mPointer);
if (js::ZoneGlobalsAreAllGray(zone)) {
continue;
}
@@ -3286,17 +3286,17 @@ nsCycleCollector::CollectWhite()
if (pinfo->IsGrayJS()) {
MOZ_ASSERT(mJSRuntime);
++numWhiteGCed;
JS::Zone* zone;
if (MOZ_UNLIKELY(pinfo->mParticipant == zoneParticipant)) {
++numWhiteJSZones;
zone = static_cast<JS::Zone*>(pinfo->mPointer);
} else {
- JS::GCCellPtr ptr(pinfo->mPointer, js::GCThingTraceKind(pinfo->mPointer));
+ JS::GCCellPtr ptr(pinfo->mPointer, JS::GCThingTraceKind(pinfo->mPointer));
zone = JS::GetTenuredGCThingZone(ptr);
}
mJSRuntime->AddZoneWaitingForGC(zone);
} else {
whiteNodes.InfallibleAppend(pinfo);
pinfo->mParticipant->Root(pinfo->mPointer);
++numWhiteNodes;
}