ES6 symbols: implement name property

Adds string-valued name property to symbols, and uses it for pretty-printing.

Requires allocating symbols in pointer space, with a custom iterator to skip the unboxed hash.

R=mstarzinger@chromium.org
BUG=

Review URL: https://codereview.chromium.org/12459026

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14053 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2013-03-22 16:51:28 +00:00
parent 71c1f69b06
commit 2657e432e4
16 changed files with 171 additions and 26 deletions

View File

@ -2215,7 +2215,7 @@ function Stringify(x, depth) {
case "string":
return "\"" + x.toString() + "\"";
case "symbol":
return "Symbol()";
return "Symbol(" + (x.name ? Stringify(x.name, depth) : "") + ")"
case "object":
if (x === null) return "null";
if (x.constructor && x.constructor.name === "Array") {

View File

@ -399,7 +399,9 @@ AllocationSpace Heap::TargetSpaceId(InstanceType type) {
ASSERT(type != ODDBALL_TYPE);
ASSERT(type != JS_GLOBAL_PROPERTY_CELL_TYPE);
if (type < FIRST_NONSTRING_TYPE) {
if (type <= LAST_NAME_TYPE) {
if (type == SYMBOL_TYPE) return OLD_POINTER_SPACE;
ASSERT(type < FIRST_NONSTRING_TYPE);
// There are four string representations: sequential strings, external
// strings, cons strings, and sliced strings.
// Only the latter two contain non-map-word pointers to heap objects.

View File

@ -1779,6 +1779,10 @@ class ScavengingVisitor : public StaticVisitorBase {
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<SlicedString::kSize>);
table_.Register(kVisitSymbol,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<Symbol::kSize>);
table_.Register(kVisitSharedFunctionInfo,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<SharedFunctionInfo::kSize>);
@ -5427,10 +5431,10 @@ MaybeObject* Heap::AllocateHashTable(int length, PretenureFlag pretenure) {
MaybeObject* Heap::AllocateSymbol(PretenureFlag pretenure) {
// Statically ensure that it is safe to allocate symbols in paged spaces.
STATIC_ASSERT(Symbol::kSize <= Page::kNonCodeObjectAreaSize);
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
AllocationSpace space = pretenure == TENURED ? OLD_POINTER_SPACE : NEW_SPACE;
Object* result;
MaybeObject* maybe = AllocateRaw(Symbol::kSize, space, OLD_DATA_SPACE);
MaybeObject* maybe = AllocateRaw(Symbol::kSize, space, OLD_POINTER_SPACE);
if (!maybe->ToObject(&result)) return maybe;
HeapObject::cast(result)->set_map_no_write_barrier(symbol_map());
@ -5446,6 +5450,7 @@ MaybeObject* Heap::AllocateSymbol(PretenureFlag pretenure) {
Symbol::cast(result)->set_hash_field(
Name::kIsNotArrayIndexMask | (hash << Name::kHashShift));
Symbol::cast(result)->set_name(undefined_value());
ASSERT(result->IsSymbol());
return result;

View File

@ -644,7 +644,17 @@ void Logger::ApiNamedSecurityCheck(Object* key) {
String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
ApiEvent("api,check-security,\"%s\"\n", *str);
} else if (key->IsSymbol()) {
ApiEvent("api,check-security,symbol(hash %x)\n", Symbol::cast(key)->Hash());
Symbol* symbol = Symbol::cast(key);
if (symbol->name()->IsUndefined()) {
ApiEvent("api,check-security,symbol(hash %x)\n",
Symbol::cast(key)->Hash());
} else {
SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
ApiEvent("api,check-security,symbol(\"%s\" hash %x)\n",
*str,
Symbol::cast(key)->Hash());
}
} else if (key->IsUndefined()) {
ApiEvent("api,check-security,undefined\n");
} else {
@ -833,8 +843,16 @@ void Logger::ApiNamedPropertyAccess(const char* tag,
String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
ApiEvent("api,%s,\"%s\",\"%s\"\n", tag, *class_name, *property_name);
} else {
uint32_t hash = Symbol::cast(name)->Hash();
ApiEvent("api,%s,\"%s\",symbol(hash %x)\n", tag, *class_name, hash);
Symbol* symbol = Symbol::cast(name);
uint32_t hash = symbol->Hash();
if (symbol->name()->IsUndefined()) {
ApiEvent("api,%s,\"%s\",symbol(hash %x)\n", tag, *class_name, hash);
} else {
SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
ApiEvent("api,%s,\"%s\",symbol(\"%s\" hash %x)\n",
tag, *class_name, *str, hash);
}
}
}
@ -902,7 +920,14 @@ void Logger::CallbackEventInternal(const char* prefix, Name* name,
String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
msg.Append(",1,\"%s%s\"", prefix, *str);
} else {
msg.Append(",1,symbol(hash %x)", prefix, Name::cast(name)->Hash());
Symbol* symbol = Symbol::cast(name);
if (symbol->name()->IsUndefined()) {
msg.Append(",1,symbol(hash %x)", prefix, symbol->Hash());
} else {
SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
msg.Append(",1,symbol(\"%s\" hash %x)", prefix, *str, symbol->Hash());
}
}
msg.Append('\n');
msg.WriteToLogFile();
@ -978,8 +1003,15 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
if (name->IsString()) {
name_buffer_->AppendString(String::cast(name));
} else {
name_buffer_->AppendBytes("symbol(hash ");
name_buffer_->AppendHex(Name::cast(name)->Hash());
Symbol* symbol = Symbol::cast(name);
name_buffer_->AppendBytes("symbol(");
if (!symbol->name()->IsUndefined()) {
name_buffer_->AppendBytes("\"");
name_buffer_->AppendString(String::cast(symbol->name()));
name_buffer_->AppendBytes("\" ");
}
name_buffer_->AppendBytes("hash ");
name_buffer_->AppendHex(symbol->Hash());
name_buffer_->AppendByte(')');
}
}
@ -1006,7 +1038,14 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
msg.AppendDetailed(String::cast(name), false);
msg.Append('"');
} else {
msg.Append("symbol(hash %x)", Name::cast(name)->Hash());
Symbol* symbol = Symbol::cast(name);
msg.Append("symbol(");
if (!symbol->name()->IsUndefined()) {
msg.Append("\"");
msg.AppendDetailed(String::cast(symbol->name()), false);
msg.Append("\" ");
}
msg.Append("hash %x)", symbol->Hash());
}
msg.Append('\n');
msg.WriteToLogFile();
@ -1036,8 +1075,15 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
if (name->IsString()) {
name_buffer_->AppendString(String::cast(name));
} else {
name_buffer_->AppendBytes("symbol(hash ");
name_buffer_->AppendHex(Name::cast(name)->Hash());
Symbol* symbol = Symbol::cast(name);
name_buffer_->AppendBytes("symbol(");
if (!symbol->name()->IsUndefined()) {
name_buffer_->AppendBytes("\"");
name_buffer_->AppendString(String::cast(symbol->name()));
name_buffer_->AppendBytes("\" ");
}
name_buffer_->AppendBytes("hash ");
name_buffer_->AppendHex(symbol->Hash());
name_buffer_->AppendByte(')');
}
}
@ -1073,7 +1119,14 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
msg.Append("\"%s\"", *str);
} else {
msg.Append("symbol(hash %x)", Name::cast(name)->Hash());
Symbol* symbol = Symbol::cast(name);
msg.Append("symbol(");
if (!symbol->name()->IsUndefined()) {
msg.Append("\"");
msg.AppendDetailed(String::cast(symbol->name()), false);
msg.Append("\" ");
}
msg.Append("hash %x)", symbol->Hash());
}
msg.Append(',');
msg.AppendAddress(shared->address());
@ -1138,7 +1191,14 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
String::cast(source)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
msg.Append("%s", *sourcestr);
} else {
msg.Append("symbol(hash %x)", Name::cast(source)->Hash());
Symbol* symbol = Symbol::cast(source);
msg.Append("symbol(");
if (!symbol->name()->IsUndefined()) {
msg.Append("\"");
msg.AppendDetailed(String::cast(symbol->name()), false);
msg.Append("\" ");
}
msg.Append("hash %x)", symbol->Hash());
}
msg.Append(":%d\",", line);
msg.AppendAddress(shared->address());
@ -1358,7 +1418,14 @@ void Logger::SuspectReadEvent(Name* name, Object* obj) {
msg.Append(String::cast(name));
msg.Append('"');
} else {
msg.Append("symbol(hash %x)", Name::cast(name)->Hash());
Symbol* symbol = Symbol::cast(name);
msg.Append("symbol(");
if (!symbol->name()->IsUndefined()) {
msg.Append("\"");
msg.AppendDetailed(String::cast(symbol->name()), false);
msg.Append("\" ");
}
msg.Append("hash %x)", symbol->Hash());
}
msg.Append('\n');
msg.WriteToLogFile();

View File

@ -220,6 +220,7 @@ void Symbol::SymbolVerify() {
CHECK(IsSymbol());
CHECK(HasHashCode());
CHECK_GT(Hash(), 0);
CHECK(name()->IsUndefined() || name()->IsString());
}

View File

@ -2505,6 +2505,9 @@ bool Name::Equals(Name* other) {
}
ACCESSORS(Symbol, name, Object, kNameOffset)
bool String::Equals(String* other) {
if (other == this) return true;
if (this->IsInternalizedString() && other->IsInternalizedString()) {

View File

@ -552,6 +552,9 @@ static const char* TypeToString(InstanceType type) {
void Symbol::SymbolPrint(FILE* out) {
HeapObject::PrintHeader(out, "Symbol");
PrintF(out, " - hash: %d\n", Hash());
PrintF(out, " - name: ");
name()->ShortPrint();
PrintF(out, "\n");
}

View File

@ -49,6 +49,11 @@ void StaticNewSpaceVisitor<StaticVisitor>::Initialize() {
SlicedString::BodyDescriptor,
int>::Visit);
table_.Register(kVisitSymbol,
&FixedBodyVisitor<StaticVisitor,
Symbol::BodyDescriptor,
int>::Visit);
table_.Register(kVisitFixedArray,
&FlexibleBodyVisitor<StaticVisitor,
FixedArray::BodyDescriptor,
@ -110,6 +115,11 @@ void StaticMarkingVisitor<StaticVisitor>::Initialize() {
SlicedString::BodyDescriptor,
void>::Visit);
table_.Register(kVisitSymbol,
&FixedBodyVisitor<StaticVisitor,
Symbol::BodyDescriptor,
void>::Visit);
table_.Register(kVisitFixedArray, &FixedArrayVisitor::Visit);
table_.Register(kVisitFixedDoubleArray, &DataObjectVisitor::Visit);

View File

@ -129,9 +129,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
Foreign::kSize);
case SYMBOL_TYPE:
return GetVisitorIdForSize(kVisitDataObject,
kVisitDataObjectGeneric,
Symbol::kSize);
return kVisitSymbol;
case FILLER_TYPE:
return kVisitDataObjectGeneric;

View File

@ -84,6 +84,7 @@ class StaticVisitorBase : public AllStatic {
V(StructGeneric) \
V(ConsString) \
V(SlicedString) \
V(Symbol) \
V(Oddball) \
V(Code) \
V(Map) \

View File

@ -1460,9 +1460,16 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
accumulator->Add("<Odd Oddball>");
break;
}
case SYMBOL_TYPE:
accumulator->Add("<Symbol: %d>", Symbol::cast(this)->Hash());
case SYMBOL_TYPE: {
Symbol* symbol = Symbol::cast(this);
accumulator->Add("<Symbol: %d", symbol->Hash());
if (!symbol->name()->IsUndefined()) {
accumulator->Add(" ");
String::cast(symbol->name())->StringShortPrint(accumulator);
}
accumulator->Add(">");
break;
}
case HEAP_NUMBER_TYPE:
accumulator->Add("<Number: ");
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
@ -1572,6 +1579,8 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v);
break;
case SYMBOL_TYPE:
Symbol::BodyDescriptor::IterateBody(this, v);
break;
case HEAP_NUMBER_TYPE:
case FILLER_TYPE:
case BYTE_ARRAY_TYPE:

View File

@ -7398,6 +7398,9 @@ class Name: public HeapObject {
// ES6 symbols.
class Symbol: public Name {
public:
// [name]: the print name of a symbol, or undefined if none.
DECL_ACCESSORS(name, Object)
// Casting.
static inline Symbol* cast(Object* obj);
@ -7406,7 +7409,11 @@ class Symbol: public Name {
DECLARE_VERIFIER(Symbol)
// Layout description.
static const int kSize = Name::kSize;
static const int kNameOffset = Name::kSize;
static const int kSize = kNameOffset + kPointerSize;
typedef FixedBodyDescriptor<kNameOffset, kNameOffset + kPointerSize, kSize>
BodyDescriptor;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Symbol);

View File

@ -684,8 +684,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateSymbol) {
NoHandleAllocation ha(isolate);
ASSERT(args.length() == 0);
return isolate->heap()->AllocateSymbol();
ASSERT(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
Symbol* symbol;
MaybeObject* maybe = isolate->heap()->AllocateSymbol();
if (!maybe->To(&symbol)) return maybe;
if (name->IsString()) symbol->set_name(*name);
return symbol;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SymbolName) {
NoHandleAllocation ha(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(Symbol, symbol, 0);
return symbol->name();
}

View File

@ -297,7 +297,8 @@ namespace internal {
F(IsJSModule, 1, 1) \
\
/* Harmony symbols */ \
F(CreateSymbol, 0, 1) \
F(CreateSymbol, 1, 1) \
F(SymbolName, 1, 1) \
\
/* Harmony proxies */ \
F(CreateJSProxy, 2, 1) \

View File

@ -30,7 +30,8 @@
var $Symbol = global.Symbol;
function SymbolConstructor(x) {
var value = IS_SYMBOL(x) ? x : %CreateSymbol();
var value =
IS_SYMBOL(x) ? x : %CreateSymbol(IS_UNDEFINED(x) ? x : ToString(x));
if (%_IsConstructCall()) {
%_SetValueOf(this, value);
} else {
@ -38,6 +39,16 @@ function SymbolConstructor(x) {
}
}
function SymbolGetName() {
var symbol = IS_SYMBOL_WRAPPER(this) ? %_ValueOf(this) : this;
if (!IS_SYMBOL(symbol)) {
throw MakeTypeError(
'incompatible_method_receiver', ["Symbol.prototype.name", this]);
}
return %SymbolName(symbol);
}
function SymbolToString() {
throw MakeTypeError('symbol_to_string');
}
@ -61,6 +72,7 @@ function SetUpSymbol() {
%FunctionSetPrototype($Symbol, new $Symbol());
%SetProperty($Symbol.prototype, "constructor", $Symbol, DONT_ENUM);
InstallGetter($Symbol.prototype, "name", SymbolGetName);
InstallFunctions($Symbol.prototype, DONT_ENUM, $Array(
"toString", SymbolToString,
"valueOf", SymbolValueOf

View File

@ -37,6 +37,9 @@ function TestNew() {
for (var i = 0; i < 2; ++i) {
for (var j = 0; j < 5; ++j) {
symbols.push(Symbol())
symbols.push(Symbol(undefined))
symbols.push(Symbol("66"))
symbols.push(Symbol(66))
symbols.push(Symbol(Symbol()))
symbols.push((new Symbol).valueOf())
symbols.push((new Symbol()).valueOf())
@ -79,6 +82,15 @@ function TestPrototype() {
TestPrototype()
function TestName() {
for (var i in symbols) {
var name = symbols[i].name
assertTrue(name === undefined || name === "66")
}
}
TestName()
function TestToString() {
for (var i in symbols) {
assertThrows(function() { String(symbols[i]) }, TypeError)