ES6 symbols: Implement Symbol intrinsic and basic functionality

- Add --harmony-symbols flag.
- Add Symbol constructor; allow symbols as (unreplaced) return value from constructors.
- Introduce %CreateSymbol and %_IsSymbol natives and respective instructions.
- Extend 'typeof' code generation to handle symbols.
- Extend CompareIC with a UNIQUE_NAMES state that (uniformly) handles internalized strings and symbols.
- Property lookup delegates to SymbolDelegate object for symbols, which only carries the toString method.
- Extend Object.prototype.toString to recognise symbols.

Per the current draft spec, symbols are actually pseudo objects that are frozen with a null prototype and only one property (toString). For simplicity, we do not treat them as proper objects for now, although typeof will return "object". Only property access works as if they were (frozen) objects (via the internal delegate object).

(Baseline CL: https://codereview.chromium.org/12223071/)

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

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13786 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2013-03-01 13:28:55 +00:00
parent 74b6f0f321
commit 090d09d685
38 changed files with 587 additions and 35 deletions

View File

@ -327,6 +327,7 @@ debug-debugger.js
EXPERIMENTAL_LIBRARY_FILES = '''
symbol.js
proxy.js
collection.js
'''.split()

View File

@ -1044,9 +1044,13 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// If the type of the result (stored in its map) is less than
// FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense.
__ CompareObjectType(r0, r3, r3, FIRST_SPEC_OBJECT_TYPE);
__ 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

@ -7067,6 +7067,57 @@ void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
}
void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::UNIQUE_NAME);
ASSERT(GetCondition() == eq);
Label miss;
// Registers containing left and right operands respectively.
Register left = r1;
Register right = r0;
Register tmp1 = r2;
Register tmp2 = r3;
// Check that both operands are heap objects.
__ JumpIfEitherSmi(left, right, &miss);
// Check that both operands are unique names. This leaves the instance
// types loaded in tmp1 and tmp2.
STATIC_ASSERT(kInternalizedTag != 0);
__ ldr(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
__ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
__ ldrb(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
__ ldrb(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
Label succeed1;
__ tst(tmp1, Operand(kIsInternalizedMask));
__ b(ne, &succeed1);
__ cmp(tmp1, Operand(SYMBOL_TYPE));
__ b(ne, &miss);
__ bind(&succeed1);
Label succeed2;
__ tst(tmp2, Operand(kIsInternalizedMask));
__ b(ne, &succeed2);
__ cmp(tmp2, Operand(SYMBOL_TYPE));
__ b(ne, &miss);
__ bind(&succeed2);
// Unique names are compared by identity.
__ cmp(left, right);
// Make sure r0 is non-zero. At this point input operands are
// guaranteed to be non-zero.
ASSERT(right.is(r0));
STATIC_ASSERT(EQUAL == 0);
STATIC_ASSERT(kSmiTag == 0);
__ mov(r0, Operand(Smi::FromInt(EQUAL)), LeaveCC, eq);
__ Ret();
__ bind(&miss);
GenerateMiss(masm);
}
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::STRING);
Label miss;

View File

@ -2754,6 +2754,28 @@ 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 +4314,10 @@ 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

@ -6091,8 +6091,15 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ CompareRoot(input, Heap::kNullValueRootIndex);
__ b(eq, true_label);
}
__ CompareObjectType(input, input, scratch,
FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
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);
}
__ b(lt, false_label);
__ CompareInstanceType(input, scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ b(gt, false_label);

View File

@ -2433,6 +2433,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
r0, holder, r3, r1, r4, name, &miss);
break;
case SYMBOL_CHECK:
// Check that the object is a symbol.
__ CompareObjectType(r1, r1, r3, SYMBOL_TYPE);
__ b(ne, &miss);
break;
case NUMBER_CHECK: {
Label fast;
// Check that the object is a smi or a heap number.

View File

@ -1446,6 +1446,9 @@ 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);
@ -1893,6 +1896,11 @@ bool Genesis::InstallExperimentalNatives() {
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount();
i++) {
if (FLAG_harmony_symbols &&
strcmp(ExperimentalNatives::GetScriptName(i).start(),
"native symbol.js") == 0) {
if (!CompileExperimentalBuiltin(isolate(), i)) return false;
}
if (FLAG_harmony_proxies &&
strcmp(ExperimentalNatives::GetScriptName(i).start(),
"native proxy.js") == 0) {

View File

@ -376,6 +376,9 @@ void ICCompareStub::Generate(MacroAssembler* masm) {
case CompareIC::INTERNALIZED_STRING:
GenerateInternalizedStrings(masm);
break;
case CompareIC::UNIQUE_NAME:
GenerateUniqueNames(masm);
break;
case CompareIC::OBJECT:
GenerateObjects(masm);
break;

View File

@ -794,9 +794,9 @@ class ICCompareStub: public PlatformCodeStub {
private:
class OpField: public BitField<int, 0, 3> { };
class LeftStateField: public BitField<int, 3, 3> { };
class RightStateField: public BitField<int, 6, 3> { };
class HandlerStateField: public BitField<int, 9, 3> { };
class LeftStateField: public BitField<int, 3, 4> { };
class RightStateField: public BitField<int, 7, 4> { };
class HandlerStateField: public BitField<int, 11, 4> { };
virtual void FinishCode(Handle<Code> code) {
code->set_stub_info(MinorKey());
@ -811,6 +811,7 @@ class ICCompareStub: public PlatformCodeStub {
void GenerateNumbers(MacroAssembler* masm);
void GenerateInternalizedStrings(MacroAssembler* masm);
void GenerateStrings(MacroAssembler* masm);
void GenerateUniqueNames(MacroAssembler* masm);
void GenerateObjects(MacroAssembler* masm);
void GenerateMiss(MacroAssembler* masm);
void GenerateKnownObjects(MacroAssembler* masm);

View File

@ -156,6 +156,7 @@ 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) \
@ -286,6 +287,7 @@ 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

@ -141,6 +141,8 @@ DEFINE_bool(harmony_typeof, false, "enable harmony semantics for typeof")
DEFINE_bool(harmony_scoping, false, "enable harmony block scoping")
DEFINE_bool(harmony_modules, false,
"enable harmony modules (implies block scoping)")
DEFINE_bool(harmony_symbols, false,
"enable harmony symbols (a.k.a. private names)")
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
DEFINE_bool(harmony_collections, false,
"enable harmony collections (sets, maps, and weak maps)")
@ -149,6 +151,7 @@ DEFINE_bool(harmony_observation, false,
DEFINE_bool(harmony, false, "enable all harmony features (except typeof)")
DEFINE_implication(harmony, harmony_scoping)
DEFINE_implication(harmony, harmony_modules)
DEFINE_implication(harmony, harmony_symbols)
DEFINE_implication(harmony, harmony_proxies)
DEFINE_implication(harmony, harmony_collections)
DEFINE_implication(harmony, harmony_observation)

View File

@ -4753,7 +4753,19 @@ void HOptimizedGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
typecheck->SetSuccessorAt(1, not_spec_object);
current_block()->Finish(typecheck);
if_spec_object->AddLeaveInlined(return_value, state);
not_spec_object->AddLeaveInlined(receiver, 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);
}
}
} else if (state->inlining_kind() == SETTER_CALL_RETURN) {
// Return from an inlined setter call. The returned value is never used, the
@ -9663,6 +9675,16 @@ 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

@ -383,6 +383,10 @@ 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

@ -6885,6 +6885,63 @@ void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
}
void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::UNIQUE_NAME);
ASSERT(GetCondition() == equal);
// Registers containing left and right operands respectively.
Register left = edx;
Register right = eax;
Register tmp1 = ecx;
Register tmp2 = ebx;
// Check that both operands are heap objects.
Label miss;
__ mov(tmp1, left);
STATIC_ASSERT(kSmiTag == 0);
__ and_(tmp1, right);
__ JumpIfSmi(tmp1, &miss, Label::kNear);
// Check that both operands are unique names. This leaves the instance
// types loaded in tmp1 and tmp2.
STATIC_ASSERT(kInternalizedTag != 0);
__ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
__ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
__ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
__ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
Label succeed1;
__ test(tmp1, Immediate(kIsInternalizedMask));
__ j(not_zero, &succeed1);
__ cmpb(tmp1, static_cast<int8_t>(SYMBOL_TYPE));
__ j(not_equal, &miss);
__ bind(&succeed1);
Label succeed2;
__ test(tmp2, Immediate(kIsInternalizedMask));
__ j(not_zero, &succeed2);
__ cmpb(tmp2, static_cast<int8_t>(SYMBOL_TYPE));
__ j(not_equal, &miss);
__ bind(&succeed2);
// Unique names are compared by identity.
Label done;
__ cmp(left, right);
// Make sure eax is non-zero. At this point input operands are
// guaranteed to be non-zero.
ASSERT(right.is(eax));
__ j(not_equal, &done, Label::kNear);
STATIC_ASSERT(EQUAL == 0);
STATIC_ASSERT(kSmiTag == 0);
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
__ bind(&done);
__ ret(0);
__ bind(&miss);
GenerateMiss(masm);
}
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::STRING);
Label miss;

