8141702: Add support for Symbol property keys
authorhannesw
Wed, 11 Nov 2015 16:28:17 +0100
changeset 33690 46a1bc24cf2c
parent 33689 a42cdb474da0
child 33691 c35520c1bef1
8141702: Add support for Symbol property keys Reviewed-by: attila, sundar
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/WeakValueCache.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArguments.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeSymbol.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyHashMap.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Symbol.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java
nashorn/test/script/basic/es6.js
nashorn/test/script/basic/es6/let.js
nashorn/test/script/basic/es6/symbols.js
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Wed Nov 11 16:28:17 2015 +0100
@@ -165,7 +165,7 @@
                 try {
                     final Object res = context.eval(global, source, global, "<shell>");
                     if (res != ScriptRuntime.UNDEFINED) {
-                        err.println(JSType.toString(res));
+                        err.println(toString(res, global));
                     }
                 } catch (final Exception exp) {
                     // Is this a ECMAScript SyntaxError at last column (of the single line)?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/WeakValueCache.java	Wed Nov 11 16:28:17 2015 +0100
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.function.Function;
+
+/**
+ * This class provides a map based cache with weakly referenced values. Cleared references are
+ * purged from the underlying map when values are retrieved or created.
+ * It uses a {@link java.util.HashMap} to store values and needs to be externally synchronized.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ */
+public final class WeakValueCache<K, V> {
+
+    private final HashMap<K, KeyValueReference<K, V>> map = new HashMap<>();
+    private final ReferenceQueue<V> refQueue = new ReferenceQueue<>();
+
+    /**
+     * Returns the value associated with {@code key}, or {@code null} if no such value exists.
+     *
+     * @param key the key
+     * @return the value or null if none exists
+     */
+    public V get(final K key) {
+        removeClearedEntries();
+        return findValue(key);
+    }
+
+    /**
+     * Returns the value associated with {@code key}, or creates and returns a new value if
+     * no value exists using the {@code creator} function.
+     *
+     * @param key the key
+     * @param creator function to create a new value
+     * @return the existing value, or a new one if none existed
+     */
+    public V getOrCreate(final K key, final Function<? super K, ? extends V> creator) {
+        removeClearedEntries();
+
+        V value = findValue(key);
+
+        if (value == null) {
+            // Define a new value if it does not exist
+            value = creator.apply(key);
+            map.put(key, new KeyValueReference<>(key, value));
+        }
+
+        return value;
+    }
+
+    private V findValue(final K key) {
+        final KeyValueReference<K, V> ref = map.get(key);
+        if (ref != null) {
+            return ref.get();
+        }
+        return null;
+    }
+
+    private void removeClearedEntries() {
+        // Remove cleared entries
+        for (;;) {
+            final KeyValueReference ref = (KeyValueReference) refQueue.poll();
+            if (ref == null) {
+                break;
+            }
+            map.remove(ref.key, ref);
+        }
+    }
+
+    private static class KeyValueReference<K, V> extends WeakReference<V> {
+        final K key;
+
+        KeyValueReference(final K key, final V value) {
+            super(value);
+            this.key = key;
+        }
+    }
+
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java	Wed Nov 11 16:28:17 2015 +0100
@@ -738,7 +738,7 @@
      * @param recovery start label for catch
      */
     void _try(final Label entry, final Label exit, final Label recovery) {
-        _try(entry, exit, recovery, (String)null, false);
+        _try(entry, exit, recovery, null, false);
     }
 
     void markLabelAsOptimisticCatchHandler(final Label label, final int liveLocalCount) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Wed Nov 11 16:28:17 2015 +0100
@@ -173,9 +173,10 @@
                 loadTuple(method, tuple);
                 method.dynamicSetIndex(callSiteFlags);
             } else {
+                assert property.getKey() instanceof String; // symbol keys not yet supported in object literals
                 method.dup();
                 loadTuple(method, tuple);
-                method.dynamicSet(property.getKey(), codegen.getCallSiteFlags(), false);
+                method.dynamicSet((String) property.getKey(), codegen.getCallSiteFlags(), false);
             }
         }
     }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Wed Nov 11 16:28:17 2015 +0100
@@ -75,6 +75,7 @@
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.ScriptingFunctions;
 import jdk.nashorn.internal.runtime.Specialization;
+import jdk.nashorn.internal.runtime.Symbol;
 import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 import jdk.nashorn.internal.runtime.linker.InvokeByName;
@@ -221,6 +222,10 @@
     @Property(name = "Number", attributes = Attribute.NOT_ENUMERABLE)
     public volatile Object number;
 
+    /** ECMA 2016 19.4.1 - Symbol constructor */
+    @Property(name = "Symbol", attributes = Attribute.NOT_ENUMERABLE)
+    public volatile Object symbol;
+
     /**
      * Getter for ECMA 15.1.4.7 Date property
      *
@@ -901,6 +906,7 @@
     private ScriptFunction builtinUint32Array;
     private ScriptFunction builtinFloat32Array;
     private ScriptFunction builtinFloat64Array;
+    private ScriptFunction builtinSymbol;
 
     /*
      * ECMA section 13.2.3 The [[ThrowTypeError]] Function Object
@@ -1106,6 +1112,8 @@
             return new NativeArray(ArrayData.allocate((int[]) obj), this);
         } else if (obj instanceof ArrayData) {
             return new NativeArray((ArrayData) obj, this);
+        } else if (obj instanceof Symbol) {
+            return new NativeSymbol((Symbol) obj, this);
         } else {
             // FIXME: more special cases? Map? List?
             return obj;
@@ -1586,7 +1594,7 @@
 
     /**
      * Get the builtin Object prototype.
-     * @return the object prototype.
+     * @return the Object prototype.
      */
     public ScriptObject getObjectPrototype() {
         return ScriptFunction.getPrototype(builtinObject);
@@ -1594,13 +1602,17 @@
 
     /**
      * Get the builtin Function prototype.
-     * @return the Function.prototype.
+     * @return the Function prototype.
      */
     public ScriptObject getFunctionPrototype() {
         return ScriptFunction.getPrototype(builtinFunction);
     }
 
-    ScriptObject getArrayPrototype() {
+    /**
+     * Get the builtin Array prototype.
+     * @return the Array prototype
+     */
+    public ScriptObject getArrayPrototype() {
         return ScriptFunction.getPrototype(builtinArray);
     }
 
@@ -1660,6 +1672,10 @@
         return ScriptFunction.getPrototype(getBuiltinJSAdapter());
     }
 
+    ScriptObject getSymbolPrototype() {
+        return ScriptFunction.getPrototype(builtinSymbol);
+    }
+
     private synchronized ScriptFunction getBuiltinArrayBuffer() {
         if (this.builtinArrayBuffer == null) {
             this.builtinArrayBuffer = initConstructorAndSwitchPoint("ArrayBuffer", ScriptFunction.class);
@@ -2127,11 +2143,11 @@
                 // ES6 15.1.8 steps 6. and 7.
                 final jdk.nashorn.internal.runtime.Property globalProperty = ownMap.findProperty(property.getKey());
                 if (globalProperty != null && !globalProperty.isConfigurable() && property.isLexicalBinding()) {
-                    throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+                    throw ECMAErrors.syntaxError("redeclare.variable", property.getKey().toString());
                 }
                 final jdk.nashorn.internal.runtime.Property lexicalProperty = lexicalMap.findProperty(property.getKey());
                 if (lexicalProperty != null && !property.isConfigurable()) {
-                    throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+                    throw ECMAErrors.syntaxError("redeclare.variable", property.getKey().toString());
                 }
             }
         }
@@ -2186,7 +2202,7 @@
     }
 
     @Override
