Bug 645416, part 3 - Symbol layout and GC support for allocating them. r=terrence.

Layout: js/src/vm/Symbol.h defines the new class JS::Symbol. JS::Symbol is the
same size as JSString on all platforms, because the allocator does not support
smaller allocations.

Allocation: Since the purpose of symbols is to serve as property keys, they are
always allocated in the atoms compartment.

We take a lock when allocating. This could probably be replaced with a
main-thread-only assertion. However, if atom allocation is not already a
bottleneck, symbol allocation probably never will be.

Symbols are given their own finalize-class in the GC. This means we allocate a
page per zone for symbols, even though they are only ever allocated in the
atoms zone. Terrence thought this could be easily fixed later. It should be; we
never touch the page, but a 32-bit virtual address space does not just have
infinite pages to spare.

A jsapi-test exercises the new symbol allocation code. A few oddities in
jsapi-tests are fixed in passing.

Discussion after review led to some new assertions about minimum object size in
AllocateObject and AllocateNonObject.

--HG--
extra : rebase_source : 45abb651d3b1b493d77a5dd0eb554f96b058c63a
This commit is contained in:
Jason Orendorff 2014-06-23 10:55:51 -05:00
parent 8ba7b56464
commit 42a78a347e
38 changed files with 402 additions and 33 deletions

View File

