ES6 symbols: turn symbols into a proper primitive type
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 22 Mar 2013 16:33:50 +0000 (16:33 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 22 Mar 2013 16:33:50 +0000 (16:33 +0000)
(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

37 files changed:
include/v8.h
src/api.cc
src/arm/builtins-arm.cc
src/arm/full-codegen-arm.cc
src/arm/lithium-codegen-arm.cc
src/arm/stub-cache-arm.cc
src/bootstrapper.cc
src/code-stubs.cc
src/code-stubs.h
src/contexts.h
src/d8.js
src/heap.h
src/hydrogen-instructions.cc
src/hydrogen.cc
src/ia32/builtins-ia32.cc
src/ia32/code-stubs-ia32.cc
src/ia32/full-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/stub-cache-ia32.cc
src/ic-inl.h
src/ic.cc
src/macros.py
src/messages.js
src/objects.cc
src/objects.h
src/runtime.cc
src/runtime.h
src/runtime.js
src/symbol.js
src/type-info.cc
src/v8globals.h
src/v8natives.js
src/x64/builtins-x64.cc
src/x64/full-codegen-x64.cc
src/x64/lithium-codegen-x64.cc
src/x64/stub-cache-x64.cc
test/mjsunit/harmony/symbols.js

index 329576f..d2b5715 100644 (file)
@@ -4274,7 +4274,7 @@ class Internals {
   static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
   static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
   static const int kContextHeaderSize = 2 * kApiPointerSize;
-  static const int kContextEmbedderDataIndex = 54;
+  static const int kContextEmbedderDataIndex = 55;
   static const int kFullStringRepresentationMask = 0x07;
   static const int kStringEncodingMask = 0x4;
   static const int kExternalTwoByteRepresentationTag = 0x02;
index 71d3f78..7fce39e 100644 (file)
@@ -3189,7 +3189,7 @@ Local<String> v8::Object::ObjectProtoToString() {
   i::Handle<i::Object> name(self->class_name(), isolate);
 
   // Native implementation of Object.prototype.toString (v8natives.js):
-  //   var c = %ClassOf(this);
+  //   var c = %_ClassOf(this);
   //   if (c === 'Arguments') c  = 'Object';
   //   return "[object " + c + "]";
 
index 2946b35..98a4c0b 100644 (file)
@@ -1105,10 +1105,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     __ CompareObjectType(r0, r1, r3, FIRST_SPEC_OBJECT_TYPE);
     __ b(ge, &exit);
 
-    // Symbols are "objects".
-    __ CompareInstanceType(r1, r3, SYMBOL_TYPE);
-    __ b(eq, &exit);
-
     // Throw away the result of the constructor invocation and use the
     // on-stack receiver as the result.
     __ bind(&use_receiver);
index 1df1649..6086645 100644 (file)
@@ -2764,28 +2764,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
 }
 
 
-void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) {
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-
-  VisitForAccumulatorValue(args->at(0));
-
-  Label materialize_true, materialize_false;
-  Label* if_true = NULL;
-  Label* if_false = NULL;
-  Label* fall_through = NULL;
-  context()->PrepareTest(&materialize_true, &materialize_false,
-                         &if_true, &if_false, &fall_through);
-
-  __ JumpIfSmi(r0, if_false);
-  __ CompareObjectType(r0, r1, r2, SYMBOL_TYPE);
-  PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
-  Split(eq, if_true, if_false, fall_through);
-
-  context()->Plug(if_true, if_false);
-}
-
-
 void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
@@ -4292,6 +4270,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
     __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset));
     __ tst(r1, Operand(1 << Map::kIsUndetectable));
     Split(eq, if_true, if_false, fall_through);
+  } else if (check->Equals(isolate()->heap()->symbol_string())) {
+    __ JumpIfSmi(r0, if_false);
+    __ CompareObjectType(r0, r0, r1, SYMBOL_TYPE);
+    Split(eq, if_true, if_false, fall_through);
   } else if (check->Equals(isolate()->heap()->boolean_string())) {
     __ CompareRoot(r0, Heap::kTrueValueRootIndex);
     __ b(eq, if_true);
@@ -4324,10 +4306,6 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
       __ CompareRoot(r0, Heap::kNullValueRootIndex);
       __ b(eq, if_true);
     }
-    if (FLAG_harmony_symbols) {
-      __ CompareObjectType(r0, r0, r1, SYMBOL_TYPE);
-      __ b(eq, if_true);
-    }
     // Check for JS objects => true.
     __ CompareObjectType(r0, r0, r1, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
     __ b(lt, if_false);
index 8040791..f65042e 100644 (file)
@@ -2301,6 +2301,12 @@ void LCodeGen::DoBranch(LBranch* instr) {
         __ bind(&not_string);
       }
 
+      if (expected.Contains(ToBooleanStub::SYMBOL)) {
+        // Symbol value -> true.
+        __ CompareInstanceType(map, ip, SYMBOL_TYPE);
+        __ b(eq, true_label);
+      }
+
       if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
         CpuFeatureScope scope(masm(), VFP2);
         // heap number -> false iff +0, -0, or NaN.
@@ -6074,6 +6080,11 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
     __ tst(ip, Operand(1 << Map::kIsUndetectable));
     final_branch_condition = eq;
 
+  } else if (type_name->Equals(heap()->symbol_string())) {
+    __ JumpIfSmi(input, false_label);
+    __ CompareObjectType(input, input, scratch, SYMBOL_TYPE);
+    final_branch_condition = eq;
+
   } else if (type_name->Equals(heap()->boolean_string())) {
     __ CompareRoot(input, Heap::kTrueValueRootIndex);
     __ b(eq, true_label);
@@ -6108,15 +6119,8 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
       __ CompareRoot(input, Heap::kNullValueRootIndex);
       __ b(eq, true_label);
     }