-    protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
+    protected FindProperty findProperty(final Object key, final boolean deep, final ScriptObject start) {
         if (lexicalScope != null && start != this && start.isScope()) {
             final FindProperty find = lexicalScope.findProperty(key, false);
             if (find != null) {
@@ -2306,6 +2322,14 @@
         this.builtinString    = initConstructorAndSwitchPoint("String", ScriptFunction.class);
         this.builtinMath      = initConstructorAndSwitchPoint("Math", ScriptObject.class);
 
+        if (env._es6) {
+            this.builtinSymbol = initConstructorAndSwitchPoint("Symbol", ScriptFunction.class);
+        } else {
+            // We need to manually delete nasgen-generated properties we don't want
+            this.delete("Symbol", false);
+            this.builtinObject.delete("getOwnPropertySymbols", false);
+        }
+
         // initialize String.prototype.length to 0
         // add String.prototype.length
         final ScriptObject stringPrototype = getStringPrototype();
@@ -2514,6 +2538,7 @@
         this.string            = this.builtinString;
         this.syntaxError       = this.builtinSyntaxError;
         this.typeError         = this.builtinTypeError;
+        this.symbol            = this.builtinSymbol;
     }
 
     private void initDebug() {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArguments.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArguments.java	Wed Nov 11 16:28:17 2015 +0100
@@ -151,7 +151,7 @@
      * ECMA 10.6 for Arguments object.
      */
     @Override
-    public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
+    public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
         final int index = ArrayIndex.getArrayIndex(key);
         if (index >= 0) {
             final boolean isMapped = isMapped(index);
@@ -159,7 +159,7 @@
 
             if (!super.defineOwnProperty(key, propertyDesc, false)) {
                 if (reject) {
-                    throw typeError("cant.redefine.property",  key, ScriptRuntime.safeToString(this));
+                    throw typeError("cant.redefine.property",  key.toString(), ScriptRuntime.safeToString(this));
                 }
                 return false;
             }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java	Wed Nov 11 16:28:17 2015 +0100
@@ -328,7 +328,7 @@
      * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
      */
     @Override
-    public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
+    public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
         final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
 
         // never be undefined as "length" is always defined and can't be deleted for arrays
@@ -369,7 +369,7 @@
             // Step 4d
             if (!succeeded) {
                 if (reject) {
-                    throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
+                    throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                 }
                 return false;
             }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java	Wed Nov 11 16:28:17 2015 +0100
@@ -135,8 +135,11 @@
     }
 
     @Override
-    protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
-        final Object retval = createProperty(name);
+    protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
+        if (!(key instanceof String)) {
+            return super.invokeNoSuchProperty(key, isScope, programPoint);
+        }
+        final Object retval = createProperty((String) key);
         if (isValid(programPoint)) {
             throw new UnwarrantedOptimismException(retval, programPoint);
         }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Wed Nov 11 16:28:17 2015 +0100
@@ -252,6 +252,23 @@
     }
 
     /**
+     * ECMA 2 19.1.2.8 Object.getOwnPropertySymbols ( O )
+     *
+     * @param self self reference
+     * @param obj  object to query for property names
+     * @return array of property names
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+    public static ScriptObject getOwnPropertySymbols(final Object self, final Object obj) {
+        if (obj instanceof ScriptObject) {
+            return new NativeArray(((ScriptObject)obj).getOwnSymbols(true));
+        } else {
+            // TODO: we don't support this on ScriptObjectMirror objects yet
+            throw notAnObject(obj);
+        }
+    }
+
+    /**
      * ECMA 15.2.3.5 Object.create ( O [, Properties] )
      *
      * @param self  self reference
@@ -288,7 +305,7 @@
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static ScriptObject defineProperty(final Object self, final Object obj, final Object prop, final Object attr) {
         final ScriptObject sobj = Global.checkObject(obj);
-        sobj.defineOwnProperty(JSType.toString(prop), attr, true);
+        sobj.defineOwnProperty(JSType.toPropertyKey(prop), attr, true);
         return sobj;
     }
 
@@ -465,6 +482,7 @@
             case BOOLEAN:
             case NUMBER:
             case STRING:
+            case SYMBOL:
                 return Global.toObject(value);
             case OBJECT:
                 return value;
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java	Wed Nov 11 16:28:17 2015 +0100
@@ -33,6 +33,7 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.lang.reflect.Array;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -106,20 +107,6 @@
         return getStringValue();
     }
 
-    @Override
-    public boolean equals(final Object other) {
-        if (other instanceof NativeString) {
-            return getStringValue().equals(((NativeString) other).getStringValue());
-        }
-
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return getStringValue().hashCode();
-    }
-
     private String getStringValue() {
         return value instanceof String ? (String) value : value.toString();
     }
@@ -382,7 +369,7 @@
     }
 
     @Override
-    public Object getOwnPropertyDescriptor(final String key) {
+    public Object getOwnPropertyDescriptor(final Object key) {
         final int index = ArrayIndex.getArrayIndex(key);
         if (index >= 0 && index < value.length()) {
             final Global global = Global.instance();
@@ -400,7 +387,12 @@
      * @return Array of keys.
      */
     @Override
-    protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
+    @SuppressWarnings("unchecked")
+    protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) {
+        if (type != String.class) {
+            return super.getOwnKeys(type, all, nonEnumerable);
+        }
+
         final List<Object> keys = new ArrayList<>();
 
         // add string index keys
@@ -409,8 +401,8 @@
         }
 
         // add super class properties
-        keys.addAll(Arrays.asList(super.getOwnKeys(all, nonEnumerable)));
-        return keys.toArray(new String[keys.size()]);
+        keys.addAll(Arrays.asList(super.getOwnKeys(type, all, nonEnumerable)));
+        return keys.toArray((T[]) Array.newInstance(type, keys.size()));
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeSymbol.java	Wed Nov 11 16:28:17 2015 +0100
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.objects;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
+import jdk.nashorn.internal.WeakValueCache;
+import jdk.nashorn.internal.objects.annotations.Attribute;
+import jdk.nashorn.internal.objects.annotations.Constructor;
+import jdk.nashorn.internal.objects.annotations.Function;
+import jdk.nashorn.internal.objects.annotations.ScriptClass;
+import jdk.nashorn.internal.objects.annotations.Where;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Symbol;
+import jdk.nashorn.internal.runtime.Undefined;
+
+/**
+ * ECMAScript 6 - 19.4 Symbol Objects
+ */
+@ScriptClass("Symbol")
+public final class NativeSymbol extends ScriptObject {
+
+    private final Symbol symbol;
+
+    // initialized by nasgen
+    private static PropertyMap $nasgenmap$;
+
+    /** See ES6 19.4.2.1 */
+    private static WeakValueCache<String, Symbol> globalSymbolRegistry = new WeakValueCache<>();
+
+    NativeSymbol(final Symbol symbol, final Global global) {
+        this(symbol, global.getSymbolPrototype(), $nasgenmap$);
+    }
+
+    private NativeSymbol(final Symbol symbol, final ScriptObject prototype, final PropertyMap map) {
+        super(prototype, map);
+        this.symbol = symbol;
+    }
+
+    private static Symbol getSymbolValue(final Object self) {
+        if (self instanceof Symbol) {
+            return (Symbol) self;
+        } else if (self instanceof NativeSymbol) {
+            return ((NativeSymbol) self).symbol;
+        } else {
+            throw typeError("not.a.symbol");
+        }
+    }
+
+    // ECMA 6 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
+    @Override
+    public Object getDefaultValue(final Class<?> typeHint) {
+        // Just return the symbol value.
+        return symbol;
+    }
+
+    /**
+     * ECMA 6 19.4.3.2 Symbol.prototype.toString ( )
+     *
+     * @param self self reference
+     * @return localized string for this Number
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE)
+    public static String toString(final Object self) {
+        return getSymbolValue(self).toString();
+    }
+
+
+    /**
+     * ECMA 6 19.4.3.3  Symbol.prototype.valueOf ( )
+     *
+     * @param self self reference
+     * @return number value for this Number
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE)
+    public static Object valueOf(final Object self) {
+        return getSymbolValue(self);
+    }
+
+    /**
+     * ECMA 6 19.4.1.1 Symbol ( [ description ] )
+     *
+     * @param newObj is this function invoked with the new operator
+     * @param self   self reference
+     * @param args   arguments
+     * @return new symbol value
+     */
+    @Constructor(arity = 1)
+    public static Object constructor(final boolean newObj, final Object self, final Object... args) {
+        if (newObj) {
+            throw typeError("symbol.as.constructor");
+        }
+        final String description = args.length > 0 && args[0] != Undefined.getUndefined() ?
+                JSType.toString(args[0]) : "";
+        return new Symbol(description);
+    }
+
+    /**
+     * ES6 19.4.2.1 Symbol.for ( key )
+     *
+     * @param self self reference
+     * @param arg the argument
+     * @return the symbol value
+     */
+    @Function(name = "for", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+    public synchronized static Object _for(final Object self, final Object arg) {
+        final String name = JSType.toString(arg);
+        return globalSymbolRegistry.getOrCreate(name, Symbol::new);
+    }
+
+    /**
+     * ES6 19.4.2.5 Symbol.keyFor ( sym )
+     *
+     * @param self self reference
+     * @param arg the argument
+     * @return the symbol name
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+    public synchronized static Object keyFor(final Object self, final Object arg) {
+        if (!(arg instanceof Symbol)) {
+            throw typeError("not.a.symbol", ScriptRuntime.safeToString(arg));
+        }
+        final String name = ((Symbol) arg).getName();
+        return globalSymbolRegistry.get(name) == arg ? name : Undefined.getUndefined();
+    }
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java	Wed Nov 11 16:28:17 2015 +0100
@@ -180,7 +180,7 @@
      * @param objectSetter    object setter
      */
     protected AccessorProperty(
-            final String key,
+            final Object key,
             final int flags,
             final int slot,
             final MethodHandle primitiveGetter,
@@ -209,7 +209,7 @@
      * @param getter the property getter
      * @param setter the property setter or null if non writable, non configurable
      */
-    private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
+    private AccessorProperty(final Object key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
         super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
         assert !isSpill();
 
@@ -249,7 +249,7 @@
      * @param structure        structure for objects associated with this property
      * @param slot             property field number or spill slot
      */
-    public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) {
+    public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot) {
         super(key, flags, slot);
 
         initGetterSetter(structure);
@@ -292,7 +292,7 @@
      * @param owner        owner of property
      * @param initialValue initial value to which the property can be set
      */
-    protected AccessorProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
+    protected AccessorProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
         this(key, flags, owner.getClass(), slot);
         setInitialValue(owner, initialValue);
     }
@@ -307,7 +307,7 @@
      * @param slot         field slot index
      * @param initialType  initial type of the property
      */
-    public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
+    public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
         this(key, flags, structure, slot);
         setType(hasDualFields() ? initialType : Object.class);
     }
@@ -603,7 +603,7 @@
     private void checkUndeclared() {
         if ((getFlags() & NEEDS_DECLARATION) != 0) {
             // a lexically defined variable that hasn't seen its declaration - throw ReferenceError
-            throw ECMAErrors.referenceError("not.defined", getKey());
+            throw ECMAErrors.referenceError("not.defined", getKey().toString());
         }
     }
 
@@ -659,7 +659,7 @@
         }
 
         if (isBuiltin()) {
-           mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey()));
+           mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey().toString()));
         }
 
         assert mh.type().returnType() == void.class : mh.type();
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Wed Nov 11 16:28:17 2015 +0100
@@ -42,10 +42,8 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
-import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
-import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.MalformedURLException;
@@ -80,6 +78,7 @@
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
 import jdk.nashorn.api.scripting.ClassFilter;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.WeakValueCache;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@@ -303,47 +302,7 @@
         }
     }
 