@ -124,6 +124,14 @@ AsCell(JSFlatString *flat)
return cell;
}
static MOZ_ALWAYS_INLINE js::gc::Cell *
AsCell(JS::Symbol *sym)
{
js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(sym);
AssertGCThingHasType(cell, JSTRACE_SYMBOL);
return cell;
}
static MOZ_ALWAYS_INLINE js::gc::Cell *
AsCell(JSScript *script)
{

View File

@ -400,6 +400,7 @@ struct RuntimeSizes
struct ZoneStats
{
#define FOR_EACH_SIZE(macro) \
macro(Other, IsLiveGCThing, symbolsGCHeap) \
macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
macro(Other, NotLiveGCThing, unusedGCThings) \
macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \

View File

@ -49,6 +49,7 @@ typedef Handle<jsid> HandleId;
typedef Handle<JSObject*> HandleObject;
typedef Handle<JSScript*> HandleScript;
typedef Handle<JSString*> HandleString;
typedef Handle<JS::Symbol*> HandleSymbol;
typedef Handle<Value> HandleValue;
typedef MutableHandle<JSFunction*> MutableHandleFunction;
@ -56,12 +57,14 @@ typedef MutableHandle<jsid> MutableHandleId;
typedef MutableHandle<JSObject*> MutableHandleObject;
typedef MutableHandle<JSScript*> MutableHandleScript;
typedef MutableHandle<JSString*> MutableHandleString;
typedef MutableHandle<JS::Symbol*> MutableHandleSymbol;
typedef MutableHandle<Value> MutableHandleValue;
typedef Rooted<JSObject*> RootedObject;
typedef Rooted<JSFunction*> RootedFunction;
typedef Rooted<JSScript*> RootedScript;
typedef Rooted<JSString*> RootedString;
typedef Rooted<JS::Symbol*> RootedSymbol;
typedef Rooted<jsid> RootedId;
typedef Rooted<JS::Value> RootedValue;
@ -70,6 +73,7 @@ typedef PersistentRooted<jsid> PersistentRootedId;
typedef PersistentRooted<JSObject*> PersistentRootedObject;
typedef PersistentRooted<JSScript*> PersistentRootedScript;
typedef PersistentRooted<JSString*> PersistentRootedString;
typedef PersistentRooted<JS::Symbol*> PersistentRootedSymbol;
typedef PersistentRooted<Value> PersistentRootedValue;
} // namespace JS

View File

@ -599,7 +599,13 @@ JSVAL_TO_GCTHING_IMPL(jsval_layout l)
static inline uint32_t
JSVAL_TRACE_KIND_IMPL(jsval_layout l)
{
return (uint32_t)(bool)JSVAL_IS_STRING_IMPL(l);
static_assert((JSVAL_TAG_STRING & 0x03) == JSTRACE_STRING,
"Value type tags must correspond with JSGCTraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == JSTRACE_SYMBOL,
"Value type tags must correspond with JSGCTraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == JSTRACE_OBJECT,
"Value type tags must correspond with JSGCTraceKinds.");
return l.s.tag & 0x03;
}
static inline bool
@ -834,7 +840,13 @@ JSVAL_TO_GCTHING_IMPL(jsval_layout l)
static inline uint32_t
JSVAL_TRACE_KIND_IMPL(jsval_layout l)
{
return (uint32_t)(bool)!(JSVAL_IS_OBJECT_IMPL(l));
static_assert((JSVAL_TAG_STRING & 0x03) == JSTRACE_STRING,
"Value type tags must correspond with JSGCTraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == JSTRACE_SYMBOL,
"Value type tags must correspond with JSGCTraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == JSTRACE_OBJECT,
"Value type tags must correspond with JSGCTraceKinds.");
return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03;
}
static inline jsval_layout

View File

@ -100,6 +100,7 @@ using JS::RootedId;
using JS::RootedObject;
using JS::RootedScript;
using JS::RootedString;
using JS::RootedSymbol;
using JS::RootedValue;
using JS::PersistentRooted;
@ -108,6 +109,7 @@ using JS::PersistentRootedId;
using JS::PersistentRootedObject;
using JS::PersistentRootedScript;
using JS::PersistentRootedString;
using JS::PersistentRootedSymbol;
using JS::PersistentRootedValue;
using JS::Handle;
@ -116,6 +118,7 @@ using JS::HandleId;
using JS::HandleObject;
using JS::HandleScript;
using JS::HandleString;
using JS::HandleSymbol;
using JS::HandleValue;
using JS::MutableHandle;
@ -124,6 +127,7 @@ using JS::MutableHandleId;
using JS::MutableHandleObject;
using JS::MutableHandleScript;
using JS::MutableHandleString;
using JS::MutableHandleSymbol;
using JS::MutableHandleValue;
using JS::NullHandleValue;

View File

@ -764,6 +764,7 @@ static const struct TraceKindPair {
{ "all", -1 },
{ "object", JSTRACE_OBJECT },
{ "string", JSTRACE_STRING },
{ "symbol", JSTRACE_SYMBOL },
};
static bool

View File

@ -11,6 +11,8 @@
#include "gc/Zone.h"
#include "vm/Symbol.h"
namespace js {
void
@ -21,6 +23,8 @@ ValueReadBarrier(const Value &value)
JSObject::readBarrier(&value.toObject());
else if (value.isString())
JSString::readBarrier(value.toString());
else if (value.isSymbol())
JS::Symbol::readBarrier(value.toSymbol());
else
JS_ASSERT(!value.isMarkable());
}

View File

@ -272,6 +272,13 @@ ShadowZoneOfStringFromAnyThread(JSString *str)
reinterpret_cast<const js::gc::Cell *>(str)->tenuredZoneFromAnyThread());
}
static inline JS::shadow::Zone *
ShadowZoneOfSymbolFromAnyThread(JS::Symbol *sym)
{
return JS::shadow::Zone::asShadowZone(
reinterpret_cast<const js::gc::Cell *>(sym)->tenuredZoneFromAnyThread());
}
MOZ_ALWAYS_INLINE JS::Zone *
ZoneOfValueFromAnyThread(const JS::Value &value)
{

View File

@ -75,6 +75,7 @@ enum AllocKind {
FINALIZE_FAT_INLINE_STRING,
FINALIZE_STRING,
FINALIZE_EXTERNAL_STRING,
FINALIZE_SYMBOL,
FINALIZE_JITCODE,
FINALIZE_LAST = FINALIZE_JITCODE
};

View File

@ -15,6 +15,7 @@
#include "vm/ArgumentsObject.h"
#include "vm/ScopeObject.h"
#include "vm/Shape.h"
#include "vm/Symbol.h"
#include "vm/TypedArrayObject.h"
#include "jscompartmentinlines.h"
@ -25,6 +26,7 @@
# include "gc/Nursery-inl.h"
#endif
#include "vm/String-inl.h"
#include "vm/Symbol-inl.h"
using namespace js;
using namespace js::gc;
@ -78,7 +80,10 @@ static inline void
PushMarkStack(GCMarker *gcmarker, Shape *thing);
static inline void
PushMarkStack(GCMarker *gcmarker, JSString *thing);
PushMarkStack(GCMarker *gcmarker, JSString *str);
static inline void
PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym);
static inline void
PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
@ -87,6 +92,7 @@ namespace js {
namespace gc {
static void MarkChildren(JSTracer *trc, JSString *str);
static void MarkChildren(JSTracer *trc, JS::Symbol *sym);
static void MarkChildren(JSTracer *trc, JSScript *script);
static void MarkChildren(JSTracer *trc, LazyScript *lazy);
static void MarkChildren(JSTracer *trc, Shape *shape);
@ -569,6 +575,7 @@ DeclMarkerImpl(String, JSString)
DeclMarkerImpl(String, JSFlatString)
DeclMarkerImpl(String, JSLinearString)
DeclMarkerImpl(String, PropertyName)
DeclMarkerImpl(Symbol, JS::Symbol)
DeclMarkerImpl(TypeObject, js::types::TypeObject)
} /* namespace gc */
@ -590,6 +597,9 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
case JSTRACE_STRING:
MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
break;
case JSTRACE_SYMBOL:
MarkInternal(trc, reinterpret_cast<JS::Symbol **>(thingp));
break;
case JSTRACE_SCRIPT:
MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
break;
@ -708,6 +718,8 @@ MarkValueInternal(JSTracer *trc, Value *v)
MarkKind(trc, &thing, v->gcKind());
if (v->isString())
v->setString((JSString *)thing);
else if (v->isSymbol())
v->setSymbol((JS::Symbol *)thing);
else
v->setObjectOrNull((JSObject *)thing);
} else {
@ -929,6 +941,10 @@ gc::IsCellAboutToBeFinalized(Cell **thingp)
JS_ASSERT((thing)->zone()->isGCMarking() || \
(rt)->isAtomsZone((thing)->zone()));
// Symbols can also be in the atoms zone.
#define JS_COMPARTMENT_ASSERT_SYM(rt, sym) \
JS_COMPARTMENT_ASSERT_STR(rt, sym)
static void
PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing)
{
@ -1199,6 +1215,16 @@ PushMarkStack(GCMarker *gcmarker, JSString *str)
ScanString(gcmarker, str);
}
static inline void
PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym)
{
JS_COMPARTMENT_ASSERT_SYM(gcmarker->runtime(), sym);
if (sym->markIfUnmarked()) {
if (JSString *desc = sym->description())
ScanString(gcmarker, desc);
}
}
void
gc::MarkChildren(JSTracer *trc, JSObject *obj)
{
@ -1214,6 +1240,12 @@ gc::MarkChildren(JSTracer *trc, JSString *str)
str->asRope().markChildren(trc);
}
static void
gc::MarkChildren(JSTracer *trc, JS::Symbol *sym)
{
sym->markChildren(trc);
}
static void
gc::MarkChildren(JSTracer *trc, JSScript *script)
{
@ -1375,6 +1407,10 @@ gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
PushArenaTyped<JSString>(gcmarker, aheader);
break;
case JSTRACE_SYMBOL:
PushArenaTyped<JS::Symbol>(gcmarker, aheader);
break;
case JSTRACE_SCRIPT:
PushArenaTyped<JSScript>(gcmarker, aheader);
break;
@ -1705,6 +1741,10 @@ js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
MarkChildren(trc, static_cast<JSString *>(thing));
break;
case JSTRACE_SYMBOL:
MarkChildren(trc, static_cast<JS::Symbol *>(thing));
break;
case JSTRACE_SCRIPT:
MarkChildren(trc, static_cast<JSScript *>(thing));
break;

View File

@ -120,6 +120,7 @@ DeclMarker(String, JSString)
DeclMarker(String, JSFlatString)
DeclMarker(String, JSLinearString)
DeclMarker(String, PropertyName)
DeclMarker(Symbol, JS::Symbol)
DeclMarker(TypeObject, types::TypeObject)
#undef DeclMarker
@ -388,7 +389,10 @@ TraceKind(const Value &v)
JS_ASSERT(v.isMarkable());
if (v.isObject())
return JSTRACE_OBJECT;
return JSTRACE_STRING;
if (v.isString())
return JSTRACE_STRING;
JS_ASSERT(v.isSymbol());
return JSTRACE_SYMBOL;
}
inline JSGCTraceKind