-    if (FLAG_harmony_symbols) {
-      __ CompareObjectType(input, input, scratch, SYMBOL_TYPE);
-      __ b(eq, true_label);
-      __ CompareInstanceType(input, scratch,
-                             FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
-    } else {
-      __ CompareObjectType(input, input, scratch,
-                           FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
-    }
+    __ CompareObjectType(input, input, scratch,
+                         FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
     __ b(lt, false_label);
     __ CompareInstanceType(input, scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
     __ b(gt, false_label);
index 3350c56..06edce8 100644 (file)
@@ -2391,6 +2391,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
       // Check that the object is a symbol.
       __ CompareObjectType(r1, r1, r3, SYMBOL_TYPE);
       __ b(ne, &miss);
+      // Check that the maps starting from the prototype haven't changed.
+      GenerateDirectLoadGlobalFunctionPrototype(
+          masm(), Context::SYMBOL_FUNCTION_INDEX, r0, &miss);
+      CheckPrototypes(
+          Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+          r0, holder, r3, r1, r4, name, &miss);
       break;
 
     case NUMBER_CHECK: {
index ce58a86..380c81e 100644 (file)
@@ -1280,7 +1280,17 @@ void Genesis::InitializeExperimentalGlobal() {
   Handle<JSObject> global = Handle<JSObject>(native_context()->global_object());
 
   // TODO(mstarzinger): Move this into Genesis::InitializeGlobal once we no
-  // longer need to live behind a flag, so functions get added to the snapshot.
+  // longer need to live behind flags, so functions get added to the snapshot.
+
+  if (FLAG_harmony_symbols) {
+    // --- S y m b o l ---
+    Handle<JSFunction> symbol_fun =
+        InstallFunction(global, "Symbol", JS_VALUE_TYPE, JSValue::kSize,
+                        isolate()->initial_object_prototype(),
+                        Builtins::kIllegal, true);
+    native_context()->set_symbol_function(*symbol_fun);
+  }
+
   if (FLAG_harmony_collections) {
     {  // -- S e t
       Handle<JSObject> prototype =
@@ -1436,9 +1446,6 @@ void Genesis::InstallNativeFunctions() {
 }
 
 void Genesis::InstallExperimentalNativeFunctions() {
-  if (FLAG_harmony_symbols) {
-    INSTALL_NATIVE(JSObject, "SymbolDelegate", symbol_delegate);
-  }
   if (FLAG_harmony_proxies) {
     INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap);
     INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap);
index 95bc1e9..1884b56 100644 (file)
@@ -510,6 +510,7 @@ void ToBooleanStub::Types::Print(StringStream* stream) const {
   if (Contains(SMI)) stream->Add("Smi");
   if (Contains(SPEC_OBJECT)) stream->Add("SpecObject");
   if (Contains(STRING)) stream->Add("String");
+  if (Contains(SYMBOL)) stream->Add("Symbol");
   if (Contains(HEAP_NUMBER)) stream->Add("HeapNumber");
 }
 
@@ -549,6 +550,9 @@ bool ToBooleanStub::Types::Record(Handle<Object> object) {
     Add(STRING);
     return !object->IsUndetectableObject() &&
         String::cast(*object)->length() != 0;
+  } else if (object->IsSymbol()) {
+    Add(SYMBOL);
+    return true;
   } else if (object->IsHeapNumber()) {
     ASSERT(!object->IsUndetectableObject());
     Add(HEAP_NUMBER);
@@ -565,6 +569,7 @@ bool ToBooleanStub::Types::Record(Handle<Object> object) {
 bool ToBooleanStub::Types::NeedsMap() const {
   return Contains(ToBooleanStub::SPEC_OBJECT)
       || Contains(ToBooleanStub::STRING)
+      || Contains(ToBooleanStub::SYMBOL)
       || Contains(ToBooleanStub::HEAP_NUMBER);
 }
 
index 6e95780..92a3450 100644 (file)
@@ -1488,6 +1488,7 @@ class ToBooleanStub: public PlatformCodeStub {
     SMI,
     SPEC_OBJECT,
     STRING,
+    SYMBOL,
     HEAP_NUMBER,
     NUMBER_OF_TYPES
   };
index cd7ed6a..a0ba2f7 100644 (file)
@@ -103,6 +103,7 @@ enum BindingFlags {
   V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
   V(STRING_FUNCTION_INDEX, JSFunction, string_function) \
   V(STRING_FUNCTION_PROTOTYPE_MAP_INDEX, Map, string_function_prototype_map) \
+  V(SYMBOL_FUNCTION_INDEX, JSFunction, symbol_function) \
   V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
   V(INTERNAL_ARRAY_FUNCTION_INDEX, JSFunction, internal_array_function) \
   V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
@@ -156,7 +157,6 @@ enum BindingFlags {
   V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
   V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \
     error_message_for_code_gen_from_strings) \
-  V(SYMBOL_DELEGATE_INDEX, JSObject, symbol_delegate) \
   V(TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX, JSFunction, \
     to_complete_property_descriptor) \
   V(DERIVED_HAS_TRAP_INDEX, JSFunction, derived_has_trap) \
@@ -251,6 +251,7 @@ class Context: public FixedArray {
     NUMBER_FUNCTION_INDEX,
     STRING_FUNCTION_INDEX,
     STRING_FUNCTION_PROTOTYPE_MAP_INDEX,
+    SYMBOL_FUNCTION_INDEX,
     OBJECT_FUNCTION_INDEX,
     INTERNAL_ARRAY_FUNCTION_INDEX,
     ARRAY_FUNCTION_INDEX,
@@ -287,7 +288,6 @@ class Context: public FixedArray {
     EMBEDDER_DATA_INDEX,
     ALLOW_CODE_GEN_FROM_STRINGS_INDEX,
     ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX,
-    SYMBOL_DELEGATE_INDEX,
     TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX,
     DERIVED_HAS_TRAP_INDEX,
     DERIVED_GET_TRAP_INDEX,
index 1ff0ce8..8b30fcf 100644 (file)
--- 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") {
index b3be86b..7a1d3c5 100644 (file)
@@ -213,6 +213,8 @@ namespace internal {
   V(prototype_string, "prototype")                                       \
   V(string_string, "string")                                             \
   V(String_string, "String")                                             \
+  V(symbol_string, "symbol")                                             \
+  V(Symbol_string, "Symbol")                                             \
   V(Date_string, "Date")                                                 \
   V(this_string, "this")                                                 \
   V(to_string_string, "toString")                                        \
index fc80748..9b96cfe 100644 (file)
@@ -1277,7 +1277,8 @@ Representation HBranch::observed_input_representation(int index) {
       ToBooleanStub::UNDEFINED |
       ToBooleanStub::NULL_TYPE |
       ToBooleanStub::SPEC_OBJECT |
-      ToBooleanStub::STRING);
+      ToBooleanStub::STRING |
+      ToBooleanStub::SYMBOL);
   if (expected_input_types_.ContainsAnyOf(tagged_types)) {
     return Representation::Tagged();
   } else if (expected_input_types_.Contains(ToBooleanStub::HEAP_NUMBER)) {
index 8dbb5dd..435805f 100644 (file)
@@ -5083,19 +5083,7 @@ void HOptimizedGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
       typecheck->SetSuccessorAt(1, not_spec_object);
       current_block()->Finish(typecheck);
       if_spec_object->AddLeaveInlined(return_value, state);
-      if (!FLAG_harmony_symbols) {
-        not_spec_object->AddLeaveInlined(receiver, state);
-      } else {
-        HHasInstanceTypeAndBranch* symbolcheck =
-          new(zone()) HHasInstanceTypeAndBranch(return_value, SYMBOL_TYPE);
-        HBasicBlock* is_symbol = graph()->CreateBasicBlock();
-        HBasicBlock* not_symbol = graph()->CreateBasicBlock();
-        symbolcheck->SetSuccessorAt(0, is_symbol);
-        symbolcheck->SetSuccessorAt(1, not_symbol);
-        not_spec_object->Finish(symbolcheck);
-        is_symbol->AddLeaveInlined(return_value, state);
-        not_symbol->AddLeaveInlined(receiver, state);
-      }
+      not_spec_object->AddLeaveInlined(receiver, state);
     }
   } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
     // Return from an inlined setter call. The returned value is never used, the
@@ -10080,16 +10068,6 @@ void HOptimizedGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
 }
 
 
-void HOptimizedGraphBuilder::GenerateIsSymbol(CallRuntime* call) {
-  ASSERT(call->arguments()->length() == 1);
-  CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
-  HValue* value = Pop();
-  HHasInstanceTypeAndBranch* result =
-      new(zone()) HHasInstanceTypeAndBranch(value, SYMBOL_TYPE);
-  return ast_context()->ReturnControl(result, call->id());
-}
-
-
 void HOptimizedGraphBuilder::GenerateIsFunction(CallRuntime* call) {
   ASSERT(call->arguments()->length() == 1);
   CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
index afb1c03..ff5ee53 100644 (file)
@@ -409,10 +409,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
     __ j(above_equal, &exit);
 
-    // Symbols are "objects".
-    __ CmpInstanceType(ecx, SYMBOL_TYPE);
-    __ j(equal, &exit);
-
     // Throw away the result of the constructor invocation and use the
     // on-stack receiver as the result.
     __ bind(&use_receiver);
index b30d519..70d507a 100644 (file)
@@ -632,6 +632,14 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
     __ bind(&not_string);
   }
 
+  if (types_.Contains(SYMBOL)) {
+    // Symbol value -> true.
+    Label not_symbol;
+    __ CmpInstanceType(map, SYMBOL_TYPE);
+    __ j(not_equal, &not_symbol, Label::kNear);
+    __ bind(&not_symbol);
+  }
+
   if (types_.Contains(HEAP_NUMBER)) {
     // heap number -> false iff +0, -0, or NaN.
     Label not_heap_number, false_result;
index ebc3a2b..efbdf13 100644 (file)
@@ -2567,7 +2567,6 @@ void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
 
 
 void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) {
-  // TODO(rossberg): incorporate symbols.
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
 
@@ -2703,28 +2702,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
 }
 
 
-void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) {
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-
-  VisitForAccumulatorValue(args->at(0));
-
-  Label materialize_true, materialize_false;
-  Label* if_true = NULL;
-  Label* if_false = NULL;
-  Label* fall_through = NULL;
-  context()->PrepareTest(&materialize_true, &materialize_false,
-                         &if_true, &if_false, &fall_through);
-
-  __ JumpIfSmi(eax, if_false);
-  __ CmpObjectType(eax, SYMBOL_TYPE, ebx);
-  PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
-  Split(equal, if_true, if_false, fall_through);
-
-  context()->Plug(if_true, if_false);
-}
-
-
 void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
@@ -4275,6 +4252,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
     __ test_b(FieldOperand(edx, Map::kBitFieldOffset),
               1 << Map::kIsUndetectable);
     Split(zero, if_true, if_false, fall_through);
+  } else if (check->Equals(isolate()->heap()->symbol_string())) {
+    __ JumpIfSmi(eax, if_false);
+    __ CmpObjectType(eax, SYMBOL_TYPE, edx);
+    Split(equal, if_true, if_false, fall_through);
   } else if (check->Equals(isolate()->heap()->boolean_string())) {
     __ cmp(eax, isolate()->factory()->true_value());
     __ j(equal, if_true);
@@ -4306,10 +4287,6 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
       __ cmp(eax, isolate()->factory()->null_value());
       __ j(equal, if_true);
     }
-    if (FLAG_harmony_symbols) {
-      __ CmpObjectType(eax, SYMBOL_TYPE, edx);
-      __ j(equal, if_true);
-    }
     __ CmpObjectType(eax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, edx);
     __ j(below, if_false);
     __ CmpInstanceType(edx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
index 205781b..588b6b6 100644 (file)
@@ -2116,6 +2116,12 @@ void LCodeGen::DoBranch(LBranch* instr) {
         __ bind(&not_string);
       }
 
+      if (expected.Contains(ToBooleanStub::SYMBOL)) {
+        // Symbol value -> true.
+        __ CmpInstanceType(map, SYMBOL_TYPE);
+        __ j(equal, true_label);
+      }
+
       if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
         // heap number -> false iff +0, -0, or NaN.
         Label not_heap_number;
@@ -5979,6 +5985,11 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
               1 << Map::kIsUndetectable);
     final_branch_condition = zero;
 
+  } else if (type_name->Equals(heap()->symbol_string())) {
+    __ JumpIfSmi(input, false_label);
+    __ CmpObjectType(input, SYMBOL_TYPE, input);
+    final_branch_condition = equal;
+
   } else if (type_name->Equals(heap()->boolean_string())) {
     __ cmp(input, factory()->true_value());
     __ j(equal, true_label);
@@ -6013,13 +6024,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
       __ cmp(input, factory()->null_value());
       __ j(equal, true_label);
     }
-    if (FLAG_harmony_symbols) {
-      __ CmpObjectType(input, SYMBOL_TYPE, input);
-      __ j(equal, true_label);
-      __ CmpInstanceType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
-    } else {
-      __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
-    }
+    __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
     __ j(below, false_label);
     __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
     __ j(above, false_label);
index f7e795e..94ce9d7 100644 (file)
@@ -2350,6 +2350,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
       // Check that the object is a symbol.
       __ CmpObjectType(edx, SYMBOL_TYPE, eax);
       __ j(not_equal, &miss);
+      // Check that the maps starting from the prototype haven't changed.
+      GenerateDirectLoadGlobalFunctionPrototype(
+          masm(), Context::SYMBOL_FUNCTION_INDEX, eax, &miss);
+      CheckPrototypes(
+          Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+          eax, holder, ebx, edx, edi, name, &miss);
       break;
 
     case NUMBER_CHECK: {
index 9439792..ca02183 100644 (file)
@@ -109,7 +109,7 @@ InlineCacheHolderFlag IC::GetCodeCacheForObject(Object* object,
   // If the object is a value, we use the prototype map for the cache.
   ASSERT(object->IsString() || object->IsSymbol() ||
          object->IsNumber() || object->IsBoolean());
-  return DELEGATE_MAP;
+  return PROTOTYPE_MAP;
 }
 
 
@@ -124,7 +124,7 @@ InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
       !object->HasFastProperties() &&
       !object->IsJSGlobalProxy() &&
       !object->IsJSGlobalObject()) {
-    return DELEGATE_MAP;
+    return PROTOTYPE_MAP;
   }
   return OWN_MAP;
 }
@@ -133,7 +133,8 @@ InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
 JSObject* IC::GetCodeCacheHolder(Isolate* isolate,
                                  Object* object,
                                  InlineCacheHolderFlag holder) {
-  Object* map_owner = holder == OWN_MAP ? object : object->GetDelegate(isolate);
+  Object* map_owner =
+      holder == OWN_MAP ? object : object->GetPrototype(isolate);
   ASSERT(map_owner->IsJSObject());
   return JSObject::cast(map_owner);
 }
index c1d11bb..b77cb7d 100644 (file)
--- 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;
index 291a898..52e87fc 100644 (file)
@@ -99,7 +99,7 @@ macro IS_UNDEFINED(arg)         = (typeof(arg) === 'undefined');
 macro IS_NUMBER(arg)            = (typeof(arg) === 'number');
 macro IS_STRING(arg)            = (typeof(arg) === 'string');
 macro IS_BOOLEAN(arg)           = (typeof(arg) === 'boolean');
-macro IS_SYMBOL(arg)            = (%_IsSymbol(arg));
+macro IS_SYMBOL(arg)            = (typeof(arg) === 'symbol');
 macro IS_OBJECT(arg)            = (%_IsObject(arg));
 macro IS_ARRAY(arg)             = (%_IsArray(arg));
 macro IS_FUNCTION(arg)          = (%_IsFunction(arg));
@@ -110,6 +110,7 @@ macro IS_WEAKMAP(arg)           = (%_ClassOf(arg) === 'WeakMap');
 macro IS_DATE(arg)              = (%_ClassOf(arg) === 'Date');
 macro IS_NUMBER_WRAPPER(arg)    = (%_ClassOf(arg) === 'Number');
 macro IS_STRING_WRAPPER(arg)    = (%_ClassOf(arg) === 'String');
+macro IS_SYMBOL_WRAPPER(arg)    = (%_ClassOf(arg) === 'Symbol');
 macro IS_BOOLEAN_WRAPPER(arg)   = (%_ClassOf(arg) === 'Boolean');
 macro IS_ERROR(arg)             = (%_ClassOf(arg) === 'Error');
 macro IS_SCRIPT(arg)            = (%_ClassOf(arg) === 'Script');
index 7353444..cbb5000 100644 (file)
@@ -150,6 +150,7 @@ var kMessages = {
   cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
   redef_external_array_element:  ["Cannot redefine a property of an object with external array elements"],
   harmony_const_assign:          ["Assignment to constant variable."],
+  symbol_to_string:              ["Conversion from symbol to string"],
   invalid_module_path:           ["Module does not export '", "%0", "', or export is not itself a module"],
   module_type_error:             ["Module '", "%0", "' used improperly"],
   module_export_undefined:       ["Export '", "%0", "' is not defined in module"],
index dbbec58..5fb9738 100644 (file)
@@ -126,10 +126,10 @@ void Object::Lookup(Name* name, LookupResult* result) {
       holder = native_context->number_function()->instance_prototype();
     } else if (IsString()) {
       holder = native_context->string_function()->instance_prototype();
+    } else if (IsSymbol()) {
+      holder = native_context->symbol_function()->instance_prototype();
     } else if (IsBoolean()) {
       holder = native_context->boolean_function()->instance_prototype();
-    } else if (IsSymbol()) {
-      holder = native_context->symbol_delegate();
     } else {
       Isolate::Current()->PushStackTraceAndDie(
           0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001);
@@ -756,7 +756,7 @@ MaybeObject* Object::GetProperty(Object* receiver,
   // holder in the prototype chain.
   // Proxy handlers do not use the proxy's prototype, so we can skip this.
   if (!result->IsHandler()) {
-    Object* last = result->IsProperty() && !receiver->IsSymbol()
+    Object* last = result->IsProperty()
         ? result->holder()
         : Object::cast(heap->null_value());
     ASSERT(this != this->GetPrototype(isolate));
@@ -837,10 +837,10 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
         holder = native_context->number_function()->instance_prototype();
       } else if (holder->IsString()) {
         holder = native_context->string_function()->instance_prototype();
+      } else if (holder->IsSymbol()) {
+        holder = native_context->symbol_function()->instance_prototype();
       } else if (holder->IsBoolean()) {
         holder = native_context->boolean_function()->instance_prototype();
-      } else if (holder->IsSymbol()) {
-        holder = native_context->symbol_delegate();
       } else if (holder->IsJSProxy()) {
         return JSProxy::cast(holder)->GetElementWithHandler(receiver, index);
       } else {
@@ -900,6 +900,9 @@ Object* Object::GetPrototype(Isolate* isolate) {
   if (heap_object->IsString()) {
     return context->string_function()->instance_prototype();
   }
+  if (heap_object->IsSymbol()) {
+    return context->symbol_function()->instance_prototype();
+  }
   if (heap_object->IsBoolean()) {
     return context->boolean_function()->instance_prototype();
   } else {
@@ -908,16 +911,6 @@ Object* Object::GetPrototype(Isolate* isolate) {
 }
 
 
-Object* Object::GetDelegate(Isolate* isolate) {
-  if (IsSymbol()) {
-    Heap* heap = Symbol::cast(this)->GetHeap();
-    Context* context = heap->isolate()->context()->native_context();
-    return context->symbol_delegate();
-  }
-  return GetPrototype(isolate);
-}
-
-
 MaybeObject* Object::GetHash(CreationFlag flag) {
   // The object is either a number, a name, an odd-ball,
   // a real JS object, or a Harmony proxy.
index 933a075..c8654e6 100644 (file)
@@ -1102,9 +1102,6 @@ class Object : public MaybeObject {
   // Return the object's prototype (might be Heap::null_value()).
   Object* GetPrototype(Isolate* isolate);
 
-  // Return the prototype, or the method holder for a value-like object.
-  Object* GetDelegate(Isolate* isolate);
-
   // Returns the permanent hash code associated with this object depending on
   // the actual object type.  Might return a failure in case no hash was
   // created yet or GC was caused by creation.
index 38f77d0..adcc2bc 100644 (file)
@@ -5035,6 +5035,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) {
       }
       ASSERT(heap_obj->IsUndefined());
       return isolate->heap()->undefined_string();
+    case SYMBOL_TYPE:
+      return isolate->heap()->symbol_string();
     case JS_FUNCTION_TYPE:
     case JS_FUNCTION_PROXY_TYPE:
       return isolate->heap()->function_string();
index 99ff49a..94edf1a 100644 (file)
@@ -512,7 +512,6 @@ namespace internal {
 #define INLINE_FUNCTION_LIST(F) \
   F(IsSmi, 1, 1)                                                             \
   F(IsNonNegativeSmi, 1, 1)                                                  \
-  F(IsSymbol, 1, 1)                                                          \
   F(IsArray, 1, 1)                                                           \
   F(IsRegExp, 1, 1)                                                          \
   F(IsConstructCall, 0, 1)                                                   \
index 09b39ff..8f4388a 100644 (file)
@@ -532,6 +532,7 @@ function ToNumber(x) {
   }
   if (IS_BOOLEAN(x)) return x ? 1 : 0;
   if (IS_UNDEFINED(x)) return $NaN;
+  if (IS_SYMBOL(x)) return $NaN;
   return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
 }
 
@@ -542,6 +543,7 @@ function NonNumberToNumber(x) {
   }
   if (IS_BOOLEAN(x)) return x ? 1 : 0;
   if (IS_UNDEFINED(x)) return $NaN;
+  if (IS_SYMBOL(x)) return $NaN;
   return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
 }
 
@@ -572,6 +574,7 @@ function ToName(x) {
 // ECMA-262, section 9.9, page 36.
 function ToObject(x) {
   if (IS_STRING(x)) return new $String(x);
+  if (IS_SYMBOL(x)) return new $Symbol(x);
   if (IS_NUMBER(x)) return new $Number(x);
   if (IS_BOOLEAN(x)) return new $Boolean(x);
   if (IS_NULL_OR_UNDEFINED(x) && !IS_UNDETECTABLE(x)) {
index b7f9dc9..b67e00b 100644 (file)
 
 "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();
index f31edb7..39a01f5 100644 (file)
@@ -283,11 +283,12 @@ Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
     case RECEIVER_MAP_CHECK:
       UNREACHABLE();
       break;
-    case SYMBOL_CHECK:
-      return Handle<JSObject>(native_context_->symbol_delegate());
     case STRING_CHECK:
       function = native_context_->string_function();
       break;
+    case SYMBOL_CHECK:
+      function = native_context_->symbol_function();
+      break;
     case NUMBER_CHECK:
       function = native_context_->number_function();
       break;
index 8874cfb..4b4d312 100644 (file)
@@ -296,7 +296,7 @@ enum CallFunctionFlags {
 
 enum InlineCacheHolderFlag {
   OWN_MAP,  // For fast properties objects.
-  DELEGATE_MAP  // For slow properties objects (except GlobalObjects).
+  PROTOTYPE_MAP  // For slow properties objects (except GlobalObjects).
 };
 
 
index 24ad22d..82e92db 100644 (file)
@@ -233,7 +233,6 @@ $Object.prototype.constructor = $Object;
 function ObjectToString() {
   if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
   if (IS_NULL(this)) return "[object Null]";
-  if (IS_SYMBOL(this)) return "[object Symbol]";
   return "[object " + %_ClassOf(ToObject(this)) + "]";
 }
 
@@ -865,7 +864,7 @@ function DefineArrayProperty(obj, p, desc, should_throw) {
   // DefineObjectProperty() to modify its value.
 
   // Step 3 - Special handling for length property.
-  if (p == "length") {
+  if (p === "length") {
     var length = obj.length;
     if (!desc.hasValue()) {
       return DefineObjectProperty(obj, "length", desc, should_throw);
index 0b70c1a..a49497e 100644 (file)
@@ -416,10 +416,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
     __ j(above_equal, &exit);
 
-    // Symbols are "objects".
-    __ CmpInstanceType(rcx, SYMBOL_TYPE);
-    __ j(equal, &exit);
-
     // Throw away the result of the constructor invocation and use the
     // on-stack receiver as the result.
     __ bind(&use_receiver);
index 8ff12df..322479d 100644 (file)
@@ -2678,28 +2678,6 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
 }
 
 
-void FullCodeGenerator::EmitIsSymbol(CallRuntime* expr) {
-  ZoneList<Expression*>* args = expr->arguments();
-  ASSERT(args->length() == 1);
-
-  VisitForAccumulatorValue(args->at(0));
-
-  Label materialize_true, materialize_false;
-  Label* if_true = NULL;
-  Label* if_false = NULL;
-  Label* fall_through = NULL;
-  context()->PrepareTest(&materialize_true, &materialize_false,
-                         &if_true, &if_false, &fall_through);
-
-  __ JumpIfSmi(rax, if_false);
-  __ CmpObjectType(rax, SYMBOL_TYPE, rbx);
-  PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
-  Split(equal, if_true, if_false, fall_through);
-
-  context()->Plug(if_true, if_false);
-}
-
-
 void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   ASSERT(args->length() == 1);
@@ -4271,6 +4249,10 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
     __ testb(FieldOperand(rdx, Map::kBitFieldOffset),
              Immediate(1 << Map::kIsUndetectable));
     Split(zero, if_true, if_false, fall_through);
+  } else if (check->Equals(isolate()->heap()->symbol_string())) {
+    __ JumpIfSmi(rax, if_false);
+    __ CmpObjectType(rax, SYMBOL_TYPE, rdx);
+    Split(equal, if_true, if_false, fall_through);
   } else if (check->Equals(isolate()->heap()->boolean_string())) {
     __ CompareRoot(rax, Heap::kTrueValueRootIndex);
     __ j(equal, if_true);
@@ -4302,10 +4284,6 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
       __ CompareRoot(rax, Heap::kNullValueRootIndex);
       __ j(equal, if_true);
     }
-    if (FLAG_harmony_symbols) {
-      __ CmpObjectType(rax, SYMBOL_TYPE, rdx);
-      __ j(equal, if_true);
-    }
     __ CmpObjectType(rax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, rdx);
     __ j(below, if_false);
     __ CmpInstanceType(rdx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
index b9bd30b..0bb676b 100644 (file)
@@ -5538,6 +5538,11 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
              Immediate(1 << Map::kIsUndetectable));
     final_branch_condition = zero;
 
+  } else if (type_name->Equals(heap()->symbol_string())) {
+    __ JumpIfSmi(input, false_label);
+    __ CmpObjectType(input, SYMBOL_TYPE, input);
+    final_branch_condition = equal;
+
   } else if (type_name->Equals(heap()->boolean_string())) {
     __ CompareRoot(input, Heap::kTrueValueRootIndex);
     __ j(equal, true_label);
@@ -5572,13 +5577,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
       __ CompareRoot(input, Heap::kNullValueRootIndex);
       __ j(equal, true_label);
     }
-    if (FLAG_harmony_symbols) {
-      __ CmpObjectType(input, SYMBOL_TYPE, input);
-      __ j(equal, true_label);
-      __ CmpInstanceType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
-    } else {
-      __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
-    }
+    __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
     __ j(below, false_label);
     __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
     __ j(above, false_label);
index 69d7a91..b6d38ca 100644 (file)
@@ -2174,6 +2174,12 @@ void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
       // Check that the object is a symbol.
       __ CmpObjectType(rdx, SYMBOL_TYPE, rax);
       __ j(not_equal, &miss);
+      // Check that the maps starting from the prototype haven't changed.
+      GenerateDirectLoadGlobalFunctionPrototype(
+          masm(), Context::SYMBOL_FUNCTION_INDEX, rax, &miss);
+      CheckPrototypes(
+          Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+          rax, holder, rbx, rdx, rdi, name, &miss);
       break;
 
     case NUMBER_CHECK: {
index cdc6362..fe7fd0a 100644 (file)
@@ -34,20 +34,19 @@ var symbols = []
 function TestNew() {
   function IndirectSymbol() { return new Symbol }
   function indirect() { return new IndirectSymbol() }
-  for (var i = 0; i < 10; ++i) {
-    symbols.push(new Symbol)
-    symbols.push(new Symbol())
-    symbols.push(Symbol())
-    symbols.push(indirect())
-  }
-  %OptimizeFunctionOnNextCall(indirect)
-  indirect()  // Call once before GC throws away type feedback.
-  gc()        // Promote existing symbols and then allocate some more.
-  for (var i = 0; i < 10; ++i) {
-    symbols.push(new Symbol)
-    symbols.push(new Symbol())
-    symbols.push(Symbol())
-    symbols.push(indirect())
+  for (var i = 0; i < 2; ++i) {
+    for (var j = 0; j < 5; ++j) {
+      symbols.push(Symbol())
+      symbols.push(Symbol(Symbol()))
+      symbols.push((new Symbol).valueOf())
+      symbols.push((new Symbol()).valueOf())
+      symbols.push((new Symbol(Symbol())).valueOf())
+      symbols.push(Object(Symbol()).valueOf())
+      symbols.push((indirect()).valueOf())
+    }
+    %OptimizeFunctionOnNextCall(indirect)
+    indirect()  // Call once before GC throws away type feedback.
+    gc()        // Promote existing symbols and then allocate some more.
   }
 }
 TestNew()
@@ -55,15 +54,70 @@ TestNew()
 
 function TestType() {
   for (var i in symbols) {
-    assertTrue(%_IsSymbol(symbols[i]))
-    assertEquals("object", typeof symbols[i])
-    assertTrue(typeof symbols[i] === "object")
-    assertEquals("[object Symbol]", Object.prototype.toString.call(symbols[i]))
+    assertEquals("symbol", typeof symbols[i])
+    assertTrue(typeof symbols[i] === "symbol")
+    assertEquals(null, %_ClassOf(symbols[i]))
+    assertEquals("Symbol", %_ClassOf(new Symbol(symbols[i])))
+    assertEquals("Symbol", %_ClassOf(Object(symbols[i])))
   }
 }
 TestType()
 
 
+function TestPrototype() {
+  assertSame(Object.prototype, Symbol.prototype.__proto__)
+  assertSame(Symbol.prototype, Symbol().__proto__)
+  assertSame(Symbol.prototype, Symbol(Symbol()).__proto__)
+  assertSame(Symbol.prototype, (new Symbol).__proto__)
+  assertSame(Symbol.prototype, (new Symbol()).__proto__)
+  assertSame(Symbol.prototype, (new Symbol(Symbol())).__proto__)
+  assertSame(Symbol.prototype, Object(Symbol()).__proto__)
+  for (var i in symbols) {
+    assertSame(Symbol.prototype, symbols[i].__proto__)
+  }
+}
+TestPrototype()
+
+
+function TestToString() {
+  for (var i in symbols) {
+    assertThrows(function() { String(symbols[i]) }, TypeError)
+    assertThrows(function() { symbols[i] + "" }, TypeError)
+    assertThrows(function() { symbols[i].toString() }, TypeError)
+    assertThrows(function() { (new Symbol(symbols[i])).toString() }, TypeError)
+    assertThrows(function() { Object(symbols[i]).toString() }, TypeError)
+    assertEquals("[object Symbol]", Object.prototype.toString.call(symbols[i]))
+  }
+}
+TestToString()
+
+
+function TestToBoolean() {
+  for (var i in symbols) {
+    assertTrue(Boolean(symbols[i]).valueOf())
+    assertFalse(!symbols[i])
+    assertTrue(!!symbols[i])
+    assertTrue(symbols[i] && true)
+    assertFalse(!symbols[i] && false)
+    assertTrue(!symbols[i] || true)
+    assertEquals(1, symbols[i] ? 1 : 2)
+    assertEquals(2, !symbols[i] ? 1 : 2)
+    if (!symbols[i]) assertUnreachable();
+    if (symbols[i]) {} else assertUnreachable();
+  }
+}
+TestToBoolean()
+
+
+function TestToNumber() {
+  for (var i in symbols) {
+    assertSame(NaN, Number(symbols[i]).valueOf())
+    assertSame(NaN, symbols[i] + 0)
+  }
+}
+TestToNumber()
+
+
 function TestEquality() {
   // Every symbol should equal itself.
   for (var i in symbols) {
@@ -88,8 +142,8 @@ TestEquality()
 
 function TestGet() {
   for (var i in symbols) {
-    assertEquals("[object Symbol]", symbols[i].toString())
-    assertEquals(undefined, symbols[i].valueOf)
+    assertThrows(function() { symbols[i].toString() }, TypeError)
+    assertEquals(symbols[i], symbols[i].valueOf())
     assertEquals(undefined, symbols[i].a)
     assertEquals(undefined, symbols[i]["a" + "b"])
     assertEquals(undefined, symbols[i]["" + "1"])
@@ -102,7 +156,9 @@ TestGet()
 function TestSet() {
   for (var i in symbols) {
     symbols[i].toString = 0
-    assertEquals("[object Symbol]", symbols[i].toString())
+    assertThrows(function() { symbols[i].toString() }, TypeError)
+    symbols[i].valueOf = 0
+    assertEquals(symbols[i], symbols[i].valueOf())
     symbols[i].a = 0
     assertEquals(undefined, symbols[i].a)
     symbols[i]["a" + "b"] = 0
@@ -179,7 +235,7 @@ function TestKeyHas() {
 
 function TestKeyEnum(obj) {
   for (var name in obj) {
-    assertFalse(%_IsSymbol(name))
+    assertTrue(typeof name !== "symbol")
   }
 }
 
@@ -195,9 +251,7 @@ function TestKeyNames(obj) {
   for (var i = 0; i < symbols.length; ++i) expected.add(symbols[i])
   for (var i = 0; i < names.length; ++i) {
     var name = names[i]
-    var asString = String(name)
-    if (asString !== name) {
-      assertEquals("[object Symbol]", asString)
+    if (typeof name === 'symbol') {
       assertTrue(expected.has(name))
       expected.delete(name)
     }