-    private final Map<CodeSource, HostClassReference> anonymousHostClasses = new HashMap<>();
-    private final ReferenceQueue<Class<?>> anonymousHostClassesRefQueue = new ReferenceQueue<>();
-
-    private static class HostClassReference extends WeakReference<Class<?>> {
-        final CodeSource codeSource;
-
-        HostClassReference(final CodeSource codeSource, final Class<?> clazz, final ReferenceQueue<Class<?>> refQueue) {
-            super(clazz, refQueue);
-            this.codeSource = codeSource;
-        }
-    }
-
-    private synchronized Class<?> getAnonymousHostClass(final CodeSource codeSource) {
-        // Remove cleared entries
-        for(;;) {
-            final HostClassReference clearedRef = (HostClassReference)anonymousHostClassesRefQueue.poll();
-            if (clearedRef == null) {
-                break;
-            }
-            anonymousHostClasses.remove(clearedRef.codeSource, clearedRef);
-        }
-
-        // Try to find an existing host class
-        final Reference<Class<?>> ref = anonymousHostClasses.get(codeSource);
-        if (ref != null) {
-            final Class<?> existingHostClass = ref.get();
-            if (existingHostClass != null) {
-                return existingHostClass;
-            }
-        }
-
-        // Define a new host class if existing is not found
-        final Class<?> newHostClass = createNewLoader().installClass(
-                // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
-                // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
-                // invoked from AnonymousContextCodeInstaller, this is okay.
-                AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
-                AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, codeSource);
-        anonymousHostClasses.put(codeSource, new HostClassReference(codeSource, newHostClass, anonymousHostClassesRefQueue));
-        return newHostClass;
-    }
+    private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>();
 
     private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
         private static final Unsafe UNSAFE = getUnsafe();
@@ -1455,7 +1414,14 @@
             final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
             installer = new NamedContextCodeInstaller(this, cs, loader);
         } else {
-            installer = new AnonymousContextCodeInstaller(this, cs, getAnonymousHostClass(cs));
+            installer = new AnonymousContextCodeInstaller(this, cs,
+                    anonymousHostClasses.getOrCreate(cs, (key) ->
+                            createNewLoader().installClass(
+                                    // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
+                                    // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
+                                    // invoked from AnonymousContextCodeInstaller, this is okay.
+                                    AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
+                                    AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs)));
         }
 
         if (storedScript == null) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java	Wed Nov 11 16:28:17 2015 +0100
@@ -105,7 +105,7 @@
      * Access map for this global - associates a symbol name with an Access object, with getter
      * and invalidation information
      */
-    private final Map<String, Access> map = new HashMap<>();
+    private final Map<Object, Access> map = new HashMap<>();
 
     private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
 
@@ -301,7 +301,7 @@
      * that might be linked as MethodHandle.constant and force relink
      * @param name name of property
      */
