diff --git a/include/v8.h b/include/v8.h index 329576fa94..d2b571567d 100644 --- a/include/v8.h +++ b/include/v8.h @@ -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; diff --git a/src/api.cc b/src/api.cc index 71d3f78eda..7fce39e3a5 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3189,7 +3189,7 @@ Local v8::Object::ObjectProtoToString() { i::Handle 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 + "]"; diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 2946b355af..98a4c0bd82 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -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); diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 1df1649d3d..6086645db3 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2764,28 +2764,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( } -void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) { - ZoneList* 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* 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); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 8040791acb..f65042ed3f 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -2301,6 +2301,12 @@ void LCodeGen::DoBranch(LBranch* instr) { __ bind(¬_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); diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 3350c56c16..06edce8d7b 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -2391,6 +2391,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle 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::cast(object->GetPrototype(isolate()))), + r0, holder, r3, r1, r4, name, &miss); break; case NUMBER_CHECK: { diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index ce58a86cf5..380c81e13a 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1280,7 +1280,17 @@ void Genesis::InitializeExperimentalGlobal() { Handle global = Handle(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 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 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); diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 95bc1e99cc..1884b56f11 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -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) { 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) { bool ToBooleanStub::Types::NeedsMap() const { return Contains(ToBooleanStub::SPEC_OBJECT) || Contains(ToBooleanStub::STRING) + || Contains(ToBooleanStub::SYMBOL) || Contains(ToBooleanStub::HEAP_NUMBER); } diff --git a/src/code-stubs.h b/src/code-stubs.h index 6e95780824..92a3450ca2 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -1488,6 +1488,7 @@ class ToBooleanStub: public PlatformCodeStub { SMI, SPEC_OBJECT, STRING, + SYMBOL, HEAP_NUMBER, NUMBER_OF_TYPES }; diff --git a/src/contexts.h b/src/contexts.h index cd7ed6adc5..a0ba2f7eb5 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -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, diff --git a/src/d8.js b/src/d8.js index 1ff0ce8980..8b30fcff8d 100644 --- a/src/d8.js +++ b/src/d8.js @@ -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") { diff --git a/src/heap.h b/src/heap.h index b3be86b7fe..7a1d3c577e 100644 --- a/src/heap.h +++ b/src/heap.h @@ -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") \ diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index fc80748a6a..9b96cfe7ca 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -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)) { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 8dbb5dd3cc..435805fbfb 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -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))); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index afb1c030ed..ff5ee53878 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -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); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index b30d5192f0..70d507ad71 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -632,6 +632,14 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { __ bind(¬_string); } + if (types_.Contains(SYMBOL)) { + // Symbol value -> true. + Label not_symbol; + __ CmpInstanceType(map, SYMBOL_TYPE); + __ j(not_equal, ¬_symbol, Label::kNear); + __ bind(¬_symbol); + } + if (types_.Contains(HEAP_NUMBER)) { // heap number -> false iff +0, -0, or NaN. Label not_heap_number, false_result; diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index ebc3a2bd5c..efbdf13544 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2567,7 +2567,6 @@ void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { - // TODO(rossberg): incorporate symbols. ZoneList* args = expr->arguments(); ASSERT(args->length() == 1); @@ -2703,28 +2702,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( } -void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) { - ZoneList* 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* 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); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 205781b9d9..588b6b6d7b 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2116,6 +2116,12 @@ void LCodeGen::DoBranch(LBranch* instr) { __ bind(¬_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); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index f7e795e788..94ce9d7f3b 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -2350,6 +2350,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle 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::cast(object->GetPrototype(isolate()))), + eax, holder, ebx, edx, edi, name, &miss); break; case NUMBER_CHECK: { diff --git a/src/ic-inl.h b/src/ic-inl.h index 9439792bed..ca02183dbd 100644 --- a/src/ic-inl.h +++ b/src/ic-inl.h @@ -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); } diff --git a/src/ic.cc b/src/ic.cc index c1d11bbb90..b77cb7d681 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -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; diff --git a/src/macros.py b/src/macros.py index 291a898f53..52e87fcb13 100644 --- a/src/macros.py +++ b/src/macros.py @@ -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'); diff --git a/src/messages.js b/src/messages.js index 7353444b4b..cbb5000d7d 100644 --- a/src/messages.js +++ b/src/messages.js @@ -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"], diff --git a/src/objects.cc b/src/objects.cc index dbbec58222..5fb973894d 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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. diff --git a/src/objects.h b/src/objects.h index 933a07599a..c8654e65d3 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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. diff --git a/src/runtime.cc b/src/runtime.cc index 38f77d0713..adcc2bc55d 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -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(); diff --git a/src/runtime.h b/src/runtime.h index 99ff49a808..94edf1a275 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -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) \ diff --git a/src/runtime.js b/src/runtime.js index 09b39ffe1d..8f4388a453 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -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)) { diff --git a/src/symbol.js b/src/symbol.js index b7f9dc9496..b67e00b895 100644 --- a/src/symbol.js +++ b/src/symbol.js @@ -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(); diff --git a/src/type-info.cc b/src/type-info.cc index f31edb7ed7..39a01f59e8 100644 --- a/src/type-info.cc +++ b/src/type-info.cc @@ -283,11 +283,12 @@ Handle TypeFeedbackOracle::GetPrototypeForPrimitiveCheck( case RECEIVER_MAP_CHECK: UNREACHABLE(); break; - case SYMBOL_CHECK: - return Handle(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; diff --git a/src/v8globals.h b/src/v8globals.h index 8874cfb25a..4b4d312f91 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -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). }; diff --git a/src/v8natives.js b/src/v8natives.js index 24ad22d96d..82e92db008 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -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); diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 0b70c1a0c0..a49497e270 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -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); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 8ff12df361..322479dec6 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2678,28 +2678,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( } -void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) { - ZoneList* 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* 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); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index b9bd30bb16..0bb676b9db 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -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); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 69d7a91b2d..b6d38ca8ef 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -2174,6 +2174,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle 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::cast(object->GetPrototype(isolate()))), + rax, holder, rbx, rdx, rdi, name, &miss); break; case NUMBER_CHECK: { diff --git a/test/mjsunit/harmony/symbols.js b/test/mjsunit/harmony/symbols.js index cdc63628ae..fe7fd0aed4 100644 --- a/test/mjsunit/harmony/symbols.js +++ b/test/mjsunit/harmony/symbols.js @@ -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) }