ES6 symbols: turn symbols into a proper primitive type

(qua last week's TC39)

Specifically:
- Install Symbol constructor function on the global object.
- Adjust code generation for typeof.
- Remove IsSymbol built-in, IS_SYMBOL macro now defined using typeof.
- Remove hack that allowed symbols as constructor results, and some other special cases.
- Remove symbol_delegate and GetDelegate function.
- Extend ToBoolean stub to handle symbols.
- Extend ToNumber to return NaN on symbols.
- Poison symbol's toString function, and thereby ToString on symbols.

R=mstarzinger@chromium.org
BUG=v8:2158

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14051 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2013-03-22 16:33:50 +00:00
parent 51a888ff48
commit 83d4a41dec
37 changed files with 240 additions and 208 deletions

View File

@ -4274,7 +4274,7 @@ class Internals {
static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
static const int kContextHeaderSize = 2 * kApiPointerSize;
static const int kContextEmbedderDataIndex = 54;
static const int kContextEmbedderDataIndex = 55;
static const int kFullStringRepresentationMask = 0x07;
static const int kStringEncodingMask = 0x4;
static const int kExternalTwoByteRepresentationTag = 0x02;

View File

@ -3189,7 +3189,7 @@ Local<String> v8::Object::ObjectProtoToString() {
i::Handle<i::Object> name(self->class_name(), isolate);
// Native implementation of Object.prototype.toString (v8natives.js):
// var c = %ClassOf(this);
// var c = %_ClassOf(this);
// if (c === 'Arguments') c = 'Object';
// return "[object " + c + "]";

View File

@ -1105,10 +1105,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ CompareObjectType(r0, r1, r3, FIRST_SPEC_OBJECT_TYPE);
__ b(ge, &exit);
// Symbols are "objects".
__ CompareInstanceType(r1, r3, SYMBOL_TYPE);
__ b(eq, &exit);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
__ bind(&use_receiver);

View File

@ -2764,28 +2764,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
}
void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
VisitForAccumulatorValue(args->at(0));
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
Label* fall_through = NULL;
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
__ JumpIfSmi(r0, if_false);
__ CompareObjectType(r0, r1, r2, SYMBOL_TYPE);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
Split(eq, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@ -4292,6 +4270,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
__ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset));
__ tst(r1, Operand(1 << Map::kIsUndetectable));
Split(eq, if_true, if_false, fall_through);
} else if (check->Equals(isolate()->heap()->symbol_string())) {
__ JumpIfSmi(r0, if_false);
__ CompareObjectType(r0, r0, r1, SYMBOL_TYPE);
Split(eq, if_true, if_false, fall_through);
} else if (check->Equals(isolate()->heap()->boolean_string())) {
__ CompareRoot(r0, Heap::kTrueValueRootIndex);
__ b(eq, if_true);
@ -4324,10 +4306,6 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
__ CompareRoot(r0, Heap::kNullValueRootIndex);
__ b(eq, if_true);
}
if (FLAG_harmony_symbols) {
__ CompareObjectType(r0, r0, r1, SYMBOL_TYPE);
__ b(eq, if_true);
}
// Check for JS objects => true.
__ CompareObjectType(r0, r0, r1, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ b(lt, if_false);

View File

@ -2301,6 +2301,12 @@ void LCodeGen::DoBranch(LBranch* instr) {
__ bind(&not_string);
}
if (expected.Contains(ToBooleanStub::SYMBOL)) {
// Symbol value -> true.
__ CompareInstanceType(map, ip, SYMBOL_TYPE);
__ b(eq, true_label);
}
if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
CpuFeatureScope scope(masm(), VFP2);
// heap number -> false iff +0, -0, or NaN.
@ -6074,6 +6080,11 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ tst(ip, Operand(1 << Map::kIsUndetectable));
final_branch_condition = eq;
} else if (type_name->Equals(heap()->symbol_string())) {
__ JumpIfSmi(input, false_label);
__ CompareObjectType(input, input, scratch, SYMBOL_TYPE);
final_branch_condition = eq;
} else if (type_name->Equals(heap()->boolean_string())) {
__ CompareRoot(input, Heap::kTrueValueRootIndex);
__ b(eq, true_label);
@ -6108,15 +6119,8 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ CompareRoot(input, Heap::kNullValueRootIndex);
__ b(eq, true_label);
}
if (FLAG_harmony_symbols) {
__ CompareObjectType(input, input, scratch, SYMBOL_TYPE);
__ b(eq, true_label);
__ CompareInstanceType(input, scratch,
FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
} else {
__ CompareObjectType(input, input, scratch,
FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
}
__ CompareObjectType(input, input, scratch,
FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ b(lt, false_label);
__ CompareInstanceType(input, scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ b(gt, false_label);

View File

@ -2391,6 +2391,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
// Check that the object is a symbol.
__ CompareObjectType(r1, r1, r3, SYMBOL_TYPE);
__ b(ne, &miss);
// Check that the maps starting from the prototype haven't changed.
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::SYMBOL_FUNCTION_INDEX, r0, &miss);
CheckPrototypes(
Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
r0, holder, r3, r1, r4, name, &miss);
break;
case NUMBER_CHECK: {

View File

@ -1280,7 +1280,17 @@ void Genesis::InitializeExperimentalGlobal() {
Handle<JSObject> global = Handle<JSObject>(native_context()->global_object());
// TODO(mstarzinger): Move this into Genesis::InitializeGlobal once we no
// longer need to live behind a flag, so functions get added to the snapshot.
// longer need to live behind flags, so functions get added to the snapshot.
if (FLAG_harmony_symbols) {
// --- S y m b o l ---
Handle<JSFunction> symbol_fun =
InstallFunction(global, "Symbol", JS_VALUE_TYPE, JSValue::kSize,
isolate()->initial_object_prototype(),
Builtins::kIllegal, true);
native_context()->set_symbol_function(*symbol_fun);
}
if (FLAG_harmony_collections) {
{ // -- S e t
Handle<JSObject> prototype =
@ -1436,9 +1446,6 @@ void Genesis::InstallNativeFunctions() {
}
void Genesis::InstallExperimentalNativeFunctions() {
if (FLAG_harmony_symbols) {
INSTALL_NATIVE(JSObject, "SymbolDelegate", symbol_delegate);
}
if (FLAG_harmony_proxies) {
INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap);
INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap);

View File

@ -510,6 +510,7 @@ void ToBooleanStub::Types::Print(StringStream* stream) const {
if (Contains(SMI)) stream->Add("Smi");
if (Contains(SPEC_OBJECT)) stream->Add("SpecObject");
if (Contains(STRING)) stream->Add("String");
if (Contains(SYMBOL)) stream->Add("Symbol");
if (Contains(HEAP_NUMBER)) stream->Add("HeapNumber");
}
@ -549,6 +550,9 @@ bool ToBooleanStub::Types::Record(Handle<Object> object) {
Add(STRING);
return !object->IsUndetectableObject() &&
String::cast(*object)->length() != 0;
} else if (object->IsSymbol()) {
Add(SYMBOL);
return true;
} else if (object->IsHeapNumber()) {
ASSERT(!object->IsUndetectableObject());
Add(HEAP_NUMBER);
@ -565,6 +569,7 @@ bool ToBooleanStub::Types::Record(Handle<Object> object) {
bool ToBooleanStub::Types::NeedsMap() const {
return Contains(ToBooleanStub::SPEC_OBJECT)
|| Contains(ToBooleanStub::STRING)
|| Contains(ToBooleanStub::SYMBOL)
|| Contains(ToBooleanStub::HEAP_NUMBER);
}

View File

@ -1488,6 +1488,7 @@ class ToBooleanStub: public PlatformCodeStub {
SMI,
SPEC_OBJECT,
STRING,
SYMBOL,
HEAP_NUMBER,
NUMBER_OF_TYPES
};

View File

@ -103,6 +103,7 @@ enum BindingFlags {
V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
V(STRING_FUNCTION_INDEX, JSFunction, string_function) \
V(STRING_FUNCTION_PROTOTYPE_MAP_INDEX, Map, string_function_prototype_map) \
V(SYMBOL_FUNCTION_INDEX, JSFunction, symbol_function) \
V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
V(INTERNAL_ARRAY_FUNCTION_INDEX, JSFunction, internal_array_function) \
V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
@ -156,7 +157,6 @@ enum BindingFlags {
V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \
error_message_for_code_gen_from_strings) \
V(SYMBOL_DELEGATE_INDEX, JSObject, symbol_delegate) \
V(TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX, JSFunction, \
to_complete_property_descriptor) \
V(DERIVED_HAS_TRAP_INDEX, JSFunction, derived_has_trap) \
@ -251,6 +251,7 @@ class Context: public FixedArray {
NUMBER_FUNCTION_INDEX,
STRING_FUNCTION_INDEX,
STRING_FUNCTION_PROTOTYPE_MAP_INDEX,
SYMBOL_FUNCTION_INDEX,
OBJECT_FUNCTION_INDEX,
INTERNAL_ARRAY_FUNCTION_INDEX,
ARRAY_FUNCTION_INDEX,
@ -287,7 +288,6 @@ class Context: public FixedArray {
EMBEDDER_DATA_INDEX,
ALLOW_CODE_GEN_FROM_STRINGS_INDEX,
ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX,
SYMBOL_DELEGATE_INDEX,
TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX,
DERIVED_HAS_TRAP_INDEX,
DERIVED_GET_TRAP_INDEX,

View File

@ -2214,7 +2214,8 @@ function Stringify(x, depth) {
return x.toString();
case "string":
return "\"" + x.toString() + "\"";
// TODO(rossberg): add symbol case
case "symbol":
return "Symbol()";
case "object":
if (x === null) return "null";
if (x.constructor && x.constructor.name === "Array") {

View File

@ -213,6 +213,8 @@ namespace internal {
V(prototype_string, "prototype") \
V(string_string, "string") \
V(String_string, "String") \
V(symbol_string, "symbol") \
V(Symbol_string, "Symbol") \
V(Date_string, "Date") \
V(this_string, "this") \
V(to_string_string, "toString") \

View File

@ -1277,7 +1277,8 @@ Representation HBranch::observed_input_representation(int index) {
ToBooleanStub::UNDEFINED |
ToBooleanStub::NULL_TYPE |
ToBooleanStub::SPEC_OBJECT |
ToBooleanStub::STRING);
ToBooleanStub::STRING |
ToBooleanStub::SYMBOL);
if (expected_input_types_.ContainsAnyOf(tagged_types)) {
return Representation::Tagged();
} else if (expected_input_types_.Contains(ToBooleanStub::HEAP_NUMBER)) {

View File

@ -5083,19 +5083,7 @@ void HOptimizedGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
typecheck->SetSuccessorAt(1, not_spec_object);
current_block()->Finish(typecheck);
if_spec_object->AddLeaveInlined(return_value, state);
if (!FLAG_harmony_symbols) {
not_spec_object->AddLeaveInlined(receiver, state);
} else {
HHasInstanceTypeAndBranch* symbolcheck =
new(zone()) HHasInstanceTypeAndBranch(return_value, SYMBOL_TYPE);
HBasicBlock* is_symbol = graph()->CreateBasicBlock();
HBasicBlock* not_symbol = graph()->CreateBasicBlock();
symbolcheck->SetSuccessorAt(0, is_symbol);
symbolcheck->SetSuccessorAt(1, not_symbol);
not_spec_object->Finish(symbolcheck);
is_symbol->AddLeaveInlined(return_value, state);
not_symbol->AddLeaveInlined(receiver, state);
}
not_spec_object->AddLeaveInlined(receiver, state);
}
} else if (state->inlining_kind() == SETTER_CALL_RETURN) {
// Return from an inlined setter call. The returned value is never used, the
@ -10080,16 +10068,6 @@ void HOptimizedGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
}
void HOptimizedGraphBuilder::GenerateIsSymbol(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceTypeAndBranch* result =
new(zone()) HHasInstanceTypeAndBranch(value, SYMBOL_TYPE);
return ast_context()->ReturnControl(result, call->id());
}
void HOptimizedGraphBuilder::GenerateIsFunction(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));

View File

@ -409,10 +409,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
__ j(above_equal, &exit);
// Symbols are "objects".
__ CmpInstanceType(ecx, SYMBOL_TYPE);
__ j(equal, &exit);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
__ bind(&use_receiver);

View File

@ -632,6 +632,14 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
__ bind(&not_string);
}
if (types_.Contains(SYMBOL)) {
// Symbol value -> true.
Label not_symbol;
__ CmpInstanceType(map, SYMBOL_TYPE);
__ j(not_equal, &not_symbol, Label::kNear);
__ bind(&not_symbol);
}
if (types_.Contains(HEAP_NUMBER)) {
// heap number -> false iff +0, -0, or NaN.
Label not_heap_number, false_result;

View File

@ -2567,7 +2567,6 @@ void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) {
// TODO(rossberg): incorporate symbols.
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@ -2703,28 +2702,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
}
void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
VisitForAccumulatorValue(args->at(0));
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
Label* fall_through = NULL;
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
__ JumpIfSmi(eax, if_false);
__ CmpObjectType(eax, SYMBOL_TYPE, ebx);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@ -4275,6 +4252,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
__ test_b(FieldOperand(edx, Map::kBitFieldOffset),
1 << Map::kIsUndetectable);
Split(zero, if_true, if_false, fall_through);
} else if (check->Equals(isolate()->heap()->symbol_string())) {
__ JumpIfSmi(eax, if_false);
__ CmpObjectType(eax, SYMBOL_TYPE, edx);
Split(equal, if_true, if_false, fall_through);
} else if (check->Equals(isolate()->heap()->boolean_string())) {
__ cmp(eax, isolate()->factory()->true_value());
__ j(equal, if_true);
@ -4306,10 +4287,6 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
__ cmp(eax, isolate()->factory()->null_value());
__ j(equal, if_true);
}
if (FLAG_harmony_symbols) {
__ CmpObjectType(eax, SYMBOL_TYPE, edx);
__ j(equal, if_true);
}
__ CmpObjectType(eax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, edx);
__ j(below, if_false);
__ CmpInstanceType(edx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);

View File

@ -2116,6 +2116,12 @@ void LCodeGen::DoBranch(LBranch* instr) {
__ bind(&not_string);
}
if (expected.Contains(ToBooleanStub::SYMBOL)) {
// Symbol value -> true.
__ CmpInstanceType(map, SYMBOL_TYPE);
__ j(equal, true_label);
}
if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
// heap number -> false iff +0, -0, or NaN.
Label not_heap_number;
@ -5979,6 +5985,11 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
1 << Map::kIsUndetectable);
final_branch_condition = zero;
} else if (type_name->Equals(heap()->symbol_string())) {
__ JumpIfSmi(input, false_label);
__ CmpObjectType(input, SYMBOL_TYPE, input);
final_branch_condition = equal;
} else if (type_name->Equals(heap()->boolean_string())) {
__ cmp(input, factory()->true_value());
__ j(equal, true_label);
@ -6013,13 +6024,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ cmp(input, factory()->null_value());
__ j(equal, true_label);
}
if (FLAG_harmony_symbols) {
__ CmpObjectType(input, SYMBOL_TYPE, input);
__ j(equal, true_label);
__ CmpInstanceType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
} else {
__ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
}
__ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
__ j(below, false_label);
__ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ j(above, false_label);

View File

@ -2350,6 +2350,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
// Check that the object is a symbol.
__ CmpObjectType(edx, SYMBOL_TYPE, eax);
__ j(not_equal, &miss);
// Check that the maps starting from the prototype haven't changed.
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::SYMBOL_FUNCTION_INDEX, eax, &miss);
CheckPrototypes(
Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
eax, holder, ebx, edx, edi, name, &miss);
break;
case NUMBER_CHECK: {

View File

@ -109,7 +109,7 @@ InlineCacheHolderFlag IC::GetCodeCacheForObject(Object* object,
// If the object is a value, we use the prototype map for the cache.
ASSERT(object->IsString() || object->IsSymbol() ||
object->IsNumber() || object->IsBoolean());
return DELEGATE_MAP;
return PROTOTYPE_MAP;
}
@ -124,7 +124,7 @@ InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
!object->HasFastProperties() &&
!object->IsJSGlobalProxy() &&
!object->IsJSGlobalObject()) {
return DELEGATE_MAP;
return PROTOTYPE_MAP;
}
return OWN_MAP;
}
@ -133,7 +133,8 @@ InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
JSObject* IC::GetCodeCacheHolder(Isolate* isolate,
Object* object,
InlineCacheHolderFlag holder) {
Object* map_owner = holder == OWN_MAP ? object : object->GetDelegate(isolate);
Object* map_owner =
holder == OWN_MAP ? object : object->GetPrototype(isolate);
ASSERT(map_owner->IsJSObject());
return JSObject::cast(map_owner);
}

View File

@ -190,7 +190,7 @@ static bool TryRemoveInvalidPrototypeDependentStub(Code* target,
// The stub was generated for JSObject but called for non-JSObject.
// IC::GetCodeCacheHolder is not applicable.
return false;
} else if (cache_holder == DELEGATE_MAP &&
} else if (cache_holder == PROTOTYPE_MAP &&
receiver->GetPrototype(isolate)->IsNull()) {
// IC::GetCodeCacheHolder is not applicable.
return false;

View File

@ -99,7 +99,7 @@ macro IS_UNDEFINED(arg) = (typeof(arg) === 'undefined');
macro IS_NUMBER(arg) = (typeof(arg) === 'number');
macro IS_STRING(arg) = (typeof(arg) === 'string');
macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
macro IS_SYMBOL(arg) = (%_IsSymbol(arg));
macro IS_SYMBOL(arg) = (typeof(arg) === 'symbol');
macro IS_OBJECT(arg) = (%_IsObject(arg));
macro IS_ARRAY(arg) = (%_IsArray(arg));
macro IS_FUNCTION(arg) = (%_IsFunction(arg));
@ -110,6 +110,7 @@ macro IS_WEAKMAP(arg) = (%_ClassOf(arg) === 'WeakMap');
macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date');
macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number');
macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String');
macro IS_SYMBOL_WRAPPER(arg) = (%_ClassOf(arg) === 'Symbol');
macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean');
macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error');
macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script');

View File

@ -150,6 +150,7 @@ var kMessages = {
cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
redef_external_array_element: ["Cannot redefine a property of an object with external array elements"],
harmony_const_assign: ["Assignment to constant variable."],
symbol_to_string: ["Conversion from symbol to string"],
invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"],
module_type_error: ["Module '", "%0", "' used improperly"],
module_export_undefined: ["Export '", "%0", "' is not defined in module"],

View File

@ -126,10 +126,10 @@ void Object::Lookup(Name* name, LookupResult* result) {
holder = native_context->number_function()->instance_prototype();
} else if (IsString()) {
holder = native_context->string_function()->instance_prototype();
} else if (IsSymbol()) {
holder = native_context->symbol_function()->instance_prototype();
} else if (IsBoolean()) {
holder = native_context->boolean_function()->instance_prototype();
} else if (IsSymbol()) {
holder = native_context->symbol_delegate();
} else {
Isolate::Current()->PushStackTraceAndDie(
0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001);
@ -756,7 +756,7 @@ MaybeObject* Object::GetProperty(Object* receiver,
// holder in the prototype chain.
// Proxy handlers do not use the proxy's prototype, so we can skip this.
if (!result->IsHandler()) {
Object* last = result->IsProperty() && !receiver->IsSymbol()
Object* last = result->IsProperty()
? result->holder()
: Object::cast(heap->null_value());
ASSERT(this != this->GetPrototype(isolate));
@ -837,10 +837,10 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
holder = native_context->number_function()->instance_prototype();
} else if (holder->IsString()) {
holder = native_context->string_function()->instance_prototype();
} else if (holder->IsSymbol()) {
holder = native_context->symbol_function()->instance_prototype();
} else if (holder->IsBoolean()) {
holder = native_context->boolean_function()->instance_prototype();
} else if (holder->IsSymbol()) {
holder = native_context->symbol_delegate();
} else if (holder->IsJSProxy()) {
return JSProxy::cast(holder)->GetElementWithHandler(receiver, index);
} else {
@ -900,6 +900,9 @@ Object* Object::GetPrototype(Isolate* isolate) {
if (heap_object->IsString()) {
return context->string_function()->instance_prototype();
}
if (heap_object->IsSymbol()) {
return context->symbol_function()->instance_prototype();
}
if (heap_object->IsBoolean()) {
return context->boolean_function()->instance_prototype();
} else {
@ -908,16 +911,6 @@ Object* Object::GetPrototype(Isolate* isolate) {
}
Object* Object::GetDelegate(Isolate* isolate) {
if (IsSymbol()) {
Heap* heap = Symbol::cast(this)->GetHeap();
Context* context = heap->isolate()->context()->native_context();
return context->symbol_delegate();
}
return GetPrototype(isolate);
}
MaybeObject* Object::GetHash(CreationFlag flag) {
// The object is either a number, a name, an odd-ball,
// a real JS object, or a Harmony proxy.

View File

@ -1102,9 +1102,6 @@ class Object : public MaybeObject {
// Return the object's prototype (might be Heap::null_value()).
Object* GetPrototype(Isolate* isolate);
// Return the prototype, or the method holder for a value-like object.
Object* GetDelegate(Isolate* isolate);
// Returns the permanent hash code associated with this object depending on
// the actual object type. Might return a failure in case no hash was
// created yet or GC was caused by creation.

View File

@ -5035,6 +5035,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) {
}
ASSERT(heap_obj->IsUndefined());
return isolate->heap()->undefined_string();
case SYMBOL_TYPE:
return isolate->heap()->symbol_string();
case JS_FUNCTION_TYPE:
case JS_FUNCTION_PROXY_TYPE:
return isolate->heap()->function_string();

View File

@ -512,7 +512,6 @@ namespace internal {
#define INLINE_FUNCTION_LIST(F) \
F(IsSmi, 1, 1) \
F(IsNonNegativeSmi, 1, 1) \
F(IsSymbol, 1, 1) \
F(IsArray, 1, 1) \
F(IsRegExp, 1, 1) \
F(IsConstructCall, 0, 1) \

View File

@ -532,6 +532,7 @@ function ToNumber(x) {
}
if (IS_BOOLEAN(x)) return x ? 1 : 0;
if (IS_UNDEFINED(x)) return $NaN;
if (IS_SYMBOL(x)) return $NaN;
return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
}
@ -542,6 +543,7 @@ function NonNumberToNumber(x) {
}
if (IS_BOOLEAN(x)) return x ? 1 : 0;
if (IS_UNDEFINED(x)) return $NaN;
if (IS_SYMBOL(x)) return $NaN;
return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
}
@ -572,6 +574,7 @@ function ToName(x) {
// ECMA-262, section 9.9, page 36.
function ToObject(x) {
if (IS_STRING(x)) return new $String(x);
if (IS_SYMBOL(x)) return new $Symbol(x);
if (IS_NUMBER(x)) return new $Number(x);
if (IS_BOOLEAN(x)) return new $Boolean(x);
if (IS_NULL_OR_UNDEFINED(x) && !IS_UNDETECTABLE(x)) {

View File

@ -27,13 +27,44 @@
"use strict";
var $Symbol = function() { return %CreateSymbol() }
global.Symbol = $Symbol
var $Symbol = global.Symbol;
// Symbols only have a toString method and no prototype.
var SymbolDelegate = {
__proto__: null,
toString: $Object.prototype.toString
function SymbolConstructor(x) {
var value = IS_SYMBOL(x) ? x : %CreateSymbol();
if (%_IsConstructCall()) {
%_SetValueOf(this, value);
} else {
return value;
}
}
$Object.freeze(SymbolDelegate)
function SymbolToString() {
throw MakeTypeError('symbol_to_string');
}
function SymbolValueOf() {
// NOTE: Both Symbol objects and values can enter here as
// 'this'. This is not as dictated by ECMA-262.
if (!IS_SYMBOL(this) && !IS_SYMBOL_WRAPPER(this)) {
throw MakeTypeError(
'incompatible_method_receiver', ["Symbol.prototype.valueOf", this]);
}
return %_ValueOf(this);
}
//-------------------------------------------------------------------
function SetUpSymbol() {
%CheckIsBootstrapping();
%SetCode($Symbol, SymbolConstructor);
%FunctionSetPrototype($Symbol, new $Symbol());
%SetProperty($Symbol.prototype, "constructor", $Symbol, DONT_ENUM);
InstallFunctions($Symbol.prototype, DONT_ENUM, $Array(
"toString", SymbolToString,
"valueOf", SymbolValueOf
));
}
SetUpSymbol();

View File

@ -283,11 +283,12 @@ Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
case RECEIVER_MAP_CHECK:
UNREACHABLE();
break;
case SYMBOL_CHECK:
return Handle<JSObject>(native_context_->symbol_delegate());
case STRING_CHECK:
function = native_context_->string_function();
break;
case SYMBOL_CHECK:
function = native_context_->symbol_function();
break;
case NUMBER_CHECK:
function = native_context_->number_function();
break;

View File

@ -296,7 +296,7 @@ enum CallFunctionFlags {
enum InlineCacheHolderFlag {
OWN_MAP, // For fast properties objects.
DELEGATE_MAP // For slow properties objects (except GlobalObjects).
PROTOTYPE_MAP // For slow properties objects (except GlobalObjects).
};

View File

@ -233,7 +233,6 @@ $Object.prototype.constructor = $Object;
function ObjectToString() {
if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
if (IS_NULL(this)) return "[object Null]";
if (IS_SYMBOL(this)) return "[object Symbol]";
return "[object " + %_ClassOf(ToObject(this)) + "]";
}
@ -865,7 +864,7 @@ function DefineArrayProperty(obj, p, desc, should_throw) {
// DefineObjectProperty() to modify its value.
// Step 3 - Special handling for length property.
if (p == "length") {
if (p === "length") {
var length = obj.length;
if (!desc.hasValue()) {
return DefineObjectProperty(obj, "length", desc, should_throw);

View File

@ -416,10 +416,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
__ j(above_equal, &exit);
// Symbols are "objects".
__ CmpInstanceType(rcx, SYMBOL_TYPE);
__ j(equal, &exit);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
__ bind(&use_receiver);

View File

@ -2678,28 +2678,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
}
void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
VisitForAccumulatorValue(args->at(0));
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
Label* fall_through = NULL;
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
__ JumpIfSmi(rax, if_false);
__ CmpObjectType(rax, SYMBOL_TYPE, rbx);
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@ -4271,6 +4249,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
__ testb(FieldOperand(rdx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
Split(zero, if_true, if_false, fall_through);
} else if (check->Equals(isolate()->heap()->symbol_string())) {
__ JumpIfSmi(rax, if_false);
__ CmpObjectType(rax, SYMBOL_TYPE, rdx);
Split(equal, if_true, if_false, fall_through);
} else if (check->Equals(isolate()->heap()->boolean_string())) {
__ CompareRoot(rax, Heap::kTrueValueRootIndex);
__ j(equal, if_true);
@ -4302,10 +4284,6 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
__ CompareRoot(rax, Heap::kNullValueRootIndex);
__ j(equal, if_true);
}
if (FLAG_harmony_symbols) {
__ CmpObjectType(rax, SYMBOL_TYPE, rdx);
__ j(equal, if_true);
}
__ CmpObjectType(rax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, rdx);
__ j(below, if_false);
__ CmpInstanceType(rdx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);

View File

@ -5538,6 +5538,11 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
Immediate(1 << Map::kIsUndetectable));
final_branch_condition = zero;
} else if (type_name->Equals(heap()->symbol_string())) {
__ JumpIfSmi(input, false_label);
__ CmpObjectType(input, SYMBOL_TYPE, input);
final_branch_condition = equal;
} else if (type_name->Equals(heap()->boolean_string())) {
__ CompareRoot(input, Heap::kTrueValueRootIndex);
__ j(equal, true_label);
@ -5572,13 +5577,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ CompareRoot(input, Heap::kNullValueRootIndex);
__ j(equal, true_label);
}
if (FLAG_harmony_symbols) {
__ CmpObjectType(input, SYMBOL_TYPE, input);
__ j(equal, true_label);
__ CmpInstanceType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
} else {
__ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
}
__ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
__ j(below, false_label);
__ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ j(above, false_label);

View File

@ -2174,6 +2174,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
// Check that the object is a symbol.
__ CmpObjectType(rdx, SYMBOL_TYPE, rax);
__ j(not_equal, &miss);
// Check that the maps starting from the prototype haven't changed.
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::SYMBOL_FUNCTION_INDEX, rax, &miss);
CheckPrototypes(
Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
rax, holder, rbx, rdx, rdi, name, &miss);
break;
case NUMBER_CHECK: {

View File

@ -34,20 +34,19 @@ var symbols = []
function TestNew() {
function IndirectSymbol() { return new Symbol }
function indirect() { return new IndirectSymbol() }
for (var i = 0; i < 10; ++i) {
symbols.push(new Symbol)
symbols.push(new Symbol())
symbols.push(Symbol())
symbols.push(indirect())
}
%OptimizeFunctionOnNextCall(indirect)
indirect() // Call once before GC throws away type feedback.
gc() // Promote existing symbols and then allocate some more.
for (var i = 0; i < 10; ++i) {
symbols.push(new Symbol)
symbols.push(new Symbol())
symbols.push(Symbol())
symbols.push(indirect())
for (var i = 0; i < 2; ++i) {
for (var j = 0; j < 5; ++j) {
symbols.push(Symbol())
symbols.push(Symbol(Symbol()))
symbols.push((new Symbol).valueOf())
symbols.push((new Symbol()).valueOf())
symbols.push((new Symbol(Symbol())).valueOf())
symbols.push(Object(Symbol()).valueOf())
symbols.push((indirect()).valueOf())
}
%OptimizeFunctionOnNextCall(indirect)
indirect() // Call once before GC throws away type feedback.
gc() // Promote existing symbols and then allocate some more.
}
}
TestNew()
@ -55,15 +54,70 @@ TestNew()
function TestType() {
for (var i in symbols) {
assertTrue(%_IsSymbol(symbols[i]))
assertEquals("object", typeof symbols[i])
assertTrue(typeof symbols[i] === "object")
assertEquals("[object Symbol]", Object.prototype.toString.call(symbols[i]))
assertEquals("symbol", typeof symbols[i])
assertTrue(typeof symbols[i] === "symbol")
assertEquals(null, %_ClassOf(symbols[i]))
assertEquals("Symbol", %_ClassOf(new Symbol(symbols[i])))
assertEquals("Symbol", %_ClassOf(Object(symbols[i])))
}
}
TestType()
function TestPrototype() {
assertSame(Object.prototype, Symbol.prototype.__proto__)
assertSame(Symbol.prototype, Symbol().__proto__)
assertSame(Symbol.prototype, Symbol(Symbol()).__proto__)
assertSame(Symbol.prototype, (new Symbol).__proto__)
assertSame(Symbol.prototype, (new Symbol()).__proto__)
assertSame(Symbol.prototype, (new Symbol(Symbol())).__proto__)
assertSame(Symbol.prototype, Object(Symbol()).__proto__)
for (var i in symbols) {
assertSame(Symbol.prototype, symbols[i].__proto__)
}
}
TestPrototype()
function TestToString() {
for (var i in symbols) {
assertThrows(function() { String(symbols[i]) }, TypeError)
assertThrows(function() { symbols[i] + "" }, TypeError)
assertThrows(function() { symbols[i].toString() }, TypeError)
assertThrows(function() { (new Symbol(symbols[i])).toString() }, TypeError)
assertThrows(function() { Object(symbols[i]).toString() }, TypeError)
assertEquals("[object Symbol]", Object.prototype.toString.call(symbols[i]))
}
}
TestToString()
function TestToBoolean() {
for (var i in symbols) {
assertTrue(Boolean(symbols[i]).valueOf())
assertFalse(!symbols[i])
assertTrue(!!symbols[i])
assertTrue(symbols[i] && true)
assertFalse(!symbols[i] && false)
assertTrue(!symbols[i] || true)
assertEquals(1, symbols[i] ? 1 : 2)
assertEquals(2, !symbols[i] ? 1 : 2)
if (!symbols[i]) assertUnreachable();
if (symbols[i]) {} else assertUnreachable();
}
}
TestToBoolean()
function TestToNumber() {
for (var i in symbols) {
assertSame(NaN, Number(symbols[i]).valueOf())
assertSame(NaN, symbols[i] + 0)
}
}
TestToNumber()
function TestEquality() {
// Every symbol should equal itself.
for (var i in symbols) {
@ -88,8 +142,8 @@ TestEquality()
function TestGet() {
for (var i in symbols) {
assertEquals("[object Symbol]", symbols[i].toString())
assertEquals(undefined, symbols[i].valueOf)
assertThrows(function() { symbols[i].toString() }, TypeError)
assertEquals(symbols[i], symbols[i].valueOf())
assertEquals(undefined, symbols[i].a)
assertEquals(undefined, symbols[i]["a" + "b"])
assertEquals(undefined, symbols[i]["" + "1"])
@ -102,7 +156,9 @@ TestGet()
function TestSet() {
for (var i in symbols) {
symbols[i].toString = 0
assertEquals("[object Symbol]", symbols[i].toString())
assertThrows(function() { symbols[i].toString() }, TypeError)
symbols[i].valueOf = 0
assertEquals(symbols[i], symbols[i].valueOf())
symbols[i].a = 0
assertEquals(undefined, symbols[i].a)
symbols[i]["a" + "b"] = 0
@ -179,7 +235,7 @@ function TestKeyHas() {
function TestKeyEnum(obj) {
for (var name in obj) {
assertFalse(%_IsSymbol(name))
assertTrue(typeof name !== "symbol")
}
}
@ -195,9 +251,7 @@ function TestKeyNames(obj) {
for (var i = 0; i < symbols.length; ++i) expected.add(symbols[i])
for (var i = 0; i < names.length; ++i) {
var name = names[i]
var asString = String(name)
if (asString !== name) {
assertEquals("[object Symbol]", asString)
if (typeof name === 'symbol') {
assertTrue(expected.has(name))
expected.delete(name)
}