-    void delete(final String name) {
+    void delete(final Object name) {
         if (!invalidatedForever.get()) {
             synchronized (this) {
                 final Access acc = map.get(name);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Wed Nov 11 16:28:17 2015 +0100
@@ -40,6 +40,7 @@
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.objects.NativeSymbol;
 import jdk.nashorn.internal.parser.Lexer;
 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
 import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
@@ -68,7 +69,10 @@
     OBJECT("object"),
 
     /** The function type */
-    FUNCTION("function");
+    FUNCTION("function"),
+
+    /** The symbol type */
+    SYMBOL("symbol");
 
     /** The type name as returned by ECMAScript "typeof" operator*/
     private final String typeName;
@@ -312,6 +316,10 @@
             return JSType.NUMBER;
         }
 
+        if (obj instanceof Symbol) {
+            return JSType.SYMBOL;
+        }
+
         if (obj == ScriptRuntime.UNDEFINED) {
             return JSType.UNDEFINED;
         }
@@ -354,6 +362,10 @@
             return JSType.UNDEFINED;
         }
 
+        if (obj instanceof Symbol) {
+            return JSType.SYMBOL;
+        }
+
         return JSType.OBJECT;
     }
 
@@ -470,9 +482,10 @@
     public static boolean isPrimitive(final Object obj) {
         return obj == null ||
                obj == ScriptRuntime.UNDEFINED ||
-               obj instanceof Boolean ||
+               isString(obj) ||
                obj instanceof Number ||
-               isString(obj);
+               obj instanceof Boolean ||
+               obj instanceof Symbol;
     }
 
    /**
@@ -614,6 +627,15 @@
     }
 
     /**
+     * See ES6 #7.1.14
+     * @param obj key object
+     * @return property key
+     */
+    public static Object toPropertyKey(final Object obj) {
+        return obj instanceof Symbol ? obj : toStringImpl(obj, false);
+    }
+
+    /**
      * If obj is an instance of {@link ConsString} cast to CharSequence, else return
      * result of {@link #toString(Object)}.
      *
@@ -787,7 +809,9 @@
      * @return a number
      */
     public static double toNumberForEq(final Object obj) {
-        return obj == null ? Double.NaN : toNumber(obj);
+        // we are not able to detect Symbol objects from codegen, so we need to
+        // handle them here to avoid throwing an error in toNumber conversion.
+        return obj == null || obj instanceof Symbol || obj instanceof NativeSymbol ? Double.NaN : toNumber(obj);
     }
 
     /**
@@ -1404,6 +1428,13 @@
             return obj.toString();
         }
 
+        if (obj instanceof Symbol) {
+            if (safe) {
+                return obj.toString();
+            }
+            throw typeError("symbol.to.string");
+        }
+
         if (safe && obj instanceof ScriptObject) {
             final ScriptObject sobj = (ScriptObject)obj;
             final Global gobj = Context.getGlobal();
@@ -1916,6 +1947,10 @@
             return Double.NaN;
         }
 
+        if (obj instanceof Symbol) {
+            throw typeError("symbol.to.number");
+        }
+
         return toNumber(toPrimitive(obj, Number.class));
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Wed Nov 11 16:28:17 2015 +0100
@@ -207,8 +207,11 @@
     }
 
     @Override
-    protected Object invokeNoSuchProperty(final String key, final boolean isScope, final int programPoint) {
-        final Object retval = createProperty(key);
+    protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
+        if (!(key instanceof String)) {
+            return super.invokeNoSuchProperty(key, isScope, programPoint);
+        }
+        final Object retval = createProperty((String) key);
         if (isValid(programPoint)) {
             throw new UnwarrantedOptimismException(retval, programPoint);
         }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java	Wed Nov 11 16:28:17 2015 +0100
@@ -100,7 +100,7 @@
     public static final int DUAL_FIELDS             = 1 << 11;
 
     /** Property key. */
-    private final String key;
+    private final Object key;
 
     /** Property flags. */
     private int flags;
@@ -127,7 +127,7 @@
      * @param flags property flags
      * @param slot  property field number or spill slot
      */
-    Property(final String key, final int flags, final int slot) {
+    Property(final Object key, final int flags, final int slot) {
         assert key != null;
         this.key   = key;
         this.flags = flags;
@@ -420,7 +420,7 @@
      * Get the key for this property. This key is an ordinary string. The "name".
      * @return key for property
      */
-    public String getKey() {
+    public Object getKey() {
         return key;
     }
 
@@ -627,7 +627,7 @@
         final StringBuilder sb   = new StringBuilder();
         final Class<?>      t = getLocalType();
 
-        sb.append(indent(getKey(), 20)).
+        sb.append(indent(getKey().toString(), 20)).
             append(" id=").
             append(Debug.id(this)).
             append(" (0x").
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyHashMap.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyHashMap.java	Wed Nov 11 16:28:17 2015 +0100
@@ -102,7 +102,7 @@
  * immutable hash map, addition is constant time.  For LinkedHashMap it's O(N+C)
  * since we have to clone the older map.
  */
-public final class PropertyHashMap implements Map <String, Property> {
+public final class PropertyHashMap implements Map <Object, Property> {
     /** Number of initial bins. Power of 2. */
     private static final int INITIAL_BINS = 32;
 
@@ -243,7 +243,7 @@
      *
      * @return New {@link PropertyHashMap}.
      */
-    public PropertyHashMap immutableRemove(final String key) {
+    public PropertyHashMap immutableRemove(final Object key) {
         if (bins != null) {
             final int binIndex = binIndex(bins, key);
             final Element bin = bins[binIndex];
@@ -271,7 +271,7 @@
      *
      * @return {@link Property} matching key or {@code null} if not found.
      */
-    public Property find(final String key) {
+    public Property find(final Object key) {
         final Element element = findElement(key);
         return element != null ? element.getProperty() : null;
     }
@@ -301,7 +301,7 @@
      *
      * @return The bin index.
      */
-    private static int binIndex(final Element[] bins, final String key) {
+    private static int binIndex(final Element[] bins, final Object key) {
         return  key.hashCode() & bins.length - 1;
     }
 
@@ -340,7 +340,7 @@
         final Element[] newBins = new Element[binSize];
         for (Element element = list; element != null; element = element.getLink()) {
             final Property property = element.getProperty();
-            final String   key      = property.getKey();
+            final Object   key      = property.getKey();
             final int      binIndex = binIndex(newBins, key);
 
             newBins[binIndex] = new Element(newBins[binIndex], property);
@@ -355,7 +355,7 @@
      *
      * @return {@link Element} matching key or {@code null} if not found.
      */
-    private Element findElement(final String key) {
+    private Element findElement(final Object key) {
         if (bins != null) {
             final int binIndex = binIndex(bins, key);
             return findElement(bins[binIndex], key);
@@ -370,7 +370,7 @@
      * @param key         {@link Element} key.
      * @return {@link Element} matching key or {@code null} if not found.
      */
-    private static Element findElement(final Element elementList, final String key) {
+    private static Element findElement(final Element elementList, final Object key) {
         final int hashCode = key.hashCode();
         for (Element element = elementList; element != null; element = element.getLink()) {
             if (element.match(key, hashCode)) {
@@ -416,7 +416,7 @@
      */
     private PropertyHashMap addNoClone(final Property property) {
         int newSize = size;
-        final String key = property.getKey();
+        final Object key = property.getKey();
         Element newList = list;
         if (bins != null) {
             final int binIndex = binIndex(bins, key);
@@ -437,7 +437,7 @@
         return new PropertyHashMap(newSize, bins, newList);
     }
 
-    private PropertyHashMap replaceNoClone(final String key, final Property property) {
+    private PropertyHashMap replaceNoClone(final Object key, final Property property) {
         if (bins != null) {
             final int binIndex = binIndex(bins, key);
             Element bin = bins[binIndex];
@@ -457,7 +457,7 @@
      *
      * @return New list with {@link Element} removed.
      */
-    private static Element removeFromList(final Element list, final String key) {
+    private static Element removeFromList(final Element list, final Object key) {
         if (list == null) {
             return null;
         }
@@ -480,7 +480,7 @@
     }
 
     // for element x. if x get link matches,
-    private static Element replaceInList(final Element list, final String key, final Property property) {
+    private static Element replaceInList(final Element list, final Object key, final Property property) {
         assert list != null;
         final int hashCode = key.hashCode();
 
@@ -519,21 +519,7 @@
 
     @Override
     public boolean containsKey(final Object key) {
-        if (key instanceof String) {
-            return findElement((String)key) != null;
-        }
-        assert key instanceof String;
-        return false;
-    }
-
-    /**
-     * Check if the map contains a key.
-     *
-     * @param key {@link Property} key.
-     *
-     * @return {@code true} of key is in {@link PropertyHashMap}.
-     */
-    public boolean containsKey(final String key) {
+        assert key instanceof String || key instanceof Symbol;
         return findElement(key) != null;
     }
 
@@ -549,29 +535,13 @@
 
     @Override
     public Property get(final Object key) {
-        if (key instanceof String) {
-            final Element element = findElement((String)key);
-            return element != null ? element.getProperty() : null;
-        }
-        assert key instanceof String;
-        return null;
-    }
-
-    /**
-     * Get the {@link Property} given a key that is an explicit {@link String}.
-     * See also {@link PropertyHashMap#get(Object)}
-     *
-     * @param key {@link Property} key.
-     *
-     * @return {@link Property}, or {@code null} if no property with that key was found.
-     */
-    public Property get(final String key) {
+        assert key instanceof String || key instanceof Symbol;
         final Element element = findElement(key);
         return element != null ? element.getProperty() : null;
     }
 
     @Override
-    public Property put(final String key, final Property value) {
+    public Property put(final Object key, final Property value) {
         throw new UnsupportedOperationException("Immutable map.");
     }
 
@@ -581,7 +551,7 @@
     }
 
     @Override
-    public void putAll(final Map<? extends String, ? extends Property> m) {
+    public void putAll(final Map<? extends Object, ? extends Property> m) {
         throw new UnsupportedOperationException("Immutable map.");
     }
 
@@ -591,8 +561,8 @@
     }
 
     @Override
-    public Set<String> keySet() {
-        final HashSet<String> set = new HashSet<>();
+    public Set<Object> keySet() {
+        final HashSet<Object> set = new HashSet<>();
         for (Element element = list; element != null; element = element.getLink()) {
             set.add(element.getKey());
         }
@@ -605,8 +575,8 @@
     }
 
     @Override
-    public Set<Entry<String, Property>> entrySet() {
-        final HashSet<Entry<String, Property>> set = new HashSet<>();
+    public Set<Entry<Object, Property>> entrySet() {
+        final HashSet<Entry<Object, Property>> set = new HashSet<>();
         for (Element element = list; element != null; element = element.getLink()) {
             set.add(element);
         }
@@ -616,7 +586,7 @@
     /**
      * List map element.
      */
-    static final class Element implements Entry<String, Property> {
+    static final class Element implements Entry<Object, Property> {
         /** Link for list construction. */
         private Element link;
 
@@ -624,7 +594,7 @@
         private final Property property;
 
         /** Element key. Kept separate for performance.) */
-        private final String key;
+        private final Object key;
 
         /** Element key hash code. */
         private final int hashCode;
@@ -640,7 +610,7 @@
             this.hashCode = this.key.hashCode();
         }
 
-        boolean match(final String otherKey, final int otherHashCode) {
+        boolean match(final Object otherKey, final int otherHashCode) {
             return this.hashCode == otherHashCode && this.key.equals(otherKey);
         }
 
@@ -655,7 +625,7 @@
         }
 
         @Override
-        public String getKey() {
+        public Object getKey() {
             return key;
         }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java	Wed Nov 11 16:28:17 2015 +0100
@@ -35,7 +35,7 @@
  */
 public class PropertyListeners {
 
-    private Map<String, WeakPropertyMapSet> listeners;
+    private Map<Object, WeakPropertyMapSet> listeners;
 
     // These counters are updated in debug mode
     private static LongAdder listenersAdded;
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Nov 11 16:28:17 2015 +0100
@@ -96,7 +96,7 @@
     private transient SharedPropertyMap sharedProtoMap;
 
     /** {@link SwitchPoint}s for gets on inherited properties. */
-    private transient HashMap<String, SwitchPoint> protoSwitches;
+    private transient HashMap<Object, SwitchPoint> protoSwitches;
 
     /** History of maps, used to limit map duplication. */
     private transient WeakHashMap<Property, Reference<PropertyMap>> history;
@@ -354,7 +354,7 @@
      *
      * @param key {@link Property} key to invalidate.
      */
-    synchronized void invalidateProtoSwitchPoint(final String key) {
+    synchronized void invalidateProtoSwitchPoint(final Object key) {
         if (protoSwitches != null) {
             final SwitchPoint sp = protoSwitches.get(key);
             if (sp != null) {
@@ -496,7 +496,7 @@
     public final synchronized PropertyMap deleteProperty(final Property property) {
         propertyDeleted(property, true);
         PropertyMap newMap = checkHistory(property);
-        final String key = property.getKey();
+        final Object key = property.getKey();
 
         if (newMap == null && properties.containsKey(key)) {
             final PropertyHashMap newProperties = properties.immutableRemove(key);
@@ -577,7 +577,7 @@
      * @param propertyFlags attribute flags of the property
      * @return the newly created UserAccessorProperty
      */
-    public final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) {
+    public final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags) {
         return new UserAccessorProperty(key, propertyFlags, getFreeSpillSlot());
     }
 
@@ -588,7 +588,7 @@
      *
      * @return {@link Property} matching key.
      */
-    public final Property findProperty(final String key) {
+    public final Property findProperty(final Object key) {
         return properties.find(key);
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Nov 11 16:28:17 2015 +0100
@@ -53,6 +53,7 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
+import java.lang.reflect.Array;
 import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -310,11 +311,11 @@
      */
     protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible) {
         PropertyMap newMap = propMap;
-        final String key = property.getKey();
+        final Object key = property.getKey();
         final Property oldProp = newMap.findProperty(key);
         if (oldProp == null) {
             if (! extensible) {
-                throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
+                throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
             }
 
             if (property instanceof UserAccessorProperty) {
@@ -330,7 +331,7 @@
             if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
                 if (oldProp instanceof UserAccessorProperty ||
                         !(oldProp.isWritable() && oldProp.isEnumerable())) {
-                    throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
+                    throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                 }
             }
         }
@@ -348,11 +349,11 @@
         final boolean extensible = newMap.isExtensible();
 
         for (final AccessorProperty property : properties) {
-            final String key = property.getKey();
+            final Object key = property.getKey();
 
             if (newMap.findProperty(key) == null) {
                 if (! extensible) {
-                    throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
+                    throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
                 }
                 newMap = newMap.addPropertyBind(property, source);
             }
@@ -456,7 +457,7 @@
      * @return Returns the Property Descriptor of the named own property of this
      * object, or undefined if absent.
      */
-    public Object getOwnPropertyDescriptor(final String key) {
+    public Object getOwnPropertyDescriptor(final Object key) {
         final Property property = getMap().findProperty(key);
 
         final Global global = Context.getGlobal();
@@ -518,7 +519,7 @@
      * Invalidate any existing global constant method handles that may exist for {@code key}.
      * @param key the property name
      */
-    protected void invalidateGlobalConstant(final String key) {
+    protected void invalidateGlobalConstant(final Object key) {
         final GlobalConstants globalConstants = getGlobalConstants();
         if (globalConstants != null) {
             globalConstants.delete(key);
@@ -534,11 +535,10 @@
      *
      * @return true if property was successfully defined
      */
-    public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
+    public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
         final Global             global  = Context.getGlobal();
         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
         final Object             current = getOwnPropertyDescriptor(key);
-        final String             name    = JSType.toString(key);
 
         invalidateGlobalConstant(key);
 
@@ -550,7 +550,7 @@
             }
             // new property added to non-extensible object
             if (reject) {
-                throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
+                throw typeError(global, "object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
             }
             return false;
         }
@@ -573,7 +573,7 @@
             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
                 // not configurable can not be made configurable
                 if (reject) {
-                    throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
+                    throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                 }
                 return false;
             }
@@ -582,7 +582,7 @@
                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
                 // cannot make non-enumerable as enumerable or vice-versa
                 if (reject) {
-                    throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
+                    throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                 }
                 return false;
             }
@@ -598,7 +598,7 @@
                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
                     newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
                     if (reject) {
-                        throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
+                        throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                     }
                     return false;
                 }
@@ -636,7 +636,7 @@
                 if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
                     newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
                     if (reject) {
-                        throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
+                        throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                     }
                     return false;
                 }
@@ -650,7 +650,7 @@
             if (!currentDesc.isConfigurable()) {
                 // not configurable can not be made configurable
                 if (reject) {
-                    throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
+                    throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
                 }
                 return false;
             }
@@ -716,7 +716,7 @@
         setArray(getArray().set(index, value, false));
     }
 
-    private void checkIntegerKey(final String key) {
+    private void checkIntegerKey(final Object key) {
         final int index = getArrayIndex(key);
 
         if (isValidArrayIndex(index)) {
@@ -734,7 +734,7 @@
       * @param key          property key
       * @param propertyDesc property descriptor for property
       */
-    public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
+    public final void addOwnProperty(final Object key, final PropertyDescriptor propertyDesc) {
         // Already checked that there is no own property with that key.
         PropertyDescriptor pdesc = propertyDesc;
 
@@ -776,7 +776,7 @@
      *
      * @return FindPropertyData or null if not found.
      */
-    public final FindProperty findProperty(final String key, final boolean deep) {
+    public final FindProperty findProperty(final Object key, final boolean deep) {
         return findProperty(key, deep, this);
     }
 
@@ -798,7 +798,7 @@
      *
      * @return FindPropertyData or null if not found.
      */
-    protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
+    protected FindProperty findProperty(final Object key, final boolean deep, final ScriptObject start) {
 
         final PropertyMap selfMap  = getMap();
         final Property    property = selfMap.findProperty(key);
@@ -820,13 +820,13 @@
     }
 
     /**
-     * Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a
+     * Low level property API. This is similar to {@link #findProperty(Object, boolean)} but returns a
      * {@code boolean} value instead of a {@link FindProperty} object.
      * @param key  Property key.
      * @param deep Whether the search should look up proto chain.
      * @return true if the property was found.
      */
-    boolean hasProperty(final String key, final boolean deep) {
+    boolean hasProperty(final Object key, final boolean deep) {
         if (getMap().findProperty(key) != null) {
             return true;
         }
@@ -841,7 +841,7 @@
         return false;
     }
 
-    private SwitchPoint findBuiltinSwitchPoint(final String key) {
+    private SwitchPoint findBuiltinSwitchPoint(final Object key) {
         for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
             final Property prop = myProto.getMap().findProperty(key);
             if (prop != null) {
@@ -866,7 +866,7 @@
      *
      * @return New property.
      */
-    public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
+    public final Property addOwnProperty(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
     }
 
@@ -881,7 +881,7 @@
      *
      * @return New property.
      */
-    public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
+    public final Property addOwnProperty(final Object key, final int propertyFlags, final Object value) {
         return addSpillProperty(key, propertyFlags, value, true);
     }
 
@@ -1349,42 +1349,60 @@
         final Set<String> keys = new HashSet<>();
         final Set<String> nonEnumerable = new HashSet<>();
         for (ScriptObject self = this; self != null; self = self.getProto()) {
-            keys.addAll(Arrays.asList(self.getOwnKeys(true, nonEnumerable)));
+            keys.addAll(Arrays.asList(self.getOwnKeys(String.class, true, nonEnumerable)));
         }
         return keys.toArray(new String[keys.size()]);
     }
 
     /**
-     * return an array of own property keys associated with the object.
+     * Return an array of own property keys associated with the object.
      *
      * @param all True if to include non-enumerable keys.
      * @return Array of keys.
      */
     public final String[] getOwnKeys(final boolean all) {
-        return getOwnKeys(all, null);
+        return getOwnKeys(String.class, all, null);
+    }
+
+    /**
+     * Return an array of own property keys associated with the object.
+     *
+     * @param all True if to include non-enumerable keys.
+     * @return Array of keys.
+     */
+    public final Symbol[] getOwnSymbols(final boolean all) {
+        return getOwnKeys(Symbol.class, all, null);
     }
 
     /**
      * return an array of own property keys associated with the object.
      *
+     * @param <T> the type returned keys.
+     * @param type the type of keys to return, either {@code String.class} or {@code Symbol.class}.
      * @param all True if to include non-enumerable keys.
-     * @param nonEnumerable set of non-enumerable properties seen already.Used
-       to filter out shadowed, but enumerable properties from proto children.
+     * @param nonEnumerable set of non-enumerable properties seen already. Used to
+     *                      filter out shadowed, but enumerable properties from proto children.
      * @return Array of keys.
      */
-    protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
+    @SuppressWarnings("unchecked")
+    protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) {
         final List<Object> keys    = new ArrayList<>();
         final PropertyMap  selfMap = this.getMap();
 
         final ArrayData array  = getArray();
 
-        for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
-            keys.add(JSType.toString(iter.next().longValue()));
+        if (type == String.class) {
+            for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
+                keys.add(JSType.toString(iter.next().longValue()));
+            }
         }
 
         for (final Property property : selfMap.getProperties()) {
             final boolean enumerable = property.isEnumerable();
-            final String key = property.getKey();
+            final Object key = property.getKey();
+            if (!type.isInstance(key)) {
+                continue;
+            }
             if (all) {
                 keys.add(key);
             } else if (enumerable) {
@@ -1396,12 +1414,12 @@
             } else {
                 // store this non-enumerable property for later proto walk
                 if (nonEnumerable != null) {
-                    nonEnumerable.add(key);
+                    nonEnumerable.add((T) key);
                 }
             }
         }
 
-        return keys.toArray(new String[keys.size()]);
+        return keys.toArray((T[]) Array.newInstance(type, keys.size()));
     }
 
     /**
@@ -2397,12 +2415,12 @@
 
     /**
      * Invoke fall back if a property is not found.
-     * @param name Name of property.
+     * @param key Name of property.
      * @param isScope is this a scope access?
      * @param programPoint program point
      * @return Result from call.
      */
-    protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
+    protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
         final Object func = (find != null)? find.getObjectValue() : null;
 
@@ -2410,9 +2428,9 @@
         if (func instanceof ScriptFunction) {
             final ScriptFunction sfunc = (ScriptFunction)func;
             final Object self = isScope && sfunc.isStrict()? UNDEFINED : this;
-            ret = ScriptRuntime.apply(sfunc, self, name);
+            ret = ScriptRuntime.apply(sfunc, self, key);
         } else if (isScope) {
-            throw referenceError("not.defined", name);
+            throw referenceError("not.defined", key.toString());
         }
 
         if (isValid(programPoint)) {
@@ -2502,7 +2520,7 @@
             final Set<String> keys = new LinkedHashSet<>();
             final Set<String> nonEnumerable = new HashSet<>();
             for (ScriptObject self = object; self != null; self = self.getProto()) {
-                keys.addAll(Arrays.asList(self.getOwnKeys(false, nonEnumerable)));
+                keys.addAll(Arrays.asList(self.getOwnKeys(String.class, false, nonEnumerable)));
             }
             this.values = keys.toArray(new String[keys.size()]);
         }
@@ -2518,7 +2536,7 @@
             final ArrayList<Object> valueList = new ArrayList<>();
             final Set<String> nonEnumerable = new HashSet<>();
             for (ScriptObject self = object; self != null; self = self.getProto()) {
-                for (final String key : self.getOwnKeys(false, nonEnumerable)) {
+                for (final String key : self.getOwnKeys(String.class, false, nonEnumerable)) {
                     valueList.add(self.get(key));
                 }
             }
@@ -2532,7 +2550,7 @@
      * @param flags  Property flags.
      * @return Added property.
      */
-    private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) {
+    private Property addSpillProperty(final Object key, final int flags, final Object value, final boolean hasInitialValue) {
         final PropertyMap propertyMap = getMap();
         final int fieldSlot  = propertyMap.getFreeFieldSlot();
         final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
@@ -2726,7 +2744,7 @@
        }
     }
 
-    private int getInt(final int index, final String key, final int programPoint) {
+    private int getInt(final int index, final Object key, final int programPoint) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
@@ -2770,7 +2788,7 @@
             return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
         }
 
-        return getInt(index, JSType.toString(primitiveKey), programPoint);
+        return getInt(index, JSType.toPropertyKey(primitiveKey), programPoint);
     }
 
     @Override
@@ -2809,7 +2827,7 @@
         return getInt(index, JSType.toString(key), programPoint);
     }
 
-    private long getLong(final int index, final String key, final int programPoint) {
+    private long getLong(final int index, final Object key, final int programPoint) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
@@ -2852,7 +2870,7 @@
             return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
         }
 
-        return getLong(index, JSType.toString(primitiveKey), programPoint);
+        return getLong(index, JSType.toPropertyKey(primitiveKey), programPoint);
     }
 
     @Override
@@ -2891,7 +2909,7 @@
         return getLong(index, JSType.toString(key), programPoint);
     }
 
-    private double getDouble(final int index, final String key, final int programPoint) {
+    private double getDouble(final int index, final Object key, final int programPoint) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
@@ -2934,7 +2952,7 @@
             return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
         }
 
-        return getDouble(index, JSType.toString(primitiveKey), programPoint);
+        return getDouble(index, JSType.toPropertyKey(primitiveKey), programPoint);
     }
 
     @Override
@@ -2973,7 +2991,7 @@
         return getDouble(index, JSType.toString(key), programPoint);
     }
 
-    private Object get(final int index, final String key) {
+    private Object get(final int index, final Object key) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
@@ -3015,7 +3033,7 @@
             return array.getObject(index);
         }
 
-        return get(index, JSType.toString(primitiveKey));
+        return get(index, JSType.toPropertyKey(primitiveKey));
     }
 
     @Override
@@ -3161,7 +3179,7 @@
      * @param key           property key
      * @param value         property value
      */
-    public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) {
+    public final void setObject(final FindProperty find, final int callSiteFlags, final Object key, final Object value) {
         FindProperty f = find;
 
         invalidateGlobalConstant(key);
@@ -3189,10 +3207,10 @@
         if (f != null) {
             if (!f.getProperty().isWritable()) {
                 if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) {
-                    throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode.
+                    throw typeError("assign.constant", key.toString()); // Overwriting ES6 const should throw also in non-strict mode.
                 }
                 if (isStrictFlag(callSiteFlags)) {
-                    throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
+                    throw typeError("property.not.writable", key.toString(), ScriptRuntime.safeToString(this));
                 }
                 return;
             }
@@ -3201,7 +3219,7 @@
 
         } else if (!isExtensible()) {
             if (isStrictFlag(callSiteFlags)) {
-                throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
+                throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
             }
         } else {
             ScriptObject sobj = this;
@@ -3235,7 +3253,7 @@
             return;
         }
 
-        final String propName = JSType.toString(primitiveKey);
+        final Object propName = JSType.toPropertyKey(primitiveKey);
         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
     }
 
@@ -3255,7 +3273,7 @@
             return;
         }
 
-        final String propName = JSType.toString(primitiveKey);
+        final Object propName = JSType.toPropertyKey(primitiveKey);
         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
     }
 
@@ -3275,7 +3293,7 @@
             return;
         }
 
-        final String propName = JSType.toString(primitiveKey);
+        final Object propName = JSType.toPropertyKey(primitiveKey);
         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
     }
 
@@ -3295,7 +3313,7 @@
             return;
         }
 
-        final String propName = JSType.toString(primitiveKey);
+        final Object propName = JSType.toPropertyKey(primitiveKey);
         setObject(findProperty(propName, true), callSiteFlags, propName, value);
     }
 
@@ -3529,7 +3547,7 @@
     public boolean has(final Object key) {
         final Object primitiveKey = JSType.toPrimitive(key);
         final int    index        = getArrayIndex(primitiveKey);
-        return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true);
+        return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), true);
     }
 
     @Override
@@ -3567,7 +3585,7 @@
     public boolean hasOwnProperty(final Object key) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
         final int    index        = getArrayIndex(primitiveKey);
-        return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false);
+        return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), false);
     }
 
     @Override
@@ -3657,7 +3675,7 @@
     }
 
     private boolean deleteObject(final Object key, final boolean strict) {
-        final String propName = JSType.toString(key);
+        final Object propName = JSType.toPropertyKey(key);
         final FindProperty find = findProperty(propName, false);
 
         if (find == null) {
@@ -3666,7 +3684,7 @@
 
         if (!find.getProperty().isConfigurable()) {
             if (strict) {
-                throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
+                throw typeError("cant.delete.property", propName.toString(), ScriptRuntime.safeToString(this));
             }
             return false;
         }
@@ -3712,7 +3730,7 @@
      * @param setter setter function for the property
      * @return the newly created UserAccessorProperty
      */
-    protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
+    protected final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
         final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags);
         //property.getSetter(Object.class, getMap());
         uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java	Wed Nov 11 16:28:17 2015 +0100
@@ -836,11 +836,11 @@
         } else if (yType == JSType.BOOLEAN) {
             // Can reverse order as y is primitive
             return equalBooleanToAny(y, x);
-        } else if (isNumberOrStringAndObject(xType, yType)) {
-            return equalNumberOrStringToObject(x, y);
-        } else if (isNumberOrStringAndObject(yType, xType)) {
+        } else if (isWrappedPrimitiveAndObject(xType, yType)) {
+            return equalWrappedPrimitiveToObject(x, y);
+        } else if (isWrappedPrimitiveAndObject(yType, xType)) {
             // Can reverse order as y is primitive
-            return equalNumberOrStringToObject(y, x);
+            return equalWrappedPrimitiveToObject(y, x);
         }
 
         return false;
@@ -854,8 +854,8 @@
         return xType == JSType.NUMBER && yType == JSType.STRING;
     }
 
-    private static boolean isNumberOrStringAndObject(final JSType xType, final JSType yType) {
-        return (xType == JSType.NUMBER || xType == JSType.STRING) && yType == JSType.OBJECT;
+    private static boolean isWrappedPrimitiveAndObject(final JSType xType, final JSType yType) {
+        return (xType == JSType.NUMBER || xType == JSType.STRING || xType == JSType.SYMBOL) && yType == JSType.OBJECT;
     }
 
     private static boolean equalNumberToString(final Object num, final Object str) {
@@ -869,7 +869,7 @@
         return equals(JSType.toNumber((Boolean)bool), any);
     }
 
-    private static boolean equalNumberOrStringToObject(final Object numOrStr, final Object any) {
+    private static boolean equalWrappedPrimitiveToObject(final Object numOrStr, final Object any) {
         return equals(numOrStr, JSType.toPrimitive(any));
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java	Wed Nov 11 16:28:17 2015 +0100
@@ -158,7 +158,7 @@
      * @param flags  the property flags
      * @param slot   spill slot
      */
-    public SpillProperty(final String key, final int flags, final int slot) {
+    public SpillProperty(final Object key, final int flags, final int slot) {
         super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot));
     }
 
@@ -174,7 +174,7 @@
         setType(hasDualFields() ? initialType : Object.class);
     }
 
-    SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
+    SpillProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
         this(key, flags, slot);
         setInitialValue(owner, initialValue);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Symbol.java	Wed Nov 11 16:28:17 2015 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.io.Serializable;
+
+/**
+ * This class represents a unique, non-String Object property key as defined in ECMAScript 6.
+ */
+public final class Symbol implements Serializable {
+
+    private final String name;
+
+    private static final long serialVersionUID = -2988436597549486913L;
+
+    /**
+     * Symbol constructor
+     * @param name symbol name
+     */
+    public Symbol(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return "Symbol(" + name + ")";
+    }
+
+    /**
+     * Return the symbol's name
+     * @return the name
+     */
+    public final String getName() {
+        return name;
+    }
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Wed Nov 11 16:28:17 2015 +0100
@@ -120,7 +120,7 @@
      * @param flags property flags
      * @param slot  spill slot
      */
-    UserAccessorProperty(final String key, final int flags, final int slot) {
+    UserAccessorProperty(final Object key, final int flags, final int slot) {
         super(key, flags, slot);
     }
 
@@ -226,7 +226,7 @@
     @Override
     public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
         try {
-            invokeObjectSetter(getAccessors((owner != null) ? owner : self), getObjectSetterInvoker(), strict ? getKey() : null, self, value);
+            invokeObjectSetter(getAccessors((owner != null) ? owner : self), getObjectSetterInvoker(), strict ? getKey().toString() : null, self, value);
         } catch (final Error | RuntimeException t) {
             throw t;
         } catch (final Throwable t) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java	Wed Nov 11 16:28:17 2015 +0100
@@ -198,7 +198,7 @@
      * @return FindPropertyData or null if not found.
      */
     @Override
-    protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
+    protected FindProperty findProperty(final Object key, final boolean deep, final ScriptObject start) {
         // We call findProperty on 'expression' with 'expression' itself as start parameter.
         // This way in ScriptObject.setObject we can tell the property is from a 'with' expression
         // (as opposed from another non-scope object in the proto chain such as Object.prototype).
@@ -210,18 +210,18 @@
     }
 
     @Override
-    protected Object invokeNoSuchProperty(final String name, final boolean isScope, final int programPoint) {
+    protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
         final FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true);
         if (find != null) {
             final Object func = find.getObjectValue();
             if (func instanceof ScriptFunction) {
                 final ScriptFunction sfunc = (ScriptFunction)func;
                 final Object self = isScope && sfunc.isStrict()? UNDEFINED : expression;
-                return ScriptRuntime.apply(sfunc, self, name);
+                return ScriptRuntime.apply(sfunc, self, key);
             }
         }
 
-        return getProto().invokeNoSuchProperty(name, isScope, programPoint);
+        return getProto().invokeNoSuchProperty(key, isScope, programPoint);
     }
 
     @Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Wed Nov 11 16:28:17 2015 +0100
@@ -154,6 +154,11 @@
 type.error.java.array.conversion.failed=Java.to conversion to array type {0} failed
 type.error.constructor.requires.new=Constructor {0} requires "new".
 type.error.new.on.nonpublic.javatype=new cannot be used with non-public java type {0}.
+type.error.invalid.weak.key=invalid value {0} used as weak key.
+type.error.symbol.to.string=Can not convert Symbol value to string.
+type.error.symbol.to.number=Can not convert Symbol value to number.
+type.error.not.a.symbol={0} is not a symbol.
+type.error.symbol.as.constructor=Symbol is not a constructor.
 
 range.error.dataview.constructor.offset=Wrong offset or length in DataView constructor
 range.error.dataview.offset=Offset is outside the bounds of the DataView
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java	Wed Nov 11 16:28:17 2015 +0100
@@ -36,6 +36,7 @@
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.io.PrintWriter;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.ResourceBundle;
@@ -47,6 +48,7 @@
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
 import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.objects.NativeSymbol;
 import jdk.nashorn.internal.parser.Parser;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ErrorManager;
@@ -54,7 +56,10 @@
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Symbol;
+import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
 import jdk.nashorn.internal.runtime.options.Options;
 
 /**
@@ -474,7 +479,7 @@
                 try {
                     final Object res = context.eval(global, source, global, "<shell>");
                     if (res != ScriptRuntime.UNDEFINED) {
-                        err.println(JSType.toString(res));
+                        err.println(toString(res, global));
                     }
                 } catch (final Exception e) {
                     err.println(e);
@@ -491,4 +496,56 @@
 
         return SUCCESS;
     }
+
+    /**
+     * Converts {@code result} to a printable string. The reason we don't use {@link JSType#toString(Object)}
+     * or {@link ScriptRuntime#safeToString(Object)} is that we want to be able to render Symbol values
+     * even if they occur within an Array, and therefore have to implement our own Array to String
+     * conversion.
+     *
+     * @param result the result
+     * @param global the global object
+     * @return the string representation
+     */
+    protected static String toString(final Object result, final Global global) {
+        if (result instanceof Symbol) {
+            // Normal implicit conversion of symbol to string would throw TypeError
+            return result.toString();
+        }
+
+        if (result instanceof NativeSymbol) {
+            return JSType.toPrimitive(result).toString();
+        }
+
+        if (isArrayWithDefaultToString(result, global)) {
+            // This should yield the same string as Array.prototype.toString but
+            // will not throw if the array contents include symbols.
+            final StringBuilder sb = new StringBuilder();
+            final Iterator<Object> iter = ArrayLikeIterator.arrayLikeIterator(result, true);
+
+            while (iter.hasNext()) {
+                final Object obj = iter.next();
+
+                if (obj != null && obj != ScriptRuntime.UNDEFINED) {
+                    sb.append(toString(obj, global));
+                }
+
+                if (iter.hasNext()) {
+                    sb.append(',');
+                }
+            }
+
+            return sb.toString();
+        }
+
+        return JSType.toString(result);
+    }
+
+    private static boolean isArrayWithDefaultToString(final Object result, final Global global) {
+        if (result instanceof ScriptObject) {
+            final ScriptObject sobj = (ScriptObject) result;
+            return sobj.isArray() && sobj.get("toString") == global.getArrayPrototype().get("toString");
+        }
+        return false;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/es6.js	Wed Nov 11 16:28:17 2015 +0100
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Make sure ECMAScript 6 features are not available in ES5 mode.
+ *
+ * @test
+ * @run
+ */
+
+if (typeof Symbol !== 'undefined' || 'Symbol' in this) {
+    Assert.fail('Symbol is defined in global scope');
+}
+
+if (typeof Object.getOwnPropertySymbols !== 'undefined' || 'getOwnPropertySymbols' in Object) {
+    Assert.fail('getOwnPropertySymbols is defined in global Object');
+}
+
+function expectError(src, msg, error) {
+    try {
+        eval(src);
+        Assert.fail(msg);
+    } catch (e) {
+        if (e.name !== error) {
+            Assert.fail('Unexpected error: ' + e);
+        }
+    }
+}
+
+expectError('let i = 0', 'let', 'SyntaxError');
+expectError('const i = 0', 'const', 'SyntaxError');
+expectError('for (let i = 0; i < 10; i++) print(i)', 'for-let', 'SyntaxError');
+expectError('0b0', 'numeric literal', 'SyntaxError');
+expectError('0o0', 'numeric litera', 'SyntaxError');
+expectError('`text`', 'template literal', 'SyntaxError');
+expectError('`${ x }`', 'template literal', 'SyntaxError');
+expectError('`text ${ x } text`', 'template literal', 'SyntaxError');
+expectError('f`text`', 'template literal', 'SyntaxError');
--- a/nashorn/test/script/basic/es6/let.js	Wed Nov 11 15:22:14 2015 +0100
+++ b/nashorn/test/script/basic/es6/let.js	Wed Nov 11 16:28:17 2015 +0100
@@ -26,7 +26,8 @@
  *
  * @test
  * @run
- * @option --language=es6 */
+ * @option --language=es6
+ */
 
 "use strict";
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/es6/symbols.js	Wed Nov 11 16:28:17 2015 +0100
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8141702: Add support for Symbol property keys
+ *
+ * @test
+ * @run
+ * @option --language=es6
+ */
+
+Assert.assertTrue(typeof Symbol === 'function');
+Assert.assertTrue(typeof Symbol() === 'symbol');
+
+Assert.assertTrue(Symbol().toString() === 'Symbol()');
+Assert.assertTrue(Symbol('foo').toString() === 'Symbol(foo)');
+Assert.assertTrue(Symbol(1).toString() === 'Symbol(1)');
+Assert.assertTrue(Symbol(true).toString() === 'Symbol(true)');
+Assert.assertTrue(Symbol([1, 2, 3]).toString() === 'Symbol(1,2,3)');
+Assert.assertTrue(Symbol(null).toString() === 'Symbol(null)');
+Assert.assertTrue(Symbol(undefined).toString() === 'Symbol()');
+
+var s1 = Symbol();
+var s2 = Symbol("s2");
+Assert.assertFalse(s1 instanceof Symbol); // not an object
+
+var obj = {};
+obj['foo'] = 'foo';
+obj[s1] = s1;
+obj['bar'] = 'bar';
+obj[1] = 1;
+obj[s2] = s2;
+
+Assert.assertTrue(obj['foo'] === 'foo');
+Assert.assertTrue(obj[s1] === s1);
+Assert.assertTrue(obj['bar'] === 'bar');
+Assert.assertTrue(obj[1] === 1);
+Assert.assertTrue(obj[s2] === s2);
+
+var expectedNames = ['1', 'foo', 'bar'];
+var expectedSymbols = [s1, s2];
+var actualNames = Object.getOwnPropertyNames(obj);
+var actualSymbols = Object.getOwnPropertySymbols(obj);
+Assert.assertTrue(expectedNames.length == actualNames.length);
+Assert.assertTrue(expectedSymbols.length == actualSymbols.length);
+
+for (var key in expectedNames) {
+    Assert.assertTrue(expectedNames[key] === actualNames[key]);
+}
+for (var key in expectedSymbols) {
+    Assert.assertTrue(expectedSymbols[key] === actualSymbols[key]);
+}
+
+// Delete
+Assert.assertTrue(delete obj[s1]);
+Assert.assertTrue(Object.getOwnPropertySymbols(obj).length === 1);
+Assert.assertTrue(Object.getOwnPropertySymbols(obj)[0] === s2);
+
+// Object.defineProperty
+Object.defineProperty(obj, s1, {value : 'hello'});
+Assert.assertTrue(obj[s1] === 'hello');
+actualSymbols = Object.getOwnPropertySymbols(obj);
+Assert.assertTrue(Object.getOwnPropertySymbols(obj).length === 2);
+Assert.assertTrue(Object.getOwnPropertySymbols(obj)[1] === s1);
+
+// Symbol called as constructor
+try {
+    new Symbol();
+    Assert.fail("Symbol invoked as constructor");
+} catch (e) {
+    if (e.name !== "TypeError" || e.message !== "Symbol is not a constructor.") {
+        Assert.fail("Unexpected error: " + e);
+    }
+}
+
+// Implicit conversion to string or number should throw
+try {
+    ' ' + s1;
+    Assert.fail("Symbol converted to string");
+} catch (e) {
+    if (e.name !== "TypeError" || e.message !== "Can not convert Symbol value to string.") {
+        Assert.fail("Unexpected error: " + e);
+    }
+}
+
+try {
+    4 * s1;
+    Assert.fail("Symbol converted to number");
+} catch (e) {
+    if (e.name !== "TypeError" || e.message !== "Can not convert Symbol value to number.") {
+        Assert.fail("Unexpected error: " + e);
+    }
+}
+
+// Symbol.for and Symbol.keyFor
+
+var uncached = Symbol('foo');
+var cached = Symbol.for('foo');
+
+Assert.assertTrue(uncached !== cached);
+Assert.assertTrue(Symbol.keyFor(uncached) === undefined);
+Assert.assertTrue(Symbol.keyFor(cached) === 'foo');
+Assert.assertTrue(cached === Symbol.for('foo'));
+Assert.assertTrue(cached === Symbol.for('f' + 'oo'));
+
+// Object wrapper
+
+var o = Object(s1);
+obj = {};
+obj[s1] = "s1";
+Assert.assertTrue(o == s1);
+Assert.assertTrue(o !== s1);
+Assert.assertTrue(typeof o === 'object');
+Assert.assertTrue(o instanceof Symbol);
+Assert.assertTrue(obj[o] == 's1');
+Assert.assertTrue(o in obj);
+
+// various non-strict comparisons that should fail
+
+Assert.assertFalse(0 == Symbol());
+Assert.assertFalse(1 == Symbol(1));
+Assert.assertFalse(null == Symbol());
+Assert.assertFalse(undefined == Symbol);
+Assert.assertFalse('Symbol()' == Symbol());
+Assert.assertFalse('Symbol(foo)' == Symbol('foo'));
+