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:
parent
51a888ff48
commit
83d4a41dec
@ -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;
|
||||
|
@ -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 + "]";
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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: {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1488,6 +1488,7 @@ class ToBooleanStub: public PlatformCodeStub {
|
||||
SMI,
|
||||
SPEC_OBJECT,
|
||||
STRING,
|
||||
SYMBOL,
|
||||
HEAP_NUMBER,
|
||||
NUMBER_OF_TYPES
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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") {
|
||||
|
@ -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") \
|
||||
|
@ -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)) {
|
||||
|
@ -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)));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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: {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
|
@ -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"],
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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) \
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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).
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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: {
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user