View File

@ -126,6 +126,7 @@ MarkExactStackRoots(T context, JSTracer *trc)
MarkExactStackRootsForType<BaseShape *, MarkBaseShapeRoot>(context, trc, "exact-baseshape");
MarkExactStackRootsForType<types::TypeObject *, MarkTypeObjectRoot>(context, trc, "exact-typeobject");
MarkExactStackRootsForType<JSString *, MarkStringRoot>(context, trc, "exact-string");
MarkExactStackRootsForType<JS::Symbol *, MarkSymbolRoot>(context, trc, "exact-symbol");
MarkExactStackRootsForType<jit::JitCode *, MarkJitCodeRoot>(context, trc, "exact-jitcode");
MarkExactStackRootsForType<JSScript *, MarkScriptRoot>(context, trc, "exact-script");
MarkExactStackRootsForType<LazyScript *, MarkLazyScriptRoot>(context, trc, "exact-lazy-script");

View File

@ -19,6 +19,8 @@
#include "gc/GCInternals.h"
#include "gc/Marking.h"
#include "vm/Symbol.h"
#include "jsgcinlines.h"
using namespace js;
@ -152,6 +154,10 @@ JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
: "string";
break;
case JSTRACE_SYMBOL:
name = "symbol";
break;
case JSTRACE_SCRIPT:
name = "script";
break;
@ -222,9 +228,26 @@ JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
bufsize -= n;
PutEscapedString(buf, bufsize, &str->asLinear(), 0);
}
else
} else {
JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
}
break;
}
case JSTRACE_SYMBOL:
{
JS::Symbol *sym = static_cast<JS::Symbol *>(thing);
if (JSString *desc = sym->description()) {
if (desc->isLinear()) {
*buf++ = ' ';
bufsize--;
PutEscapedString(buf, bufsize, &desc->asLinear(), 0);
} else {
JS_snprintf(buf, bufsize, "<nonlinear desc>");
}
} else {
JS_snprintf(buf, bufsize, "<null>");
}
break;
}