View File

@ -2560,6 +2560,7 @@ void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) {
// TODO(rossberg): incorporate symbols.
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 1);
@ -2695,6 +2696,28 @@ 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);
@ -4276,6 +4299,10 @@ 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

@ -5968,7 +5968,13 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ cmp(input, factory()->null_value());
__ j(equal, true_label);
}
__ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
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);
}
__ j(below, false_label);
__ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ j(above, false_label);

View File

@ -2366,6 +2366,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
eax, holder, ebx, edx, edi, name, &miss);
break;
case SYMBOL_CHECK:
// Check that the object is a symbol.
__ CmpObjectType(edx, SYMBOL_TYPE, eax);
__ j(not_equal, &miss);
break;
case NUMBER_CHECK: {
Label fast;
// Check that the object is a smi or a heap number.

View File

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

View File

@ -179,7 +179,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 == PROTOTYPE_MAP &&
} else if (cache_holder == DELEGATE_MAP &&
receiver->GetPrototype(isolate)->IsNull()) {
// IC::GetCodeCacheHolder is not applicable.
return false;
@ -2378,6 +2378,7 @@ const char* CompareIC::GetStateName(State state) {
case NUMBER: return "NUMBER";
case INTERNALIZED_STRING: return "INTERNALIZED_STRING";
case STRING: return "STRING";
case UNIQUE_NAME: return "UNIQUE_NAME";
case OBJECT: return "OBJECT";
case KNOWN_OBJECT: return "KNOWN_OBJECT";
case GENERIC: return "GENERIC";
@ -2396,6 +2397,7 @@ static CompareIC::State InputState(CompareIC::State old_state,
if (value->IsHeapNumber()) return CompareIC::NUMBER;
if (value->IsInternalizedString()) return CompareIC::INTERNALIZED_STRING;
if (value->IsString()) return CompareIC::STRING;
if (value->IsSymbol()) return CompareIC::UNIQUE_NAME;
if (value->IsJSObject()) return CompareIC::OBJECT;
break;
case CompareIC::SMI:
@ -2408,10 +2410,13 @@ static CompareIC::State InputState(CompareIC::State old_state,
case CompareIC::INTERNALIZED_STRING:
if (value->IsInternalizedString()) return CompareIC::INTERNALIZED_STRING;
if (value->IsString()) return CompareIC::STRING;
if (value->IsSymbol()) return CompareIC::UNIQUE_NAME;
break;
case CompareIC::STRING:
if (value->IsInternalizedString() || value->IsString())
return CompareIC::STRING;
if (value->IsString()) return CompareIC::STRING;
break;
case CompareIC::UNIQUE_NAME:
if (value->IsUniqueName()) return CompareIC::UNIQUE_NAME;
break;
case CompareIC::OBJECT:
if (value->IsJSObject()) return CompareIC::OBJECT;
@ -2451,6 +2456,7 @@ CompareIC::State CompareIC::TargetState(State old_state,
}
if (x->IsString() && y->IsString()) return STRING;
if (!Token::IsEqualityOp(op_)) return GENERIC;
if (x->IsUniqueName() && y->IsUniqueName()) return UNIQUE_NAME;
if (x->IsJSObject() && y->IsJSObject()) {
if (Handle<JSObject>::cast(x)->map() ==
Handle<JSObject>::cast(y)->map()) {
@ -2464,7 +2470,9 @@ CompareIC::State CompareIC::TargetState(State old_state,
return x->IsNumber() && y->IsNumber() ? NUMBER : GENERIC;
case INTERNALIZED_STRING:
ASSERT(Token::IsEqualityOp(op_));
return x->IsString() && y->IsString() ? STRING : GENERIC;
if (x->IsString() && y->IsString()) return STRING;
if (x->IsUniqueName() && y->IsUniqueName()) return UNIQUE_NAME;
return GENERIC;
case NUMBER:
// If the failure was due to one side changing from smi to heap number,
// then keep the state (if other changed at the same time, we will get
@ -2477,6 +2485,7 @@ CompareIC::State CompareIC::TargetState(State old_state,
if (x->IsJSObject() && y->IsJSObject()) return OBJECT;
return GENERIC;
case STRING:
case UNIQUE_NAME:
case OBJECT:
case GENERIC:
return GENERIC;

View File

@ -764,6 +764,7 @@ class CompareIC: public IC {
NUMBER,
STRING,
INTERNALIZED_STRING,
UNIQUE_NAME, // Symbol or InternalizedString
OBJECT, // JSObject
KNOWN_OBJECT, // JSObject with specific map (faster check)
GENERIC

View File

@ -99,6 +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_OBJECT(arg) = (%_IsObject(arg));
macro IS_ARRAY(arg) = (%_IsArray(arg));
macro IS_FUNCTION(arg) = (%_IsFunction(arg));

View File

@ -186,18 +186,22 @@ TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE)
TYPE_CHECKER(Symbol, SYMBOL_TYPE)
bool Object::IsName() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() <= LAST_NAME_TYPE;
}
bool Object::IsString() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() < FIRST_NONSTRING_TYPE;
}
bool Object::IsName() {
return IsString() || IsSymbol();
}
bool Object::IsUniqueName() {
return IsInternalizedString() || IsSymbol();
}
bool Object::IsSpecObject() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() >= FIRST_SPEC_OBJECT_TYPE;
@ -2429,6 +2433,7 @@ CAST_ACCESSOR(JSObject)
CAST_ACCESSOR(Smi)
CAST_ACCESSOR(HeapObject)
CAST_ACCESSOR(HeapNumber)
CAST_ACCESSOR(Name)
CAST_ACCESSOR(Oddball)
CAST_ACCESSOR(JSGlobalPropertyCell)
CAST_ACCESSOR(SharedFunctionInfo)

View File

@ -142,6 +142,8 @@ void Object::Lookup(String* name, LookupResult* result) {
holder = native_context->string_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);
@ -624,7 +626,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()
Object* last = result->IsProperty() && !receiver->IsSymbol()
? result->holder()
: Object::cast(heap->null_value());
ASSERT(this != this->GetPrototype(isolate));
@ -709,6 +711,8 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
holder = native_context->string_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 {
@ -776,6 +780,16 @@ 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 string, an odd-ball,
// a real JS object, or a Harmony proxy.
@ -783,8 +797,8 @@ MaybeObject* Object::GetHash(CreationFlag flag) {
uint32_t hash = ComputeLongHash(double_to_uint64(Number()));
return Smi::FromInt(hash & Smi::kMaxValue);
}
if (IsString()) {
uint32_t hash = String::cast(this)->Hash();
if (IsName()) {
uint32_t hash = Name::cast(this)->Hash();
return Smi::FromInt(hash);
}
if (IsOddball()) {

View File

@ -677,7 +677,10 @@ enum InstanceType {
FIRST_TYPE = 0x0,
LAST_TYPE = JS_FUNCTION_TYPE,
INVALID_TYPE = FIRST_TYPE - 1,
FIRST_NAME_TYPE = FIRST_TYPE,
LAST_NAME_TYPE = SYMBOL_TYPE,
FIRST_UNIQUE_NAME_TYPE = INTERNALIZED_STRING_TYPE,
LAST_UNIQUE_NAME_TYPE = SYMBOL_TYPE,
FIRST_NONSTRING_TYPE = SYMBOL_TYPE,
// Boundaries for testing for an external array.
FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE,
@ -855,6 +858,7 @@ class MaybeObject BASE_EMBEDDED {
#define HEAP_OBJECT_TYPE_LIST(V) \
V(HeapNumber) \
V(Name) \
V(UniqueName) \
V(String) \
V(SeqString) \
V(ExternalString) \
@ -1036,6 +1040,9 @@ 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

@ -681,6 +681,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateSymbol) {
NoHandleAllocation ha(isolate);
ASSERT(args.length() == 0);
return isolate->heap()->AllocateSymbol();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSReceiver, handler, 0);

View File

@ -296,6 +296,9 @@ namespace internal {
/* Harmony modules */ \
F(IsJSModule, 1, 1) \
\
/* Harmony symbols */ \
F(CreateSymbol, 0, 1) \
\
/* Harmony proxies */ \
F(CreateJSProxy, 2, 1) \
F(CreateJSFunctionProxy, 4, 1) \
@ -509,6 +512,7 @@ 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) \
@ -542,7 +546,7 @@ namespace internal {
// ----------------------------------------------------------------------------
// INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
// INLINE_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
// with a native call of the form %_name from within JS code that also have
// a corresponding runtime function, that is called for slow cases.
// Entries have the form F(name, number of arguments, number of return values).

View File

@ -584,6 +584,8 @@ Handle<Code> StubCache::ComputeCallConstant(int argc,
CheckType check = RECEIVER_MAP_CHECK;
if (object->IsString()) {
check = STRING_CHECK;
} else if (object->IsSymbol()) {
check = SYMBOL_CHECK;
} else if (object->IsNumber()) {
check = NUMBER_CHECK;
} else if (object->IsBoolean()) {
@ -633,7 +635,8 @@ Handle<Code> StubCache::ComputeCallField(int argc,
// TODO(1233596): We cannot do receiver map check for non-JS objects
// because they may be represented as immediates without a
// map. Instead, we check against the map in the holder.
if (object->IsNumber() || object->IsBoolean() || object->IsString()) {
if (object->IsNumber() || object->IsSymbol() ||
object->IsBoolean() || object->IsString()) {
object = holder;
}
@ -671,7 +674,8 @@ Handle<Code> StubCache::ComputeCallInterceptor(int argc,
// TODO(1233596): We cannot do receiver map check for non-JS objects
// because they may be represented as immediates without a
// map. Instead, we check against the map in the holder.
if (object->IsNumber() || object->IsBoolean() || object->IsString()) {
if (object->IsNumber() || object->IsSymbol() ||
object->IsBoolean() || object->IsString()) {
object = holder;
}

39
src/symbol.js Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"use strict";
var $Symbol = function() { return %CreateSymbol() }
global.Symbol = $Symbol
// Symbols only have a toString method and no prototype.
var SymbolDelegate = {
__proto__: null,
toString: $Object.prototype.toString
}
$Object.freeze(SymbolDelegate)

View File

@ -265,6 +265,8 @@ 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;

View File

@ -276,6 +276,7 @@ enum InlineCacheState {
enum CheckType {
RECEIVER_MAP_CHECK,
STRING_CHECK,
SYMBOL_CHECK,
NUMBER_CHECK,
BOOLEAN_CHECK
};
@ -293,7 +294,7 @@ enum CallFunctionFlags {
enum InlineCacheHolderFlag {
OWN_MAP, // For fast properties objects.
PROTOTYPE_MAP // For slow properties objects (except GlobalObjects).
DELEGATE_MAP // For slow properties objects (except GlobalObjects).
};

View File

@ -231,10 +231,9 @@ $Object.prototype.constructor = $Object;
// ECMA-262 - 15.2.4.2
function ObjectToString() {
if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
return '[object Undefined]';
}
if (IS_NULL(this)) return '[object Null]';
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)) + "]";
}

View File

@ -389,6 +389,10 @@ 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

@ -5876,6 +5876,61 @@ void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
}
void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::UNIQUE_NAME);
ASSERT(GetCondition() == equal);
// Registers containing left and right operands respectively.
Register left = rdx;
Register right = rax;
Register tmp1 = rcx;
Register tmp2 = rbx;
// Check that both operands are heap objects.
Label miss;
Condition cond = masm->CheckEitherSmi(left, right, tmp1);
__ j(cond, &miss, Label::kNear);
// Check that both operands are unique names. This leaves the instance
// types loaded in tmp1 and tmp2.
STATIC_ASSERT(kInternalizedTag != 0);
__ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
__ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
__ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
__ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
Label succeed1;
__ testb(tmp1, Immediate(kIsInternalizedMask));
__ j(not_zero, &succeed1, Label::kNear);
__ cmpb(tmp1, Immediate(static_cast<int8_t>(SYMBOL_TYPE)));
__ j(not_equal, &miss, Label::kNear);
__ bind(&succeed1);
Label succeed2;
__ testb(tmp2, Immediate(kIsInternalizedMask));
__ j(not_zero, &succeed2, Label::kNear);
__ cmpb(tmp2, Immediate(static_cast<int8_t>(SYMBOL_TYPE)));
__ j(not_equal, &miss, Label::kNear);
__ bind(&succeed2);
// Unique names are compared by identity.
Label done;
__ cmpq(left, right);
// Make sure rax is non-zero. At this point input operands are
// guaranteed to be non-zero.
ASSERT(right.is(rax));
__ j(not_equal, &done, Label::kNear);
STATIC_ASSERT(EQUAL == 0);
STATIC_ASSERT(kSmiTag == 0);
__ Move(rax, Smi::FromInt(EQUAL));
__ bind(&done);
__ ret(0);
__ bind(&miss);
GenerateMiss(masm);
}
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::STRING);
Label miss;

View File

@ -2671,6 +2671,28 @@ 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);
@ -4272,6 +4294,10 @@ 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

@ -5552,7 +5552,13 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ CompareRoot(input, Heap::kNullValueRootIndex);
__ j(equal, true_label);
}
__ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
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);
}
__ j(below, false_label);
__ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
__ j(above, false_label);

View File

@ -2191,6 +2191,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
rax, holder, rbx, rdx, rdi, name, &miss);
break;
case SYMBOL_CHECK:
// Check that the object is a symbol.
__ CmpObjectType(rdx, SYMBOL_TYPE, rax);
__ j(not_equal, &miss);
break;
case NUMBER_CHECK: {
Label fast;
// Check that the object is a smi or a heap number.

View File

@ -0,0 +1,127 @@
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --harmony-symbols --harmony-collections
// Flags: --expose-gc --allow-natives-syntax
var symbols = []
// Test different forms of constructor calls, all equivalent.
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())
}
}
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]))
}
}
TestType()
function TestEquality() {
// Every symbol should equal itself.
for (var i in symbols) {
assertSame(symbols[i], symbols[i])
assertEquals(symbols[i], symbols[i])
assertTrue(Object.is(symbols[i], symbols[i]))
assertTrue(symbols[i] === symbols[i])
assertTrue(symbols[i] == symbols[i])
}
// All symbols should be distinct.
for (var i = 0; i < symbols.length; ++i) {
for (var j = i + 1; j < symbols.length; ++j) {
assertFalse(Object.is(symbols[i], symbols[j]))
assertFalse(symbols[i] === symbols[j])
assertFalse(symbols[i] == symbols[j])
}
}
}
TestEquality()
function TestGet() {
for (var i in symbols) {
assertEquals("[object Symbol]", symbols[i].toString())
assertEquals(undefined, symbols[i].valueOf)
assertEquals(undefined, symbols[i].a)
assertEquals(undefined, symbols[i]["a" + "b"])
assertEquals(undefined, symbols[i]["" + "1"])
assertEquals(undefined, symbols[i][62])
}
}
TestGet()
function TestSet() {
for (var i in symbols) {
symbols[i].toString = 0
assertEquals("[object Symbol]", symbols[i].toString())
symbols[i].a = 0
assertEquals(undefined, symbols[i].a)
symbols[i]["a" + "b"] = 0
assertEquals(undefined, symbols[i]["a" + "b"])
symbols[i][62] = 0
assertEquals(undefined, symbols[i][62])
}
}
TestSet()
function TestMap() {
var map = new Map;
for (var i in symbols) {
map.set(symbols[i], i)
}
for (var i in symbols) {
assertTrue(map.has(symbols[i]))
assertEquals(i, map.get(symbols[i]))
}
}
TestMap()

View File

@ -794,6 +794,7 @@
],
'experimental_library_files': [
'../../src/macros.py',
'../../src/symbol.js',
'../../src/proxy.js',
'../../src/collection.js',
'../../src/object-observe.js'