ES6 symbols: prevent reflection, proxy, and observe APIs from leaking symbols
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 22 Mar 2013 17:27:44 +0000 (17:27 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 22 Mar 2013 17:27:44 +0000 (17:27 +0000)
R=svenpanne@chromium.org
BUG=v8:2158

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14056 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

13 files changed:
src/d8.cc
src/mirror-debugger.js
src/object-observe.js
src/objects.cc
src/objects.h
src/runtime.cc
src/runtime.h
src/symbol.js
src/v8natives.js
test/cctest/test-api.cc
test/mjsunit/harmony/object-observe.js
test/mjsunit/harmony/proxies-symbols.js [new file with mode: 0644]
test/mjsunit/harmony/symbols.js

index 805a0cf..941f34d 100644 (file)
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -227,11 +227,13 @@ bool Shell::ExecuteString(Isolate* isolate,
           }
 #if !defined(V8_SHARED)
         } else {
+          v8::TryCatch try_catch;
           Context::Scope context_scope(utility_context_);
           Handle<Object> global = utility_context_->Global();
           Handle<Value> fun = global->Get(String::New("Stringify"));
           Handle<Value> argv[1] = { result };
           Handle<Value> s = Handle<Function>::Cast(fun)->Call(global, 1, argv);
+          if (try_catch.HasCaught()) return true;
           v8::String::Utf8Value str(s);
           fwrite(*str, sizeof(**str), str.length(), stdout);
           printf("\n");
index 7f1a05a..e1fd872 100644 (file)
@@ -638,7 +638,7 @@ ObjectMirror.prototype.propertyNames = function(kind, limit) {
   // Find all the named properties.
   if (kind & PropertyKind.Named) {
     // Get the local property names.
-    propertyNames = %GetLocalPropertyNames(this.value_);
+    propertyNames = %GetLocalPropertyNames(this.value_, true);
     total += propertyNames.length;
 
     // Get names for named interceptor properties if any.
index b35f547..bfb4a65 100644 (file)
@@ -117,6 +117,9 @@ function ObjectUnobserve(object, callback) {
 }
 
 function EnqueueChangeRecord(changeRecord, observers) {
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (IS_SYMBOL(changeRecord.name)) return;
+
   for (var i = 0; i < observers.length; i++) {
     var observer = observers[i];
     var observerInfo = observerInfoMap.get(observer);
index c73c61d..9d6c055 100644 (file)
@@ -375,6 +375,9 @@ MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw,
   Handle<Object> receiver(receiver_raw, isolate);
   Handle<Object> name(name_raw, isolate);
 
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (name->IsSymbol()) return isolate->heap()->undefined_value();
+
   Handle<Object> args[] = { receiver, name };
   Handle<Object> result = CallTrap(
     "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args);
@@ -2757,6 +2760,9 @@ bool JSProxy::HasPropertyWithHandler(Name* name_raw) {
   Handle<Object> receiver(this, isolate);
   Handle<Object> name(name_raw, isolate);
 
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (name->IsSymbol()) return false;
+
   Handle<Object> args[] = { name };
   Handle<Object> result = CallTrap(
     "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args);
@@ -2778,6 +2784,9 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
   Handle<Object> name(name_raw, isolate);
   Handle<Object> value(value_raw, isolate);
 
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (name->IsSymbol()) return *value;
+
   Handle<Object> args[] = { receiver, name, value };
   CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args);
   if (isolate->has_pending_exception()) return Failure::Exception();
@@ -2800,6 +2809,12 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler(
   Handle<Object> value(value_raw, isolate);
   Handle<Object> handler(this->handler(), isolate);  // Trap might morph proxy.
 
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (name->IsSymbol()) {
+    *done = false;
+    return isolate->heap()->the_hole_value();
+  }
+
   *done = true;  // except where redefined...
   Handle<Object> args[] = { name };
   Handle<Object> result = proxy->CallTrap(
@@ -2808,7 +2823,7 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler(
 
   if (result->IsUndefined()) {
     *done = false;
-    return GetHeap()->the_hole_value();
+    return isolate->heap()->the_hole_value();
   }
 
   // Emulate [[GetProperty]] semantics for proxies.
@@ -2889,6 +2904,9 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
   Handle<JSProxy> receiver(this);
   Handle<Object> name(name_raw, isolate);
 
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (name->IsSymbol()) return isolate->heap()->false_value();
+
   Handle<Object> args[] = { name };
   Handle<Object> result = CallTrap(
     "delete", Handle<Object>(), ARRAY_SIZE(args), args);
@@ -2929,6 +2947,9 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
   Handle<JSReceiver> receiver(receiver_raw);
   Handle<Object> name(name_raw, isolate);
 
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (name->IsSymbol()) return ABSENT;
+
   Handle<Object> args[] = { name };
   Handle<Object> result = CallTrap(
     "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args);
@@ -11588,18 +11609,22 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
 // Fill in the names of local properties into the supplied storage. The main
 // purpose of this function is to provide reflection information for the object
 // mirrors.
-void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
-  ASSERT(storage->length() >= (NumberOfLocalProperties() - index));
+void JSObject::GetLocalPropertyNames(
+    FixedArray* storage, int index, PropertyAttributes filter) {
+  ASSERT(storage->length() >= (NumberOfLocalProperties(filter) - index));
   if (HasFastProperties()) {
     int real_size = map()->NumberOfOwnDescriptors();
     DescriptorArray* descs = map()->instance_descriptors();
-    ASSERT(storage->length() >= index + real_size);
     for (int i = 0; i < real_size; i++) {
-      storage->set(index + i, descs->GetKey(i));
+      if ((descs->GetDetails(i).attributes() & filter) == 0 &&
+          ((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) {
+        storage->set(index++, descs->GetKey(i));
+      }
     }
   } else {
     property_dictionary()->CopyKeysTo(storage,
                                       index,
+                                      filter,
                                       NameDictionary::UNSORTED);
   }
 }
@@ -12350,6 +12375,7 @@ template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::Shrink(
 template void Dictionary<NameDictionaryShape, Name*>::CopyKeysTo(
     FixedArray*,
     int,
+    PropertyAttributes,
     Dictionary<NameDictionaryShape, Name*>::SortMode);
 
 template int
@@ -13592,6 +13618,7 @@ template<typename Shape, typename Key>
 void Dictionary<Shape, Key>::CopyKeysTo(
     FixedArray* storage,
     int index,
+    PropertyAttributes filter,
     typename Dictionary<Shape, Key>::SortMode sort_mode) {
   ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
       static_cast<PropertyAttributes>(NONE)));
@@ -13601,7 +13628,8 @@ void Dictionary<Shape, Key>::CopyKeysTo(
     if (HashTable<Shape, Key>::IsKey(k)) {
       PropertyDetails details = DetailsAt(i);
       if (details.IsDeleted()) continue;
-      storage->set(index++, k);
+      PropertyAttributes attr = details.attributes();
+      if ((attr & filter) == 0) storage->set(index++, k);
     }
   }
   if (sort_mode == Dictionary<Shape, Key>::SORTED) {
index 8a1bcd2..b19db9e 100644 (file)
@@ -2057,7 +2057,8 @@ class JSObject: public JSReceiver {
   int NumberOfLocalProperties(PropertyAttributes filter = NONE);
   // Fill in details for properties into storage starting at the specified
   // index.
-  void GetLocalPropertyNames(FixedArray* storage, int index);
+  void GetLocalPropertyNames(
+      FixedArray* storage, int index, PropertyAttributes filter = NONE);
 
   // Returns the number of properties on this object filtering out properties
   // with the specified attributes (ignoring interceptors).
@@ -3255,7 +3256,10 @@ class Dictionary: public HashTable<Shape, Key> {
                   PropertyAttributes filter,
                   SortMode sort_mode);
   // Fill in details for properties into storage.
-  void CopyKeysTo(FixedArray* storage, int index, SortMode sort_mode);
+  void CopyKeysTo(FixedArray* storage,
+                  int index,
+                  PropertyAttributes filter,
+                  SortMode sort_mode);
 
   // Accessors for next enumeration index.
   void SetNextEnumerationIndex(int index) {
index e1a666f..2626f9a 100644 (file)
@@ -683,9 +683,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
 
 
 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateSymbol) {
-  NoHandleAllocation ha(isolate);
+  HandleScope scope(isolate);
   ASSERT(args.length() == 1);
-  CONVERT_ARG_HANDLE_CHECKED(Object, name, 0);
+  Handle<Object> name(args[0], isolate);
   RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
   Symbol* symbol;
   MaybeObject* maybe = isolate->heap()->AllocateSymbol();
@@ -4745,11 +4745,13 @@ static int LocalPrototypeChainLength(JSObject* obj) {
 // args[0]: object
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
   HandleScope scope(isolate);
-  ASSERT(args.length() == 1);
+  ASSERT(args.length() == 2);
   if (!args[0]->IsJSObject()) {
     return isolate->heap()->undefined_value();
   }
   CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+  CONVERT_BOOLEAN_ARG_CHECKED(include_symbols, 1);
+  PropertyAttributes filter = include_symbols ? NONE : SYMBOLIC;
 
   // Skip the global proxy as it has no properties and always delegates to the
   // real global object.
@@ -4782,7 +4784,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
       return *isolate->factory()->NewJSArray(0);
     }
     int n;
-    n = jsproto->NumberOfLocalProperties();
+    n = jsproto->NumberOfLocalProperties(filter);
     local_property_count[i] = n;
     total_property_count += n;
     if (i < length - 1) {
@@ -4799,7 +4801,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
   int proto_with_hidden_properties = 0;
   int next_copy_index = 0;
   for (int i = 0; i < length; i++) {
-    jsproto->GetLocalPropertyNames(*names, next_copy_index);
+    jsproto->GetLocalPropertyNames(*names, next_copy_index, filter);
     next_copy_index += local_property_count[i];
     if (jsproto->HasHiddenProperties()) {
       proto_with_hidden_properties++;
@@ -4809,7 +4811,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
     }
   }
 
-  // Filter out name of hidden propeties object.
+  // Filter out name of hidden properties object.
   if (proto_with_hidden_properties > 0) {
     Handle<FixedArray> old_names = names;
     names = isolate->factory()->NewFixedArray(
index f53625e..c3f241e 100644 (file)
@@ -55,7 +55,7 @@ namespace internal {
   F(IsPropertyEnumerable, 2, 1) \
   F(GetPropertyNames, 1, 1) \
   F(GetPropertyNamesFast, 1, 1) \
-  F(GetLocalPropertyNames, 1, 1) \
+  F(GetLocalPropertyNames, 2, 1) \
   F(GetLocalElementNames, 1, 1) \
   F(GetInterceptorInfo, 1, 1) \
   F(GetNamedInterceptorPropertyNames, 1, 1) \
index f478bac..fb7476f 100644 (file)
@@ -44,7 +44,6 @@ function SymbolGetName() {
   if (!IS_SYMBOL(symbol)) {
     throw MakeTypeError(
         'incompatible_method_receiver', ["Symbol.prototype.name", this]);
-
   }
   return %SymbolName(symbol);
 }
index 82e92db..d9dc096 100644 (file)
@@ -256,6 +256,9 @@ function ObjectValueOf() {
 // ECMA-262 - 15.2.4.5
 function ObjectHasOwnProperty(V) {
   if (%IsJSProxy(this)) {
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+    if (IS_SYMBOL(V)) return false;
+
     var handler = %GetHandler(this);
     return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, ToName(V));
   }
@@ -278,6 +281,9 @@ function ObjectIsPrototypeOf(V) {
 function ObjectPropertyIsEnumerable(V) {
   var P = ToName(V);
   if (%IsJSProxy(this)) {
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+    if (IS_SYMBOL(V)) return false;
+
     var desc = GetOwnProperty(this, P);
     return IS_UNDEFINED(desc) ? false : desc.isEnumerable();
   }
@@ -645,6 +651,9 @@ function CallTrap2(handler, name, defaultTrap, x, y) {
 function GetOwnProperty(obj, v) {
   var p = ToName(v);
   if (%IsJSProxy(obj)) {
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+    if (IS_SYMBOL(v)) return void 0;
+
     var handler = %GetHandler(obj);
     var descriptor = CallTrap1(handler, "getOwnPropertyDescriptor", void 0, p);
     if (IS_UNDEFINED(descriptor)) return descriptor;
@@ -685,6 +694,9 @@ function Delete(obj, p, should_throw) {
 
 // Harmony proxies.
 function DefineProxyProperty(obj, p, attributes, should_throw) {
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (IS_SYMBOL(p)) return false;
+
   var handler = %GetHandler(obj);
   var result = CallTrap2(handler, "defineProperty", void 0, p, attributes);
   if (!ToBoolean(result)) {
@@ -950,6 +962,9 @@ function DefineArrayProperty(obj, p, desc, should_throw) {
 // ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
 function DefineOwnProperty(obj, p, desc, should_throw) {
   if (%IsJSProxy(obj)) {
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+    if (IS_SYMBOL(p)) return false;
+
     var attributes = FromGenericPropertyDescriptor(desc);
     return DefineProxyProperty(obj, p, attributes, should_throw);
   } else if (IS_ARRAY(obj)) {
@@ -987,16 +1002,20 @@ function ToNameArray(obj, trap, includeSymbols) {
   }
   var n = ToUint32(obj.length);
   var array = new $Array(n);
+  var realLength = 0;
   var names = { __proto__: null };  // TODO(rossberg): use sets once ready.
   for (var index = 0; index < n; index++) {
     var s = ToName(obj[index]);
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
     if (IS_SYMBOL(s) && !includeSymbols) continue;
     if (%HasLocalProperty(names, s)) {
       throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]);
     }
     array[index] = s;
+    ++realLength;
     names[s] = 0;
   }
+  array.length = realLength;
   return array;
 }
 
@@ -1010,7 +1029,7 @@ function ObjectGetOwnPropertyNames(obj) {
   if (%IsJSProxy(obj)) {
     var handler = %GetHandler(obj);
     var names = CallTrap0(handler, "getOwnPropertyNames", void 0);
-    return ToNameArray(names, "getOwnPropertyNames", true);
+    return ToNameArray(names, "getOwnPropertyNames", false);
   }
 
   var nameArrays = new InternalArray();
@@ -1036,7 +1055,7 @@ function ObjectGetOwnPropertyNames(obj) {
   // Find all the named properties.
 
   // Get the local property names.
-  nameArrays.push(%GetLocalPropertyNames(obj));
+  nameArrays.push(%GetLocalPropertyNames(obj, false));
 
   // Get names for named interceptor properties if any.
   if ((interceptorInfo & 2) != 0) {
@@ -1056,7 +1075,8 @@ function ObjectGetOwnPropertyNames(obj) {
     var propertySet = { __proto__: null };
     var j = 0;
     for (var i = 0; i < propertyNames.length; ++i) {
-      var name = ToName(propertyNames[i]);
+      if (IS_SYMBOL(propertyNames[i])) continue;
+      var name = ToString(propertyNames[i]);
       // We need to check for the exact property value since for intrinsic
       // properties like toString if(propertySet["toString"]) will always
       // succeed.
index 9b1371e..a9780f0 100644 (file)
@@ -8376,7 +8376,7 @@ THREADED_TEST(Regress91517) {
   // Call the runtime version of GetLocalPropertyNames() on the natively
   // created object through JavaScript.
   context->Global()->Set(v8_str("obj"), o4);
-  CompileRun("var names = %GetLocalPropertyNames(obj);");
+  CompileRun("var names = %GetLocalPropertyNames(obj, true);");
 
   ExpectInt32("names.length", 1006);
   ExpectTrue("names.indexOf(\"baz\") >= 0");
index 584d9e8..263154a 100644 (file)
@@ -26,7 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Flags: --harmony-observation --harmony-proxies --harmony-collections
-// Flags: --allow-natives-syntax
+// Flags: --harmony-symbols --allow-natives-syntax
 
 var allObservers = [];
 function reset() {
@@ -448,6 +448,29 @@ observer.assertCallbackRecords([
 ]);
 
 
+// Observing symbol properties (not).
+print("*****")
+reset();
+var obj = {}
+var symbol = Symbol("secret");
+Object.observe(obj, observer.callback);
+obj[symbol] = 3;
+delete obj[symbol];
+Object.defineProperty(obj, symbol, {get: function() {}, configurable: true});
+Object.defineProperty(obj, symbol, {value: 6});
+Object.defineProperty(obj, symbol, {writable: false});
+delete obj[symbol];
+Object.defineProperty(obj, symbol, {value: 7});
+++obj[symbol];
+obj[symbol]++;
+obj[symbol] *= 3;
+delete obj[symbol];
+obj.__defineSetter__(symbol, function() {});
+obj.__defineGetter__(symbol, function() {});
+Object.deliverChangeRecords(observer.callback);
+observer.assertNotCalled();
+
+
 // Test all kinds of objects generically.
 function TestObserveConfigurable(obj, prop) {
   reset();
diff --git a/test/mjsunit/harmony/proxies-symbols.js b/test/mjsunit/harmony/proxies-symbols.js
new file mode 100644 (file)
index 0000000..8920e39
--- /dev/null
@@ -0,0 +1,106 @@
+// 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-proxies --harmony-symbols
+
+
+// Helper.
+
+function TestWithProxies(test, x, y, z) {
+  test(Proxy.create, x, y, z)
+  test(function(h) {return Proxy.createFunction(h, function() {})}, x, y, z)
+}
+
+
+// No symbols should leak to proxy traps.
+
+function TestNoSymbolsToTrap(handler) {
+  TestWithProxies(TestNoSymbolsToTrap2, handler)
+}
+
+function TestNoSymbolsToTrap2(create, handler) {
+  var p = create(handler)
+  var o = Object.create(p)
+  var symbol = Symbol("secret")
+
+  assertFalse(symbol in p)
+  assertFalse(symbol in o)
+  assertEquals(undefined, p[symbol])
+  assertEquals(undefined, o[symbol])
+  assertEquals(47, p[symbol] = 47)
+  assertEquals(47, o[symbol] = 47)
+  assertFalse(delete p[symbol])
+  assertTrue(delete o[symbol])
+  assertTrue(delete o[symbol])
+  assertFalse({}.hasOwnProperty.call(p, symbol))
+  assertFalse({}.hasOwnProperty.call(o, symbol))
+  assertEquals(undefined, Object.getOwnPropertyDescriptor(p, symbol))
+  assertEquals(undefined, Object.getOwnPropertyDescriptor(o, symbol))
+}
+
+
+TestNoSymbolsToTrap({
+  has: assertUnreachable,
+  hasOwn: assertUnreachable,
+  get: assertUnreachable,
+  set: assertUnreachable,
+  delete: assertUnreachable,
+  getPropertyDescriptor: assertUnreachable,
+  getOwnPropertyDescriptor: assertUnreachable,
+  defineProperty: assertUnreachable
+})
+
+
+// All symbols returned from proxy traps should be filtered.
+
+function TestNoSymbolsFromTrap(handler) {
+  TestWithProxies(TestNoSymbolsFromTrap2, handler)
+}
+
+function TestNoSymbolsFromTrap2(create, handler) {
+  var p = create(handler)
+  var o = Object.create(p)
+
+  assertEquals(0, Object.keys(p).length)
+  assertEquals(0, Object.keys(o).length)
+  assertEquals(0, Object.getOwnPropertyNames(p).length)
+  assertEquals(0, Object.getOwnPropertyNames(o).length)
+  for (var n in p) assertUnreachable()
+  for (var n in o) assertUnreachable()
+}
+
+
+function MakeSymbolArray() {
+  return [Symbol(), Symbol("a")]
+}
+
+TestNoSymbolsFromTrap({
+  enumerate: MakeSymbolArray,
+  keys: MakeSymbolArray,
+  getPropertyNames: MakeSymbolArray,
+  getOwnPropertyNames: MakeSymbolArray
+})
index 3083d06..5e1515e 100644 (file)
@@ -247,7 +247,7 @@ function TestKeyHas() {
 
 function TestKeyEnum(obj) {
   for (var name in obj) {
-    assertTrue(typeof name !== "symbol")
+    assertEquals("string", typeof name)
   }
 }
 
@@ -256,19 +256,9 @@ function TestKeyNames(obj) {
   assertEquals(0, Object.keys(obj).length)
 
   var names = Object.getOwnPropertyNames(obj)
-  assertTrue(symbols.length <= names.length)
-  // TODO(rossberg): once we have iterators, the following would be:
-  //   var expected = new Set(symbols)
-  var expected = new Set
-  for (var i = 0; i < symbols.length; ++i) expected.add(symbols[i])
-  for (var i = 0; i < names.length; ++i) {
-    var name = names[i]
-    if (typeof name === 'symbol') {
-      assertTrue(expected.has(name))
-      expected.delete(name)
-    }
+  for (var i in names) {
+    assertEquals("string", typeof names[i])
   }
-  assertEquals(0, expected.size)
 }