View File

@ -89,7 +89,19 @@ tests.h:
false, failing the test.
This is like CHECK(sameValue(a, b)) but with a more detailed error
message. See sameValue below.
message on failure. See sameValue below.
CHECK_EQUAL(const T &a, const U &b);
CHECK(a == b), but with a more detailed error message.
CHECK_NULL(const T *ptr);
CHECK(ptr == nullptr), but with a more detailed error message.
(This is here because CHECK_EQUAL(ptr, nullptr) fails to compile on GCC
2.5 and before.)
bool knownFail;

View File

@ -68,6 +68,7 @@ UNIFIED_SOURCES += [
'testSourcePolicy.cpp',
'testStringBuffer.cpp',
'testStructuredClone.cpp',
'testSymbol.cpp',
'testToIntWidth.cpp',
'testTrap.cpp',
'testTypedArrays.cpp',

View File

@ -83,7 +83,7 @@ doResolve(JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
{
CHECK_EQUAL(resolveExitCount, 0);
AutoIncrCounters incr(this);
CHECK_EQUAL(obj, obj1 || obj == obj2);
CHECK(obj == obj1 || obj == obj2);
CHECK(JSID_IS_STRING(id));

View File

@ -0,0 +1,28 @@
/* 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-tests/tests.h"
BEGIN_TEST(testSymbol_New)
{
using namespace JS;
RootedString desc(cx, nullptr);
RootedSymbol sym1(cx);
CHECK(sym1 = NewSymbol(cx, desc));
CHECK_NULL(GetSymbolDescription(sym1));
RootedValue v(cx, SymbolValue(sym1));
CHECK_EQUAL(JS_TypeOfValue(cx, v), JSTYPE_SYMBOL);
RootedSymbol sym2(cx);
CHECK(sym2 = NewSymbol(cx, desc));
CHECK(sym1 != sym2);
CHECK(desc = JS_NewStringCopyZ(cx, "ponies"));
CHECK(sym2 = NewSymbol(cx, desc));
CHECK_SAME(StringValue(GetSymbolDescription(sym2)), StringValue(desc));
return true;
}
END_TEST(testSymbol_New)

View File

@ -182,7 +182,7 @@ JSObject *newCCW(HandleObject sourceZone, HandleObject destZone)
RootedObject object(cx);
{
JSAutoCompartment ac(cx, destZone);
object = JS_NewObject(cx, nullptr, NullPtr(), NullPtr());
object = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
if (!object)
return nullptr;
}

View File

@ -155,8 +155,11 @@ class JSAPITest
return JSAPITestString(JS_VersionToString(v));
}
template<typename T>
bool checkEqual(const T &actual, const T &expected,
// Note that in some still-supported GCC versions (we think anything before
// GCC 4.6), this template does not work when the second argument is
// nullptr. It infers type U = long int. Use CHECK_NULL instead.
template <typename T, typename U>
bool checkEqual(const T &actual, const U &expected,
const char *actualExpr, const char *expectedExpr,
const char *filename, int lineno) {
return (actual == expected) ||
@ -165,24 +168,27 @@ class JSAPITest
", got (" + actualExpr + ") = " + toSource(actual), filename, lineno);
}
// There are many cases where the static types of 'actual' and 'expected'
// are not identical, and C++ is understandably cautious about automatic
// coercions. So catch those cases and forcibly coerce, then use the
// identical-type specialization. This may do bad things if the types are
// actually *not* compatible.
template<typename T, typename U>
bool checkEqual(const T &actual, const U &expected,
const char *actualExpr, const char *expectedExpr,
const char *filename, int lineno) {
return checkEqual(U(actual), expected, actualExpr, expectedExpr, filename, lineno);
}
#define CHECK_EQUAL(actual, expected) \
do { \
if (!checkEqual(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
return false; \
} while (false)
template <typename T>
bool checkNull(const T *actual, const char *actualExpr,
const char *filename, int lineno) {
return (actual == nullptr) ||
fail(JSAPITestString("CHECK_NULL failed: expected nullptr, got (") +
actualExpr + ") = " + toSource(actual),
filename, lineno);
}
#define CHECK_NULL(actual) \
do { \
if (!checkNull(actual, #actual, __FILE__, __LINE__)) \
return false; \
} while (false)
bool checkSame(jsval actualArg, jsval expectedArg,
const char *actualExpr, const char *expectedExpr,
const char *filename, int lineno) {

View File

@ -76,6 +76,7 @@
#include "vm/SharedArrayObject.h"
#include "vm/StopIterationObject.h"
#include "vm/StringBuffer.h"
#include "vm/Symbol.h"
#include "vm/TypedArrayObject.h"
#include "vm/WeakMapObject.h"
#include "vm/WrapperObject.h"
@ -5616,6 +5617,23 @@ JS_EncodeStringToBuffer(JSContext *cx, JSString *str, char *buffer, size_t lengt
return necessaryLength;
}
JS_PUBLIC_API(JS::Symbol *)
JS::NewSymbol(JSContext *cx, HandleString description)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
if (description)
assertSameCompartment(cx, description);
return Symbol::new_(cx, description);
}
JS_PUBLIC_API(JSString *)
JS::GetSymbolDescription(HandleSymbol symbol)
{
return symbol->description();
}
JS_PUBLIC_API(bool)
JS_Stringify(JSContext *cx, MutableHandleValue vp, HandleObject replacer,
HandleValue space, JSONWriteCallback callback, void *data)

View File

@ -4351,6 +4351,34 @@ AddonIdOfObject(JSObject *obj);
} // namespace JS
/************************************************************************/
/*
* 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(Symbol *)
NewSymbol(JSContext *cx, HandleString description);
/*
* Get the [[Description]] attribute of the given symbol.
*
* This function is infallible. If it returns null, that means the symbol's
* [[Description]] is undefined.
*/
JS_PUBLIC_API(JSString *)
GetSymbolDescription(HandleSymbol symbol);
} /* namespace JS */
/************************************************************************/
/*
* JSON functions

View File

@ -27,6 +27,7 @@
#include "jsobjinlines.h"
#include "vm/String-inl.h"
#include "vm/Symbol-inl.h"
using namespace js;
using namespace js::gc;
@ -380,6 +381,9 @@ AtomizeAndCopyChars(ExclusiveContext *cx, const CharT *tbchars, size_t length, I
JSFlatString *flat = js_NewStringCopyN<NoGC>(cx, tbchars, length);
if (!flat) {
// Grudgingly forgo last-ditch GC. The alternative would be to release
// the lock, manually GC here, and retry from the top. If you fix this,
// please also fix or comment the similar case in Symbol::new_.
js_ReportOutOfMemory(cx);
return nullptr;
}

View File

@ -18,6 +18,7 @@
#include "vm/HelperThreads.h"
#include "vm/Interpreter.h"
#include "vm/ProxyObject.h"
#include "vm/Symbol.h"
#include "gc/ForkJoinNursery-inl.h"

View File

@ -11,6 +11,8 @@
#include "gc/Barrier.h"
#include "jscntxtinlines.h"
inline void
JSCompartment::initGlobal(js::GlobalObject &global)
{

View File

@ -980,6 +980,8 @@ JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
JSObject::writeBarrierPre(static_cast<JSObject*>(cell));
else if (kind == JSTRACE_STRING)
JSString::writeBarrierPre(static_cast<JSString*>(cell));
else if (kind == JSTRACE_SYMBOL)
JS::Symbol::writeBarrierPre(static_cast<JS::Symbol*>(cell));
else if (kind == JSTRACE_SCRIPT)
JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
else if (kind == JSTRACE_LAZY_SCRIPT)

View File

@ -213,6 +213,7 @@
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
#include "vm/String.h"
#include "vm/Symbol.h"
#include "vm/TraceLogging.h"
#include "vm/WrapperObject.h"
@ -277,6 +278,7 @@ const uint32_t Arena::ThingSizes[] = {
sizeof(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */
sizeof(JSString), /* FINALIZE_STRING */
sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */
sizeof(JS::Symbol), /* FINALIZE_SYMBOL */
sizeof(jit::JitCode), /* FINALIZE_JITCODE */
};
@ -303,6 +305,7 @@ const uint32_t Arena::FirstThingOffsets[] = {
OFFSET(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */
OFFSET(JSString), /* FINALIZE_STRING */
OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */
OFFSET(JS::Symbol), /* FINALIZE_SYMBOL */
OFFSET(jit::JitCode), /* FINALIZE_JITCODE */
};
@ -314,6 +317,7 @@ js::gc::TraceKindAsAscii(JSGCTraceKind kind)
switch(kind) {
case JSTRACE_OBJECT: return "JSTRACE_OBJECT";
case JSTRACE_STRING: return "JSTRACE_STRING";
case JSTRACE_SYMBOL: return "JSTRACE_SYMBOL";
case JSTRACE_SCRIPT: return "JSTRACE_SCRIPT";
case JSTRACE_LAZY_SCRIPT: return "JSTRACE_SCRIPT";
case JSTRACE_JITCODE: return "JSTRACE_JITCODE";
@ -373,9 +377,10 @@ static const AllocKind BackgroundPhaseObjects[] = {
FINALIZE_OBJECT16_BACKGROUND
};
static const AllocKind BackgroundPhaseStrings[] = {
static const AllocKind BackgroundPhaseStringsAndSymbols[] = {
FINALIZE_FAT_INLINE_STRING,
FINALIZE_STRING
FINALIZE_STRING,
FINALIZE_SYMBOL
};
static const AllocKind BackgroundPhaseShapes[] = {
@ -386,14 +391,14 @@ static const AllocKind BackgroundPhaseShapes[] = {
static const AllocKind * const BackgroundPhases[] = {
BackgroundPhaseObjects,
BackgroundPhaseStrings,
BackgroundPhaseStringsAndSymbols,
BackgroundPhaseShapes
};
static const int BackgroundPhaseCount = sizeof(BackgroundPhases) / sizeof(AllocKind*);
static const int BackgroundPhaseLength[] = {
sizeof(BackgroundPhaseObjects) / sizeof(AllocKind),
sizeof(BackgroundPhaseStrings) / sizeof(AllocKind),
sizeof(BackgroundPhaseStringsAndSymbols) / sizeof(AllocKind),
sizeof(BackgroundPhaseShapes) / sizeof(AllocKind)
};
@ -598,6 +603,8 @@ FinalizeArenas(FreeOp *fop,
return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget);
case FINALIZE_EXTERNAL_STRING:
return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget);
case FINALIZE_SYMBOL:
return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget);
case FINALIZE_JITCODE:
#ifdef JS_ION
{
@ -2007,12 +2014,13 @@ ArenaLists::queueObjectsForSweep(FreeOp *fop)
}
void
ArenaLists::queueStringsForSweep(FreeOp *fop)
ArenaLists::queueStringsAndSymbolsForSweep(FreeOp *fop)
{
gcstats::AutoPhase ap(fop->runtime()->gc.stats, gcstats::PHASE_SWEEP_STRING);
queueForBackgroundSweep(fop, FINALIZE_FAT_INLINE_STRING);
queueForBackgroundSweep(fop, FINALIZE_STRING);
queueForBackgroundSweep(fop, FINALIZE_SYMBOL);
queueForForegroundSweep(fop, FINALIZE_EXTERNAL_STRING);
}
@ -4135,7 +4143,7 @@ GCRuntime::beginSweepingZoneGroup()
}
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
gcstats::AutoSCC scc(stats, zoneGroupIndex);
zone->allocator.arenas.queueStringsForSweep(&fop);
zone->allocator.arenas.queueStringsAndSymbolsForSweep(&fop);
}
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
gcstats::AutoSCC scc(stats, zoneGroupIndex);

View File

@ -25,6 +25,10 @@ struct JSCompartment;
class JSFlatString;
class JSLinearString;
namespace JS {
class Symbol;
} /* namespace JS */
namespace js {
class ArgumentsObject;
@ -121,6 +125,7 @@ MapAllocToTraceKind(AllocKind kind)
JSTRACE_STRING, /* FINALIZE_FAT_INLINE_STRING */
JSTRACE_STRING, /* FINALIZE_STRING */
JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */
JSTRACE_SYMBOL, /* FINALIZE_SYMBOL */
JSTRACE_JITCODE, /* FINALIZE_JITCODE */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
@ -148,6 +153,7 @@ template <> struct MapTypeToTraceKind<JSAtom> { static const JSGCTrace
template <> struct MapTypeToTraceKind<JSString> { static const JSGCTraceKind kind = JSTRACE_STRING; };
template <> struct MapTypeToTraceKind<JSFlatString> { static const JSGCTraceKind kind = JSTRACE_STRING; };
template <> struct MapTypeToTraceKind<JSLinearString> { static const JSGCTraceKind kind = JSTRACE_STRING; };
template <> struct MapTypeToTraceKind<JS::Symbol> { static const JSGCTraceKind kind = JSTRACE_SYMBOL; };
template <> struct MapTypeToTraceKind<PropertyName> { static const JSGCTraceKind kind = JSTRACE_STRING; };
template <> struct MapTypeToTraceKind<jit::JitCode> { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
@ -165,6 +171,7 @@ template <> struct MapTypeToFinalizeKind<types::TypeObject> { static const Alloc
template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; };
template <> struct MapTypeToFinalizeKind<JSString> { static const AllocKind kind = FINALIZE_STRING; };
template <> struct MapTypeToFinalizeKind<JSExternalString> { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; };
template <> struct MapTypeToFinalizeKind<JS::Symbol> { static const AllocKind kind = FINALIZE_SYMBOL; };
template <> struct MapTypeToFinalizeKind<jit::JitCode> { static const AllocKind kind = FINALIZE_JITCODE; };
#if defined(JSGC_GENERATIONAL) || defined(DEBUG)
@ -193,6 +200,7 @@ IsNurseryAllocable(AllocKind kind)
false, /* FINALIZE_FAT_INLINE_STRING */
false, /* FINALIZE_STRING */
false, /* FINALIZE_EXTERNAL_STRING */
false, /* FINALIZE_SYMBOL */
false, /* FINALIZE_JITCODE */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
@ -229,6 +237,7 @@ IsFJNurseryAllocable(AllocKind kind)
false, /* FINALIZE_FAT_INLINE_STRING */
false, /* FINALIZE_STRING */
false, /* FINALIZE_EXTERNAL_STRING */
false, /* FINALIZE_SYMBOL */
false, /* FINALIZE_JITCODE */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
@ -261,6 +270,7 @@ IsBackgroundFinalized(AllocKind kind)
true, /* FINALIZE_FAT_INLINE_STRING */
true, /* FINALIZE_STRING */
false, /* FINALIZE_EXTERNAL_STRING */
true, /* FINALIZE_SYMBOL */
false, /* FINALIZE_JITCODE */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == FINALIZE_LIMIT);
@ -799,7 +809,7 @@ class ArenaLists
}
void queueObjectsForSweep(FreeOp *fop);
void queueStringsForSweep(FreeOp *fop);
void queueStringsAndSymbolsForSweep(FreeOp *fop);
void queueShapesForSweep(FreeOp *fop);
void queueScriptsForSweep(FreeOp *fop);
void queueJitCodeForSweep(FreeOp *fop);

View File

@ -547,6 +547,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
JS_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
kind == FINALIZE_STRING ||
kind == FINALIZE_FAT_INLINE_STRING ||
kind == FINALIZE_SYMBOL ||
kind == FINALIZE_JITCODE);
JS_ASSERT(!rt->isHeapBusy());
JS_ASSERT(rt->gc.isAllocAllowed());
@ -606,6 +607,10 @@ AllocateObject(ThreadSafeContext *cx, AllocKind kind, size_t nDynamicSlots, Init
size_t thingSize = Arena::thingSize(kind);
JS_ASSERT(thingSize == Arena::thingSize(kind));
JS_ASSERT(thingSize >= sizeof(JSObject));
static_assert(sizeof(JSObject) >= CellSize,
"All allocations must be at least the allocator-imposed minimum size.");
if (!CheckAllocatorState<allowGC>(cx, kind))
return nullptr;
@ -652,6 +657,9 @@ template <typename T, AllowGC allowGC>
inline T *
AllocateNonObject(ThreadSafeContext *cx)
{
static_assert(sizeof(T) >= CellSize,
"All allocations must be at least the allocator-imposed minimum size.");
AllocKind kind = MapTypeToFinalizeKind<T>::kind;
size_t thingSize = sizeof(T);

View File

@ -71,6 +71,7 @@ enum JSType {
JSTYPE_NUMBER, /* number */
JSTYPE_BOOLEAN, /* boolean */
JSTYPE_NULL, /* null */
JSTYPE_SYMBOL, /* symbol */
JSTYPE_LIMIT
};
@ -104,6 +105,7 @@ enum JSIterateOp {
enum JSGCTraceKind {
JSTRACE_OBJECT,
JSTRACE_STRING,
JSTRACE_SYMBOL,
JSTRACE_SCRIPT,
/*
@ -343,6 +345,7 @@ enum ThingRootKind
THING_ROOT_BASE_SHAPE,
THING_ROOT_TYPE_OBJECT,
THING_ROOT_STRING,
THING_ROOT_SYMBOL,
THING_ROOT_JIT_CODE,
THING_ROOT_SCRIPT,
THING_ROOT_LAZY_SCRIPT,
@ -386,6 +389,7 @@ template <> struct RootKind<JSObject *> : SpecificRootKind<JSObject *, THING_ROO
template <> struct RootKind<JSFlatString *> : SpecificRootKind<JSFlatString *, THING_ROOT_STRING> {};
template <> struct RootKind<JSFunction *> : SpecificRootKind<JSFunction *, THING_ROOT_OBJECT> {};
template <> struct RootKind<JSString *> : SpecificRootKind<JSString *, THING_ROOT_STRING> {};
template <> struct RootKind<JS::Symbol *> : SpecificRootKind<JS::Symbol *, THING_ROOT_SYMBOL> {};
template <> struct RootKind<JSScript *> : SpecificRootKind<JSScript *, THING_ROOT_SCRIPT> {};
template <> struct RootKind<jsid> : SpecificRootKind<jsid, THING_ROOT_ID> {};
template <> struct RootKind<JS::Value> : SpecificRootKind<JS::Value, THING_ROOT_VALUE> {};

View File

@ -197,6 +197,7 @@ UNIFIED_SOURCES += [
'vm/String.cpp',
'vm/StringBuffer.cpp',
'vm/StructuredClone.cpp',
'vm/Symbol.cpp',
'vm/ThreadPool.cpp',
'vm/TypedArrayObject.cpp',
'vm/UbiNode.cpp',

View File

@ -209,6 +209,7 @@
macro(string, string, "string") \
macro(number, number, "number") \
macro(boolean, boolean, "boolean") \
macro(null, null, "null")
macro(null, null, "null") \
macro(symbol, symbol, "symbol")
#endif /* vm_CommonPropertyNames_h */

View File

@ -813,8 +813,10 @@ js::TypeOfValue(const Value &v)
return JSTYPE_VOID;
if (v.isObject())
return TypeOfObject(&v.toObject());
JS_ASSERT(v.isBoolean());
return JSTYPE_BOOLEAN;
if (v.isBoolean())
return JSTYPE_BOOLEAN;
JS_ASSERT(v.isSymbol());
return JSTYPE_SYMBOL;
}
/*

View File

@ -20,6 +20,7 @@
#include "vm/Runtime.h"
#include "vm/Shape.h"
#include "vm/String.h"
#include "vm/Symbol.h"
#include "vm/WrapperObject.h"
using mozilla::DebugOnly;
@ -400,6 +401,10 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
break;
}
case JSTRACE_SYMBOL:
zStats->symbolsGCHeap += thingSize;
break;
case JSTRACE_SHAPE: {
Shape *shape = static_cast<Shape *>(thing);
CompartmentStats *cStats = GetCompartmentStats(shape->compartment());

25
js/src/vm/Symbol-inl.h Normal file
View File

@ -0,0 +1,25 @@
/* -*- 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/. */
#ifndef vm_Symbol_inl_h
#define vm_Symbol_inl_h
#include "vm/Symbol.h"
#include "gc/Marking.h"
#include "js/RootingAPI.h"
#include "jsgcinlines.h"
inline void
JS::Symbol::markChildren(JSTracer *trc)
{
if (description_)
MarkStringUnbarriered(trc, &description_, "description");
}
#endif /* vm_Symbol_inl_h */

42
js/src/vm/Symbol.cpp Normal file
View File

@ -0,0 +1,42 @@
/* -*- 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 "vm/Symbol.h"
#include "jscntxt.h"
#include "jscompartment.h"
#include "gc/Rooting.h"
#include "jscompartmentinlines.h"
#include "jsgcinlines.h"
using JS::Symbol;
using namespace js;
Symbol *
Symbol::new_(ExclusiveContext *cx, JSString *description)
{
RootedAtom atom(cx);
if (description) {
atom = AtomizeString(cx, description);
if (!atom)
return nullptr;
}
// Lock to allocate. If symbol allocation becomes a bottleneck, this can
// probably be replaced with an assertion that we're on the main thread.
AutoLockForExclusiveAccess lock(cx);
AutoCompartment ac(cx, cx->atomsCompartment());
// Following AtomizeAndCopyChars, we grudgingly forgo last-ditch GC here.
Symbol *p = gc::AllocateNonObject<Symbol, NoGC>(cx);
if (!p) {
js_ReportOutOfMemory(cx);
return nullptr;
}
return new (p) Symbol(atom);
}

46
js/src/vm/Symbol.h Normal file
View File

@ -0,0 +1,46 @@
/* -*- 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/. */
#ifndef vm_Symbol_h
#define vm_Symbol_h
#include "mozilla/Attributes.h"
#include "gc/Barrier.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
namespace JS {
class Symbol : public js::gc::BarrieredCell<Symbol>
{
private:
uint32_t unused1_; // This field will be used before long.
JSAtom *description_;
// The minimum allocation size is sizeof(JSString): 16 bytes on 32-bit
// architectures and 24 bytes on 64-bit. 8 bytes of padding makes Symbol
// the minimum size on both.
uint64_t unused2_;
explicit Symbol(JSAtom *desc) : description_(desc) {}
Symbol(const Symbol &) MOZ_DELETE;
void operator=(const Symbol &) MOZ_DELETE;
public:
static Symbol *new_(js::ExclusiveContext *cx, JSString *description);
JSAtom *description() const { return description_; }
static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SYMBOL; }
inline void markChildren(JSTracer *trc);
inline void finalize(js::FreeOp *) {}
};
} /* namespace JS */
#endif /* vm_Symbol_h */

View File

@ -1909,6 +1909,10 @@ ReportZoneStats(const JS::ZoneStats &zStats,
MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("symbols/gc-heap"),
zStats.symbolsGCHeap,
"Symbols.");
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
zStats.gcHeapArenaAdmin,
"Bookkeeping information and alignment padding within GC arenas.");

View File

@ -574,6 +574,7 @@ CycleCollectedJSRuntime::DescribeGCThing(bool aIsMarked, void* aThing,
static const char trace_types[][11] = {
"Object",
"String",
"Symbol",
"Script",
"LazyScript",
"IonCode",