changeset 1646:ee71a66a48c7

Merge
author lana
date Thu, 12 Nov 2015 14:14:37 -0800
parents 435d7217b35d 2cebe18ffc70
children 53edf7f3f10c
files src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NumberToString.java
diffstat 87 files changed, 308294 insertions(+), 1172 deletions(-) [+]
line wrap: on
line diff
--- a/docs/DEVELOPER_README	Thu Nov 12 10:39:12 2015 -0800
+++ b/docs/DEVELOPER_README	Thu Nov 12 14:14:37 2015 -0800
@@ -35,12 +35,14 @@
 
 SYSTEM PROPERTY: -Dnashorn.unstable.relink.threshold=x
 
-This property controls how many call site misses are allowed before a 
-callsite is relinked with "apply" semantics to never change again. 
-In the case of megamorphic callsites, this is necessary, or the 
-program would spend all its time swapping out callsite targets. Dynalink 
-has a default value (currently 8 relinks) for this property if it 
-is not explicitly set.
+NOTE: This property is deprecated in favor of the 
+"--unstable-relink-threshold" command line option. It controls how many
+call site misses are allowed before a callsite is relinked with "apply"
+semantics to never change again. In the case of megamorphic callsites, 
+this is necessary, or the program would spend all its time swapping out 
+callsite targets. When neither the system property nor the command line
+option are specified, defaults to 8, or 16 with optimistic types turned
+on.
 
 
 SYSTEM PROPERTY: -Dnashorn.compiler.splitter.threshold=x
@@ -607,6 +609,10 @@
 	                         enterexit [trace callsite enter/exit], objects [print object properties].)
 		param: [=[option,]*]   
 
+	-urt, --unstable-relink-threshold (Number of times a dynamic call site has to be relinked before it 
+	                                  is considered unstable, when the runtime will try to link it as 
+	                                  if it is megamorphic.)
+
 	--verify-code (Verify byte code before running.)
 		param: [true|false]   default: false
 
--- a/make/build.xml	Thu Nov 12 10:39:12 2015 -0800
+++ b/make/build.xml	Thu Nov 12 14:14:37 2015 -0800
@@ -311,6 +311,10 @@
        <fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/test/resources"/>
     </copy>
 
+    <copy todir="${build.test.classes.dir}/jdk/nashorn/internal/runtime/doubleconv/test/resources">
+      <fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/doubleconv/test/resources"/>
+    </copy>
+
     <copy todir="${build.test.classes.dir}/jdk/nashorn/api/scripting/test/resources">
        <fileset dir="${test.src.dir}/jdk/nashorn/api/scripting/test/resources"/>
     </copy>
@@ -436,6 +440,7 @@
           <include name="**/runtime/test/*Test.class"/>
           <include name="**/runtime/regexp/test/*Test.class"/>
           <include name="**/runtime/regexp/joni/test/*Test.class"/>
+          <include name="**/runtime/doubleconv/test/*Test.class"/>
           <include name="**/framework/*Test.class"/>
      </fileset>
   </target>
--- a/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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)?
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CompositeOperation.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CompositeOperation.java	Thu Nov 12 14:14:37 2015 -0800
@@ -139,7 +139,7 @@
  * {@code SET_ELEMENT}; other standard operations should not be combined. The
  * constructor will allow any combination of operations, though.
  */
-public class CompositeOperation implements Operation {
+public final class CompositeOperation implements Operation {
     private final Operation[] operations;
 
     /**
@@ -228,10 +228,10 @@
      */
     @Override
     public boolean equals(final Object obj) {
-        if (obj == null || obj.getClass() != CompositeOperation.class) {
-            return false;
+        if (obj instanceof CompositeOperation) {
+            return Arrays.equals(operations, ((CompositeOperation)obj).operations);
         }
-        return Arrays.equals(operations, ((CompositeOperation)obj).operations);
+        return false;
     }
 
     /**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java	Thu Nov 12 14:14:37 2015 -0800
@@ -137,7 +137,7 @@
      * Default value for {@link #setUnstableRelinkThreshold(int) unstable relink
      * threshold}.
      */
-    public static final int DEFAULT_UNSTABLE_RELINK_THRESHOLD = 8;
+    private static final int DEFAULT_UNSTABLE_RELINK_THRESHOLD = 8;
 
     private boolean classLoaderExplicitlySet = false;
     private ClassLoader classLoader;
@@ -272,7 +272,7 @@
     /**
      * Sets the unstable relink threshold; the number of times a call site is
      * relinked after which it will be considered unstable, and subsequent link
-     * requests for it will indicate this.
+     * requests for it will indicate this. Defaults to 8 when not set explicitly.
      * @param unstableRelinkThreshold the new threshold. Must not be less than
      * zero. The value of zero means that call sites will never be considered
      * unstable.
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NamedOperation.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NamedOperation.java	Thu Nov 12 14:14:37 2015 -0800
@@ -98,7 +98,7 @@
  * the documentation for all {@link StandardOperation} members describes how
  * they are affected by being incorporated into a named operation.
  */
-public class NamedOperation implements Operation {
+public final class NamedOperation implements Operation {
     private final Operation baseOperation;
     private final Object name;
 
@@ -145,13 +145,11 @@
      */
     @Override
     public boolean equals(final Object obj) {
-        if (obj == null) {
-            return false;
-        } else if(obj.getClass() != NamedOperation.class) {
-            return false;
+        if (obj instanceof NamedOperation) {
+            final NamedOperation other = (NamedOperation)obj;
+            return baseOperation.equals(other.baseOperation) && name.equals(other.name);
         }
-        final NamedOperation other = (NamedOperation)obj;
-        return baseOperation.equals(other.baseOperation) && name.equals(other.name);
+        return false;
     }
 
     /**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Thu Nov 12 14:14:37 2015 -0800
@@ -93,11 +93,11 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.CompositeOperation;
 import jdk.internal.dynalink.NamedOperation;
@@ -200,20 +200,20 @@
 
     abstract FacetIntrospector createFacetIntrospector();
 
-    Collection<String> getReadablePropertyNames() {
+    Set<String> getReadablePropertyNames() {
         return getUnmodifiableKeys(propertyGetters);
     }
 
-    Collection<String> getWritablePropertyNames() {
+    Set<String> getWritablePropertyNames() {
         return getUnmodifiableKeys(propertySetters);
     }
 
-    Collection<String> getMethodNames() {
+    Set<String> getMethodNames() {
         return getUnmodifiableKeys(methods);
     }
 
-    private static Collection<String> getUnmodifiableKeys(final Map<String, ?> m) {
-        return Collections.unmodifiableCollection(m.keySet());
+    private static Set<String> getUnmodifiableKeys(final Map<String, ?> m) {
+        return Collections.unmodifiableSet(m.keySet());
     }
 
     /**
@@ -416,9 +416,7 @@
         return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
     }
 
-    SingleDynamicMethod getConstructorMethod(final String signature) {
-        return null;
-    }
+    abstract SingleDynamicMethod getConstructorMethod(final String signature);
 
     private MethodHandle getAssignableGuard(final MethodType type) {
         return Guards.asType(assignableGuard, type);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java	Thu Nov 12 14:14:37 2015 -0800
@@ -152,6 +152,11 @@
         return null;
     }
 
+    @Override
+    SingleDynamicMethod getConstructorMethod(final String signature) {
+        return null;
+    }
+
     private static final MethodHandle GET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "get",
             MethodType.methodType(Object.class, int.class));
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java	Thu Nov 12 14:14:37 2015 -0800
@@ -84,8 +84,8 @@
 package jdk.internal.dynalink.beans;
 
 import java.lang.invoke.MethodHandles.Lookup;
-import java.util.Collection;
 import java.util.Collections;
+import java.util.Set;
 import jdk.internal.dynalink.DynamicLinkerFactory;
 import jdk.internal.dynalink.StandardOperation;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -229,11 +229,11 @@
     }
 
     /**
-     * Returns a collection of names of all readable instance properties of a class.
+     * Returns a set of names of all readable instance properties of a class.
      * @param clazz the class
-     * @return a collection of names of all readable instance properties of a class.
+     * @return a set of names of all readable instance properties of a class.
      */
-    public static Collection<String> getReadableInstancePropertyNames(final Class<?> clazz) {
+    public static Set<String> getReadableInstancePropertyNames(final Class<?> clazz) {
         final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
         if(linker instanceof BeanLinker) {
             return ((BeanLinker)linker).getReadablePropertyNames();
@@ -242,11 +242,11 @@
     }
 
     /**
-     * Returns a collection of names of all writable instance properties of a class.
+     * Returns a set of names of all writable instance properties of a class.
      * @param clazz the class
-     * @return a collection of names of all writable instance properties of a class.
+     * @return a set of names of all writable instance properties of a class.
      */
-    public static Collection<String> getWritableInstancePropertyNames(final Class<?> clazz) {
+    public static Set<String> getWritableInstancePropertyNames(final Class<?> clazz) {
         final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
         if(linker instanceof BeanLinker) {
             return ((BeanLinker)linker).getWritablePropertyNames();
@@ -255,11 +255,11 @@
     }
 
     /**
-     * Returns a collection of names of all instance methods of a class.
+     * Returns a set of names of all instance methods of a class.
      * @param clazz the class
-     * @return a collection of names of all instance methods of a class.
+     * @return a set of names of all instance methods of a class.
      */
-    public static Collection<String> getInstanceMethodNames(final Class<?> clazz) {
+    public static Set<String> getInstanceMethodNames(final Class<?> clazz) {
         final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
         if(linker instanceof BeanLinker) {
             return ((BeanLinker)linker).getMethodNames();
@@ -268,29 +268,29 @@
     }
 
     /**
-     * Returns a collection of names of all readable static properties of a class.
+     * Returns a set of names of all readable static properties of a class.
      * @param clazz the class
-     * @return a collection of names of all readable static properties of a class.
+     * @return a set of names of all readable static properties of a class.
      */
-    public static Collection<String> getReadableStaticPropertyNames(final Class<?> clazz) {
+    public static Set<String> getReadableStaticPropertyNames(final Class<?> clazz) {
         return StaticClassLinker.getReadableStaticPropertyNames(clazz);
     }
 
     /**
-     * Returns a collection of names of all writable static properties of a class.
+     * Returns a set of names of all writable static properties of a class.
      * @param clazz the class
-     * @return a collection of names of all writable static properties of a class.
+     * @return a set of names of all writable static properties of a class.
      */
-    public static Collection<String> getWritableStaticPropertyNames(final Class<?> clazz) {
+    public static Set<String> getWritableStaticPropertyNames(final Class<?> clazz) {
         return StaticClassLinker.getWritableStaticPropertyNames(clazz);
     }
 
     /**
-     * Returns a collection of names of all static methods of a class.
+     * Returns a set of names of all static methods of a class.
      * @param clazz the class
-     * @return a collection of names of all static methods of a class.
+     * @return a set of names of all static methods of a class.
      */
-    public static Collection<String> getStaticMethodNames(final Class<?> clazz) {
+    public static Set<String> getStaticMethodNames(final Class<?> clazz) {
         return StaticClassLinker.getStaticMethodNames(clazz);
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClass.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClass.java	Thu Nov 12 14:14:37 2015 -0800
@@ -136,6 +136,10 @@
 
     private static final long serialVersionUID = 1L;
 
+    /**
+     * The runtime {@code Class} object whose static members this
+     * {@code StaticClass} represents.
+     */
     private final Class<?> clazz;
 
     /*private*/ StaticClass(final Class<?> clazz) {
@@ -164,6 +168,11 @@
         return "StaticClass[" + clazz.getName() + "]";
     }
 
+    /**
+     * Returns {@link #forClass(Class)} for the underlying {@code clazz} field
+     * ensuring that deserialization doesn't create non-canonical instances.
+     * @return {@link #forClass(Class)} for the underlying {@code clazz} field.
+     */
     private Object readResolve() {
         return forClass(clazz);
     }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java	Thu Nov 12 14:14:37 2015 -0800
@@ -88,7 +88,7 @@
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Array;
 import java.util.Arrays;
-import java.util.Collection;
+import java.util.Set;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.NamedOperation;
 import jdk.internal.dynalink.StandardOperation;
@@ -171,15 +171,15 @@
         return linkers.get(clazz).getConstructorMethod(signature);
     }
 
-    static Collection<String> getReadableStaticPropertyNames(final Class<?> clazz) {
+    static Set<String> getReadableStaticPropertyNames(final Class<?> clazz) {
         return linkers.get(clazz).getReadablePropertyNames();
     }
 
-    static Collection<String> getWritableStaticPropertyNames(final Class<?> clazz) {
+    static Set<String> getWritableStaticPropertyNames(final Class<?> clazz) {
         return linkers.get(clazz).getWritablePropertyNames();
     }
 
-    static Collection<String> getStaticMethodNames(final Class<?> clazz) {
+    static Set<String> getStaticMethodNames(final Class<?> clazz) {
         return linkers.get(clazz).getMethodNames();
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/CompositeGuardingDynamicLinker.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/CompositeGuardingDynamicLinker.java	Thu Nov 12 14:14:37 2015 -0800
@@ -83,7 +83,6 @@
 
 package jdk.internal.dynalink.linker.support;
 
-import java.io.Serializable;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
@@ -97,9 +96,7 @@
  * other guarding dynamic linkers in its
  * {@link #getGuardedInvocation(LinkRequest, LinkerServices)}.
  */
-public class CompositeGuardingDynamicLinker implements GuardingDynamicLinker, Serializable {
-
-    private static final long serialVersionUID = 1L;
+public class CompositeGuardingDynamicLinker implements GuardingDynamicLinker {
 
     private final GuardingDynamicLinker[] linkers;
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/CompositeTypeBasedGuardingDynamicLinker.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/CompositeTypeBasedGuardingDynamicLinker.java	Thu Nov 12 14:14:37 2015 -0800
@@ -83,7 +83,6 @@
 
 package jdk.internal.dynalink.linker.support;
 
-import java.io.Serializable;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
@@ -102,9 +101,7 @@
  * type is encountered, the linking is delegated to those linkers only, speeding
  * up dispatch.
  */
-public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardingDynamicLinker, Serializable {
-    private static final long serialVersionUID = 1L;
-
+public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardingDynamicLinker {
     // Using a separate static class instance so there's no strong reference from the class value back to the composite
     // linker.
     private static class ClassToLinker extends ClassValue<List<TypeBasedGuardingDynamicLinker>> {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/TypeUtilities.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/TypeUtilities.java	Thu Nov 12 14:14:37 2015 -0800
@@ -94,7 +94,7 @@
 /**
  * Various static utility methods for working with Java types.
  */
-public class TypeUtilities {
+public final class TypeUtilities {
     static final Class<Object> OBJECT_CLASS = Object.class;
 
     private TypeUtilities() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/WeakValueCache.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,98 @@
+/*
+ * 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) {
+        // Remove cleared entries
+        for (;;) {
+            final KeyValueReference<?, ?> ref = (KeyValueReference) refQueue.poll();
+            if (ref == null) {
+                break;
+            }
+            map.remove(ref.key, ref);
+        }
+
+        final KeyValueReference<K, V> ref = map.get(key);
+        if (ref != null) {
+            return ref.get();
+        }
+        return null;
+    }
+
+    /**
+     * 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) {
+        V value = get(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 static class KeyValueReference<K, V> extends WeakReference<V> {
+        final K key;
+
+        KeyValueReference(final K key, final V value) {
+            super(value);
+            this.key = key;
+        }
+    }
+
+}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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;
@@ -94,8 +95,8 @@
     // (__FILE__, __DIR__, __LINE__)
     private static final Object LAZY_SENTINEL = new Object();
 
-    private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class);
-    private final InvokeByName VALUE_OF  = new InvokeByName("valueOf",  ScriptObject.class);
+    private InvokeByName TO_STRING;
+    private InvokeByName VALUE_OF;
 
     /**
      * Optimistic builtin names that require switchpoint invalidation
@@ -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
@@ -1073,6 +1079,9 @@
             return;
         }
 
+        TO_STRING = new InvokeByName("toString", ScriptObject.class);
+        VALUE_OF  = new InvokeByName("valueOf",  ScriptObject.class);
+
         this.engine = eng;
         if (this.engine != null) {
             this.scontext = new ThreadLocal<>();
@@ -1103,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;
@@ -1357,18 +1368,27 @@
         return desc;
     }
 
-    private static <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) {
+    private <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) {
         final T obj = map.get(key);
         if (obj != null) {
             return obj;
         }
 
+        final Global oldGlobal = Context.getGlobal();
+        final boolean differentGlobal = oldGlobal != this;
         try {
+            if (differentGlobal) {
+                Context.setGlobal(this);
+            }
             final T newObj = creator.call();
             final T existingObj = map.putIfAbsent(key, newObj);
             return existingObj != null ? existingObj : newObj;
         } catch (final Exception exp) {
             throw new RuntimeException(exp);
+        } finally {
+            if (differentGlobal) {
+                Context.setGlobal(oldGlobal);
+            }
         }
     }
 
@@ -1574,7 +1594,7 @@
 
     /**
      * Get the builtin Object prototype.
-     * @return the object prototype.
+     * @return the Object prototype.
      */
     public ScriptObject getObjectPrototype() {
         return ScriptFunction.getPrototype(builtinObject);
@@ -1582,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);
     }
 
@@ -1648,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);
@@ -2115,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());
                 }
             }
         }
@@ -2174,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) {
@@ -2294,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();
@@ -2502,6 +2538,7 @@
         this.string            = this.builtinString;
         this.syntaxError       = this.builtinSyntaxError;
         this.typeError         = this.builtinTypeError;
+        this.symbol            = this.builtinSymbol;
     }
 
     private void initDebug() {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArguments.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArguments.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeNumber.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeNumber.java	Thu Nov 12 14:14:37 2015 -0800
@@ -49,6 +49,7 @@
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
 import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
 
 /**
@@ -184,13 +185,7 @@
             return JSType.toString(x);
         }
 
-        final NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
-        format.setMinimumFractionDigits(fractionDigits);
-        format.setMaximumFractionDigits(fractionDigits);
-        format.setGroupingUsed(false);
-        format.setRoundingMode(RoundingMode.HALF_UP);
-
-        return format.format(x);
+        return DoubleConversion.toFixed(x, fractionDigits);
     }
 
     /**
@@ -267,7 +262,7 @@
             return "0";
         }
 
-        return fixExponent(String.format(Locale.US, "%." + p + "g", x), false);
+        return DoubleConversion.toPrecision(x, p);
     }
 
     /**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeString.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeSymbol.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AccessorProperty.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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;
@@ -73,12 +71,14 @@
 import java.util.function.Supplier;
 import java.util.logging.Level;
 import javax.script.ScriptEngine;
+import jdk.internal.dynalink.DynamicLinker;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.Opcodes;
 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;
@@ -89,12 +89,13 @@
 import jdk.nashorn.internal.objects.Global;
 import jdk.nashorn.internal.parser.Parser;
 import jdk.nashorn.internal.runtime.events.RuntimeEvent;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
 import jdk.nashorn.internal.runtime.logging.Loggable;
 import jdk.nashorn.internal.runtime.logging.Logger;
 import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
 import jdk.nashorn.internal.runtime.options.Options;
-import sun.misc.Unsafe;
+import jdk.internal.misc.Unsafe;
 
 /**
  * This class manages the global state of execution. Context is immutable.
@@ -301,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();
@@ -507,14 +468,14 @@
     final boolean _strict;
 
     /** class loader to resolve classes from script. */
-    private final ClassLoader  appLoader;
-
-    /** Class loader to load classes from -classpath option, if set. */
-    private final ClassLoader  classPathLoader;
+    private final ClassLoader appLoader;
 
     /** Class loader to load classes compiled from scripts. */
     private final ScriptLoader scriptLoader;
 
+    /** Dynamic linker for linking call sites in script code loaded by this context */
+    private final DynamicLinker dynamicLinker;
+
     /** Current error manager. */
     private final ErrorManager errors;
 
@@ -626,7 +587,6 @@
         this.classFilter = classFilter;
         this.env       = new ScriptEnvironment(options, out, err);
         this._strict   = env._strict;
-        this.appLoader = appLoader;
         if (env._loader_per_compile) {
             this.scriptLoader = null;
             this.uniqueScriptId = null;
@@ -636,18 +596,19 @@
         }
         this.errors    = errors;
 
-        // if user passed -classpath option, make a class loader with that and set it as
-        // thread context class loader so that script can access classes from that path.
+        // if user passed -classpath option, make a URLClassLoader with that and
+        // the app loader as the parent.
         final String classPath = options.getString("classpath");
         if (!env._compile_only && classPath != null && !classPath.isEmpty()) {
             // make sure that caller can create a class loader.
             if (sm != null) {
-                sm.checkPermission(new RuntimePermission("createClassLoader"));
+                sm.checkCreateClassLoader();
             }
-            this.classPathLoader = NashornLoader.createClassLoader(classPath);
+            this.appLoader = NashornLoader.createClassLoader(classPath, appLoader);
         } else {
-            this.classPathLoader = null;
+            this.appLoader = appLoader;
         }
+        this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader, env._unstable_relink_threshold);
 
         final int cacheSize = env._class_cache_size;
         if (cacheSize > 0) {
@@ -1181,15 +1142,6 @@
             checkPackageAccess(sm, fullName);
         }
 
-        // try the script -classpath loader, if that is set
-        if (classPathLoader != null) {
-            try {
-                return Class.forName(fullName, true, classPathLoader);
-            } catch (final ClassNotFoundException ignored) {
-                // ignore, continue search
-            }
-        }
-
         // Try finding using the "app" loader.
         return Class.forName(fullName, true, appLoader);
     }
@@ -1308,6 +1260,26 @@
         return getContext(getGlobal());
     }
 
+    /**
+     * Gets the Nashorn dynamic linker for the specified class. If the class is
+     * a script class, the dynamic linker associated with its context is
+     * returned. Otherwise the dynamic linker associated with the current
+     * context is returned.
+     * @param clazz the class for which we want to retrieve a dynamic linker.
+     * @return the Nashorn dynamic linker for the specified class.
+     */
+    public static DynamicLinker getDynamicLinker(final Class<?> clazz) {
+        return fromClass(clazz).dynamicLinker;
+    }
+
+    /**
+     * Gets the Nashorn dynamic linker associated with the current context.
+     * @return the Nashorn dynamic linker for the current context.
+     */
+    public static DynamicLinker getDynamicLinker() {
+        return getContextTrusted().dynamicLinker;
+    }
+
     static Context getContextTrustedOrNull() {
         final Global global = Context.getGlobal();
         return global == null ? null : getContext(global);
@@ -1338,15 +1310,10 @@
     }
 
     private URL getResourceURL(final String resName) {
-        // try the classPathLoader if we have and then
-        // try the appLoader if non-null.
-        if (classPathLoader != null) {
-            return classPathLoader.getResource(resName);
-        } else if (appLoader != null) {
+        if (appLoader != null) {
             return appLoader.getResource(resName);
         }
-
-        return null;
+        return ClassLoader.getSystemResource(resName);
     }
 
     private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
@@ -1447,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) {
@@ -1683,5 +1657,4 @@
     public SwitchPoint getBuiltinSwitchPoint(final String name) {
         return builtinSwitchPoints.get(name);
     }
-
 }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Thu Nov 12 14:14:37 2015 -0800
@@ -40,8 +40,10 @@
 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;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
 /**
@@ -67,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;
@@ -311,6 +316,10 @@
             return JSType.NUMBER;
         }
 
+        if (obj instanceof Symbol) {
+            return JSType.SYMBOL;
+        }
+
         if (obj == ScriptRuntime.UNDEFINED) {
             return JSType.UNDEFINED;
         }
@@ -353,6 +362,10 @@
             return JSType.UNDEFINED;
         }
 
+        if (obj instanceof Symbol) {
+            return JSType.SYMBOL;
+        }
+
         return JSType.OBJECT;
     }
 
@@ -469,9 +482,10 @@
     public static boolean isPrimitive(final Object obj) {
         return obj == null ||
                obj == ScriptRuntime.UNDEFINED ||
+               isString(obj) ||
+               obj instanceof Number ||
                obj instanceof Boolean ||
-               obj instanceof Number ||
-               isString(obj);
+               obj instanceof Symbol;
     }
 
    /**
@@ -613,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)}.
      *
@@ -687,7 +710,7 @@
             return "NaN";
         }
 
-        return NumberToString.stringFor(num);
+        return DoubleConversion.toShortestString(num);
     }
 
     /**
@@ -786,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);
     }
 
     /**
@@ -1403,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();
@@ -1915,6 +1947,10 @@
             return Double.NaN;
         }
 
+        if (obj instanceof Symbol) {
+            throw typeError("symbol.to.number");
+        }
+
         return toNumber(toPrimitive(obj, Number.class));
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NashornLoader.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NashornLoader.java	Thu Nov 12 14:14:37 2015 -0800
@@ -35,7 +35,6 @@
 import java.security.PermissionCollection;
 import java.security.Permissions;
 import java.security.SecureClassLoader;
-import jdk.nashorn.tools.Shell;
 
 /**
  * Superclass for Nashorn class loader classes.
@@ -103,10 +102,10 @@
     /**
      * Create a secure URL class loader for the given classpath
      * @param classPath classpath for the loader to search from
+     * @param parent the parent class loader for the new class loader
      * @return the class loader
      */
-    static ClassLoader createClassLoader(final String classPath) {
-        final ClassLoader parent = Shell.class.getClassLoader();
+    static ClassLoader createClassLoader(final String classPath, final ClassLoader parent) {
         final URL[] urls = pathToURLs(classPath);
         return URLClassLoader.newInstance(urls, parent);
     }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NumberToString.java	Thu Nov 12 10:39:12 2015 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,786 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, 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.math.BigInteger;
-
-/**
- * JavaScript number to string conversion, refinement of sun.misc.FloatingDecimal.
- */
-public final class NumberToString {
-    /** Is not a number flag */
-    private final boolean isNaN;
-
-    /** Is a negative number flag. */
-    private boolean isNegative;
-
-    /** Decimal exponent value (for E notation.) */
-    private int decimalExponent;
-
-    /** Actual digits. */
-    private char digits[];
-
-    /** Number of digits to use. (nDigits <= digits.length). */
-    private int nDigits;
-
-    /*
-     * IEEE-754 constants.
-     */
-
-    //private static final long   signMask           = 0x8000000000000000L;
-    private static final int    expMask            = 0x7FF;
-    private static final long   fractMask          = 0x000F_FFFF_FFFF_FFFFL;
-    private static final int    expShift           = 52;
-    private static final int    expBias            = 1_023;
-    private static final long   fractHOB           = (1L << expShift);
-    private static final long   expOne             = ((long)expBias) << expShift;
-    private static final int    maxSmallBinExp     = 62;
-    private static final int    minSmallBinExp     = -(63 / 3);
-
-    /** Powers of 5 fitting a long. */
-    private static final long powersOf5[] = {
-        1L,
-        5L,
-        5L * 5,
-        5L * 5 * 5,
-        5L * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
-        5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
-    };
-
-    // Approximately ceil(log2(longPowers5[i])).
-    private static final int nBitsPowerOf5[] = {
-        0,
-        3,
-        5,
-        7,
-        10,
-        12,
-        14,
-        17,
-        19,
-        21,
-        24,
-        26,
-        28,
-        31,
-        33,
-        35,
-        38,
-        40,
-        42,
-        45,
-        47,
-        49,
-        52,
-        54,
-        56,
-        59,
-        61
-    };
-
-    /** Digits used for infinity result. */
-    private static final char infinityDigits[]   = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' };
-
-    /** Digits used for NaN result. */
-    private static final char nanDigits[]        = { 'N', 'a', 'N' };
-
-    /** Zeros used to pad result. */
-    private static final char zeroes[]           = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' };
-
-
-    /**
-     * Convert a number into a JavaScript string.
-     * @param value Double to convert.
-     * @return JavaScript formated number.
-     */
-    public static String stringFor(final double value) {
-        return new NumberToString(value).toString();
-    }
-
-    /*
-     * Constructor.
-     */
-
-    private NumberToString(final double value) {
-        // Double as bits.
-        long bits = Double.doubleToLongBits(value);
-
-        // Get upper word.
-        final int upper = (int)(bits >> 32);
-
-        // Detect sign.
-        isNegative = upper < 0;
-
-        // Extract exponent.
-        int exponent = (upper >> (expShift - 32)) & expMask;
-
-        // Clear sign and exponent.
-        bits &= fractMask;
-
-        // Detect NaN.
-        if (exponent == expMask) {
-            isNaN = true;
-
-            // Detect Infinity.
-            if (bits == 0L) {
-                digits =  infinityDigits;
-            } else {
-                digits = nanDigits;
-                isNegative = false;
-            }
-
-            nDigits = digits.length;
-
-            return;
-        }
-
-        // We have a working double.
-        isNaN = false;
-
-        int nSignificantBits;
-
-        // Detect denormalized value.
-        if (exponent == 0) {
-            // Detect zero value.
-            if (bits == 0L) {
-                decimalExponent = 0;
-                digits = zeroes;
-                nDigits = 1;
-
-                return;
-            }
-
-            // Normalize value, using highest significant bit as HOB.
-            while ((bits & fractHOB) == 0L) {
-                bits <<= 1;
-                exponent -= 1;
-            }
-
-            // Compute number of significant bits.
-            nSignificantBits = expShift + exponent +1;
-            // Bias exponent by HOB.
-            exponent += 1;
-        } else {
-            // Add implicit HOB.
-            bits |= fractHOB;
-            // Compute number of significant bits.
-            nSignificantBits = expShift + 1;
-        }
-
-        // Unbias exponent (represents bit shift).
-        exponent -= expBias;
-
-        // Determine the number of significant bits in the fraction.
-        final int nFractBits = countSignificantBits(bits);
-
-        // Number of bits to the right of the decimal.
-        final int nTinyBits = Math.max(0, nFractBits - exponent - 1);
-
-        // Computed decimal exponent.
-        int decExponent;
-
-        if (exponent <= maxSmallBinExp && exponent >= minSmallBinExp) {
-            // Look more closely at the number to decide if,
-            // with scaling by 10^nTinyBits, the result will fit in
-            // a long.
-            if (nTinyBits < powersOf5.length && (nFractBits + nBitsPowerOf5[nTinyBits]) < 64) {
-                /*
-                 * We can do this:
-                 * take the fraction bits, which are normalized.
-                 * (a) nTinyBits == 0: Shift left or right appropriately
-                 *     to align the binary point at the extreme right, i.e.
-                 *     where a long int point is expected to be. The integer
-                 *     result is easily converted to a string.
-                 * (b) nTinyBits > 0: Shift right by expShift - nFractBits,
-                 *     which effectively converts to long and scales by
-                 *     2^nTinyBits. Then multiply by 5^nTinyBits to
-                 *     complete the scaling. We know this won't overflow
-                 *     because we just counted the number of bits necessary
-                 *     in the result. The integer you get from this can
-                 *     then be converted to a string pretty easily.
-                 */
-
-                if (nTinyBits == 0) {
-                    long halfULP;
-
-                    if (exponent > nSignificantBits) {
-                        halfULP = 1L << (exponent - nSignificantBits - 1);
-                    } else {
-                        halfULP = 0L;
-                    }
-
-                    if (exponent >= expShift) {
-                        bits <<= exponent - expShift;
-                    } else {
-                        bits >>>= expShift - exponent;
-                    }
-
-                    // Discard non-significant low-order bits, while rounding,
-                    // up to insignificant value.
-                    int i;
-                    for (i = 0; halfULP >= 10L; i++) {
-                        halfULP /= 10L;
-                    }
-
-                    /**
-                     * This is the easy subcase --
-                     * all the significant bits, after scaling, are held in bits.
-                     * isNegative and decExponent tell us what processing and scaling
-                     * has already been done. Exceptional cases have already been
-                     * stripped out.
-                     * In particular:
-                     *      bits is a finite number (not Infinite, nor NaN)
-                     *      bits > 0L (not zero, nor negative).
-                     *
-                     * The only reason that we develop the digits here, rather than
-                     * calling on Long.toString() is that we can do it a little faster,
-                     * and besides want to treat trailing 0s specially. If Long.toString
-                     * changes, we should re-evaluate this strategy!
-                     */
-
-                    int decExp = 0;
-
-                    if (i != 0) {
-                         // 10^i == 5^i * 2^i
-                        final long powerOf10 = powersOf5[i] << i;
-                        final long residue = bits % powerOf10;
-                        bits /= powerOf10;
-                        decExp += i;
-
-                        if (residue >= (powerOf10 >> 1)) {
-                            // Round up based on the low-order bits we're discarding.
-                            bits++;
-                        }
-                    }
-
-                    int ndigits = 20;
-                    final char[] digits0 = new char[26];
-                    int digitno = ndigits - 1;
-                    int c = (int)(bits % 10L);
-                    bits /= 10L;
-
-                    while (c == 0) {
-                        decExp++;
-                        c = (int)(bits % 10L);
-                        bits /= 10L;
-                    }
-
-                    while (bits != 0L) {
-                        digits0[digitno--] = (char)(c + '0');
-                        decExp++;
-                        c = (int)(bits % 10L);
-                        bits /= 10;
-                    }
-
-                    digits0[digitno] = (char)(c + '0');
-
-                    ndigits -= digitno;
-                    final char[] result = new char[ndigits];
-                    System.arraycopy(digits0, digitno, result, 0, ndigits);
-
-                    this.digits          = result;
-                    this.decimalExponent = decExp + 1;
-                    this.nDigits         = ndigits;
-
-                    return;
-                }
-            }
-        }
-
-        /*
-         * This is the hard case. We are going to compute large positive
-         * integers B and S and integer decExp, s.t.
-         *      d = (B / S) * 10^decExp
-         *      1 <= B / S < 10
-         * Obvious choices are:
-         *      decExp = floor(log10(d))
-         *      B      = d * 2^nTinyBits * 10^max(0, -decExp)
-         *      S      = 10^max(0, decExp) * 2^nTinyBits
-         * (noting that nTinyBits has already been forced to non-negative)
-         * I am also going to compute a large positive integer
-         *      M      = (1/2^nSignificantBits) * 2^nTinyBits * 10^max(0, -decExp)
-         * i.e. M is (1/2) of the ULP of d, scaled like B.
-         * When we iterate through dividing B/S and picking off the
-         * quotient bits, we will know when to stop when the remainder
-         * is <= M.
-         *
-         * We keep track of powers of 2 and powers of 5.
-         */
-
-        /*
-         * Estimate decimal exponent. (If it is small-ish,
-         * we could double-check.)
-         *
-         * First, scale the mantissa bits such that 1 <= d2 < 2.
-         * We are then going to estimate
-         *          log10(d2) ~=~  (d2-1.5)/1.5 + log(1.5)
-         * and so we can estimate
-         *      log10(d) ~=~ log10(d2) + binExp * log10(2)
-         * take the floor and call it decExp.
-         */
-        final double d2 = Double.longBitsToDouble(expOne | (bits & ~fractHOB));
-        decExponent = (int)Math.floor((d2 - 1.5D) * 0.289529654D + 0.176091259D + exponent * 0.301029995663981D);
-
-        // Powers of 2 and powers of 5, respectively, in B.
-        final int B5 = Math.max(0, -decExponent);
-        int B2 = B5 + nTinyBits + exponent;
-
-        // Powers of 2 and powers of 5, respectively, in S.
-        final int S5 = Math.max(0, decExponent);
-        int S2 = S5 + nTinyBits;
-
-        // Powers of 2 and powers of 5, respectively, in M.
-        final int M5 = B5;
-        int M2 = B2 - nSignificantBits;
-
-        /*
-         * The long integer fractBits contains the (nFractBits) interesting
-         * bits from the mantissa of d (hidden 1 added if necessary) followed
-         * by (expShift + 1 - nFractBits) zeros. In the interest of compactness,
-         * I will shift out those zeros before turning fractBits into a
-         * BigInteger. The resulting whole number will be
-         *      d * 2^(nFractBits - 1 - binExp).
-         */
-
-        bits >>>= expShift + 1 - nFractBits;
-        B2 -= nFractBits - 1;
-        final int common2factor = Math.min(B2, S2);
-        B2 -= common2factor;
-        S2 -= common2factor;
-        M2 -= common2factor;
-
-        /*
-         * HACK!!For exact powers of two, the next smallest number
-         * is only half as far away as we think (because the meaning of
-         * ULP changes at power-of-two bounds) for this reason, we
-         * hack M2. Hope this works.
-         */
-        if (nFractBits == 1) {
-            M2 -= 1;
-        }
-
-        if (M2 < 0) {
-            // Oops.  Since we cannot scale M down far enough,
-            // we must scale the other values up.
-            B2 -= M2;
-            S2 -= M2;
-            M2 =  0;
-        }
-
-        /*
-         * Construct, Scale, iterate.
-         * Some day, we'll write a stopping test that takes
-         * account of the asymmetry of the spacing of floating-point
-         * numbers below perfect powers of 2
-         * 26 Sept 96 is not that day.
-         * So we use a symmetric test.
-         */
-
-        final char digits0[] = this.digits = new char[32];
-        int  ndigit;
-        boolean low, high;
-        long lowDigitDifference;
-        int  q;
-
-        /*
-         * Detect the special cases where all the numbers we are about
-         * to compute will fit in int or long integers.
-         * In these cases, we will avoid doing BigInteger arithmetic.
-         * We use the same algorithms, except that we "normalize"
-         * our FDBigInts before iterating. This is to make division easier,
-         * as it makes our fist guess (quotient of high-order words)
-         * more accurate!
-         */
-
-        // Binary digits needed to represent B, approx.
-        final int Bbits = nFractBits + B2 + ((B5 < nBitsPowerOf5.length) ? nBitsPowerOf5[B5] : (B5*3));
-        // Binary digits needed to represent 10*S, approx.
-        final int tenSbits = S2 + 1 + (((S5 + 1) < nBitsPowerOf5.length) ? nBitsPowerOf5[(S5 + 1)] : ((S5 + 1) * 3));
-
-        if (Bbits < 64 && tenSbits < 64) {
-            long b = (bits * powersOf5[B5]) << B2;
-            final long s = powersOf5[S5] << S2;
-            long m = powersOf5[M5] << M2;
-            final long tens = s * 10L;
-
-            /*
-             * Unroll the first iteration. If our decExp estimate
-             * was too high, our first quotient will be zero. In this
-             * case, we discard it and decrement decExp.
-             */
-
-            ndigit = 0;
-            q = (int)(b / s);
-            b = 10L * (b % s);
-            m *= 10L;
-            low  = b <  m;
-            high = (b + m) > tens;
-
-            if (q == 0 && !high) {
-                // Ignore leading zero.
-                decExponent--;
-            } else {
-                digits0[ndigit++] = (char)('0' + q);
-            }
-
-            if (decExponent < -3 || decExponent >= 8) {
-                high = low = false;
-            }
-
-            while (!low && !high) {
-                q = (int)(b / s);
-                b = 10 * (b % s);
-                m *= 10;
-
-                if (m > 0L) {
-                    low  = b < m;
-                    high = (b + m) > tens;
-                } else {
-                    low = true;
-                    high = true;
-                }
-
-                if (low && q == 0) {
-                    break;
-                }
-                digits0[ndigit++] = (char)('0' + q);
-            }
-
-            lowDigitDifference = (b << 1) - tens;
-        } else {
-            /*
-             * We must do BigInteger arithmetic.
-             * First, construct our BigInteger initial values.
-             */
-
-            BigInteger Bval = multiplyPowerOf5And2(BigInteger.valueOf(bits), B5, B2);
-            BigInteger Sval = constructPowerOf5And2(S5, S2);
-            BigInteger Mval = constructPowerOf5And2(M5, M2);
-
-
-            // Normalize so that BigInteger division works better.
-            final int shiftBias = Long.numberOfLeadingZeros(bits) - 4;
-            Bval = Bval.shiftLeft(shiftBias);
-            Mval = Mval.shiftLeft(shiftBias);
-            Sval = Sval.shiftLeft(shiftBias);
-            final BigInteger tenSval = Sval.multiply(BigInteger.TEN);
-
-            /*
-             * Unroll the first iteration. If our decExp estimate
-             * was too high, our first quotient will be zero. In this
-             * case, we discard it and decrement decExp.
-             */
-
-            ndigit = 0;
-
-            BigInteger[] quoRem = Bval.divideAndRemainder(Sval);
-            q    = quoRem[0].intValue();
-            Bval = quoRem[1].multiply(BigInteger.TEN);
-            Mval = Mval.multiply(BigInteger.TEN);
-            low  = (Bval.compareTo(Mval) < 0);
-            high = (Bval.add(Mval).compareTo(tenSval) > 0);
-
-            if (q == 0 && !high) {
-                // Ignore leading zero.
-                decExponent--;
-            } else {
-                digits0[ndigit++] = (char)('0' + q);
-            }
-
-            if (decExponent < -3 || decExponent >= 8) {
-                high = low = false;
-            }
-
-            while(!low && !high) {
-                quoRem = Bval.divideAndRemainder(Sval);
-                q = quoRem[0].intValue();
-                Bval = quoRem[1].multiply(BigInteger.TEN);
-                Mval = Mval.multiply(BigInteger.TEN);
-                low  = (Bval.compareTo(Mval) < 0);
-                high = (Bval.add(Mval).compareTo(tenSval) > 0);
-
-                if (low && q == 0) {
-                    break;
-                }
-                digits0[ndigit++] = (char)('0' + q);
-            }
-
-            if (high && low) {
-                Bval = Bval.shiftLeft(1);
-                lowDigitDifference = Bval.compareTo(tenSval);
-            } else {
-                lowDigitDifference = 0L;
-            }
-        }
-
-        this.decimalExponent = decExponent + 1;
-        this.digits          = digits0;
-        this.nDigits         = ndigit;
-
-        /*
-         * Last digit gets rounded based on stopping condition.
-         */
-
-        if (high) {
-            if (low) {
-                if (lowDigitDifference == 0L) {
-                    // it's a tie!
-                    // choose based on which digits we like.
-                    if ((digits0[nDigits - 1] & 1) != 0) {
-                        roundup();
-                    }
-                } else if (lowDigitDifference > 0) {
-                    roundup();
-                }
-            } else {
-                roundup();
-            }
-        }
-    }
-
-    /**
-     * Count number of significant bits.
-     * @param bits Double's fraction.
-     * @return Number of significant bits.
-     */
-    private static int countSignificantBits(final long bits) {
-        if (bits != 0) {
-            return 64 - Long.numberOfLeadingZeros(bits) - Long.numberOfTrailingZeros(bits);
-        }
-
-        return 0;
-    }
-
-    /*
-     * Cache big powers of 5 handy for future reference.
-     */
-    private static BigInteger powerOf5Cache[];
-
-    /**
-     * Determine the largest power of 5 needed (as BigInteger.)
-     * @param power Power of 5.
-     * @return BigInteger of power of 5.
-     */
-    private static BigInteger bigPowerOf5(final int power) {
-        if (powerOf5Cache == null) {
-            powerOf5Cache = new BigInteger[power + 1];
-        } else if (powerOf5Cache.length <= power) {
-            final BigInteger t[] = new BigInteger[ power+1 ];
-            System.arraycopy(powerOf5Cache, 0, t, 0, powerOf5Cache.length);
-            powerOf5Cache = t;
-        }
-
-        if (powerOf5Cache[power] != null) {
-            return powerOf5Cache[power];
-        } else if (power < powersOf5.length) {
-            return powerOf5Cache[power] = BigInteger.valueOf(powersOf5[power]);
-        } else {
-            // Construct the value recursively.
-            // in order to compute 5^p,
-            // compute its square root, 5^(p/2) and square.
-            // or, let q = p / 2, r = p -q, then
-            // 5^p = 5^(q+r) = 5^q * 5^r
-            final int q = power >> 1;
-            final int r = power - q;
-            BigInteger bigQ = powerOf5Cache[q];
-
-            if (bigQ == null) {
-                bigQ = bigPowerOf5(q);
-            }
-
-            if (r < powersOf5.length) {
-                return (powerOf5Cache[power] = bigQ.multiply(BigInteger.valueOf(powersOf5[r])));
-            }
-            BigInteger bigR = powerOf5Cache[ r ];
-
-            if (bigR == null) {
-                bigR = bigPowerOf5(r);
-            }
-
-            return (powerOf5Cache[power] = bigQ.multiply(bigR));
-        }
-    }
-
-    /**
-     * Multiply BigInteger by powers of 5 and 2 (i.e., 10)
-     * @param value Value to multiply.
-     * @param p5    Power of 5.
-     * @param p2    Power of 2.
-     * @return Result.
-     */
-    private static BigInteger multiplyPowerOf5And2(final BigInteger value, final int p5, final int p2) {
-        BigInteger returnValue = value;
-
-        if (p5 != 0) {
-            returnValue = returnValue.multiply(bigPowerOf5(p5));
-        }
-
-        if (p2 != 0) {
-            returnValue = returnValue.shiftLeft(p2);
-        }
-
-        return returnValue;
-    }
-
-    /**
-     * Construct a BigInteger power of 5 and 2 (i.e., 10)
-     * @param p5    Power of 5.
-     * @param p2    Power of 2.
-     * @return Result.
-     */
-    private static BigInteger constructPowerOf5And2(final int p5, final int p2) {
-        BigInteger v = bigPowerOf5(p5);
-
-        if (p2 != 0) {
-            v = v.shiftLeft(p2);
-        }
-
-        return v;
-    }
-
-    /**
-     * Round up last digit by adding one to the least significant digit.
-     * In the unlikely event there is a carry out, deal with it.
-     * assert that this will only happen where there
-     * is only one digit, e.g. (float)1e-44 seems to do it.
-     */
-    private void roundup() {
-        int i;
-        int q = digits[ i = (nDigits-1)];
-
-        while (q == '9' && i > 0) {
-            if (decimalExponent < 0) {
-                nDigits--;
-            } else {
-                digits[i] = '0';
-            }
-
-            q = digits[--i];
-        }
-
-        if (q == '9') {
-            // Carryout! High-order 1, rest 0s, larger exp.
-            decimalExponent += 1;
-            digits[0] = '1';
-
-            return;
-        }
-
-        digits[i] = (char)(q + 1);
-    }
-
-    /**
-     * Format final number string.
-     * @return Formatted string.
-     */
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder(32);
-
-        if (isNegative) {
-            sb.append('-');
-        }
-
-        if (isNaN) {
-            sb.append(digits, 0, nDigits);
-        } else {
-            if (decimalExponent > 0 && decimalExponent <= 21) {
-                final int charLength = Math.min(nDigits, decimalExponent);
-                sb.append(digits, 0, charLength);
-
-                if (charLength < decimalExponent) {
-                    sb.append(zeroes, 0, decimalExponent - charLength);
-                } else if (charLength < nDigits) {
-                    sb.append('.');
-                    sb.append(digits, charLength, nDigits - charLength);
-                }
-            } else if (decimalExponent <=0 && decimalExponent > -6) {
-                sb.append('0');
-                sb.append('.');
-
-                if (decimalExponent != 0) {
-                    sb.append(zeroes, 0, -decimalExponent);
-                }
-
-                sb.append(digits, 0, nDigits);
-            } else {
-                sb.append(digits[0]);
-
-                if (nDigits > 1) {
-                    sb.append('.');
-                    sb.append(digits, 1, nDigits - 1);
-                }
-
-                sb.append('e');
-                final int exponent;
-                int e;
-
-                if (decimalExponent <= 0) {
-                    sb.append('-');
-                    exponent = e = -decimalExponent + 1;
-                } else {
-                    sb.append('+');
-                    exponent = e = decimalExponent - 1;
-                }
-
-                if (exponent > 99) {
-                    sb.append((char)(e / 100 + '0'));
-                    e %= 100;
-                }
-
-                if (exponent > 9) {
-                    sb.append((char)(e / 10 + '0'));
-                    e %= 10;
-                }
-
-                sb.append((char)(e + '0'));
-            }
-        }
-
-        return sb.toString();
-    }
-}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Property.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyHashMap.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyHashMap.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Thu Nov 12 14:14:37 2015 -0800
@@ -105,6 +105,12 @@
     /** Enable experimental ECMAScript 6 features. */
     public final boolean _es6;
 
+
+    /** Number of times a dynamic call site has to be relinked before it is
+     * considered unstable (and thus should be linked as if it were megamorphic).
+     */
+    public final int _unstable_relink_threshold;
+
     /** Argument passed to compile only if optimistic compilation should take place */
     public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic";
 
@@ -287,6 +293,25 @@
         _version              = options.getBoolean("version");
         _verify_code          = options.getBoolean("verify.code");
 
+        final int configuredUrt = options.getInteger("unstable.relink.threshold");
+        // The default for this property is -1, so we can easily detect when
+        // it is not specified on command line.
+        if (configuredUrt < 0) {
+            // In this case, use a default of 8, or 16 for optimistic types.
+            // Optimistic types come with dual fields, and in order to get
+            // performance on benchmarks with a lot of object instantiation and
+            // then field reassignment, it can take slightly more relinks to
+            // become stable with type changes swapping out an entire property
+            // map and making a map guard fail. Also, honor the "nashorn.*"
+            // system property for now. It was documented in DEVELOPER_README
+            // so we should recognize it for the time being.
+            _unstable_relink_threshold = Options.getIntProperty(
+                    "nashorn.unstable.relink.threshold",
+                    _optimistic_types ? 16 : 8);
+        } else {
+            _unstable_relink_threshold = configuredUrt;
+        }
+
         final String anonClasses = options.getString("anonymous.classes");
         if (anonClasses == null || anonClasses.equals("auto")) {
             _anonymousClasses = AnonymousClasses.AUTO;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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 (isPrimitiveAndObject(xType, yType)) {
+            return equalWrappedPrimitiveToObject(x, y);
+        } else if (isPrimitiveAndObject(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 isPrimitiveAndObject(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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SpillProperty.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Symbol.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/Bignum.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,842 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+import java.util.Arrays;
+
+class Bignum {
+
+    // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
+    // This bignum can encode much bigger numbers, since it contains an
+    // exponent.
+    static final int kMaxSignificantBits = 3584;
+
+    static final int kChunkSize = 32;       // size of int
+    static final int kDoubleChunkSize = 64; // size of long
+    // With bigit size of 28 we loose some bits, but a double still fits easily
+    // into two ints, and more importantly we can use the Comba multiplication.
+    static final int kBigitSize = 28;
+    static final int kBigitMask = (1 << kBigitSize) - 1;
+    // Every instance allocates kbigitLength ints on the stack. Bignums cannot
+    // grow. There are no checks if the stack-allocated space is sufficient.
+    static final int kBigitCapacity = kMaxSignificantBits / kBigitSize;
+
+    private int[] bigits_ = new int[kBigitCapacity];
+    // A vector backed by bigits_buffer_. This way accesses to the array are
+    // checked for out-of-bounds errors.
+    // Vector<int> bigits_;
+    private int used_digits_;
+    // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
+    private int exponent_;
+
+    Bignum() {}
+
+    void times10() { multiplyByUInt32(10); }
+
+    static boolean equal(final Bignum a, final Bignum b) {
+        return compare(a, b) == 0;
+    }
+    static boolean lessEqual(final Bignum a, final Bignum b) {
+        return compare(a, b) <= 0;
+    }
+    static boolean less(final Bignum a, final Bignum b) {
+        return compare(a, b) < 0;
+    }
+
+    // Returns a + b == c
+    static boolean plusEqual(final Bignum a, final Bignum b, final Bignum c) {
+        return plusCompare(a, b, c) == 0;
+    }
+    // Returns a + b <= c
+    static boolean plusLessEqual(final Bignum a, final Bignum b, final Bignum c) {
+        return plusCompare(a, b, c) <= 0;
+    }
+    // Returns a + b < c
+    static boolean plusLess(final Bignum a, final Bignum b, final Bignum c) {
+        return plusCompare(a, b, c) < 0;
+    }
+
+    private void ensureCapacity(final int size) {
+        if (size > kBigitCapacity) {
+            throw new RuntimeException();
+        }
+    }
+
+    // BigitLength includes the "hidden" digits encoded in the exponent.
+    int bigitLength() { return used_digits_ + exponent_; }
+
+    // Guaranteed to lie in one Bigit.
+    void assignUInt16(final char value) {
+        assert (kBigitSize >= 16);
+        zero();
+        if (value == 0) return;
+
+        ensureCapacity(1);
+        bigits_[0] = value;
+        used_digits_ = 1;
+    }
+
+
+    void assignUInt64(long value) {
+        final  int kUInt64Size = 64;
+
+        zero();
+        if (value == 0) return;
+
+        final int needed_bigits = kUInt64Size / kBigitSize + 1;
+        ensureCapacity(needed_bigits);
+        for (int i = 0; i < needed_bigits; ++i) {
+            bigits_[i] = (int) (value & kBigitMask);
+            value = value >>> kBigitSize;
+        }
+        used_digits_ = needed_bigits;
+        clamp();
+    }
+
+
+    void assignBignum(final Bignum other) {
+        exponent_ = other.exponent_;
+        for (int i = 0; i < other.used_digits_; ++i) {
+            bigits_[i] = other.bigits_[i];
+        }
+        // Clear the excess digits (if there were any).
+        for (int i = other.used_digits_; i < used_digits_; ++i) {
+            bigits_[i] = 0;
+        }
+        used_digits_ = other.used_digits_;
+    }
+
+
+    static long readUInt64(final String str,
+                           final int from,
+                           final int digits_to_read) {
+        long result = 0;
+        for (int i = from; i < from + digits_to_read; ++i) {
+            final int digit = str.charAt(i) - '0';
+            assert (0 <= digit && digit <= 9);
+            result = result * 10 + digit;
+        }
+        return result;
+    }
+
+
+    void assignDecimalString(final String str) {
+        // 2^64 = 18446744073709551616 > 10^19
+        final int kMaxUint64DecimalDigits = 19;
+        zero();
+        int length = str.length();
+        int pos = 0;
+        // Let's just say that each digit needs 4 bits.
+        while (length >= kMaxUint64DecimalDigits) {
+            final long digits = readUInt64(str, pos, kMaxUint64DecimalDigits);
+            pos += kMaxUint64DecimalDigits;
+            length -= kMaxUint64DecimalDigits;
+            multiplyByPowerOfTen(kMaxUint64DecimalDigits);
+            addUInt64(digits);
+        }
+        final long digits = readUInt64(str, pos, length);
+        multiplyByPowerOfTen(length);
+        addUInt64(digits);
+        clamp();
+    }
+
+
+    static int hexCharValue(final char c) {
+        if ('0' <= c && c <= '9') return c - '0';
+        if ('a' <= c && c <= 'f') return 10 + c - 'a';
+        assert ('A' <= c && c <= 'F');
+        return 10 + c - 'A';
+    }
+
+
+    void assignHexString(final String str) {
+        zero();
+        final int length = str.length();
+
+        final int needed_bigits = length * 4 / kBigitSize + 1;
+        ensureCapacity(needed_bigits);
+        int string_index = length - 1;
+        for (int i = 0; i < needed_bigits - 1; ++i) {
+            // These bigits are guaranteed to be "full".
+            int current_bigit = 0;
+            for (int j = 0; j < kBigitSize / 4; j++) {
+                current_bigit += hexCharValue(str.charAt(string_index--)) << (j * 4);
+            }
+            bigits_[i] = current_bigit;
+        }
+        used_digits_ = needed_bigits - 1;
+
+        int most_significant_bigit = 0;  // Could be = 0;
+        for (int j = 0; j <= string_index; ++j) {
+            most_significant_bigit <<= 4;
+            most_significant_bigit += hexCharValue(str.charAt(j));
+        }
+        if (most_significant_bigit != 0) {
+            bigits_[used_digits_] = most_significant_bigit;
+            used_digits_++;
+        }
+        clamp();
+    }
+
+
+    void addUInt64(final long operand) {
+        if (operand == 0) return;
+        final Bignum other = new Bignum();
+        other.assignUInt64(operand);
+        addBignum(other);
+    }
+
+
+    void addBignum(final Bignum other) {
+        assert (isClamped());
+        assert (other.isClamped());
+
+        // If this has a greater exponent than other append zero-bigits to this.
+        // After this call exponent_ <= other.exponent_.
+        align(other);
+
+        // There are two possibilities:
+        //   aaaaaaaaaaa 0000  (where the 0s represent a's exponent)
+        //     bbbbb 00000000
+        //   ----------------
+        //   ccccccccccc 0000
+        // or
+        //    aaaaaaaaaa 0000
+        //  bbbbbbbbb 0000000
+        //  -----------------
+        //  cccccccccccc 0000
+        // In both cases we might need a carry bigit.
+
+        ensureCapacity(1 + Math.max(bigitLength(), other.bigitLength()) - exponent_);
+        int carry = 0;
+        int bigit_pos = other.exponent_ - exponent_;
+        assert (bigit_pos >= 0);
+        for (int i = 0; i < other.used_digits_; ++i) {
+            final int sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
+            bigits_[bigit_pos] = sum & kBigitMask;
+            carry = sum >>> kBigitSize;
+            bigit_pos++;
+        }
+
+        while (carry != 0) {
+            final int sum = bigits_[bigit_pos] + carry;
+            bigits_[bigit_pos] = sum & kBigitMask;
+            carry = sum >>> kBigitSize;
+            bigit_pos++;
+        }
+        used_digits_ = Math.max(bigit_pos, used_digits_);
+        assert (isClamped());
+    }
+
+
+    void subtractBignum(final Bignum other) {
+        assert (isClamped());
+        assert (other.isClamped());
+        // We require this to be bigger than other.
+        assert (lessEqual(other, this));
+
+        align(other);
+
+        final int offset = other.exponent_ - exponent_;
+        int borrow = 0;
+        int i;
+        for (i = 0; i < other.used_digits_; ++i) {
+            assert ((borrow == 0) || (borrow == 1));
+            final int difference = bigits_[i + offset] - other.bigits_[i] - borrow;
+            bigits_[i + offset] = difference & kBigitMask;
+            borrow = difference >>> (kChunkSize - 1);
+        }
+        while (borrow != 0) {
+            final int difference = bigits_[i + offset] - borrow;
+            bigits_[i + offset] = difference & kBigitMask;
+            borrow = difference >>> (kChunkSize - 1);
+            ++i;
+        }
+        clamp();
+    }
+
+
+    void shiftLeft(final int shift_amount) {
+        if (used_digits_ == 0) return;
+        exponent_ += shift_amount / kBigitSize;
+        final int local_shift = shift_amount % kBigitSize;
+        ensureCapacity(used_digits_ + 1);
+        bigitsShiftLeft(local_shift);
+    }
+
+
+    void multiplyByUInt32(final int factor) {
+        if (factor == 1) return;
+        if (factor == 0) {
+            zero();
+            return;
+        }
+        if (used_digits_ == 0) return;
+
+        // The product of a bigit with the factor is of size kBigitSize + 32.
+        // Assert that this number + 1 (for the carry) fits into double int.
+        assert (kDoubleChunkSize >= kBigitSize + 32 + 1);
+        long carry = 0;
+        for (int i = 0; i < used_digits_; ++i) {
+            final long product = (factor & 0xFFFFFFFFL) * bigits_[i] + carry;
+            bigits_[i] = (int) (product & kBigitMask);
+            carry = product >>> kBigitSize;
+        }
+        while (carry != 0) {
+            ensureCapacity(used_digits_ + 1);
+            bigits_[used_digits_] = (int) (carry & kBigitMask);
+            used_digits_++;
+            carry >>>= kBigitSize;
+        }
+    }
+
+
+    void multiplyByUInt64(final long factor) {
+        if (factor == 1) return;
+        if (factor == 0) {
+            zero();
+            return;
+        }
+        assert (kBigitSize < 32);
+        long carry = 0;
+        final long low = factor & 0xFFFFFFFFL;
+        final long high = factor >>> 32;
+        for (int i = 0; i < used_digits_; ++i) {
+            final long product_low = low * bigits_[i];
+            final long product_high = high * bigits_[i];
+            final long tmp = (carry & kBigitMask) + product_low;
+            bigits_[i] = (int) (tmp & kBigitMask);
+            carry = (carry >>> kBigitSize) + (tmp >>> kBigitSize) +
+                    (product_high << (32 - kBigitSize));
+        }
+        while (carry != 0) {
+            ensureCapacity(used_digits_ + 1);
+            bigits_[used_digits_] = (int) (carry & kBigitMask);
+            used_digits_++;
+            carry >>>= kBigitSize;
+        }
+    }
+
+
+    void multiplyByPowerOfTen(final int exponent) {
+        final long kFive27 = 0x6765c793fa10079dL;
+        final int kFive1 = 5;
+        final int kFive2 = kFive1 * 5;
+        final int kFive3 = kFive2 * 5;
+        final int kFive4 = kFive3 * 5;
+        final int kFive5 = kFive4 * 5;
+        final int kFive6 = kFive5 * 5;
+        final int kFive7 = kFive6 * 5;
+        final int kFive8 = kFive7 * 5;
+        final int kFive9 = kFive8 * 5;
+        final int kFive10 = kFive9 * 5;
+        final int kFive11 = kFive10 * 5;
+        final int kFive12 = kFive11 * 5;
+        final int kFive13 = kFive12 * 5;
+        final int kFive1_to_12[] =
+                { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
+                        kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
+
+        assert (exponent >= 0);
+        if (exponent == 0) return;
+        if (used_digits_ == 0) return;
+
+        // We shift by exponent at the end just before returning.
+        int remaining_exponent = exponent;
+        while (remaining_exponent >= 27) {
+            multiplyByUInt64(kFive27);
+            remaining_exponent -= 27;
+        }
+        while (remaining_exponent >= 13) {
+            multiplyByUInt32(kFive13);
+            remaining_exponent -= 13;
+        }
+        if (remaining_exponent > 0) {
+            multiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
+        }
+        shiftLeft(exponent);
+    }
+
+
+    void square() {
+        assert (isClamped());
+        final int product_length = 2 * used_digits_;
+        ensureCapacity(product_length);
+
+        // Comba multiplication: compute each column separately.
+        // Example: r = a2a1a0 * b2b1b0.
+        //    r =  1    * a0b0 +
+        //        10    * (a1b0 + a0b1) +
+        //        100   * (a2b0 + a1b1 + a0b2) +
+        //        1000  * (a2b1 + a1b2) +
+        //        10000 * a2b2
+        //
+        // In the worst case we have to accumulate nb-digits products of digit*digit.
+        //
+        // Assert that the additional number of bits in a DoubleChunk are enough to
+        // sum up used_digits of Bigit*Bigit.
+        if ((1L << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
+            throw new RuntimeException("unimplemented");
+        }
+        long accumulator = 0;
+        // First shift the digits so we don't overwrite them.
+        final int copy_offset = used_digits_;
+        for (int i = 0; i < used_digits_; ++i) {
+            bigits_[copy_offset + i] = bigits_[i];
+        }
+        // We have two loops to avoid some 'if's in the loop.
+        for (int i = 0; i < used_digits_; ++i) {
+            // Process temporary digit i with power i.
+            // The sum of the two indices must be equal to i.
+            int bigit_index1 = i;
+            int bigit_index2 = 0;
+            // Sum all of the sub-products.
+            while (bigit_index1 >= 0) {
+                final int int1 = bigits_[copy_offset + bigit_index1];
+                final int int2 = bigits_[copy_offset + bigit_index2];
+                accumulator += ((long) int1) * int2;
+                bigit_index1--;
+                bigit_index2++;
+            }
+            bigits_[i] = (int) (accumulator & kBigitMask);
+            accumulator >>>= kBigitSize;
+        }
+        for (int i = used_digits_; i < product_length; ++i) {
+            int bigit_index1 = used_digits_ - 1;
+            int bigit_index2 = i - bigit_index1;
+            // Invariant: sum of both indices is again equal to i.
+            // Inner loop runs 0 times on last iteration, emptying accumulator.
+            while (bigit_index2 < used_digits_) {
+                final int int1 = bigits_[copy_offset + bigit_index1];
+                final int int2 = bigits_[copy_offset + bigit_index2];
+                accumulator += ((long) int1) * int2;
+                bigit_index1--;
+                bigit_index2++;
+            }
+            // The overwritten bigits_[i] will never be read in further loop iterations,
+            // because bigit_index1 and bigit_index2 are always greater
+            // than i - used_digits_.
+            bigits_[i] = (int) (accumulator & kBigitMask);
+            accumulator >>>= kBigitSize;
+        }
+        // Since the result was guaranteed to lie inside the number the
+        // accumulator must be 0 now.
+        assert (accumulator == 0);
+
+        // Don't forget to update the used_digits and the exponent.
+        used_digits_ = product_length;
+        exponent_ *= 2;
+        clamp();
+    }
+
+
+    void assignPowerUInt16(int base, final int power_exponent) {
+        assert (base != 0);
+        assert (power_exponent >= 0);
+        if (power_exponent == 0) {
+            assignUInt16((char) 1);
+            return;
+        }
+        zero();
+        int shifts = 0;
+        // We expect base to be in range 2-32, and most often to be 10.
+        // It does not make much sense to implement different algorithms for counting
+        // the bits.
+        while ((base & 1) == 0) {
+            base >>>= 1;
+            shifts++;
+        }
+        int bit_size = 0;
+        int tmp_base = base;
+        while (tmp_base != 0) {
+            tmp_base >>>= 1;
+            bit_size++;
+        }
+        final int final_size = bit_size * power_exponent;
+        // 1 extra bigit for the shifting, and one for rounded final_size.
+        ensureCapacity(final_size / kBigitSize + 2);
+
+        // Left to Right exponentiation.
+        int mask = 1;
+        while (power_exponent >= mask) mask <<= 1;
+
+        // The mask is now pointing to the bit above the most significant 1-bit of
+        // power_exponent.
+        // Get rid of first 1-bit;
+        mask >>>= 2;
+        long this_value = base;
+
+        boolean delayed_multipliciation = false;
+        final long max_32bits = 0xFFFFFFFFL;
+        while (mask != 0 && this_value <= max_32bits) {
+            this_value = this_value * this_value;
+            // Verify that there is enough space in this_value to perform the
+            // multiplication.  The first bit_size bits must be 0.
+            if ((power_exponent & mask) != 0) {
+                final long base_bits_mask =
+                        ~((1L << (64 - bit_size)) - 1);
+                final boolean high_bits_zero = (this_value & base_bits_mask) == 0;
+                if (high_bits_zero) {
+                    this_value *= base;
+                } else {
+                    delayed_multipliciation = true;
+                }
+            }
+            mask >>>= 1;
+        }
+        assignUInt64(this_value);
+        if (delayed_multipliciation) {
+            multiplyByUInt32(base);
+        }
+
+        // Now do the same thing as a bignum.
+        while (mask != 0) {
+            square();
+            if ((power_exponent & mask) != 0) {
+                multiplyByUInt32(base);
+            }
+            mask >>>= 1;
+        }
+
+        // And finally add the saved shifts.
+        shiftLeft(shifts * power_exponent);
+    }
+
+
+    // Precondition: this/other < 16bit.
+    char divideModuloIntBignum(final Bignum other) {
+        assert (isClamped());
+        assert (other.isClamped());
+        assert (other.used_digits_ > 0);
+
+        // Easy case: if we have less digits than the divisor than the result is 0.
+        // Note: this handles the case where this == 0, too.
+        if (bigitLength() < other.bigitLength()) {
+            return 0;
+        }
+
+        align(other);
+
+        char result = 0;
+
+        // Start by removing multiples of 'other' until both numbers have the same
+        // number of digits.
+        while (bigitLength() > other.bigitLength()) {
+            // This naive approach is extremely inefficient if `this` divided by other
+            // is big. This function is implemented for doubleToString where
+            // the result should be small (less than 10).
+            assert (other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16));
+            assert (bigits_[used_digits_ - 1] < 0x10000);
+            // Remove the multiples of the first digit.
+            // Example this = 23 and other equals 9. -> Remove 2 multiples.
+            result += (bigits_[used_digits_ - 1]);
+            subtractTimes(other, bigits_[used_digits_ - 1]);
+        }
+
+        assert (bigitLength() == other.bigitLength());
+
+        // Both bignums are at the same length now.
+        // Since other has more than 0 digits we know that the access to
+        // bigits_[used_digits_ - 1] is safe.
+        final int this_bigit = bigits_[used_digits_ - 1];
+        final int other_bigit = other.bigits_[other.used_digits_ - 1];
+
+        if (other.used_digits_ == 1) {
+            // Shortcut for easy (and common) case.
+            final int quotient = Integer.divideUnsigned(this_bigit, other_bigit);
+            bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
+            assert (Integer.compareUnsigned(quotient, 0x10000) < 0);
+            result += quotient;
+            clamp();
+            return result;
+        }
+
+        final int division_estimate = Integer.divideUnsigned(this_bigit, (other_bigit + 1));
+        assert (Integer.compareUnsigned(division_estimate, 0x10000) < 0);
+        result += division_estimate;
+        subtractTimes(other, division_estimate);
+
+        if (other_bigit * (division_estimate + 1) > this_bigit) {
+            // No need to even try to subtract. Even if other's remaining digits were 0
+            // another subtraction would be too much.
+            return result;
+        }
+
+        while (lessEqual(other, this)) {
+            subtractBignum(other);
+            result++;
+        }
+        return result;
+    }
+
+
+    static int sizeInHexChars(int number) {
+        assert (number > 0);
+        int result = 0;
+        while (number != 0) {
+            number >>>= 4;
+            result++;
+        }
+        return result;
+    }
+
+
+    static char hexCharOfValue(final int value) {
+        assert (0 <= value && value <= 16);
+        if (value < 10) return (char) (value + '0');
+        return (char) (value - 10 + 'A');
+    }
+
+
+    String toHexString() {
+        assert (isClamped());
+        // Each bigit must be printable as separate hex-character.
+        assert (kBigitSize % 4 == 0);
+        final int kHexCharsPerBigit = kBigitSize / 4;
+
+        if (used_digits_ == 0) {
+            return "0";
+        }
+
+        final int needed_chars = (bigitLength() - 1) * kHexCharsPerBigit +
+                sizeInHexChars(bigits_[used_digits_ - 1]);
+        final StringBuilder buffer = new StringBuilder(needed_chars);
+        buffer.setLength(needed_chars);
+
+        int string_index = needed_chars - 1;
+        for (int i = 0; i < exponent_; ++i) {
+            for (int j = 0; j < kHexCharsPerBigit; ++j) {
+                buffer.setCharAt(string_index--, '0');
+            }
+        }
+        for (int i = 0; i < used_digits_ - 1; ++i) {
+            int current_bigit = bigits_[i];
+            for (int j = 0; j < kHexCharsPerBigit; ++j) {
+                buffer.setCharAt(string_index--, hexCharOfValue(current_bigit & 0xF));
+                current_bigit >>>= 4;
+            }
+        }
+        // And finally the last bigit.
+        int most_significant_bigit = bigits_[used_digits_ - 1];
+        while (most_significant_bigit != 0) {
+            buffer.setCharAt(string_index--, hexCharOfValue(most_significant_bigit & 0xF));
+            most_significant_bigit >>>= 4;
+        }
+        return buffer.toString();
+    }
+
+
+    int bigitAt(final int index) {
+        if (index >= bigitLength()) return 0;
+        if (index < exponent_) return 0;
+        return bigits_[index - exponent_];
+    }
+
+
+    static int compare(final Bignum a, final Bignum b) {
+        assert (a.isClamped());
+        assert (b.isClamped());
+        final int bigit_length_a = a.bigitLength();
+        final int bigit_length_b = b.bigitLength();
+        if (bigit_length_a < bigit_length_b) return -1;
+        if (bigit_length_a > bigit_length_b) return +1;
+        for (int i = bigit_length_a - 1; i >= Math.min(a.exponent_, b.exponent_); --i) {
+            final int bigit_a = a.bigitAt(i);
+            final int bigit_b = b.bigitAt(i);
+            if (bigit_a < bigit_b) return -1;
+            if (bigit_a > bigit_b) return +1;
+            // Otherwise they are equal up to this digit. Try the next digit.
+        }
+        return 0;
+    }
+
+
+    static int plusCompare(final Bignum a, final Bignum b, final Bignum c) {
+        assert (a.isClamped());
+        assert (b.isClamped());
+        assert (c.isClamped());
+        if (a.bigitLength() < b.bigitLength()) {
+            return plusCompare(b, a, c);
+        }
+        if (a.bigitLength() + 1 < c.bigitLength()) return -1;
+        if (a.bigitLength() > c.bigitLength()) return +1;
+        // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than
+        // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one
+        // of 'a'.
+        if (a.exponent_ >= b.bigitLength() && a.bigitLength() < c.bigitLength()) {
+            return -1;
+        }
+
+        int borrow = 0;
+        // Starting at min_exponent all digits are == 0. So no need to compare them.
+        final int min_exponent = Math.min(Math.min(a.exponent_, b.exponent_), c.exponent_);
+        for (int i = c.bigitLength() - 1; i >= min_exponent; --i) {
+            final int int_a = a.bigitAt(i);
+            final int int_b = b.bigitAt(i);
+            final int int_c = c.bigitAt(i);
+            final int sum = int_a + int_b;
+            if (sum > int_c + borrow) {
+                return +1;
+            } else {
+                borrow = int_c + borrow - sum;
+                if (borrow > 1) return -1;
+                borrow <<= kBigitSize;
+            }
+        }
+        if (borrow == 0) return 0;
+        return -1;
+    }
+
+
+    void clamp() {
+        while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
+            used_digits_--;
+        }
+        if (used_digits_ == 0) {
+            // Zero.
+            exponent_ = 0;
+        }
+    }
+
+
+    boolean isClamped() {
+        return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
+    }
+
+
+    void zero() {
+        for (int i = 0; i < used_digits_; ++i) {
+            bigits_[i] = 0;
+        }
+        used_digits_ = 0;
+        exponent_ = 0;
+    }
+
+
+    void align(final Bignum other) {
+        if (exponent_ > other.exponent_) {
+            // If "X" represents a "hidden" digit (by the exponent) then we are in the
+            // following case (a == this, b == other):
+            // a:  aaaaaaXXXX   or a:   aaaaaXXX
+            // b:     bbbbbbX      b: bbbbbbbbXX
+            // We replace some of the hidden digits (X) of a with 0 digits.
+            // a:  aaaaaa000X   or a:   aaaaa0XX
+            final int zero_digits = exponent_ - other.exponent_;
+            ensureCapacity(used_digits_ + zero_digits);
+            for (int i = used_digits_ - 1; i >= 0; --i) {
+                bigits_[i + zero_digits] = bigits_[i];
+            }
+            for (int i = 0; i < zero_digits; ++i) {
+                bigits_[i] = 0;
+            }
+            used_digits_ += zero_digits;
+            exponent_ -= zero_digits;
+            assert (used_digits_ >= 0);
+            assert (exponent_ >= 0);
+        }
+    }
+
+
+    void bigitsShiftLeft(final int shift_amount) {
+        assert (shift_amount < kBigitSize);
+        assert (shift_amount >= 0);
+        int carry = 0;
+        for (int i = 0; i < used_digits_; ++i) {
+            final int new_carry = bigits_[i] >>> (kBigitSize - shift_amount);
+            bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
+            carry = new_carry;
+        }
+        if (carry != 0) {
+            bigits_[used_digits_] = carry;
+            used_digits_++;
+        }
+    }
+
+
+    void subtractTimes(final Bignum other, final int factor) {
+        assert (exponent_ <= other.exponent_);
+        if (factor < 3) {
+            for (int i = 0; i < factor; ++i) {
+                subtractBignum(other);
+            }
+            return;
+        }
+        int borrow = 0;
+        final int exponent_diff = other.exponent_ - exponent_;
+        for (int i = 0; i < other.used_digits_; ++i) {
+            final long product = ((long) factor) * other.bigits_[i];
+            final long remove = borrow + product;
+            final int difference = bigits_[i + exponent_diff] - (int) (remove & kBigitMask);
+            bigits_[i + exponent_diff] = difference & kBigitMask;
+            borrow = (int) ((difference >>> (kChunkSize - 1)) +
+                    (remove >>> kBigitSize));
+        }
+        for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
+            if (borrow == 0) return;
+            final int difference = bigits_[i] - borrow;
+            bigits_[i] = difference & kBigitMask;
+            borrow = difference >>> (kChunkSize - 1);
+        }
+        clamp();
+    }
+
+    @Override
+    public String toString() {
+        return "Bignum" + Arrays.toString(bigits_);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/BignumDtoa.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,647 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+// Dtoa implementation based on our own Bignum implementation, supporting
+// all conversion modes but slightly slower than the specialized implementations.
+class BignumDtoa {
+
+    private static int normalizedExponent(long significand, int exponent) {
+        assert (significand != 0);
+        while ((significand & IeeeDouble.kHiddenBit) == 0) {
+            significand = significand << 1;
+            exponent = exponent - 1;
+        }
+        return exponent;
+    }
+
+    // Converts the given double 'v' to ascii.
+    // The result should be interpreted as buffer * 10^(point-length).
+    // The buffer will be null-terminated.
+    //
+    // The input v must be > 0 and different from NaN, and Infinity.
+    //
+    // The output depends on the given mode:
+    //  - SHORTEST: produce the least amount of digits for which the internal
+    //   identity requirement is still satisfied. If the digits are printed
+    //   (together with the correct exponent) then reading this number will give
+    //   'v' again. The buffer will choose the representation that is closest to
+    //   'v'. If there are two at the same distance, than the number is round up.
+    //   In this mode the 'requested_digits' parameter is ignored.
+    //  - FIXED: produces digits necessary to print a given number with
+    //   'requested_digits' digits after the decimal point. The produced digits
+    //   might be too short in which case the caller has to fill the gaps with '0's.
+    //   Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+    //   Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
+    //     buffer="2", point=0.
+    //   Note: the length of the returned buffer has no meaning wrt the significance
+    //   of its digits. That is, just because it contains '0's does not mean that
+    //   any other digit would not satisfy the internal identity requirement.
+    //  - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+    //   Even though the length of produced digits usually equals
+    //   'requested_digits', the function is allowed to return fewer digits, in
+    //   which case the caller has to fill the missing digits with '0's.
+    //   Halfway cases are again rounded up.
+    // 'BignumDtoa' expects the given buffer to be big enough to hold all digits
+    // and a terminating null-character.
+    static void bignumDtoa(final double v, final DtoaMode mode, final int requested_digits,
+                    final DtoaBuffer buffer) {
+        assert (v > 0);
+        assert (!IeeeDouble.isSpecial(IeeeDouble.doubleToLong(v)));
+        final long significand;
+        final int exponent;
+        final boolean lower_boundary_is_closer;
+
+        final long l = IeeeDouble.doubleToLong(v);
+        significand = IeeeDouble.significand(l);
+        exponent = IeeeDouble.exponent(l);
+        lower_boundary_is_closer = IeeeDouble.lowerBoundaryIsCloser(l);
+
+        final boolean need_boundary_deltas = mode == DtoaMode.SHORTEST;
+
+        final boolean is_even = (significand & 1) == 0;
+        assert (significand != 0);
+        final int normalizedExponent = normalizedExponent(significand, exponent);
+        // estimated_power might be too low by 1.
+        final int estimated_power = estimatePower(normalizedExponent);
+
+        // Shortcut for Fixed.
+        // The requested digits correspond to the digits after the point. If the
+        // number is much too small, then there is no need in trying to get any
+        // digits.
+        if (mode == DtoaMode.FIXED && -estimated_power - 1 > requested_digits) {
+            buffer.reset();
+            // Set decimal-point to -requested_digits. This is what Gay does.
+            // Note that it should not have any effect anyways since the string is
+            // empty.
+            buffer.decimalPoint = -requested_digits;
+            return;
+        }
+
+        final Bignum numerator = new Bignum();
+        final Bignum denominator = new Bignum();
+        final Bignum delta_minus = new Bignum();
+        final Bignum delta_plus = new Bignum();
+        // Make sure the bignum can grow large enough. The smallest double equals
+        // 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
+        // The maximum double is 1.7976931348623157e308 which needs fewer than
+        // 308*4 binary digits.
+        assert (Bignum.kMaxSignificantBits >= 324*4);
+        initialScaledStartValues(significand, exponent, lower_boundary_is_closer,
+                estimated_power, need_boundary_deltas,
+                numerator, denominator,
+                delta_minus, delta_plus);
+        // We now have v = (numerator / denominator) * 10^estimated_power.
+        buffer.decimalPoint = fixupMultiply10(estimated_power, is_even,
+                numerator, denominator,
+                delta_minus, delta_plus);
+        // We now have v = (numerator / denominator) * 10^(decimal_point-1), and
+        //  1 <= (numerator + delta_plus) / denominator < 10
+        switch (mode) {
+            case SHORTEST:
+                generateShortestDigits(numerator, denominator,
+                        delta_minus, delta_plus,
+                        is_even, buffer);
+                break;
+            case FIXED:
+                bignumToFixed(requested_digits,
+                        numerator, denominator,
+                        buffer);
+                break;
+            case PRECISION:
+                generateCountedDigits(requested_digits,
+                        numerator, denominator,
+                        buffer);
+                break;
+            default:
+                throw new RuntimeException();
+        }
+    }
+
+
+    // The procedure starts generating digits from the left to the right and stops
+    // when the generated digits yield the shortest decimal representation of v. A
+    // decimal representation of v is a number lying closer to v than to any other
+    // double, so it converts to v when read.
+    //
+    // This is true if d, the decimal representation, is between m- and m+, the
+    // upper and lower boundaries. d must be strictly between them if !is_even.
+    //           m- := (numerator - delta_minus) / denominator
+    //           m+ := (numerator + delta_plus) / denominator
+    //
+    // Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
+    //   If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
+    //   will be produced. This should be the standard precondition.
+    static void generateShortestDigits(final Bignum numerator, final Bignum denominator,
+                                       final Bignum delta_minus, Bignum delta_plus,
+                                       final boolean is_even,
+                                       final DtoaBuffer buffer) {
+        // Small optimization: if delta_minus and delta_plus are the same just reuse
+        // one of the two bignums.
+        if (Bignum.equal(delta_minus, delta_plus)) {
+            delta_plus = delta_minus;
+        }
+        for (;;) {
+            final char digit;
+            digit = numerator.divideModuloIntBignum(denominator);
+            assert (digit <= 9);  // digit is a uint16_t and therefore always positive.
+            // digit = numerator / denominator (integer division).
+            // numerator = numerator % denominator.
+            buffer.append((char) (digit + '0'));
+
+            // Can we stop already?
+            // If the remainder of the division is less than the distance to the lower
+            // boundary we can stop. In this case we simply round down (discarding the
+            // remainder).
+            // Similarly we test if we can round up (using the upper boundary).
+            final boolean in_delta_room_minus;
+            final boolean in_delta_room_plus;
+            if (is_even) {
+                in_delta_room_minus = Bignum.lessEqual(numerator, delta_minus);
+            } else {
+                in_delta_room_minus = Bignum.less(numerator, delta_minus);
+            }
+            if (is_even) {
+                in_delta_room_plus =
+                        Bignum.plusCompare(numerator, delta_plus, denominator) >= 0;
+            } else {
+                in_delta_room_plus =
+                        Bignum.plusCompare(numerator, delta_plus, denominator) > 0;
+            }
+            if (!in_delta_room_minus && !in_delta_room_plus) {
+                // Prepare for next iteration.
+                numerator.times10();
+                delta_minus.times10();
+                // We optimized delta_plus to be equal to delta_minus (if they share the
+                // same value). So don't multiply delta_plus if they point to the same
+                // object.
+                if (delta_minus != delta_plus) {
+                    delta_plus.times10();
+                }
+            } else if (in_delta_room_minus && in_delta_room_plus) {
+                // Let's see if 2*numerator < denominator.
+                // If yes, then the next digit would be < 5 and we can round down.
+                final int compare = Bignum.plusCompare(numerator, numerator, denominator);
+                if (compare < 0) {
+                    // Remaining digits are less than .5. -> Round down (== do nothing).
+                } else if (compare > 0) {
+                    // Remaining digits are more than .5 of denominator. -> Round up.
+                    // Note that the last digit could not be a '9' as otherwise the whole
+                    // loop would have stopped earlier.
+                    // We still have an assert here in case the preconditions were not
+                    // satisfied.
+                    assert (buffer.chars[buffer.length - 1] != '9');
+                    buffer.chars[buffer.length - 1]++;
+                } else {
+                    // Halfway case.
+                    // TODO(floitsch): need a way to solve half-way cases.
+                    //   For now let's round towards even (since this is what Gay seems to
+                    //   do).
+
+                    if ((buffer.chars[buffer.length - 1] - '0') % 2 == 0) {
+                        // Round down => Do nothing.
+                    } else {
+                        assert (buffer.chars[buffer.length - 1] != '9');
+                        buffer.chars[buffer.length - 1]++;
+                    }
+                }
+                return;
+            } else if (in_delta_room_minus) {
+                // Round down (== do nothing).
+                return;
+            } else {  // in_delta_room_plus
+                // Round up.
+                // Note again that the last digit could not be '9' since this would have
+                // stopped the loop earlier.
+                // We still have an ASSERT here, in case the preconditions were not
+                // satisfied.
+                assert (buffer.chars[buffer.length -1] != '9');
+                buffer.chars[buffer.length - 1]++;
+                return;
+            }
+        }
+    }
+
+
+    // Let v = numerator / denominator < 10.
+    // Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
+    // from left to right. Once 'count' digits have been produced we decide wether
+    // to round up or down. Remainders of exactly .5 round upwards. Numbers such
+    // as 9.999999 propagate a carry all the way, and change the
+    // exponent (decimal_point), when rounding upwards.
+    static void generateCountedDigits(final int count,
+                                      final Bignum numerator, final Bignum denominator,
+                                      final DtoaBuffer buffer) {
+        assert (count >= 0);
+        for (int i = 0; i < count - 1; ++i) {
+            final char digit;
+            digit = numerator.divideModuloIntBignum(denominator);
+            assert (digit <= 9);  // digit is a uint16_t and therefore always positive.
+            // digit = numerator / denominator (integer division).
+            // numerator = numerator % denominator.
+            buffer.chars[i] = (char)(digit + '0');
+            // Prepare for next iteration.
+            numerator.times10();
+        }
+        // Generate the last digit.
+        char digit;
+        digit = numerator.divideModuloIntBignum(denominator);
+        if (Bignum.plusCompare(numerator, numerator, denominator) >= 0) {
+            digit++;
+        }
+        assert (digit <= 10);
+        buffer.chars[count - 1] = (char) (digit + '0');
+        // Correct bad digits (in case we had a sequence of '9's). Propagate the
+        // carry until we hat a non-'9' or til we reach the first digit.
+        for (int i = count - 1; i > 0; --i) {
+            if (buffer.chars[i] != '0' + 10) break;
+            buffer.chars[i] = '0';
+            buffer.chars[i - 1]++;
+        }
+        if (buffer.chars[0] == '0' + 10) {
+            // Propagate a carry past the top place.
+            buffer.chars[0] = '1';
+            buffer.decimalPoint++;
+        }
+        buffer.length = count;
+    }
+
+
+    // Generates 'requested_digits' after the decimal point. It might omit
+    // trailing '0's. If the input number is too small then no digits at all are
+    // generated (ex.: 2 fixed digits for 0.00001).
+    //
+    // Input verifies:  1 <= (numerator + delta) / denominator < 10.
+    static void bignumToFixed(final int requested_digits,
+                              final Bignum numerator, final Bignum denominator,
+                              final DtoaBuffer buffer) {
+        // Note that we have to look at more than just the requested_digits, since
+        // a number could be rounded up. Example: v=0.5 with requested_digits=0.
+        // Even though the power of v equals 0 we can't just stop here.
+        if (-buffer.decimalPoint > requested_digits) {
+            // The number is definitively too small.
+            // Ex: 0.001 with requested_digits == 1.
+            // Set decimal-decimalPoint to -requested_digits. This is what Gay does.
+            // Note that it should not have any effect anyways since the string is
+            // empty.
+            buffer.decimalPoint = -requested_digits;
+            buffer.length = 0;
+            // return;
+        } else if (-buffer.decimalPoint == requested_digits) {
+            // We only need to verify if the number rounds down or up.
+            // Ex: 0.04 and 0.06 with requested_digits == 1.
+            assert (buffer.decimalPoint == -requested_digits);
+            // Initially the fraction lies in range (1, 10]. Multiply the denominator
+            // by 10 so that we can compare more easily.
+            denominator.times10();
+            if (Bignum.plusCompare(numerator, numerator, denominator) >= 0) {
+                // If the fraction is >= 0.5 then we have to include the rounded
+                // digit.
+                buffer.chars[0] = '1';
+                buffer.length = 1;
+                buffer.decimalPoint++;
+            } else {
+                // Note that we caught most of similar cases earlier.
+                buffer.length = 0;
+            }
+            // return;
+        } else {
+            // The requested digits correspond to the digits after the point.
+            // The variable 'needed_digits' includes the digits before the point.
+            final int needed_digits = buffer.decimalPoint + requested_digits;
+            generateCountedDigits(needed_digits,
+                    numerator, denominator,
+                    buffer);
+        }
+    }
+
+
+    // Returns an estimation of k such that 10^(k-1) <= v < 10^k where
+    // v = f * 2^exponent and 2^52 <= f < 2^53.
+    // v is hence a normalized double with the given exponent. The output is an
+    // approximation for the exponent of the decimal approimation .digits * 10^k.
+    //
+    // The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
+    // Note: this property holds for v's upper boundary m+ too.
+    //    10^k <= m+ < 10^k+1.
+    //   (see explanation below).
+    //
+    // Examples:
+    //  EstimatePower(0)   => 16
+    //  EstimatePower(-52) => 0
+    //
+    // Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
+    static int estimatePower(final int exponent) {
+        // This function estimates log10 of v where v = f*2^e (with e == exponent).
+        // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
+        // Note that f is bounded by its container size. Let p = 53 (the double's
+        // significand size). Then 2^(p-1) <= f < 2^p.
+        //
+        // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
+        // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
+        // The computed number undershoots by less than 0.631 (when we compute log3
+        // and not log10).
+        //
+        // Optimization: since we only need an approximated result this computation
+        // can be performed on 64 bit integers. On x86/x64 architecture the speedup is
+        // not really measurable, though.
+        //
+        // Since we want to avoid overshooting we decrement by 1e10 so that
+        // floating-point imprecisions don't affect us.
+        //
+        // Explanation for v's boundary m+: the computation takes advantage of
+        // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
+        // (even for denormals where the delta can be much more important).
+
+        final double k1Log10 = 0.30102999566398114;  // 1/lg(10)
+
+        // For doubles len(f) == 53 (don't forget the hidden bit).
+        final int kSignificandSize = IeeeDouble.kSignificandSize;
+        final double estimate = Math.ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
+        return (int) estimate;
+    }
+
+
+    // See comments for InitialScaledStartValues.
+    static void initialScaledStartValuesPositiveExponent(
+            final long significand, final int exponent,
+            final int estimated_power, final boolean need_boundary_deltas,
+            final Bignum numerator, final Bignum denominator,
+            final Bignum delta_minus, final Bignum delta_plus) {
+        // A positive exponent implies a positive power.
+        assert (estimated_power >= 0);
+        // Since the estimated_power is positive we simply multiply the denominator
+        // by 10^estimated_power.
+
+        // numerator = v.
+        numerator.assignUInt64(significand);
+        numerator.shiftLeft(exponent);
+        // denominator = 10^estimated_power.
+        denominator.assignPowerUInt16(10, estimated_power);
+
+        if (need_boundary_deltas) {
+            // Introduce a common denominator so that the deltas to the boundaries are
+            // integers.
+            denominator.shiftLeft(1);
+            numerator.shiftLeft(1);
+            // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+            // denominator (of 2) delta_plus equals 2^e.
+            delta_plus.assignUInt16((char) 1);
+            delta_plus.shiftLeft(exponent);
+            // Same for delta_minus. The adjustments if f == 2^p-1 are done later.
+            delta_minus.assignUInt16((char) 1);
+            delta_minus.shiftLeft(exponent);
+        }
+    }
+
+
+    // See comments for InitialScaledStartValues
+    static void initialScaledStartValuesNegativeExponentPositivePower(
+            final long significand, final int exponent,
+            final int estimated_power, final boolean need_boundary_deltas,
+            final Bignum numerator, final Bignum denominator,
+            final Bignum delta_minus, final Bignum delta_plus) {
+        // v = f * 2^e with e < 0, and with estimated_power >= 0.
+        // This means that e is close to 0 (have a look at how estimated_power is
+        // computed).
+
+        // numerator = significand
+        //  since v = significand * 2^exponent this is equivalent to
+        //  numerator = v * / 2^-exponent
+        numerator.assignUInt64(significand);
+        // denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
+        denominator.assignPowerUInt16(10, estimated_power);
+        denominator.shiftLeft(-exponent);
+
+        if (need_boundary_deltas) {
+            // Introduce a common denominator so that the deltas to the boundaries are
+            // integers.
+            denominator.shiftLeft(1);
+            numerator.shiftLeft(1);
+            // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+            // denominator (of 2) delta_plus equals 2^e.
+            // Given that the denominator already includes v's exponent the distance
+            // to the boundaries is simply 1.
+            delta_plus.assignUInt16((char) 1);
+            // Same for delta_minus. The adjustments if f == 2^p-1 are done later.
+            delta_minus.assignUInt16((char) 1);
+        }
+    }
+
+
+    // See comments for InitialScaledStartValues
+    static void initialScaledStartValuesNegativeExponentNegativePower(
+            final long significand, final int exponent,
+            final int estimated_power, final boolean need_boundary_deltas,
+            final Bignum numerator, final Bignum denominator,
+            final Bignum delta_minus, final Bignum delta_plus) {
+        // Instead of multiplying the denominator with 10^estimated_power we
+        // multiply all values (numerator and deltas) by 10^-estimated_power.
+
+        // Use numerator as temporary container for power_ten.
+        final Bignum power_ten = numerator;
+        power_ten.assignPowerUInt16(10, -estimated_power);
+
+        if (need_boundary_deltas) {
+            // Since power_ten == numerator we must make a copy of 10^estimated_power
+            // before we complete the computation of the numerator.
+            // delta_plus = delta_minus = 10^estimated_power
+            delta_plus.assignBignum(power_ten);
+            delta_minus.assignBignum(power_ten);
+        }
+
+        // numerator = significand * 2 * 10^-estimated_power
+        //  since v = significand * 2^exponent this is equivalent to
+        // numerator = v * 10^-estimated_power * 2 * 2^-exponent.
+        // Remember: numerator has been abused as power_ten. So no need to assign it
+        //  to itself.
+        assert (numerator == power_ten);
+        numerator.multiplyByUInt64(significand);
+
+        // denominator = 2 * 2^-exponent with exponent < 0.
+        denominator.assignUInt16((char) 1);
+        denominator.shiftLeft(-exponent);
+
+        if (need_boundary_deltas) {
+            // Introduce a common denominator so that the deltas to the boundaries are
+            // integers.
+            numerator.shiftLeft(1);
+            denominator.shiftLeft(1);
+            // With this shift the boundaries have their correct value, since
+            // delta_plus = 10^-estimated_power, and
+            // delta_minus = 10^-estimated_power.
+            // These assignments have been done earlier.
+            // The adjustments if f == 2^p-1 (lower boundary is closer) are done later.
+        }
+    }
+
+
+    // Let v = significand * 2^exponent.
+    // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+    // and denominator. The functions GenerateShortestDigits and
+    // GenerateCountedDigits will then convert this ratio to its decimal
+    // representation d, with the required accuracy.
+    // Then d * 10^estimated_power is the representation of v.
+    // (Note: the fraction and the estimated_power might get adjusted before
+    // generating the decimal representation.)
+    //
+    // The initial start values consist of:
+    //  - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
+    //  - a scaled (common) denominator.
+    //  optionally (used by GenerateShortestDigits to decide if it has the shortest
+    //  decimal converting back to v):
+    //  - v - m-: the distance to the lower boundary.
+    //  - m+ - v: the distance to the upper boundary.
+    //
+    // v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
+    //
+    // Let ep == estimated_power, then the returned values will satisfy:
+    //  v / 10^ep = numerator / denominator.
+    //  v's boundarys m- and m+:
+    //    m- / 10^ep == v / 10^ep - delta_minus / denominator
+    //    m+ / 10^ep == v / 10^ep + delta_plus / denominator
+    //  Or in other words:
+    //    m- == v - delta_minus * 10^ep / denominator;
+    //    m+ == v + delta_plus * 10^ep / denominator;
+    //
+    // Since 10^(k-1) <= v < 10^k    (with k == estimated_power)
+    //  or       10^k <= v < 10^(k+1)
+    //  we then have 0.1 <= numerator/denominator < 1
+    //           or    1 <= numerator/denominator < 10
+    //
+    // It is then easy to kickstart the digit-generation routine.
+    //
+    // The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST
+    // or BIGNUM_DTOA_SHORTEST_SINGLE.
+
+    static void initialScaledStartValues(final long significand,
+                                         final int exponent,
+                                         final boolean lower_boundary_is_closer,
+                                         final int estimated_power,
+                                         final boolean need_boundary_deltas,
+                                         final Bignum numerator,
+                                         final Bignum denominator,
+                                         final Bignum delta_minus,
+                                         final Bignum delta_plus) {
+        if (exponent >= 0) {
+            initialScaledStartValuesPositiveExponent(
+                    significand, exponent, estimated_power, need_boundary_deltas,
+                    numerator, denominator, delta_minus, delta_plus);
+        } else if (estimated_power >= 0) {
+            initialScaledStartValuesNegativeExponentPositivePower(
+                    significand, exponent, estimated_power, need_boundary_deltas,
+                    numerator, denominator, delta_minus, delta_plus);
+        } else {
+            initialScaledStartValuesNegativeExponentNegativePower(
+                    significand, exponent, estimated_power, need_boundary_deltas,
+                    numerator, denominator, delta_minus, delta_plus);
+        }
+
+        if (need_boundary_deltas && lower_boundary_is_closer) {
+            // The lower boundary is closer at half the distance of "normal" numbers.
+            // Increase the common denominator and adapt all but the delta_minus.
+            denominator.shiftLeft(1);  // *2
+            numerator.shiftLeft(1);    // *2
+            delta_plus.shiftLeft(1);   // *2
+        }
+    }
+
+
+    // This routine multiplies numerator/denominator so that its values lies in the
+    // range 1-10. That is after a call to this function we have:
+    //    1 <= (numerator + delta_plus) /denominator < 10.
+    // Let numerator the input before modification and numerator' the argument
+    // after modification, then the output-parameter decimal_point is such that
+    //  numerator / denominator * 10^estimated_power ==
+    //    numerator' / denominator' * 10^(decimal_point - 1)
+    // In some cases estimated_power was too low, and this is already the case. We
+    // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
+    // estimated_power) but do not touch the numerator or denominator.
+    // Otherwise the routine multiplies the numerator and the deltas by 10.
+    static int fixupMultiply10(final int estimated_power, final boolean is_even,
+                                final Bignum numerator, final Bignum denominator,
+                                final Bignum delta_minus, final Bignum delta_plus) {
+        final boolean in_range;
+        final int decimal_point;
+        if (is_even) {
+            // For IEEE doubles half-way cases (in decimal system numbers ending with 5)
+            // are rounded to the closest floating-point number with even significand.
+            in_range = Bignum.plusCompare(numerator, delta_plus, denominator) >= 0;
+        } else {
+            in_range = Bignum.plusCompare(numerator, delta_plus, denominator) > 0;
+        }
+        if (in_range) {
+            // Since numerator + delta_plus >= denominator we already have
+            // 1 <= numerator/denominator < 10. Simply update the estimated_power.
+            decimal_point = estimated_power + 1;
+        } else {
+            decimal_point = estimated_power;
+            numerator.times10();
+            if (Bignum.equal(delta_minus, delta_plus)) {
+                delta_minus.times10();
+                delta_plus.assignBignum(delta_minus);
+            } else {
+                delta_minus.times10();
+                delta_plus.times10();
+            }
+        }
+        return decimal_point;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/CachedPowers.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+public class CachedPowers {
+
+    static class CachedPower {
+        final private long significand;
+        final private int binaryExponent;
+        final private int decimalExponent;
+
+        CachedPower(final long significand, final int binaryExponent, final int decimalExponent) {
+            this.significand = significand;
+            this.binaryExponent = binaryExponent;
+            this.decimalExponent = decimalExponent;
+        }
+    }
+
+    static private final CachedPower[] kCachedPowers = {
+            new CachedPower(0xfa8fd5a0081c0288L, -1220, -348),
+            new CachedPower(0xbaaee17fa23ebf76L, -1193, -340),
+            new CachedPower(0x8b16fb203055ac76L, -1166, -332),
+            new CachedPower(0xcf42894a5dce35eaL, -1140, -324),
+            new CachedPower(0x9a6bb0aa55653b2dL, -1113, -316),
+            new CachedPower(0xe61acf033d1a45dfL, -1087, -308),
+            new CachedPower(0xab70fe17c79ac6caL, -1060, -300),
+            new CachedPower(0xff77b1fcbebcdc4fL, -1034, -292),
+            new CachedPower(0xbe5691ef416bd60cL, -1007, -284),
+            new CachedPower(0x8dd01fad907ffc3cL, -980, -276),
+            new CachedPower(0xd3515c2831559a83L, -954, -268),
+            new CachedPower(0x9d71ac8fada6c9b5L, -927, -260),
+            new CachedPower(0xea9c227723ee8bcbL, -901, -252),
+            new CachedPower(0xaecc49914078536dL, -874, -244),
+            new CachedPower(0x823c12795db6ce57L, -847, -236),
+            new CachedPower(0xc21094364dfb5637L, -821, -228),
+            new CachedPower(0x9096ea6f3848984fL, -794, -220),
+            new CachedPower(0xd77485cb25823ac7L, -768, -212),
+            new CachedPower(0xa086cfcd97bf97f4L, -741, -204),
+            new CachedPower(0xef340a98172aace5L, -715, -196),
+            new CachedPower(0xb23867fb2a35b28eL, -688, -188),
+            new CachedPower(0x84c8d4dfd2c63f3bL, -661, -180),
+            new CachedPower(0xc5dd44271ad3cdbaL, -635, -172),
+            new CachedPower(0x936b9fcebb25c996L, -608, -164),
+            new CachedPower(0xdbac6c247d62a584L, -582, -156),
+            new CachedPower(0xa3ab66580d5fdaf6L, -555, -148),
+            new CachedPower(0xf3e2f893dec3f126L, -529, -140),
+            new CachedPower(0xb5b5ada8aaff80b8L, -502, -132),
+            new CachedPower(0x87625f056c7c4a8bL, -475, -124),
+            new CachedPower(0xc9bcff6034c13053L, -449, -116),
+            new CachedPower(0x964e858c91ba2655L, -422, -108),
+            new CachedPower(0xdff9772470297ebdL, -396, -100),
+            new CachedPower(0xa6dfbd9fb8e5b88fL, -369, -92),
+            new CachedPower(0xf8a95fcf88747d94L, -343, -84),
+            new CachedPower(0xb94470938fa89bcfL, -316, -76),
+            new CachedPower(0x8a08f0f8bf0f156bL, -289, -68),
+            new CachedPower(0xcdb02555653131b6L, -263, -60),
+            new CachedPower(0x993fe2c6d07b7facL, -236, -52),
+            new CachedPower(0xe45c10c42a2b3b06L, -210, -44),
+            new CachedPower(0xaa242499697392d3L, -183, -36),
+            new CachedPower(0xfd87b5f28300ca0eL, -157, -28),
+            new CachedPower(0xbce5086492111aebL, -130, -20),
+            new CachedPower(0x8cbccc096f5088ccL, -103, -12),
+            new CachedPower(0xd1b71758e219652cL, -77, -4),
+            new CachedPower(0x9c40000000000000L, -50, 4),
+            new CachedPower(0xe8d4a51000000000L, -24, 12),
+            new CachedPower(0xad78ebc5ac620000L, 3, 20),
+            new CachedPower(0x813f3978f8940984L, 30, 28),
+            new CachedPower(0xc097ce7bc90715b3L, 56, 36),
+            new CachedPower(0x8f7e32ce7bea5c70L, 83, 44),
+            new CachedPower(0xd5d238a4abe98068L, 109, 52),
+            new CachedPower(0x9f4f2726179a2245L, 136, 60),
+            new CachedPower(0xed63a231d4c4fb27L, 162, 68),
+            new CachedPower(0xb0de65388cc8ada8L, 189, 76),
+            new CachedPower(0x83c7088e1aab65dbL, 216, 84),
+            new CachedPower(0xc45d1df942711d9aL, 242, 92),
+            new CachedPower(0x924d692ca61be758L, 269, 100),
+            new CachedPower(0xda01ee641a708deaL, 295, 108),
+            new CachedPower(0xa26da3999aef774aL, 322, 116),
+            new CachedPower(0xf209787bb47d6b85L, 348, 124),
+            new CachedPower(0xb454e4a179dd1877L, 375, 132),
+            new CachedPower(0x865b86925b9bc5c2L, 402, 140),
+            new CachedPower(0xc83553c5c8965d3dL, 428, 148),
+            new CachedPower(0x952ab45cfa97a0b3L, 455, 156),
+            new CachedPower(0xde469fbd99a05fe3L, 481, 164),
+            new CachedPower(0xa59bc234db398c25L, 508, 172),
+            new CachedPower(0xf6c69a72a3989f5cL, 534, 180),
+            new CachedPower(0xb7dcbf5354e9beceL, 561, 188),
+            new CachedPower(0x88fcf317f22241e2L, 588, 196),
+            new CachedPower(0xcc20ce9bd35c78a5L, 614, 204),
+            new CachedPower(0x98165af37b2153dfL, 641, 212),
+            new CachedPower(0xe2a0b5dc971f303aL, 667, 220),
+            new CachedPower(0xa8d9d1535ce3b396L, 694, 228),
+            new CachedPower(0xfb9b7cd9a4a7443cL, 720, 236),
+            new CachedPower(0xbb764c4ca7a44410L, 747, 244),
+            new CachedPower(0x8bab8eefb6409c1aL, 774, 252),
+            new CachedPower(0xd01fef10a657842cL, 800, 260),
+            new CachedPower(0x9b10a4e5e9913129L, 827, 268),
+            new CachedPower(0xe7109bfba19c0c9dL, 853, 276),
+            new CachedPower(0xac2820d9623bf429L, 880, 284),
+            new CachedPower(0x80444b5e7aa7cf85L, 907, 292),
+            new CachedPower(0xbf21e44003acdd2dL, 933, 300),
+            new CachedPower(0x8e679c2f5e44ff8fL, 960, 308),
+            new CachedPower(0xd433179d9c8cb841L, 986, 316),
+            new CachedPower(0x9e19db92b4e31ba9L, 1013, 324),
+            new CachedPower(0xeb96bf6ebadf77d9L, 1039, 332),
+            new CachedPower(0xaf87023b9bf0ee6bL, 1066, 340)
+    };
+
+    static final int kCachedPowersOffset = 348;  // -1 * the first decimal_exponent.
+    static final double kD_1_LOG2_10 = 0.30102999566398114;  //  1 / lg(10)
+    // Difference between the decimal exponents in the table above.
+    static final int kDecimalExponentDistance = 8;
+    static final int kMinDecimalExponent = -348;
+    static final int kMaxDecimalExponent = 340;
+
+    static int getCachedPowerForBinaryExponentRange(
+            final int min_exponent,
+            final int max_exponent,
+            final DiyFp power) {
+        final int kQ = DiyFp.kSignificandSize;
+        final double k = Math.ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
+        final int index =
+                (kCachedPowersOffset + (int) k - 1) / kDecimalExponentDistance + 1;
+        assert (0 <= index && index < kCachedPowers.length);
+        final CachedPower cached_power = kCachedPowers[index];
+        assert (min_exponent <= cached_power.binaryExponent);
+        assert (cached_power.binaryExponent <= max_exponent);
+        power.setF(cached_power.significand);
+        power.setE(cached_power.binaryExponent);
+        return cached_power.decimalExponent;
+    }
+
+
+    static int getCachedPowerForDecimalExponent(final int requested_exponent,
+                                                final DiyFp power) {
+        assert (kMinDecimalExponent <= requested_exponent);
+        assert (requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
+        final int index =
+                (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
+        final CachedPower cached_power = kCachedPowers[index];
+        power.setF(cached_power.significand);
+        power.setE(cached_power.binaryExponent);
+        final int found_exponent = cached_power.decimalExponent;
+        assert (found_exponent <= requested_exponent);
+        assert (requested_exponent < found_exponent + kDecimalExponentDistance);
+        return cached_power.decimalExponent;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DiyFp.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+// This "Do It Yourself Floating Point" class implements a floating-point number
+// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
+// have the most significant bit of the significand set.
+// Multiplication and Subtraction do not normalize their results.
+// DiyFp are not designed to contain special doubles (NaN and Infinity).
+class DiyFp {
+
+    private long f_;
+    private int e_;
+
+    static final int kSignificandSize = 64;
+    static final long kUint64MSB = 0x8000000000000000L;
+
+
+    DiyFp() {
+        this.f_ = 0;
+        this.e_ = 0;
+    }
+
+    DiyFp(final long f, final int e) {
+        this.f_ = f;
+        this.e_ = e;
+    }
+
+    // this = this - other.
+    // The exponents of both numbers must be the same and the significand of this
+    // must be bigger than the significand of other.
+    // The result will not be normalized.
+    void subtract(final DiyFp other) {
+        assert (e_ == other.e_);
+        assert Long.compareUnsigned(f_, other.f_) >= 0;
+        f_ -= other.f_;
+    }
+
+    // Returns a - b.
+    // The exponents of both numbers must be the same and this must be bigger
+    // than other. The result will not be normalized.
+    static DiyFp minus(final DiyFp a, final DiyFp b) {
+        final DiyFp result = new DiyFp(a.f_, a.e_);
+        result.subtract(b);
+        return result;
+    }
+
+
+    // this = this * other.
+    final void multiply(final DiyFp other) {
+        // Simply "emulates" a 128 bit multiplication.
+        // However: the resulting number only contains 64 bits. The least
+        // significant 64 bits are only used for rounding the most significant 64
+        // bits.
+        final long kM32 = 0xFFFFFFFFL;
+        final long a = f_ >>> 32;
+        final long b = f_ & kM32;
+        final long c = other.f_ >>> 32;
+        final long d = other.f_ & kM32;
+        final long ac = a * c;
+        final long bc = b * c;
+        final long ad = a * d;
+        final long bd = b * d;
+        long tmp = (bd >>> 32) + (ad & kM32) + (bc & kM32);
+        // By adding 1U << 31 to tmp we round the final result.
+        // Halfway cases will be round up.
+        tmp += 1L << 31;
+        final long result_f = ac + (ad >>> 32) + (bc >>> 32) + (tmp >>> 32);
+        e_ += other.e_ + 64;
+        f_ = result_f;
+    }
+
+    // returns a * b;
+    static DiyFp times(final DiyFp a, final DiyFp b) {
+        final DiyFp result = new DiyFp(a.f_, a.e_);
+        result.multiply(b);
+        return result;
+    }
+
+    void normalize() {
+        assert(f_ != 0);
+        long significand = this.f_;
+        int exponent = this.e_;
+
+        // This method is mainly called for normalizing boundaries. In general
+        // boundaries need to be shifted by 10 bits. We thus optimize for this case.
+        final long k10MSBits = 0xFFC00000L << 32;
+        while ((significand & k10MSBits) == 0) {
+            significand <<= 10;
+            exponent -= 10;
+        }
+        while ((significand & kUint64MSB) == 0) {
+            significand <<= 1;
+            exponent--;
+        }
+        this.f_ = significand;
+        this.e_ = exponent;
+    }
+
+    static DiyFp normalize(final DiyFp a) {
+        final DiyFp result = new DiyFp(a.f_, a.e_);
+        result.normalize();
+        return result;
+    }
+
+    long f() { return f_; }
+    int e() { return e_; }
+
+    void setF(final long new_value) { f_ = new_value; }
+    void setE(final int new_value) { e_ = new_value; }
+
+    @Override
+    public String toString() {
+        return "DiyFp[f=" + f_ + ", e=" + e_ + "]";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DoubleConversion.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+/**
+ * This class provides the public API for the double conversion package.
+ */
+public final class DoubleConversion {
+
+    private final static int BUFFER_LENGTH = 30;
+
+    /**
+     * Converts a double number to its shortest string representation.
+     *
+     * @param value number to convert
+     * @return formatted number
+     */
+    public static String toShortestString(final double value) {
+        final DtoaBuffer buffer = new DtoaBuffer(FastDtoa.kFastDtoaMaximalLength);
+        final double absValue = Math.abs(value);
+
+        if (value < 0) {
+            buffer.isNegative = true;
+        }
+
+        if (!fastDtoaShortest(absValue, buffer)) {
+            buffer.reset();
+            bignumDtoa(absValue, DtoaMode.SHORTEST, 0, buffer);
+        }
+
+        return buffer.format(DtoaMode.SHORTEST, 0);
+    }
+
+    /**
+     * Converts a double number to a string representation with a fixed number of digits
+     * after the decimal point.
+     *
+     * @param value number to convert.
+     * @param requestedDigits number of digits after decimal point
+     * @return formatted number
+     */
+    public static String toFixed(final double value, final int requestedDigits) {
+        final DtoaBuffer buffer = new DtoaBuffer(BUFFER_LENGTH);
+        final double absValue = Math.abs(value);
+
+        if (value < 0) {
+            buffer.isNegative = true;
+        }
+
+        if (value == 0) {
+            buffer.append('0');
+            buffer.decimalPoint = 1;
+        } else if (!fixedDtoa(absValue, requestedDigits, buffer)) {
+            buffer.reset();
+            bignumDtoa(absValue, DtoaMode.FIXED, requestedDigits, buffer);
+        }
+
+        return buffer.format(DtoaMode.FIXED, requestedDigits);
+    }
+
+    /**
+     * Converts a double number to a string representation with a fixed number of digits.
+     *
+     * @param value number to convert
+     * @param precision number of digits to create
+     * @return formatted number
+     */
+    public static String toPrecision(final double value, final int precision) {
+        final DtoaBuffer buffer = new DtoaBuffer(precision);
+        final double absValue = Math.abs(value);
+
+        if (value < 0) {
+            buffer.isNegative = true;
+        }
+
+        if (value == 0) {
+            for (int i = 0; i < precision; i++) {
+                buffer.append('0');
+            }
+            buffer.decimalPoint = 1;
+
+        } else if (!fastDtoaCounted(absValue, precision, buffer)) {
+            buffer.reset();
+            bignumDtoa(absValue, DtoaMode.PRECISION, precision, buffer);
+        }
+
+        return buffer.format(DtoaMode.PRECISION, 0);
+    }
+
+    /**
+     * Converts a double number to a string representation using the
+     * {@code BignumDtoa} algorithm and the specified conversion mode
+     * and number of digits.
+     *
+     * @param v number to convert
+     * @param mode conversion mode
+     * @param digits number of digits
+     * @param buffer buffer to use
+     */
+    public static void bignumDtoa(final double v, final DtoaMode mode, final int digits, final DtoaBuffer buffer) {
+        assert(v > 0);
+        assert(!Double.isNaN(v));
+        assert(!Double.isInfinite(v));
+
+        BignumDtoa.bignumDtoa(v, mode, digits, buffer);
+    }
+
+    /**
+     * Converts a double number to its shortest string representation
+     * using the {@code FastDtoa} algorithm.
+     *
+     * @param v number to convert
+     * @param buffer buffer to use
+     * @return true if conversion succeeded
+     */
+    public static boolean fastDtoaShortest(final double v, final DtoaBuffer buffer) {
+        assert(v > 0);
+        assert(!Double.isNaN(v));
+        assert(!Double.isInfinite(v));
+
+        return FastDtoa.grisu3(v, buffer);
+    }
+
+    /**
+     * Converts a double number to a string representation with the
+     * given number of digits using the {@code FastDtoa} algorithm.
+     *
+     * @param v number to convert
+     * @param precision number of digits to generate
+     * @param buffer buffer to use
+     * @return true if conversion succeeded
+     */
+    public static boolean fastDtoaCounted(final double v, final int precision, final DtoaBuffer buffer) {
+        assert(v > 0);
+        assert(!Double.isNaN(v));
+        assert(!Double.isInfinite(v));
+
+        return FastDtoa.grisu3Counted(v, precision, buffer);
+    }
+
+    /**
+     * Converts a double number to a string representation with a
+     * fixed number of digits after the decimal point using the
+     * {@code FixedDtoa} algorithm.
+     *
+     * @param v number to convert.
+     * @param digits number of digits after the decimal point
+     * @param buffer buffer to use
+     * @return true if conversion succeeded
+     */
+    public static boolean fixedDtoa(final double v, final int digits, final DtoaBuffer buffer) {
+        assert(v > 0);
+        assert(!Double.isNaN(v));
+        assert(!Double.isInfinite(v));
+
+        return FixedDtoa.fastFixedDtoa(v, digits, buffer);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaBuffer.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+/**
+ * A buffer for generating string representations of doubles.
+ */
+public class DtoaBuffer {
+
+    // The character buffer
+    final char[] chars;
+
+    // The number of characters in the buffer
+    int length = 0;
+
+    // The position of the decimal point
+    int decimalPoint = 0;
+
+    // Is this a negative number?
+    boolean isNegative = false;
+
+    /**
+     * Maximal length of numbers converted by FastDtoa
+     */
+    public static final int kFastDtoaMaximalLength = FastDtoa.kFastDtoaMaximalLength;
+
+    /**
+     * Create a buffer with the given capacity.
+     * @param capacity the capacity of the buffer.
+     */
+    public DtoaBuffer(final int capacity) {
+        chars = new char[capacity];
+    }
+
+    /**
+     * Append a character to the buffer, increasing its length.
+     * @param c character
+     */
+    void append(final char c) {
+        chars[length++] = c;
+    }
+
+    /**
+     * Clear the buffer contents and set its length to {@code 0}.
+     */
+    public void reset() {
+        length = 0;
+        decimalPoint = 0;
+    }
+
+    /**
+     * Get the raw digits of this buffer as string.
+     * @return the raw buffer contents
+     */
+    public String getRawDigits() {
+        return new String(chars, 0, length);
+    }
+
+    /**
+     * Get the position of the decimal point.
+     * @return the decimal point position
+     */
+    public int getDecimalPoint() {
+        return decimalPoint;
+    }
+
+    /**
+     * Returns the number of characters in the buffer.
+     * @return buffer length
+     */
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * Returns the formatted buffer content as string, using the specified conversion mode
+     * and padding.
+     *
+     * @param mode conversion mode
+     * @param digitsAfterPoint number of digits after point
+     * @return formatted string
+     */
+    public String format(final DtoaMode mode, final int digitsAfterPoint) {
+        final StringBuilder buffer = new StringBuilder();
+        if (isNegative) {
+            buffer.append('-');
+        }
+
+        // check for minus sign
+        switch (mode) {
+            case SHORTEST:
+                if (decimalPoint < -5 || decimalPoint > 21) {
+                    toExponentialFormat(buffer);
+                } else {
+                    toFixedFormat(buffer, digitsAfterPoint);
+                }
+                break;
+            case FIXED:
+                toFixedFormat(buffer, digitsAfterPoint);
+                break;
+            case PRECISION:
+                if (decimalPoint < -5 || decimalPoint > length) {
+                    toExponentialFormat(buffer);
+                } else {
+                    toFixedFormat(buffer, digitsAfterPoint);
+                }
+                break;
+        }
+
+        return buffer.toString();
+    }
+
+    private void toFixedFormat(final StringBuilder buffer, final int digitsAfterPoint) {
+        if (decimalPoint <= 0) {
+            // < 1,
+            buffer.append('0');
+            if (length > 0) {
+                buffer.append('.');
+                final int padding = -decimalPoint;
+                for (int i = 0; i < padding; i++) {
+                    buffer.append('0');
+                }
+                buffer.append(chars, 0, length);
+            }
+        } else if (decimalPoint >= length) {
+            // large integer, add trailing zeroes
+            buffer.append(chars, 0, length);
+            for (int i = length; i < decimalPoint; i++) {
+                buffer.append('0');
+            }
+        } else if (decimalPoint < length) {
+            // >= 1, split decimals and insert decimalPoint
+            buffer.append(chars, 0, decimalPoint);
+            buffer.append('.');
+            buffer.append(chars, decimalPoint, length - decimalPoint);
+        }
+
+        // Create trailing zeros if requested
+        if (digitsAfterPoint > 0) {
+            if (decimalPoint >= length) {
+                buffer.append('.');
+            }
+            for (int i = Math.max(0, length - decimalPoint); i < digitsAfterPoint; i++) {
+                buffer.append('0');
+            }
+        }
+    }
+
+    private void toExponentialFormat(final StringBuilder buffer) {
+        buffer.append(chars[0]);
+        if (length > 1) {
+            // insert decimal decimalPoint if more than one digit was produced
+            buffer.append('.');
+            buffer.append(chars, 1, length - 1);
+        }
+        buffer.append('e');
+        final int exponent = decimalPoint - 1;
+        if (exponent > 0) {
+            buffer.append('+');
+        }
+        buffer.append(exponent);
+    }
+
+    @Override
+    public String toString() {
+        return "[chars:" + new String(chars, 0, length) + ", decimalPoint:" + decimalPoint + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaMode.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+/**
+ * This defines the string conversion modes supported by this package.
+ * The original C++ library also supports SHORTEST-SINGLE for single
+ * precision floats but we don't since we always operate with doubles.
+ */
+public enum DtoaMode {
+    /**
+     * Produce the shortest correct representation.
+     * For example the output of 0.299999999999999988897 is (the less accurate
+     * but correct) 0.3.
+     */
+    SHORTEST,
+    /**
+     * Produce a fixed number of digits after the decimal point.
+     * For instance fixed(0.1, 4) becomes 0.1000
+     * If the input number is big, the output will be big.
+     */
+    FIXED,
+    /**
+     * Fixed number of digits (independent of the decimal point).
+     */
+    PRECISION
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/FastDtoa.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,651 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+// Fast Dtoa implementation supporting shortest and precision modes. Does not
+// work for all numbers so BugnumDtoa is used as fallback.
+class FastDtoa {
+
+    // FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
+    // include the terminating '\0' character.
+    static final int kFastDtoaMaximalLength = 17;
+
+    // The minimal and maximal target exponent define the range of w's binary
+    // exponent, where 'w' is the result of multiplying the input by a cached power
+    // of ten.
+    //
+    // A different range might be chosen on a different platform, to optimize digit
+    // generation, but a smaller range requires more powers of ten to be cached.
+    static final int kMinimalTargetExponent = -60;
+    static final int kMaximalTargetExponent = -32;
+
+
+    // Adjusts the last digit of the generated number, and screens out generated
+    // solutions that may be inaccurate. A solution may be inaccurate if it is
+    // outside the safe interval, or if we cannot prove that it is closer to the
+    // input than a neighboring representation of the same length.
+    //
+    // Input: * buffer containing the digits of too_high / 10^kappa
+    //        * distance_too_high_w == (too_high - w).f() * unit
+    //        * unsafe_interval == (too_high - too_low).f() * unit
+    //        * rest = (too_high - buffer * 10^kappa).f() * unit
+    //        * ten_kappa = 10^kappa * unit
+    //        * unit = the common multiplier
+    // Output: returns true if the buffer is guaranteed to contain the closest
+    //    representable number to the input.
+    //  Modifies the generated digits in the buffer to approach (round towards) w.
+    static boolean roundWeed(final DtoaBuffer buffer,
+                             final long distance_too_high_w,
+                             final long unsafe_interval,
+                             long rest,
+                             final long ten_kappa,
+                             final long unit) {
+        final long small_distance = distance_too_high_w - unit;
+        final long big_distance = distance_too_high_w + unit;
+        // Let w_low  = too_high - big_distance, and
+        //     w_high = too_high - small_distance.
+        // Note: w_low < w < w_high
+        //
+        // The real w (* unit) must lie somewhere inside the interval
+        // ]w_low; w_high[ (often written as "(w_low; w_high)")
+
+        // Basically the buffer currently contains a number in the unsafe interval
+        // ]too_low; too_high[ with too_low < w < too_high
+        //
+        //  too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        //                     ^v 1 unit            ^      ^                 ^      ^
+        //  boundary_high ---------------------     .      .                 .      .
+        //                     ^v 1 unit            .      .                 .      .
+        //   - - - - - - - - - - - - - - - - - - -  +  - - + - - - - - -     .      .
+        //                                          .      .         ^       .      .
+        //                                          .  big_distance  .       .      .
+        //                                          .      .         .       .    rest
+        //                              small_distance     .         .       .      .
+        //                                          v      .         .       .      .
+        //  w_high - - - - - - - - - - - - - - - - - -     .         .       .      .
+        //                     ^v 1 unit                   .         .       .      .
+        //  w ----------------------------------------     .         .       .      .
+        //                     ^v 1 unit                   v         .       .      .
+        //  w_low  - - - - - - - - - - - - - - - - - - - - -         .       .      .
+        //                                                           .       .      v
+        //  buffer --------------------------------------------------+-------+--------
+        //                                                           .       .
+        //                                                  safe_interval    .
+        //                                                           v       .
+        //   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -     .
+        //                     ^v 1 unit                                     .
+        //  boundary_low -------------------------                     unsafe_interval
+        //                     ^v 1 unit                                     v
+        //  too_low  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        //
+        //
+        // Note that the value of buffer could lie anywhere inside the range too_low
+        // to too_high.
+        //
+        // boundary_low, boundary_high and w are approximations of the real boundaries
+        // and v (the input number). They are guaranteed to be precise up to one unit.
+        // In fact the error is guaranteed to be strictly less than one unit.
+        //
+        // Anything that lies outside the unsafe interval is guaranteed not to round
+        // to v when read again.
+        // Anything that lies inside the safe interval is guaranteed to round to v
+        // when read again.
+        // If the number inside the buffer lies inside the unsafe interval but not
+        // inside the safe interval then we simply do not know and bail out (returning
+        // false).
+        //
+        // Similarly we have to take into account the imprecision of 'w' when finding
+        // the closest representation of 'w'. If we have two potential
+        // representations, and one is closer to both w_low and w_high, then we know
+        // it is closer to the actual value v.
+        //
+        // By generating the digits of too_high we got the largest (closest to
+        // too_high) buffer that is still in the unsafe interval. In the case where
+        // w_high < buffer < too_high we try to decrement the buffer.
+        // This way the buffer approaches (rounds towards) w.
+        // There are 3 conditions that stop the decrementation process:
+        //   1) the buffer is already below w_high
+        //   2) decrementing the buffer would make it leave the unsafe interval
+        //   3) decrementing the buffer would yield a number below w_high and farther
+        //      away than the current number. In other words:
+        //              (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
+        // Instead of using the buffer directly we use its distance to too_high.
+        // Conceptually rest ~= too_high - buffer
+        // We need to do the following tests in this order to avoid over- and
+        // underflows.
+        assert (Long.compareUnsigned(rest, unsafe_interval) <= 0);
+        while (Long.compareUnsigned(rest, small_distance) < 0 &&  // Negated condition 1
+                Long.compareUnsigned(unsafe_interval - rest, ten_kappa) >= 0 &&  // Negated condition 2
+                (Long.compareUnsigned(rest + ten_kappa, small_distance) < 0 ||  // buffer{-1} > w_high
+                        Long.compareUnsigned(small_distance - rest, rest + ten_kappa - small_distance) >= 0)) {
+            buffer.chars[buffer.length - 1]--;
+            rest += ten_kappa;
+        }
+
+        // We have approached w+ as much as possible. We now test if approaching w-
+        // would require changing the buffer. If yes, then we have two possible
+        // representations close to w, but we cannot decide which one is closer.
+        if (Long.compareUnsigned(rest, big_distance) < 0 &&
+                Long.compareUnsigned(unsafe_interval - rest, ten_kappa) >= 0 &&
+                (Long.compareUnsigned(rest + ten_kappa, big_distance) < 0 ||
+                        Long.compareUnsigned(big_distance - rest, rest + ten_kappa - big_distance) > 0)) {
+            return false;
+        }
+
+        // Weeding test.
+        //   The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
+        //   Since too_low = too_high - unsafe_interval this is equivalent to
+        //      [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
+        //   Conceptually we have: rest ~= too_high - buffer
+        return Long.compareUnsigned(2 * unit, rest) <= 0 && Long.compareUnsigned(rest, unsafe_interval - 4 * unit) <= 0;
+    }
+
+    // Rounds the buffer upwards if the result is closer to v by possibly adding
+    // 1 to the buffer. If the precision of the calculation is not sufficient to
+    // round correctly, return false.
+    // The rounding might shift the whole buffer in which case the kappa is
+    // adjusted. For example "99", kappa = 3 might become "10", kappa = 4.
+    //
+    // If 2*rest > ten_kappa then the buffer needs to be round up.
+    // rest can have an error of +/- 1 unit. This function accounts for the
+    // imprecision and returns false, if the rounding direction cannot be
+    // unambiguously determined.
+    //
+    // Precondition: rest < ten_kappa.
+    // Changed return type to int to let caller know they should increase kappa (return value 2)
+    static int roundWeedCounted(final char[] buffer,
+                                final int length,
+                                final long rest,
+                                final long  ten_kappa,
+                                final long  unit) {
+        assert(Long.compareUnsigned(rest, ten_kappa) < 0);
+        // The following tests are done in a specific order to avoid overflows. They
+        // will work correctly with any uint64 values of rest < ten_kappa and unit.
+        //
+        // If the unit is too big, then we don't know which way to round. For example
+        // a unit of 50 means that the real number lies within rest +/- 50. If
+        // 10^kappa == 40 then there is no way to tell which way to round.
+        if (Long.compareUnsigned(unit, ten_kappa) >= 0) return 0;
+        // Even if unit is just half the size of 10^kappa we are already completely
+        // lost. (And after the previous test we know that the expression will not
+        // over/underflow.)
+        if (Long.compareUnsigned(ten_kappa - unit, unit) <= 0) return 0;
+        // If 2 * (rest + unit) <= 10^kappa we can safely round down.
+        if (Long.compareUnsigned(ten_kappa - rest, rest) > 0 && Long.compareUnsigned(ten_kappa - 2 * rest, 2 * unit) >= 0) {
+            return 1;
+        }
+        // If 2 * (rest - unit) >= 10^kappa, then we can safely round up.
+        if (Long.compareUnsigned(rest, unit) > 0 && Long.compareUnsigned(ten_kappa - (rest - unit), (rest - unit)) <= 0) {
+            // Increment the last digit recursively until we find a non '9' digit.
+            buffer[length - 1]++;
+            for (int i = length - 1; i > 0; --i) {
+                if (buffer[i] != '0' + 10) break;
+                buffer[i] = '0';
+                buffer[i - 1]++;
+            }
+            // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the
+            // exception of the first digit all digits are now '0'. Simply switch the
+            // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and
+            // the power (the kappa) is increased.
+            if (buffer[0] == '0' + 10) {
+                buffer[0] = '1';
+                // Return value of 2 tells caller to increase (*kappa) += 1
+                return 2;
+            }
+            return 1;
+        }
+        return 0;
+    }
+
+    // Returns the biggest power of ten that is less than or equal to the given
+    // number. We furthermore receive the maximum number of bits 'number' has.
+    //
+    // Returns power == 10^(exponent_plus_one-1) such that
+    //    power <= number < power * 10.
+    // If number_bits == 0 then 0^(0-1) is returned.
+    // The number of bits must be <= 32.
+    // Precondition: number < (1 << (number_bits + 1)).
+
+    // Inspired by the method for finding an integer log base 10 from here:
+    // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+    static final int kSmallPowersOfTen[] =
+    {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+            1000000000};
+
+    // Returns the biggest power of ten that is less than or equal than the given
+    // number. We furthermore receive the maximum number of bits 'number' has.
+    // If number_bits == 0 then 0^-1 is returned
+    // The number of bits must be <= 32.
+    // Precondition: (1 << number_bits) <= number < (1 << (number_bits + 1)).
+    static long biggestPowerTen(final int number,
+                                final int number_bits) {
+        final int power, exponent_plus_one;
+        assert ((number & 0xFFFFFFFFL) < (1l << (number_bits + 1)));
+        // 1233/4096 is approximately 1/lg(10).
+        int exponent_plus_one_guess = ((number_bits + 1) * 1233 >>> 12);
+        // We increment to skip over the first entry in the kPowersOf10 table.
+        // Note: kPowersOf10[i] == 10^(i-1).
+        exponent_plus_one_guess++;
+        // We don't have any guarantees that 2^number_bits <= number.
+        if (number < kSmallPowersOfTen[exponent_plus_one_guess]) {
+            exponent_plus_one_guess--;
+        }
+        power = kSmallPowersOfTen[exponent_plus_one_guess];
+        exponent_plus_one = exponent_plus_one_guess;
+
+        return ((long) power << 32) | (long) exponent_plus_one;
+    }
+
+    // Generates the digits of input number w.
+    // w is a floating-point number (DiyFp), consisting of a significand and an
+    // exponent. Its exponent is bounded by kMinimalTargetExponent and
+    // kMaximalTargetExponent.
+    //       Hence -60 <= w.e() <= -32.
+    //
+    // Returns false if it fails, in which case the generated digits in the buffer
+    // should not be used.
+    // Preconditions:
+    //  * low, w and high are correct up to 1 ulp (unit in the last place). That
+    //    is, their error must be less than a unit of their last digits.
+    //  * low.e() == w.e() == high.e()
+    //  * low < w < high, and taking into account their error: low~ <= high~
+    //  * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+    // Postconditions: returns false if procedure fails.
+    //   otherwise:
+    //     * buffer is not null-terminated, but len contains the number of digits.
+    //     * buffer contains the shortest possible decimal digit-sequence
+    //       such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
+    //       correct values of low and high (without their error).
+    //     * if more than one decimal representation gives the minimal number of
+    //       decimal digits then the one closest to W (where W is the correct value
+    //       of w) is chosen.
+    // Remark: this procedure takes into account the imprecision of its input
+    //   numbers. If the precision is not enough to guarantee all the postconditions
+    //   then false is returned. This usually happens rarely (~0.5%).
+    //
+    // Say, for the sake of example, that
+    //   w.e() == -48, and w.f() == 0x1234567890abcdef
+    // w's value can be computed by w.f() * 2^w.e()
+    // We can obtain w's integral digits by simply shifting w.f() by -w.e().
+    //  -> w's integral part is 0x1234
+    //  w's fractional part is therefore 0x567890abcdef.
+    // Printing w's integral part is easy (simply print 0x1234 in decimal).
+    // In order to print its fraction we repeatedly multiply the fraction by 10 and
+    // get each digit. Example the first digit after the point would be computed by
+    //   (0x567890abcdef * 10) >> 48. -> 3
+    // The whole thing becomes slightly more complicated because we want to stop
+    // once we have enough digits. That is, once the digits inside the buffer
+    // represent 'w' we can stop. Everything inside the interval low - high
+    // represents w. However we have to pay attention to low, high and w's
+    // imprecision.
+    static boolean digitGen(final DiyFp low,
+                            final DiyFp w,
+                            final DiyFp high,
+                            final DtoaBuffer buffer,
+                            final int mk) {
+        assert(low.e() == w.e() && w.e() == high.e());
+        assert Long.compareUnsigned(low.f() + 1, high.f() - 1) <= 0;
+        assert(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+        // low, w and high are imprecise, but by less than one ulp (unit in the last
+        // place).
+        // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
+        // the new numbers are outside of the interval we want the final
+        // representation to lie in.
+        // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
+        // numbers that are certain to lie in the interval. We will use this fact
+        // later on.
+        // We will now start by generating the digits within the uncertain
+        // interval. Later we will weed out representations that lie outside the safe
+        // interval and thus _might_ lie outside the correct interval.
+        long unit = 1;
+        final DiyFp too_low = new DiyFp(low.f() - unit, low.e());
+        final DiyFp too_high = new DiyFp(high.f() + unit, high.e());
+        // too_low and too_high are guaranteed to lie outside the interval we want the
+        // generated number in.
+        final DiyFp unsafe_interval = DiyFp.minus(too_high, too_low);
+        // We now cut the input number into two parts: the integral digits and the
+        // fractionals. We will not write any decimal separator though, but adapt
+        // kappa instead.
+        // Reminder: we are currently computing the digits (stored inside the buffer)
+        // such that:   too_low < buffer * 10^kappa < too_high
+        // We use too_high for the digit_generation and stop as soon as possible.
+        // If we stop early we effectively round down.
+        final DiyFp one = new DiyFp(1l << -w.e(), w.e());
+        // Division by one is a shift.
+        int integrals = (int)(too_high.f() >>> -one.e());
+        // Modulo by one is an and.
+        long fractionals = too_high.f() & (one.f() - 1);
+        int divisor;
+        final int divisor_exponent_plus_one;
+        final long result = biggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.e()));
+        divisor = (int) (result >>> 32);
+        divisor_exponent_plus_one = (int) result;
+        int kappa = divisor_exponent_plus_one;
+        // Loop invariant: buffer = too_high / 10^kappa  (integer division)
+        // The invariant holds for the first iteration: kappa has been initialized
+        // with the divisor exponent + 1. And the divisor is the biggest power of ten
+        // that is smaller than integrals.
+        while (kappa > 0) {
+            final int digit = integrals / divisor;
+            assert (digit <= 9);
+            buffer.append((char) ('0' + digit));
+            integrals %= divisor;
+            kappa--;
+            // Note that kappa now equals the exponent of the divisor and that the
+            // invariant thus holds again.
+            final long rest =
+                    ((long) integrals << -one.e()) + fractionals;
+            // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
+            // Reminder: unsafe_interval.e() == one.e()
+            if (Long.compareUnsigned(rest, unsafe_interval.f()) < 0) {
+                // Rounding down (by not emitting the remaining digits) yields a number
+                // that lies within the unsafe interval.
+                buffer.decimalPoint = buffer.length - mk + kappa;
+                return roundWeed(buffer, DiyFp.minus(too_high, w).f(),
+                        unsafe_interval.f(), rest,
+                        (long) divisor << -one.e(), unit);
+            }
+            divisor /= 10;
+        }
+
+        // The integrals have been generated. We are at the point of the decimal
+        // separator. In the following loop we simply multiply the remaining digits by
+        // 10 and divide by one. We just need to pay attention to multiply associated
+        // data (like the interval or 'unit'), too.
+        // Note that the multiplication by 10 does not overflow, because w.e >= -60
+        // and thus one.e >= -60.
+        assert (one.e() >= -60);
+        assert (fractionals < one.f());
+        assert (Long.compareUnsigned(Long.divideUnsigned(0xFFFFFFFFFFFFFFFFL, 10), one.f()) >= 0);
+        for (;;) {
+            fractionals *= 10;
+            unit *= 10;
+            unsafe_interval.setF(unsafe_interval.f() * 10);
+            // Integer division by one.
+            final int digit = (int) (fractionals >>> -one.e());
+            assert (digit <= 9);
+            buffer.append((char) ('0' + digit));
+            fractionals &= one.f() - 1;  // Modulo by one.
+            kappa--;
+            if (Long.compareUnsigned(fractionals, unsafe_interval.f()) < 0) {
+                buffer.decimalPoint = buffer.length - mk + kappa;
+                return roundWeed(buffer, DiyFp.minus(too_high, w).f() * unit,
+                        unsafe_interval.f(), fractionals, one.f(), unit);
+            }
+        }
+    }
+
+    // Generates (at most) requested_digits digits of input number w.
+    // w is a floating-point number (DiyFp), consisting of a significand and an
+    // exponent. Its exponent is bounded by kMinimalTargetExponent and
+    // kMaximalTargetExponent.
+    //       Hence -60 <= w.e() <= -32.
+    //
+    // Returns false if it fails, in which case the generated digits in the buffer
+    // should not be used.
+    // Preconditions:
+    //  * w is correct up to 1 ulp (unit in the last place). That
+    //    is, its error must be strictly less than a unit of its last digit.
+    //  * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+    //
+    // Postconditions: returns false if procedure fails.
+    //   otherwise:
+    //     * buffer is not null-terminated, but length contains the number of
+    //       digits.
+    //     * the representation in buffer is the most precise representation of
+    //       requested_digits digits.
+    //     * buffer contains at most requested_digits digits of w. If there are less
+    //       than requested_digits digits then some trailing '0's have been removed.
+    //     * kappa is such that
+    //            w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2.
+    //
+    // Remark: This procedure takes into account the imprecision of its input
+    //   numbers. If the precision is not enough to guarantee all the postconditions
+    //   then false is returned. This usually happens rarely, but the failure-rate
+    //   increases with higher requested_digits.
+    static boolean digitGenCounted(final DiyFp w,
+                                   int requested_digits,
+                                   final DtoaBuffer buffer,
+                                   final int mk) {
+        assert (kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+        assert (kMinimalTargetExponent >= -60);
+        assert (kMaximalTargetExponent <= -32);
+        // w is assumed to have an error less than 1 unit. Whenever w is scaled we
+        // also scale its error.
+        long w_error = 1;
+        // We cut the input number into two parts: the integral digits and the
+        // fractional digits. We don't emit any decimal separator, but adapt kappa
+        // instead. Example: instead of writing "1.2" we put "12" into the buffer and
+        // increase kappa by 1.
+        final DiyFp one = new DiyFp(1l << -w.e(), w.e());
+        // Division by one is a shift.
+        int integrals = (int) (w.f() >>> -one.e());
+        // Modulo by one is an and.
+        long fractionals = w.f() & (one.f() - 1);
+        int divisor;
+        final int divisor_exponent_plus_one;
+        final long biggestPower = biggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.e()));
+        divisor = (int) (biggestPower >>> 32);
+        divisor_exponent_plus_one = (int) biggestPower;
+        int kappa = divisor_exponent_plus_one;
+
+        // Loop invariant: buffer = w / 10^kappa  (integer division)
+        // The invariant holds for the first iteration: kappa has been initialized
+        // with the divisor exponent + 1. And the divisor is the biggest power of ten
+        // that is smaller than 'integrals'.
+        while (kappa > 0) {
+            final int digit = integrals / divisor;
+            assert (digit <= 9);
+            buffer.append((char) ('0' + digit));
+            requested_digits--;
+            integrals %= divisor;
+            kappa--;
+            // Note that kappa now equals the exponent of the divisor and that the
+            // invariant thus holds again.
+            if (requested_digits == 0) break;
+            divisor /= 10;
+        }
+
+        if (requested_digits == 0) {
+            final long rest =
+                    ((long) (integrals) << -one.e()) + fractionals;
+            final int result = roundWeedCounted(buffer.chars, buffer.length, rest,
+                    (long) divisor << -one.e(), w_error);
+            buffer.decimalPoint = buffer.length - mk + kappa + (result == 2 ? 1 : 0);
+            return result > 0;
+        }
+
+        // The integrals have been generated. We are at the decimalPoint of the decimal
+        // separator. In the following loop we simply multiply the remaining digits by
+        // 10 and divide by one. We just need to pay attention to multiply associated
+        // data (the 'unit'), too.
+        // Note that the multiplication by 10 does not overflow, because w.e >= -60
+        // and thus one.e >= -60.
+        assert (one.e() >= -60);
+        assert (fractionals < one.f());
+        assert (Long.compareUnsigned(Long.divideUnsigned(0xFFFFFFFFFFFFFFFFL, 10), one.f()) >= 0);
+        while (requested_digits > 0 && fractionals > w_error) {
+            fractionals *= 10;
+            w_error *= 10;
+            // Integer division by one.
+            final int digit = (int) (fractionals >>> -one.e());
+            assert (digit <= 9);
+            buffer.append((char) ('0' + digit));
+            requested_digits--;
+            fractionals &= one.f() - 1;  // Modulo by one.
+            kappa--;
+        }
+        if (requested_digits != 0) return false;
+        final int result = roundWeedCounted(buffer.chars, buffer.length, fractionals, one.f(), w_error);
+        buffer.decimalPoint = buffer.length - mk + kappa + (result == 2 ? 1 : 0);
+        return result > 0;
+    }
+
+
+    // Provides a decimal representation of v.
+    // Returns true if it succeeds, otherwise the result cannot be trusted.
+    // There will be *length digits inside the buffer (not null-terminated).
+    // If the function returns true then
+    //        v == (double) (buffer * 10^decimal_exponent).
+    // The digits in the buffer are the shortest representation possible: no
+    // 0.09999999999999999 instead of 0.1. The shorter representation will even be
+    // chosen even if the longer one would be closer to v.
+    // The last digit will be closest to the actual v. That is, even if several
+    // digits might correctly yield 'v' when read again, the closest will be
+    // computed.
+    static boolean grisu3(final double v, final DtoaBuffer buffer) {
+        final long d64 = IeeeDouble.doubleToLong(v);
+        final DiyFp w = IeeeDouble.asNormalizedDiyFp(d64);
+        // boundary_minus and boundary_plus are the boundaries between v and its
+        // closest floating-point neighbors. Any number strictly between
+        // boundary_minus and boundary_plus will round to v when convert to a double.
+        // Grisu3 will never output representations that lie exactly on a boundary.
+        final DiyFp boundary_minus = new DiyFp(), boundary_plus = new DiyFp();
+        IeeeDouble.normalizedBoundaries(d64, boundary_minus, boundary_plus);
+        assert(boundary_plus.e() == w.e());
+        final DiyFp ten_mk = new DiyFp();  // Cached power of ten: 10^-k
+        final int mk;                      // -k
+        final int ten_mk_minimal_binary_exponent =
+                kMinimalTargetExponent - (w.e() + DiyFp.kSignificandSize);
+        final int ten_mk_maximal_binary_exponent =
+                kMaximalTargetExponent - (w.e() + DiyFp.kSignificandSize);
+        mk = CachedPowers.getCachedPowerForBinaryExponentRange(
+                ten_mk_minimal_binary_exponent,
+                ten_mk_maximal_binary_exponent,
+           ten_mk);
+        assert(kMinimalTargetExponent <= w.e() + ten_mk.e() +
+                DiyFp.kSignificandSize &&
+                kMaximalTargetExponent >= w.e() + ten_mk.e() +
+                        DiyFp.kSignificandSize);
+        // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+        // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+        // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+        // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+        // off by a small amount.
+        // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+        // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+        //           (f-1) * 2^e < w*10^k < (f+1) * 2^e
+        final DiyFp scaled_w = DiyFp.times(w, ten_mk);
+        assert(scaled_w.e() ==
+                boundary_plus.e() + ten_mk.e() + DiyFp.kSignificandSize);
+        // In theory it would be possible to avoid some recomputations by computing
+        // the difference between w and boundary_minus/plus (a power of 2) and to
+        // compute scaled_boundary_minus/plus by subtracting/adding from
+        // scaled_w. However the code becomes much less readable and the speed
+        // enhancements are not terriffic.
+        final DiyFp scaled_boundary_minus = DiyFp.times(boundary_minus, ten_mk);
+        final DiyFp scaled_boundary_plus  = DiyFp.times(boundary_plus,  ten_mk);
+
+        // DigitGen will generate the digits of scaled_w. Therefore we have
+        // v == (double) (scaled_w * 10^-mk).
+        // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
+        // integer than it will be updated. For instance if scaled_w == 1.23 then
+        // the buffer will be filled with "123" und the decimal_exponent will be
+        // decreased by 2.
+        final boolean result = digitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
+                buffer, mk);
+        return result;
+    }
+
+    // The "counted" version of grisu3 (see above) only generates requested_digits
+    // number of digits. This version does not generate the shortest representation,
+    // and with enough requested digits 0.1 will at some point print as 0.9999999...
+    // Grisu3 is too imprecise for real halfway cases (1.5 will not work) and
+    // therefore the rounding strategy for halfway cases is irrelevant.
+    static boolean grisu3Counted(final double v,
+                                 final int requested_digits,
+                                 final DtoaBuffer buffer) {
+        final long d64 = IeeeDouble.doubleToLong(v);
+        final DiyFp w = IeeeDouble.asNormalizedDiyFp(d64);
+        final DiyFp ten_mk = new DiyFp();  // Cached power of ten: 10^-k
+        final int mk;                      // -k
+        final int ten_mk_minimal_binary_exponent =
+                kMinimalTargetExponent - (w.e() + DiyFp.kSignificandSize);
+        final int ten_mk_maximal_binary_exponent =
+                kMaximalTargetExponent - (w.e() + DiyFp.kSignificandSize);
+        mk = CachedPowers.getCachedPowerForBinaryExponentRange(
+                ten_mk_minimal_binary_exponent,
+                ten_mk_maximal_binary_exponent,
+                ten_mk);
+        assert ((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+                DiyFp.kSignificandSize) &&
+                (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+                        DiyFp.kSignificandSize));
+        // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+        // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+        // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+        // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+        // off by a small amount.
+        // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+        // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+        //           (f-1) * 2^e < w*10^k < (f+1) * 2^e
+        final DiyFp scaled_w = DiyFp.times(w, ten_mk);
+
+        // We now have (double) (scaled_w * 10^-mk).
+        // DigitGen will generate the first requested_digits digits of scaled_w and
+        // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
+        // will not always be exactly the same since DigitGenCounted only produces a
+        // limited number of digits.)
+        final boolean result = digitGenCounted(scaled_w, requested_digits,
+                buffer, mk);
+        return result;
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/FixedDtoa.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,425 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+class FixedDtoa {
+
+    // Represents a 128bit type. This class should be replaced by a native type on
+    // platforms that support 128bit integers.
+    static class UInt128 {
+
+        private static final long kMask32 = 0xFFFFFFFFL;
+        // Value == (high_bits_ << 64) + low_bits_
+        private long high_bits_;
+        private long low_bits_;
+
+        UInt128(final long high_bits, final long low_bits) {
+            this.high_bits_ = high_bits;
+            this.low_bits_ = low_bits;
+        }
+
+        void multiply(final int multiplicand) {
+            long accumulator;
+
+            accumulator = (low_bits_ & kMask32) * multiplicand;
+            long part = accumulator & kMask32;
+            accumulator >>>= 32;
+            accumulator = accumulator + (low_bits_ >>> 32) * multiplicand;
+            low_bits_ = (accumulator << 32) + part;
+            accumulator >>>= 32;
+            accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
+            part = accumulator & kMask32;
+            accumulator >>>= 32;
+            accumulator = accumulator + (high_bits_ >>> 32) * multiplicand;
+            high_bits_ = (accumulator << 32) + part;
+            assert ((accumulator >>> 32) == 0);
+        }
+
+        void shift(final int shift_amount) {
+            assert (-64 <= shift_amount && shift_amount <= 64);
+            if (shift_amount == 0) {
+                return;
+            } else if (shift_amount == -64) {
+                high_bits_ = low_bits_;
+                low_bits_ = 0;
+            } else if (shift_amount == 64) {
+                low_bits_ = high_bits_;
+                high_bits_ = 0;
+            } else if (shift_amount <= 0) {
+                high_bits_ <<= -shift_amount;
+                high_bits_ += low_bits_ >>> (64 + shift_amount);
+                low_bits_ <<= -shift_amount;
+            } else {
+                low_bits_ >>>= shift_amount;
+                low_bits_ += high_bits_ << (64 - shift_amount);
+                high_bits_ >>>= shift_amount;
+            }
+        }
+
+        // Modifies *this to *this MOD (2^power).
+        // Returns *this DIV (2^power).
+        int divModPowerOf2(final int power) {
+            if (power >= 64) {
+                final int result = (int) (high_bits_ >>> (power - 64));
+                high_bits_ -= (long) (result) << (power - 64);
+                return result;
+            } else {
+                final long part_low = low_bits_ >>> power;
+                final long part_high = high_bits_ << (64 - power);
+                final int result = (int) (part_low + part_high);
+                high_bits_ = 0;
+                low_bits_ -= part_low << power;
+                return result;
+            }
+        }
+
+        boolean isZero() {
+            return high_bits_ == 0 && low_bits_ == 0;
+        }
+
+        int bitAt(final int position) {
+            if (position >= 64) {
+                return (int) (high_bits_ >>> (position - 64)) & 1;
+            } else {
+                return (int) (low_bits_ >>> position) & 1;
+            }
+        }
+
+    };
+
+
+    static final  int kDoubleSignificandSize = 53;  // Includes the hidden bit.
+
+
+    static void fillDigits32FixedLength(int number, final int requested_length,
+                                        final DtoaBuffer buffer) {
+        for (int i = requested_length - 1; i >= 0; --i) {
+            buffer.chars[buffer.length + i] = (char) ('0' + Integer.remainderUnsigned(number, 10));
+            number = Integer.divideUnsigned(number, 10);
+        }
+        buffer.length += requested_length;
+    }
+
+
+    static void fillDigits32(int number, final DtoaBuffer buffer) {
+        int number_length = 0;
+        // We fill the digits in reverse order and exchange them afterwards.
+        while (number != 0) {
+            final int digit = Integer.remainderUnsigned(number, 10);
+            number = Integer.divideUnsigned(number, 10);
+            buffer.chars[buffer.length + number_length] = (char) ('0' + digit);
+            number_length++;
+        }
+        // Exchange the digits.
+        int i = buffer.length;
+        int j = buffer.length + number_length - 1;
+        while (i < j) {
+            final char tmp = buffer.chars[i];
+            buffer.chars[i] = buffer.chars[j];
+            buffer.chars[j] = tmp;
+            i++;
+            j--;
+        }
+        buffer.length += number_length;
+    }
+
+
+    static void fillDigits64FixedLength(long number, final DtoaBuffer buffer) {
+        final int kTen7 = 10000000;
+        // For efficiency cut the number into 3 uint32_t parts, and print those.
+        final int part2 = (int) Long.remainderUnsigned(number, kTen7);
+        number = Long.divideUnsigned(number, kTen7);
+        final int part1 = (int) Long.remainderUnsigned(number, kTen7);
+        final int part0 = (int) Long.divideUnsigned(number, kTen7);
+
+        fillDigits32FixedLength(part0, 3, buffer);
+        fillDigits32FixedLength(part1, 7, buffer);
+        fillDigits32FixedLength(part2, 7, buffer);
+    }
+
+
+    static void FillDigits64(long number, final DtoaBuffer buffer) {
+        final int kTen7 = 10000000;
+        // For efficiency cut the number into 3 uint32_t parts, and print those.
+        final int part2 = (int) Long.remainderUnsigned(number, kTen7);
+        number = Long.divideUnsigned(number, kTen7);
+        final int part1 = (int) Long.remainderUnsigned(number, kTen7);
+        final int part0 = (int) Long.divideUnsigned(number, kTen7);
+
+        if (part0 != 0) {
+            fillDigits32(part0, buffer);
+            fillDigits32FixedLength(part1, 7, buffer);
+            fillDigits32FixedLength(part2, 7, buffer);
+        } else if (part1 != 0) {
+            fillDigits32(part1, buffer);
+            fillDigits32FixedLength(part2, 7, buffer);
+        } else {
+            fillDigits32(part2, buffer);
+        }
+    }
+
+
+    static void roundUp(final DtoaBuffer buffer) {
+        // An empty buffer represents 0.
+        if (buffer.length == 0) {
+            buffer.chars[0] = '1';
+            buffer.decimalPoint = 1;
+            buffer.length = 1;
+            return;
+        }
+        // Round the last digit until we either have a digit that was not '9' or until
+        // we reached the first digit.
+        buffer.chars[buffer.length - 1]++;
+        for (int i = buffer.length - 1; i > 0; --i) {
+            if (buffer.chars[i] != '0' + 10) {
+                return;
+            }
+            buffer.chars[i] = '0';
+            buffer.chars[i - 1]++;
+        }
+        // If the first digit is now '0' + 10, we would need to set it to '0' and add
+        // a '1' in front. However we reach the first digit only if all following
+        // digits had been '9' before rounding up. Now all trailing digits are '0' and
+        // we simply switch the first digit to '1' and update the decimal-point
+        // (indicating that the point is now one digit to the right).
+        if (buffer.chars[0] == '0' + 10) {
+            buffer.chars[0] = '1';
+            buffer.decimalPoint++;
+        }
+    }
+
+
+    // The given fractionals number represents a fixed-point number with binary
+    // point at bit (-exponent).
+    // Preconditions:
+    //   -128 <= exponent <= 0.
+    //   0 <= fractionals * 2^exponent < 1
+    //   The buffer holds the result.
+    // The function will round its result. During the rounding-process digits not
+    // generated by this function might be updated, and the decimal-point variable
+    // might be updated. If this function generates the digits 99 and the buffer
+    // already contained "199" (thus yielding a buffer of "19999") then a
+    // rounding-up will change the contents of the buffer to "20000".
+    static void fillFractionals(long fractionals, final int exponent,
+                                final int fractional_count, final DtoaBuffer buffer) {
+        assert (-128 <= exponent && exponent <= 0);
+        // 'fractionals' is a fixed-decimalPoint number, with binary decimalPoint at bit
+        // (-exponent). Inside the function the non-converted remainder of fractionals
+        // is a fixed-decimalPoint number, with binary decimalPoint at bit 'decimalPoint'.
+        if (-exponent <= 64) {
+            // One 64 bit number is sufficient.
+            assert (fractionals >>> 56 == 0);
+            int point = -exponent;
+            for (int i = 0; i < fractional_count; ++i) {
+                if (fractionals == 0) break;
+                // Instead of multiplying by 10 we multiply by 5 and adjust the point
+                // location. This way the fractionals variable will not overflow.
+                // Invariant at the beginning of the loop: fractionals < 2^point.
+                // Initially we have: point <= 64 and fractionals < 2^56
+                // After each iteration the point is decremented by one.
+                // Note that 5^3 = 125 < 128 = 2^7.
+                // Therefore three iterations of this loop will not overflow fractionals
+                // (even without the subtraction at the end of the loop body). At this
+                // time point will satisfy point <= 61 and therefore fractionals < 2^point
+                // and any further multiplication of fractionals by 5 will not overflow.
+                fractionals *= 5;
+                point--;
+                final int digit = (int) (fractionals >>> point);
+                assert (digit <= 9);
+                buffer.chars[buffer.length] = (char) ('0' + digit);
+                buffer.length++;
+                fractionals -= (long) (digit) << point;
+            }
+            // If the first bit after the point is set we have to round up.
+            if (((fractionals >>> (point - 1)) & 1) == 1) {
+                roundUp(buffer);
+            }
+        } else {  // We need 128 bits.
+            assert (64 < -exponent && -exponent <= 128);
+            final UInt128 fractionals128 = new UInt128(fractionals, 0);
+            fractionals128.shift(-exponent - 64);
+            int point = 128;
+            for (int i = 0; i < fractional_count; ++i) {
+                if (fractionals128.isZero()) break;
+                // As before: instead of multiplying by 10 we multiply by 5 and adjust the
+                // point location.
+                // This multiplication will not overflow for the same reasons as before.
+                fractionals128.multiply(5);
+                point--;
+                final int digit = fractionals128.divModPowerOf2(point);
+                assert (digit <= 9);
+                buffer.chars[buffer.length] = (char) ('0' + digit);
+                buffer.length++;
+            }
+            if (fractionals128.bitAt(point - 1) == 1) {
+                roundUp(buffer);
+            }
+        }
+    }
+
+
+    // Removes leading and trailing zeros.
+    // If leading zeros are removed then the decimal point position is adjusted.
+    static void trimZeros(final DtoaBuffer buffer) {
+        while (buffer.length > 0 && buffer.chars[buffer.length - 1] == '0') {
+            buffer.length--;
+        }
+        int first_non_zero = 0;
+        while (first_non_zero < buffer.length && buffer.chars[first_non_zero] == '0') {
+            first_non_zero++;
+        }
+        if (first_non_zero != 0) {
+            for (int i = first_non_zero; i < buffer.length; ++i) {
+                buffer.chars[i - first_non_zero] = buffer.chars[i];
+            }
+            buffer.length -= first_non_zero;
+            buffer.decimalPoint -= first_non_zero;
+        }
+    }
+
+
+    static boolean fastFixedDtoa(final double v,
+                                 final int fractional_count,
+                                 final DtoaBuffer buffer) {
+        final long kMaxUInt32 = 0xFFFFFFFFL;
+        final long l = IeeeDouble.doubleToLong(v);
+        long significand = IeeeDouble.significand(l);
+        final int exponent = IeeeDouble.exponent(l);
+        // v = significand * 2^exponent (with significand a 53bit integer).
+        // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
+        // don't know how to compute the representation. 2^73 ~= 9.5*10^21.
+        // If necessary this limit could probably be increased, but we don't need
+        // more.
+        if (exponent > 20) return false;
+        if (fractional_count > 20) return false;
+        // At most kDoubleSignificandSize bits of the significand are non-zero.
+        // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
+        // bits:  0..11*..0xxx..53*..xx
+        if (exponent + kDoubleSignificandSize > 64) {
+            // The exponent must be > 11.
+            //
+            // We know that v = significand * 2^exponent.
+            // And the exponent > 11.
+            // We simplify the task by dividing v by 10^17.
+            // The quotient delivers the first digits, and the remainder fits into a 64
+            // bit number.
+            // Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
+            final long kFive17 = 0xB1A2BC2EC5L;  // 5^17
+            long divisor = kFive17;
+            final int divisor_power = 17;
+            long dividend = significand;
+            final int quotient;
+            final long remainder;
+            // Let v = f * 2^e with f == significand and e == exponent.
+            // Then need q (quotient) and r (remainder) as follows:
+            //   v            = q * 10^17       + r
+            //   f * 2^e      = q * 10^17       + r
+            //   f * 2^e      = q * 5^17 * 2^17 + r
+            // If e > 17 then
+            //   f * 2^(e-17) = q * 5^17        + r/2^17
+            // else
+            //   f  = q * 5^17 * 2^(17-e) + r/2^e
+            if (exponent > divisor_power) {
+                // We only allow exponents of up to 20 and therefore (17 - e) <= 3
+                dividend <<= exponent - divisor_power;
+                quotient = (int) Long.divideUnsigned(dividend, divisor);
+                remainder = Long.remainderUnsigned(dividend, divisor) << divisor_power;
+            } else {
+                divisor <<= divisor_power - exponent;
+                quotient = (int) Long.divideUnsigned(dividend, divisor);
+                remainder = Long.remainderUnsigned(dividend, divisor) << exponent;
+            }
+            fillDigits32(quotient, buffer);
+            fillDigits64FixedLength(remainder, buffer);
+            buffer.decimalPoint = buffer.length;
+        } else if (exponent >= 0) {
+            // 0 <= exponent <= 11
+            significand <<= exponent;
+            FillDigits64(significand, buffer);
+            buffer.decimalPoint = buffer.length;
+        } else if (exponent > -kDoubleSignificandSize) {
+            // We have to cut the number.
+            final long integrals = significand >>> -exponent;
+            final long fractionals = significand - (integrals << -exponent);
+            if (Long.compareUnsigned(integrals, kMaxUInt32) > 0) {
+                FillDigits64(integrals, buffer);
+            } else {
+                fillDigits32((int) (integrals), buffer);
+            }
+            buffer.decimalPoint = buffer.length;
+            fillFractionals(fractionals, exponent, fractional_count, buffer);
+        } else if (exponent < -128) {
+            // This configuration (with at most 20 digits) means that all digits must be
+            // 0.
+            assert (fractional_count <= 20);
+            buffer.reset();
+            buffer.decimalPoint = -fractional_count;
+        } else {
+            buffer.decimalPoint = 0;
+            fillFractionals(significand, exponent, fractional_count, buffer);
+        }
+        trimZeros(buffer);
+        if (buffer.length == 0) {
+            // The string is empty and the decimal_point thus has no importance. Mimick
+            // Gay's dtoa and and set it to -fractional_count.
+            buffer.decimalPoint = -fractional_count;
+        }
+        return true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/IeeeDouble.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv;
+
+// Helper functions for doubles.
+class IeeeDouble {
+
+    // We assume that doubles and long have the same endianness.
+    static long doubleToLong(final double d)   { return Double.doubleToRawLongBits(d); }
+    static double longToDouble(final long d64) { return Double.longBitsToDouble(d64); }
+
+    static final long kSignMask = 0x8000000000000000L;
+    static final long kExponentMask = 0x7FF0000000000000L;
+    static final long kSignificandMask = 0x000FFFFFFFFFFFFFL;
+    static final long kHiddenBit = 0x0010000000000000L;
+    static final int kPhysicalSignificandSize = 52;  // Excludes the hidden bit.
+    static final int kSignificandSize = 53;
+
+    static private final int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+    static private final int kDenormalExponent = -kExponentBias + 1;
+    static private final int kMaxExponent = 0x7FF - kExponentBias;
+    static private final long kInfinity = 0x7FF0000000000000L;
+    static private final long kNaN = 0x7FF8000000000000L;
+
+    static DiyFp asDiyFp(final long d64) {
+        assert (!isSpecial(d64));
+        return new DiyFp(significand(d64), exponent(d64));
+    }
+
+    // The value encoded by this Double must be strictly greater than 0.
+    static DiyFp asNormalizedDiyFp(final long d64) {
+        assert (value(d64) > 0.0);
+        long f = significand(d64);
+        int e = exponent(d64);
+
+        // The current double could be a denormal.
+        while ((f & kHiddenBit) == 0) {
+            f <<= 1;
+            e--;
+        }
+        // Do the final shifts in one go.
+        f <<= DiyFp.kSignificandSize - kSignificandSize;
+        e -= DiyFp.kSignificandSize - kSignificandSize;
+
+        return new DiyFp(f, e);
+    }
+
+    // Returns the next greater double. Returns +infinity on input +infinity.
+    static double nextDouble(final long d64) {
+        if (d64 == kInfinity) return longToDouble(kInfinity);
+        if (sign(d64) < 0 && significand(d64) == 0) {
+            // -0.0
+            return 0.0;
+        }
+        if (sign(d64) < 0) {
+            return longToDouble(d64 - 1);
+        } else {
+            return longToDouble(d64 + 1);
+        }
+    }
+
+    static double previousDouble(final long d64) {
+        if (d64 == (kInfinity | kSignMask)) return -longToDouble(kInfinity);
+        if (sign(d64) < 0) {
+            return longToDouble(d64 + 1);
+        } else {
+            if (significand(d64) == 0) return -0.0;
+            return longToDouble(d64 - 1);
+        }
+    }
+
+    static int exponent(final long d64) {
+        if (isDenormal(d64)) return kDenormalExponent;
+
+        final int biased_e = (int) ((d64 & kExponentMask) >>> kPhysicalSignificandSize);
+        return biased_e - kExponentBias;
+    }
+
+    static long significand(final long d64) {
+        final long significand = d64 & kSignificandMask;
+        if (!isDenormal(d64)) {
+            return significand + kHiddenBit;
+        } else {
+            return significand;
+        }
+    }
+
+    // Returns true if the double is a denormal.
+    static boolean isDenormal(final long d64) {
+        return (d64 & kExponentMask) == 0L;
+    }
+
+    // We consider denormals not to be special.
+    // Hence only Infinity and NaN are special.
+    static boolean isSpecial(final long d64) {
+        return (d64 & kExponentMask) == kExponentMask;
+    }
+
+    static boolean isNaN(final long d64) {
+        return ((d64 & kExponentMask) == kExponentMask) &&
+                ((d64 & kSignificandMask) != 0L);
+    }
+
+
+    static boolean isInfinite(final long d64) {
+        return ((d64 & kExponentMask) == kExponentMask) &&
+                ((d64 & kSignificandMask) == 0L);
+    }
+
+
+    static int sign(final long d64) {
+        return (d64 & kSignMask) == 0L ? 1 : -1;
+    }
+
+
+    // Computes the two boundaries of this.
+    // The bigger boundary (m_plus) is normalized. The lower boundary has the same
+    // exponent as m_plus.
+    // Precondition: the value encoded by this Double must be greater than 0.
+    static void normalizedBoundaries(final long d64, final DiyFp m_minus, final DiyFp m_plus) {
+        assert (value(d64) > 0.0);
+        final DiyFp v = asDiyFp(d64);
+        m_plus.setF((v.f() << 1) + 1);
+        m_plus.setE(v.e() - 1);
+        m_plus.normalize();
+        if (lowerBoundaryIsCloser(d64)) {
+            m_minus.setF((v.f() << 2) - 1);
+            m_minus.setE(v.e() - 2);
+        } else {
+            m_minus.setF((v.f() << 1) - 1);
+            m_minus.setE(v.e() - 1);
+        }
+        m_minus.setF(m_minus.f() << (m_minus.e() - m_plus.e()));
+        m_minus.setE(m_plus.e());
+    }
+
+    static boolean lowerBoundaryIsCloser(final long d64) {
+        // The boundary is closer if the significand is of the form f == 2^p-1 then
+        // the lower boundary is closer.
+        // Think of v = 1000e10 and v- = 9999e9.
+        // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
+        // at a distance of 1e8.
+        // The only exception is for the smallest normal: the largest denormal is
+        // at the same distance as its successor.
+        // Note: denormals have the same exponent as the smallest normals.
+        final boolean physical_significand_is_zero = ((d64 & kSignificandMask) == 0);
+        return physical_significand_is_zero && (exponent(d64) != kDenormalExponent);
+    }
+
+    static double value(final long d64) {
+        return longToDouble(d64);
+    }
+
+    // Returns the significand size for a given order of magnitude.
+    // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
+    // This function returns the number of significant binary digits v will have
+    // once it's encoded into a double. In almost all cases this is equal to
+    // kSignificandSize. The only exceptions are denormals. They start with
+    // leading zeroes and their effective significand-size is hence smaller.
+    static int significandSizeForOrderOfMagnitude(final int order) {
+        if (order >= (kDenormalExponent + kSignificandSize)) {
+            return kSignificandSize;
+        }
+        if (order <= kDenormalExponent) return 0;
+        return order - kDenormalExponent;
+    }
+
+    static double Infinity() {
+        return longToDouble(kInfinity);
+    }
+
+    static double NaN() {
+        return longToDouble(kNaN);
+    }
+
+}
+
+
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Thu Nov 12 14:14:37 2015 -0800
@@ -49,12 +49,12 @@
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * This class houses bootstrap method for invokedynamic instructions generated by compiler.
@@ -67,28 +67,19 @@
 
     private static final MethodHandle VOID_TO_OBJECT = MH.constant(Object.class, ScriptRuntime.UNDEFINED);
 
-    /**
-     * The default dynalink relink threshold for megamorphism is 8. In the case
-     * of object fields only, it is fine. However, with dual fields, in order to get
-     * performance on benchmarks with a lot of object instantiation and then field
-     * reassignment, it can take slightly more relinks to become stable with type
-     * changes swapping out an entire property map and making a map guard fail.
-     * Since we need to set this value statically it must work with possibly changing
-     * optimistic types and dual fields settings. A higher value does not seem to have
-     * any other negative performance implication when running with object-only fields,
-     * so we choose a higher value here.
-     *
-     * See for example octane.gbemu, run with --log=fields:warning to study
-     * megamorphic behavior
-     */
-    private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 16;
-
     // do not create me!!
     private Bootstrap() {
     }
 
-    private static final DynamicLinker dynamicLinker;
-    static {
+    /**
+     * Creates a Nashorn dynamic linker with the given app class loader.
+     * @param appLoader the app class loader. It will be used to discover
+     * additional language runtime linkers (if any).
+     * @param unstableRelinkThreshold the unstable relink threshold
+     * @return a newly created dynamic linker.
+     */
+    public static DynamicLinker createDynamicLinker(final ClassLoader appLoader,
+            final int unstableRelinkThreshold) {
         final DynamicLinkerFactory factory = new DynamicLinkerFactory();
         final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker();
         factory.setPrioritizedLinkers(
@@ -116,15 +107,11 @@
             }
         });
         factory.setInternalObjectsFilter(NashornBeansLinker.createHiddenObjectFilter());
-        final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
-        if (relinkThreshold > -1) {
-            factory.setUnstableRelinkThreshold(relinkThreshold);
-        }
+        factory.setUnstableRelinkThreshold(unstableRelinkThreshold);
 
         // Linkers for any additional language runtimes deployed alongside Nashorn will be picked up by the factory.
-        factory.setClassLoader(Bootstrap.class.getClassLoader());
-
-        dynamicLinker = factory.createLinker();
+        factory.setClassLoader(appLoader);
+        return factory.createLinker();
     }
 
     /**
@@ -202,7 +189,7 @@
      * @return CallSite with MethodHandle to appropriate method or null if not found.
      */
     public static CallSite bootstrap(final Lookup lookup, final String opDesc, final MethodType type, final int flags) {
-        return dynamicLinker.link(LinkerCallSite.newLinkerCallSite(lookup, opDesc, type, flags));
+        return Context.getDynamicLinker(lookup.lookupClass()).link(LinkerCallSite.newLinkerCallSite(lookup, opDesc, type, flags));
     }
 
     /**
@@ -461,7 +448,7 @@
      * @return Nashorn's internal dynamic linker's services object.
      */
     public static LinkerServices getLinkerServices() {
-        return dynamicLinker.getLinkerServices();
+        return Context.getDynamicLinker().getLinkerServices();
     }
 
     /**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Thu Nov 12 14:14:37 2015 -0800
@@ -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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Options.properties	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Options.properties	Thu Nov 12 14:14:37 2015 -0800
@@ -389,6 +389,18 @@
     desc="Use VM anonymous classes for compiled scripts." \
 }
 
+nashorn.option.unstable.relink.threshold ={                   \
+    name="--unstable-relink-threshold",                       \
+    short_name="-urt",                                        \
+    desc="Number of times a dynamic call site has to be \
+    relinked before it is considered unstable, when the \
+    runtime will try to link it as if it is megamorphic.",    \
+    is_undocumented=true,                                     \
+    type=Integer,                                             \
+    default=-1                                                \
+}
+
+
 nashorn.option.verify.code = {              \
     name="--verify-code",                   \
     is_undocumented=true,                   \
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java	Thu Nov 12 14:14:37 2015 -0800
@@ -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;
+    }
 }
--- a/test/script/basic/JDK-8011578.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/JDK-8011578.js	Thu Nov 12 14:14:37 2015 -0800
@@ -25,7 +25,7 @@
  * JDK-8011578 : -Dnashorn.unstable.relink.threshold=1 causes tests to fail.
  *
  * @test
- * @option -Dnashorn.unstable.relink.threshold=1
+ * @option --unstable-relink-threshold=1
  * @run
  */
 
--- a/test/script/basic/JDK-8044750.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/JDK-8044750.js	Thu Nov 12 14:14:37 2015 -0800
@@ -25,8 +25,7 @@
  * JDK-8044750: megamorphic getter for scope objects does not call __noSuchProperty__ hook
  *
  * @test
- * @fork
- * @option -Dnashorn.unstable.relink.threshold=16
+ * @option --unstable-relink-threshold=16
  * @run
  */
 
--- a/test/script/basic/JDK-8047369.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/JDK-8047369.js	Thu Nov 12 14:14:37 2015 -0800
@@ -167,8 +167,8 @@
 CODE, "set \":\"(a) {}");
 
 // bug JDK-8047366
-// evalExpectValue("(1000000000000000128).toString()", "1000000000000000100");
-// evalExpectValue("(1000000000000000128).toFixed().toString()", "1000000000000000128");
+evalExpectValue("(1000000000000000128).toString()", "1000000000000000100");
+evalExpectValue("(1000000000000000128).toFixed().toString()", "1000000000000000128");
 
 try {
     Function("-", {
--- a/test/script/basic/JDK-8062141.js.EXPECTED	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/JDK-8062141.js.EXPECTED	Thu Nov 12 14:14:37 2015 -0800
@@ -114,7 +114,7 @@
 {"a":truer}
          ^
 [1,2,3]
-[9223372036854773800,9223372036854774800,9223372036854776000]
+[9223372036854774000,9223372036854775000,9223372036854776000]
 [1.1,1.2,1.3]
 [1,1.2,9223372036854776000,null,true]
 {"a":"string","b":1,"c":1.2,"d":9223372036854776000,"e":null,"f":true}
--- a/test/script/basic/JDK-8136544.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/JDK-8136544.js	Thu Nov 12 14:14:37 2015 -0800
@@ -25,8 +25,7 @@
  * JDK-8136544: Call site switching to megamorphic causes incorrect property read
  *
  * @test
- * @fork
- * @option -Dnashorn.unstable.relink.threshold=8
+ * @option --unstable-relink-threshold=8
  * @run
  */
 
--- a/test/script/basic/JDK-8136694.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/JDK-8136694.js	Thu Nov 12 14:14:37 2015 -0800
@@ -25,8 +25,7 @@
  * JDK-8136694: Megemorphic scope access does not throw ReferenceError when property is missing
  *
  * @test
- * @fork
- * @option -Dnashorn.unstable.relink.threshold=16
+ * @option --unstable-relink-threshold=16
  * @run
  */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8141505.js	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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-8141505: floating point parse incorrect on big integer
+ *
+ * @test
+ * @run
+ */
+
+Assert.assertEquals(String(-3.33333333333333e+18), "-3333333333333330000");
+
--- a/test/script/basic/NASHORN-389.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/NASHORN-389.js	Thu Nov 12 14:14:37 2015 -0800
@@ -35,6 +35,6 @@
 var y = -1.23456789e+20;
 print(x.toFixed(9));
 print(y.toFixed(9).indexOf(",") === -1); // no grouping
-//print(y.toFixed(9)); // FIXME expected: -123456788999999995904.000000000
-//print(1000000000000000128); // FIXME expected: 1000000000000000100
-//print((1000000000000000128).toFixed(0)); // FIXME expected: 1000000000000000128
+print(y.toFixed(9));
+print(1000000000000000128); 
+print((1000000000000000128).toFixed(0));
--- a/test/script/basic/NASHORN-389.js.EXPECTED	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/NASHORN-389.js.EXPECTED	Thu Nov 12 14:14:37 2015 -0800
@@ -3,3 +3,6 @@
 0.00001
 -1.23456789e+21
 true
+-123456788999999995904.000000000
+1000000000000000100
+1000000000000000128
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6.js	Thu Nov 12 14:14:37 2015 -0800
@@ -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/test/script/basic/es6/let.js	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/script/basic/es6/let.js	Thu Nov 12 14:14:37 2015 -0800
@@ -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/test/script/basic/es6/symbols.js	Thu Nov 12 14:14:37 2015 -0800
@@ -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'));
+
--- a/test/src/jdk/nashorn/api/javaaccess/test/ArrayConversionTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/javaaccess/test/ArrayConversionTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -40,6 +40,10 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+/**
+ * @test
+ * @run testng/othervm jdk.nashorn.api.javaaccess.test.ArrayConversionTest
+ */
 @SuppressWarnings("javadoc")
 public class ArrayConversionTest {
     private static ScriptEngine e = null;
--- a/test/src/jdk/nashorn/api/javaaccess/test/ConsStringTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/javaaccess/test/ConsStringTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -39,6 +39,10 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+/**
+ * @test
+ * @run testng jdk.nashorn.api.javaaccess.test.ConsStringTest
+ */
 @SuppressWarnings("javadoc")
 public class ConsStringTest {
     private static ScriptEngine e = null;
--- a/test/src/jdk/nashorn/api/scripting/test/InvocableTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/scripting/test/InvocableTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -40,6 +40,10 @@
 
 /**
  * Tests for javax.script.Invocable implementation of nashorn.
+ *
+ * @test
+ * @build jdk.nashorn.api.scripting.test.VariableArityTestInterface jdk.nashorn.api.scripting.test.InvocableTest
+ * @run testng jdk.nashorn.api.scripting.test.InvocableTest
  */
 @SuppressWarnings("javadoc")
 public class InvocableTest {
--- a/test/src/jdk/nashorn/api/scripting/test/JSONCompatibleTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/scripting/test/JSONCompatibleTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -39,6 +39,10 @@
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+/**
+ * @test
+ * @run testng jdk.nashorn.api.scripting.test.JSONCompatibleTest
+ */
 public class JSONCompatibleTest {
 
     /**
--- a/test/src/jdk/nashorn/api/scripting/test/PluggableJSObjectTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/scripting/test/PluggableJSObjectTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -47,6 +47,9 @@
  *
  * JDK-8024615: Refactor ScriptObjectMirror and JSObject to support external
  * JSObject implementations.
+ *
+ * @test
+ * @run testng jdk.nashorn.api.scripting.test.PluggableJSObjectTest
  */
 @SuppressWarnings("javadoc")
 public class PluggableJSObjectTest {
--- a/test/src/jdk/nashorn/api/scripting/test/ScriptEngineSecurityTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/scripting/test/ScriptEngineSecurityTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -38,6 +38,9 @@
 
 /**
  * jsr223 tests for security access checks.
+ *
+ * @test
+ * @run testng/othervm jdk.nashorn.api.scripting.test.ScriptEngineSecurityTest
  */
 @SuppressWarnings("javadoc")
 public class ScriptEngineSecurityTest {
@@ -244,7 +247,7 @@
         final ScriptEngineManager m = new ScriptEngineManager();
         final ScriptEngine e = m.getEngineByName("nashorn");
         final Runnable r = (Runnable)Proxy.newProxyInstance(
-            ScriptEngineTest.class.getClassLoader(),
+            ScriptEngineSecurityTest.class.getClassLoader(),
             new Class[] { Runnable.class },
             new InvocationHandler() {
                 @Override
--- a/test/src/jdk/nashorn/api/scripting/test/ScriptObjectMirrorTest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/scripting/test/ScriptObjectMirrorTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -47,6 +47,9 @@
 
 /**
  * Tests to check jdk.nashorn.api.scripting.ScriptObjectMirror API.
+ *
+ * @test
+ * @run testng jdk.nashorn.api.scripting.test.ScriptObjectMirrorTest
  */
 @SuppressWarnings("javadoc")
 public class ScriptObjectMirrorTest {
--- a/test/src/jdk/nashorn/api/tree/test/ParseAPITest.java	Thu Nov 12 10:39:12 2015 -0800
+++ b/test/src/jdk/nashorn/api/tree/test/ParseAPITest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -37,17 +37,38 @@
 
 /**
  * Test for nashorn Parser API (jdk.nashorn.api.tree.*)
+ *
+ * @test
+ * @run testng jdk.nashorn.api.tree.test.ParseAPITest
  */
 public class ParseAPITest {
 
     private static final boolean VERBOSE   = Boolean.valueOf(System.getProperty("parserapitest.verbose"));
     private static final boolean TEST262   = Boolean.valueOf(System.getProperty("parserapitest.test262"));
 
-    private static final String TEST_BASIC_DIR     = System.getProperty("test.basic.dir");
-    private static final String TEST_MAPTESTS_DIR  = System.getProperty("test.maptests.dir");
-    private static final String TEST_SANDBOX_DIR   = System.getProperty("test.sandbox.dir");
-    private static final String TEST_TRUSTED_DIR   = System.getProperty("test.trusted.dir");
-    private static final String TEST262_SUITE_DIR  = System.getProperty("test262.suite.dir");
+    private static final String TEST_BASIC_DIR;
+    private static final String TEST_MAPTESTS_DIR;
+    private static final String TEST_SANDBOX_DIR;
+    private static final String TEST_TRUSTED_DIR;
+    private static final String TEST262_SUITE_DIR;
+
+    static {
+        String testSrc = System.getProperty("test.src");
+        if (testSrc != null) {
+            String testScriptDir = testSrc + "/../../../../../../script/";
+            TEST_BASIC_DIR    = testScriptDir + "basic";
+            TEST_MAPTESTS_DIR = testScriptDir + "maptests";
+            TEST_SANDBOX_DIR  = testScriptDir + "sandbox";
+            TEST_TRUSTED_DIR  = testScriptDir + "trusted";
+            TEST262_SUITE_DIR = testScriptDir + "external/test262/test/suite";
+        } else {
+            TEST_BASIC_DIR     = System.getProperty("test.basic.dir");
+            TEST_MAPTESTS_DIR  = System.getProperty("test.maptests.dir");
+            TEST_SANDBOX_DIR   = System.getProperty("test.sandbox.dir");
+            TEST_TRUSTED_DIR   = System.getProperty("test.trusted.dir");
+            TEST262_SUITE_DIR  = System.getProperty("test262.suite.dir");
+        }
+    }
 
     interface TestFilter {
         public boolean exclude(File file, String content);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/jdk/nashorn/internal/runtime/doubleconv/test/BignumDtoaTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,352 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv.test;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
+import jdk.nashorn.internal.runtime.doubleconv.DtoaBuffer;
+import jdk.nashorn.internal.runtime.doubleconv.DtoaMode;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * FastDtoa tests
+ */
+@SuppressWarnings("javadoc")
+public class BignumDtoaTest {
+
+    final static private int BUFFER_SIZE = 100;
+
+    // Removes trailing '0' digits.
+    // Can return the empty string if all digits are 0.
+    private static String trimRepresentation(final String representation) {
+        final int len = representation.length();
+        int i;
+        for (i = len - 1; i >= 0; --i) {
+            if (representation.charAt(i) != '0') break;
+        }
+        return representation.substring(0, i + 1);
+    }
+
+    @Test
+    public void testBignumVarious() {
+        final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
+
+        DoubleConversion.bignumDtoa(1, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("1", buffer.getRawDigits());
+        assertEquals(1, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(1.0, DtoaMode.FIXED, 3, buffer);
+        assertTrue(3 >= buffer.getLength() - buffer.getDecimalPoint());
+        assertEquals("1", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(1, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(1.0, DtoaMode.PRECISION, 3, buffer);
+        assertTrue(3 >= buffer.getLength());
+        assertEquals("1", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(1, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(1.5, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("15", buffer.getRawDigits());
+        assertEquals(1, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(1.5, DtoaMode.FIXED, 10, buffer);
+        assertTrue(10 >= buffer.getLength() - buffer.getDecimalPoint());
+        assertEquals("15", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(1, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(1.5, DtoaMode.PRECISION, 10, buffer);
+        assertTrue(10 >= buffer.getLength());
+        assertEquals("15", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(1, buffer.getDecimalPoint());
+        buffer.reset();
+
+        final double min_double = 5e-324;
+        DoubleConversion.bignumDtoa(min_double, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("5", buffer.getRawDigits());
+        assertEquals(-323, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(min_double, DtoaMode.FIXED, 5, buffer);
+        assertTrue(5 >= buffer.getLength() - buffer.getDecimalPoint());
+        assertEquals("", trimRepresentation(buffer.getRawDigits()));
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(min_double, DtoaMode.PRECISION, 5, buffer);
+        assertTrue(5 >= buffer.getLength());
+        assertEquals("49407", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(-323, buffer.getDecimalPoint());
+        buffer.reset();
+
+        final double max_double = 1.7976931348623157e308;
+        DoubleConversion.bignumDtoa(max_double, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("17976931348623157", buffer.getRawDigits());
+        assertEquals(309, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(max_double, DtoaMode.PRECISION, 7, buffer);
+        assertTrue(7 >= buffer.getLength());
+        assertEquals("1797693", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(309, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(4294967272.0, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("4294967272", buffer.getRawDigits());
+        assertEquals(10, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(4294967272.0, DtoaMode.FIXED, 5, buffer);
+        assertEquals("429496727200000", buffer.getRawDigits());
+        assertEquals(10, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(4294967272.0, DtoaMode.PRECISION, 14, buffer);
+        assertTrue(14 >= buffer.getLength());
+        assertEquals("4294967272", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(10, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(4.1855804968213567e298, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("4185580496821357", buffer.getRawDigits());
+        assertEquals(299, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(4.1855804968213567e298, DtoaMode.PRECISION, 20, buffer);
+        assertTrue(20 >= buffer.getLength());
+        assertEquals("41855804968213567225", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(299, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(5.5626846462680035e-309, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("5562684646268003", buffer.getRawDigits());
+        assertEquals(-308, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(5.5626846462680035e-309, DtoaMode.PRECISION, 1, buffer);
+        assertTrue(1 >= buffer.getLength());
+        assertEquals("6", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(-308, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(2147483648.0, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("2147483648", buffer.getRawDigits());
+        assertEquals(10, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(2147483648.0, DtoaMode.FIXED, 2, buffer);
+        assertTrue(2 >= buffer.getLength() - buffer.getDecimalPoint());
+        assertEquals("2147483648", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(10, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(2147483648.0, DtoaMode.PRECISION, 5, buffer);
+        assertTrue(5 >= buffer.getLength());
+        assertEquals("21475", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(10, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(3.5844466002796428e+298, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("35844466002796428", buffer.getRawDigits());
+        assertEquals(299, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(3.5844466002796428e+298, DtoaMode.PRECISION, 10, buffer);
+        assertTrue(10 >= buffer.getLength());
+        assertEquals("35844466", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(299, buffer.getDecimalPoint());
+        buffer.reset();
+
+        final long smallest_normal64 = 0x0010000000000000L;
+        double v = Double.longBitsToDouble(smallest_normal64);
+        DoubleConversion.bignumDtoa(v, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("22250738585072014", buffer.getRawDigits());
+        assertEquals(-307, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 20, buffer);
+        assertTrue(20 >= buffer.getLength());
+        assertEquals("22250738585072013831", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(-307, buffer.getDecimalPoint());
+        buffer.reset();
+
+        final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
+        v = Double.longBitsToDouble(largest_denormal64);
+        DoubleConversion.bignumDtoa(v, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("2225073858507201", buffer.getRawDigits());
+        assertEquals(-307, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 20, buffer);
+        assertTrue(20 >= buffer.getLength());
+        assertEquals("2225073858507200889", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(-307, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(4128420500802942e-24, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("4128420500802942", buffer.getRawDigits());
+        assertEquals(-8, buffer.getDecimalPoint());
+        buffer.reset();
+
+        DoubleConversion.bignumDtoa(3.9292015898194142585311918e-10, DtoaMode.SHORTEST, 0, buffer);
+        assertEquals("39292015898194143", buffer.getRawDigits());
+        buffer.reset();
+
+        v = 4194304.0;
+        DoubleConversion.bignumDtoa(v, DtoaMode.FIXED, 5, buffer);
+        assertTrue(5 >= buffer.getLength() - buffer.getDecimalPoint());
+        assertEquals("4194304", trimRepresentation(buffer.getRawDigits()));
+        buffer.reset();
+
+        v = 3.3161339052167390562200598e-237;
+        DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 19, buffer);
+        assertTrue(19 >= buffer.getLength());
+        assertEquals("3316133905216739056", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(-236, buffer.getDecimalPoint());
+        buffer.reset();
+
+        v = 7.9885183916008099497815232e+191;
+        DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 4, buffer);
+        assertTrue(4 >= buffer.getLength());
+        assertEquals("7989", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(192, buffer.getDecimalPoint());
+        buffer.reset();
+
+        v = 1.0000000000000012800000000e+17;
+        DoubleConversion.bignumDtoa(v, DtoaMode.FIXED, 1, buffer);
+        assertTrue(1 >= buffer.getLength() - buffer.getDecimalPoint());
+        assertEquals("100000000000000128", trimRepresentation(buffer.getRawDigits()));
+        assertEquals(18, buffer.getDecimalPoint());
+        buffer.reset();
+    }
+
+
+    @Test
+    public void testBignumShortest() {
+        new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-shortest.txt")))
+                .lines()
+                .forEach(line -> {
+                    if (line.isEmpty() || line.startsWith("//")) {
+                        return; // comment or empty line
+                    }
+                    final String[] tokens = line.split(",\\s+");
+                    assertEquals(tokens.length, 3, "*" + line + "*");
+                    final double v = Double.parseDouble(tokens[0]);
+                    final String str = tokens[1].replace('"', ' ').trim();;
+                    final int point = Integer.parseInt(tokens[2]);
+                    final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
+
+                    DoubleConversion.bignumDtoa(v, DtoaMode.SHORTEST, 0, buffer);
+                    assertEquals(str, buffer.getRawDigits());
+                    assertEquals(point, buffer.getDecimalPoint());
+                });
+    }
+
+    @Test
+    public void testBignumFixed()  {
+        new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-fixed.txt")))
+                .lines()
+                .forEach(line -> {
+                    if (line.isEmpty() || line.startsWith("//")) {
+                        return; // comment or empty line
+                    }
+                    final String[] tokens = line.split(",\\s+");
+                    assertEquals(tokens.length, 4);
+                    final double v = Double.parseDouble(tokens[0]);
+                    final int digits = Integer.parseInt(tokens[1]);
+                    final String str = tokens[2].replace('"', ' ').trim();
+                    final int point = Integer.parseInt(tokens[3]);
+                    final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
+
+                    DoubleConversion.bignumDtoa(v, DtoaMode.FIXED, digits, buffer);
+                    assertEquals(str, trimRepresentation(buffer.getRawDigits()));
+                    assertEquals(point, buffer.getDecimalPoint());
+                });
+    }
+
+    @Test
+    public void testBignumPrecision() {
+        new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-precision.txt")))
+                .lines()
+                .forEach(line -> {
+                    if (line.isEmpty() || line.startsWith("//")) {
+                        return; // comment or empty line
+                    }
+                    final String[] tokens = line.split(",\\s+");
+                    assertEquals(tokens.length, 4);
+                    final double v = Double.parseDouble(tokens[0]);
+                    final int digits = Integer.parseInt(tokens[1]);
+                    final String str = tokens[2].replace('"', ' ').trim();
+                    final int point = Integer.parseInt(tokens[3]);
+                    final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
+
+                    DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, digits, buffer);
+                    assertEquals(str, trimRepresentation(buffer.getRawDigits()));
+                    assertEquals(point, buffer.getDecimalPoint());
+                });
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/jdk/nashorn/internal/runtime/doubleconv/test/BignumTest.java	Thu Nov 12 14:14:37 2015 -0800
@@ -0,0 +1,1486 @@
+/*
+ * 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.
+ */
+
+// This file is available under and governed by the GNU General Public
+// License version 2 only, as published by the Free Software Foundation.
+// However, the following notice accompanied the original version of this
+// file:
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package jdk.nashorn.internal.runtime.doubleconv.test;
+
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Bignum class tests
+ *
+ * @test
+ * @run testng jdk.nashorn.internal.runtime.doubleconv.test.BignumTest
+ */
+@SuppressWarnings("javadoc")
+public class BignumTest {
+
+    static final Class<?> Bignum;
+    static final Constructor<?> ctor;
+
+    static {
+        try {
+            Bignum = Class.forName("jdk.nashorn.internal.runtime.doubleconv.Bignum");
+            ctor = Bignum.getDeclaredConstructor();
+            ctor.setAccessible(true);
+        } catch (final Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Method method(final String name, final Class<?>... params) throws NoSuchMethodException {
+        final Method m = Bignum.getDeclaredMethod(name, params);
+        m.setAccessible(true);
+        return m;
+    }
+
+    @Test
+    public void testAssign() throws Exception {
+
+        Object bignum = ctor.newInstance();
+        Object bignum2 = ctor.newInstance();
+
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignUInt64 = method("assignUInt64", long.class);
+        final Method assignDecimalString = method("assignDecimalString", String.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method toHexString = method("toHexString");
+
+        assignUInt16.invoke(bignum, (char) 0);
+        assertEquals(toHexString.invoke(bignum), "0");
+        assignUInt16.invoke(bignum, (char) 0xA);
+        assertEquals(toHexString.invoke(bignum), "A");
+        assignUInt16.invoke(bignum, (char) 0x20);
+        assertEquals(toHexString.invoke(bignum), "20");
+
+        assignUInt64.invoke(bignum, 0);
+        assertEquals(toHexString.invoke(bignum), "0");
+        assignUInt64.invoke(bignum, 0xA);
+        assertEquals(toHexString.invoke(bignum), "A");
+        assignUInt64.invoke(bignum, 0x20);
+        assertEquals(toHexString.invoke(bignum), "20");
+        assignUInt64.invoke(bignum, 0x100);
+        assertEquals(toHexString.invoke(bignum), "100");
+
+        // The first real test, since this will not fit into one bigit.
+        assignUInt64.invoke(bignum, 0x12345678L);
+        assertEquals(toHexString.invoke(bignum), "12345678");
+
+        assignUInt64.invoke(bignum, 0xFFFFFFFFFFFFFFFFL);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFFFF");
+
+        assignUInt64.invoke(bignum, 0x123456789ABCDEF0L);
+        assertEquals(toHexString.invoke(bignum), "123456789ABCDEF0");
+
+        assignUInt64.invoke(bignum2, 0x123456789ABCDEF0L);
+        assertEquals(toHexString.invoke(bignum2), "123456789ABCDEF0");
+
+        assignDecimalString.invoke(bignum, "0");
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignDecimalString.invoke(bignum, "1");
+        assertEquals(toHexString.invoke(bignum), "1");
+
+        assignDecimalString.invoke(bignum, "1234567890");
+        assertEquals(toHexString.invoke(bignum), "499602D2");
+
+        assignHexString.invoke(bignum, "0");
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignHexString.invoke(bignum, "123456789ABCDEF0");
+        assertEquals(toHexString.invoke(bignum), "123456789ABCDEF0");
+    }
+
+    @Test
+    public void testShiftLeft() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method toHexString = method("toHexString");
+
+
+        assignHexString.invoke(bignum, "0");
+        shiftLeft.invoke(bignum, 100);
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignHexString.invoke(bignum, "1");
+        shiftLeft.invoke(bignum, 1);
+        assertEquals(toHexString.invoke(bignum), "2");
+
+        assignHexString.invoke(bignum, "1");
+        shiftLeft.invoke(bignum, 4);
+        assertEquals(toHexString.invoke(bignum), "10");
+
+        assignHexString.invoke(bignum, "1");
+        shiftLeft.invoke(bignum, 32);
+        assertEquals(toHexString.invoke(bignum), "100000000");
+
+        assignHexString.invoke(bignum, "1");
+        shiftLeft.invoke(bignum, 64);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000");
+
+        assignHexString.invoke(bignum, "123456789ABCDEF");
+        shiftLeft.invoke(bignum, 64);
+        assertEquals(toHexString.invoke(bignum), "123456789ABCDEF0000000000000000");
+        shiftLeft.invoke(bignum, 1);
+        assertEquals(toHexString.invoke(bignum), "2468ACF13579BDE0000000000000000");
+    }
+
+
+
+    @Test
+    public void testAddUInt64() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+
+        final Method addUInt64 = method("addUInt64", long.class);
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method toHexString = method("toHexString");
+
+        assignHexString.invoke(bignum, "0");
+        addUInt64.invoke(bignum, 0xA);
+        assertEquals(toHexString.invoke(bignum), "A");
+
+        assignHexString.invoke(bignum, "1");
+        addUInt64.invoke(bignum, 0xA);
+        assertEquals(toHexString.invoke(bignum), "B");
+
+        assignHexString.invoke(bignum, "1");
+        addUInt64.invoke(bignum, 0x100);
+        assertEquals(toHexString.invoke(bignum), "101");
+
+        assignHexString.invoke(bignum, "1");
+        addUInt64.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "10000");
+
+        assignHexString.invoke(bignum, "FFFFFFF");
+        addUInt64.invoke(bignum, 0x1);
+        assertEquals(toHexString.invoke(bignum), "10000000");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000000");
+        addUInt64.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "1000000000000000000000000000000000000000FFFF");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        addUInt64.invoke(bignum, 0x1);
+        assertEquals(toHexString.invoke(bignum), "100000000000000000000000000000000000000000000");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        addUInt64.invoke(bignum, 0x1);
+        assertEquals(toHexString.invoke(bignum), "100000000000000000000000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        addUInt64.invoke(bignum, 1);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000001");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        addUInt64.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "1000000000000000000000FFFF");
+
+        assignHexString.invoke(bignum, "0");
+        addUInt64.invoke(bignum, 0xA00000000L);
+        assertEquals(toHexString.invoke(bignum), "A00000000");
+
+        assignHexString.invoke(bignum, "1");
+        addUInt64.invoke(bignum, 0xA00000000L);
+        assertEquals(toHexString.invoke(bignum), "A00000001");
+
+        assignHexString.invoke(bignum, "1");
+        addUInt64.invoke(bignum, 0x10000000000L);
+        assertEquals(toHexString.invoke(bignum), "10000000001");
+
+        assignHexString.invoke(bignum, "1");
+        addUInt64.invoke(bignum, 0xFFFF00000000L);
+        assertEquals(toHexString.invoke(bignum), "FFFF00000001");
+
+        assignHexString.invoke(bignum, "FFFFFFF");
+        addUInt64.invoke(bignum, 0x100000000L);
+        assertEquals(toHexString.invoke(bignum), "10FFFFFFF");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000000");
+        addUInt64.invoke(bignum, 0xFFFF00000000L);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000000000000FFFF00000000");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        addUInt64.invoke(bignum, 0x100000000L);
+        assertEquals(toHexString.invoke(bignum), "1000000000000000000000000000000000000FFFFFFFF");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        addUInt64.invoke(bignum, 0x100000000L);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000100000000");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        addUInt64.invoke(bignum, 0xFFFF00000000L);
+        assertEquals(toHexString.invoke(bignum), "10000000000000FFFF00000000");
+    }
+
+    @Test
+    public void testAddBignum() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+        final Object other = ctor.newInstance();
+
+        final Method addBignum = method("addBignum", Bignum);
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method toHexString = method("toHexString");
+
+        assignHexString.invoke(other, "1");
+        assignHexString.invoke(bignum, "0");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "1");
+
+        assignHexString.invoke(bignum, "1");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "2");
+
+        assignHexString.invoke(bignum, "FFFFFFF");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFF");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "100000000000000");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000000");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000000000000000000000001");
+
+        assignHexString.invoke(other, "1000000000000");
+
+        assignHexString.invoke(bignum, "1");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "1000000000001");
+
+        assignHexString.invoke(bignum, "FFFFFFF");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "100000FFFFFFF");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000000");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000000000001000000000000");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "1000000000000000000000000000000FFFFFFFFFFFF");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000001000000000000");
+
+        shiftLeft.invoke(other, 64);
+        // other == "10000000000000000000000000000"
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000000001");
+
+        assignHexString.invoke(bignum, "FFFFFFF");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "1000000000000000000000FFFFFFF");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000000");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000000010000000000000000000000000000");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "100000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        addBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10010000000000000000000000000");
+    }
+
+
+    @Test
+    public void testSubtractBignum() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+        final Object other = ctor.newInstance();
+
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method subtractBignum = method("subtractBignum", Bignum);
+
+        final Method toHexString = method("toHexString");
+
+        assignHexString.invoke(bignum, "1");
+        assignHexString.invoke(other, "0");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "1");
+
+        assignHexString.invoke(bignum, "2");
+        assignHexString.invoke(other, "0");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "2");
+
+        assignHexString.invoke(bignum, "10000000");
+        assignHexString.invoke(other, "1");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFF");
+
+        assignHexString.invoke(bignum, "100000000000000");
+        assignHexString.invoke(other, "1");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFF");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000001");
+        assignHexString.invoke(other, "1");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000000000000000000000000");
+
+        assignHexString.invoke(bignum, "1000000000001");
+        assignHexString.invoke(other, "1000000000000");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "1");
+
+        assignHexString.invoke(bignum, "100000FFFFFFF");
+        assignHexString.invoke(other, "1000000000000");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFF");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000001000000000000");
+        assignHexString.invoke(other, "1000000000000");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "10000000000000000000000000000000000000000000");
+
+        assignHexString.invoke(bignum, "1000000000000000000000000000000FFFFFFFFFFFF");
+        assignHexString.invoke(other, "1000000000000");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        // "10 0000 0000 0000 0000 0000 0000"
+        assignHexString.invoke(other, "1000000000000");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFF000000000000");
+
+        assignHexString.invoke(other, "1000000000000");
+        shiftLeft.invoke(other, 48);
+        // other == "1000000000000000000000000"
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        // bignum == "10000000000000000000000000"
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "F000000000000000000000000");
+
+        assignUInt16.invoke(other, (char) 0x1);
+        shiftLeft.invoke(other, 35);
+        // other == "800000000"
+        assignHexString.invoke(bignum, "FFFFFFF");
+        shiftLeft.invoke(bignum, 60);
+        // bignum = FFFFFFF000000000000000
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFEFFFFFF800000000");
+
+        assignHexString.invoke(bignum, "10000000000000000000000000000000000000000000");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000000");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        subtractBignum.invoke(bignum, other);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFF");
+    }
+
+
+    @Test
+    public void testMultiplyUInt32() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method assignDecimalString = method("assignDecimalString", String.class);
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method multiplyByUInt32 = method("multiplyByUInt32", int.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method toHexString = method("toHexString");
+
+        assignHexString.invoke(bignum, "0");
+        multiplyByUInt32.invoke(bignum, 0x25);
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignHexString.invoke(bignum, "2");
+        multiplyByUInt32.invoke(bignum, 0x5);
+        assertEquals(toHexString.invoke(bignum), "A");
+
+        assignHexString.invoke(bignum, "10000000");
+        multiplyByUInt32.invoke(bignum, 0x9);
+        assertEquals(toHexString.invoke(bignum), "90000000");
+
+        assignHexString.invoke(bignum, "100000000000000");
+        multiplyByUInt32.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFF00000000000000");
+
+        assignHexString.invoke(bignum, "100000000000000");
+        multiplyByUInt32.invoke(bignum, 0xFFFFFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFF00000000000000");
+
+        assignHexString.invoke(bignum, "1234567ABCD");
+        multiplyByUInt32.invoke(bignum, 0xFFF);
+        assertEquals(toHexString.invoke(bignum), "12333335552433");
+
+        assignHexString.invoke(bignum, "1234567ABCD");
+        multiplyByUInt32.invoke(bignum, 0xFFFFFFF);
+        assertEquals(toHexString.invoke(bignum), "12345679998A985433");
+
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt32.invoke(bignum, 0x2);
+        assertEquals(toHexString.invoke(bignum), "1FFFFFFFFFFFFFFFE");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt32.invoke(bignum, 0x4);
+        assertEquals(toHexString.invoke(bignum), "3FFFFFFFFFFFFFFFC");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt32.invoke(bignum, 0xF);
+        assertEquals(toHexString.invoke(bignum), "EFFFFFFFFFFFFFFF1");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt32.invoke(bignum, 0xFFFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFFFEFFFFFFFFFF000001");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        // "10 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt32.invoke(bignum, 2);
+        assertEquals(toHexString.invoke(bignum), "20000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        // "10 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt32.invoke(bignum, 0xF);
+        assertEquals(toHexString.invoke(bignum), "F0000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0xFFFF);
+        shiftLeft.invoke(bignum, 100);
+        // "FFFF0 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt32.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFE00010000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0xFFFF);
+        shiftLeft.invoke(bignum, 100);
+        // "FFFF0 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt32.invoke(bignum, 0xFFFFFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFEFFFF00010000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0xFFFF);
+        shiftLeft.invoke(bignum, 100);
+        // "FFFF0 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt32.invoke(bignum, 0xFFFFFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFEFFFF00010000000000000000000000000");
+
+        assignDecimalString.invoke(bignum, "15611230384529777");
+        multiplyByUInt32.invoke(bignum, 10000000);
+        assertEquals(toHexString.invoke(bignum), "210EDD6D4CDD2580EE80");
+    }
+
+
+
+    @Test
+    public void testMultiplyUInt64() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignDecimalString = method("assignDecimalString", String.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method multiplyByUInt64 = method("multiplyByUInt64", long.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method toHexString = method("toHexString");
+
+        assignHexString.invoke(bignum, "0");
+        multiplyByUInt64.invoke(bignum, 0x25);
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignHexString.invoke(bignum, "2");
+        multiplyByUInt64.invoke(bignum, 0x5);
+        assertEquals(toHexString.invoke(bignum), "A");
+
+        assignHexString.invoke(bignum, "10000000");
+        multiplyByUInt64.invoke(bignum, 0x9);
+        assertEquals(toHexString.invoke(bignum), "90000000");
+
+        assignHexString.invoke(bignum, "100000000000000");
+        multiplyByUInt64.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFF00000000000000");
+
+        assignHexString.invoke(bignum, "100000000000000");
+        multiplyByUInt64.invoke(bignum, 0xFFFFFFFFFFFFFFFFL);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFFFF00000000000000");
+
+        assignHexString.invoke(bignum, "1234567ABCD");
+        multiplyByUInt64.invoke(bignum, 0xFFF);
+        assertEquals(toHexString.invoke(bignum), "12333335552433");
+
+        assignHexString.invoke(bignum, "1234567ABCD");
+        multiplyByUInt64.invoke(bignum, 0xFFFFFFFFFFL);
+        assertEquals(toHexString.invoke(bignum), "1234567ABCBDCBA985433");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt64.invoke(bignum, 0x2);
+        assertEquals(toHexString.invoke(bignum), "1FFFFFFFFFFFFFFFE");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt64.invoke(bignum, 0x4);
+        assertEquals(toHexString.invoke(bignum), "3FFFFFFFFFFFFFFFC");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt64.invoke(bignum, 0xF);
+        assertEquals(toHexString.invoke(bignum), "EFFFFFFFFFFFFFFF1");
+
+        assignHexString.invoke(bignum, "FFFFFFFFFFFFFFFF");
+        multiplyByUInt64.invoke(bignum, 0xFFFFFFFFFFFFFFFFL);
+        assertEquals(toHexString.invoke(bignum), "FFFFFFFFFFFFFFFE0000000000000001");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        // "10 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt64.invoke(bignum, 2);
+        assertEquals(toHexString.invoke(bignum), "20000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0x1);
+        shiftLeft.invoke(bignum, 100);
+        // "10 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt64.invoke(bignum, 0xF);
+        assertEquals(toHexString.invoke(bignum), "F0000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0xFFFF);
+        shiftLeft.invoke(bignum, 100);
+        // "FFFF0 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt64.invoke(bignum, 0xFFFF);
+        assertEquals(toHexString.invoke(bignum), "FFFE00010000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0xFFFF);
+        shiftLeft.invoke(bignum, 100);
+        // "FFFF0 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt64.invoke(bignum, 0xFFFFFFFFL);
+        assertEquals(toHexString.invoke(bignum), "FFFEFFFF00010000000000000000000000000");
+
+        assignUInt16.invoke(bignum, (char) 0xFFFF);
+        shiftLeft.invoke(bignum, 100);
+        // "FFFF0 0000 0000 0000 0000 0000 0000"
+        multiplyByUInt64.invoke(bignum, 0xFFFFFFFFFFFFFFFFL);
+        assertEquals(toHexString.invoke(bignum), "FFFEFFFFFFFFFFFF00010000000000000000000000000");
+
+        assignDecimalString.invoke(bignum, "15611230384529777");
+        multiplyByUInt64.invoke(bignum, 0x8ac7230489e80000L);
+        assertEquals(toHexString.invoke(bignum), "1E10EE4B11D15A7F3DE7F3C7680000");
+    }
+
+    @Test
+    public void testMultiplyPowerOfTen() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+        final Object bignum2 = ctor.newInstance();
+
+        final Method assignBignum = method("assignBignum", Bignum);
+        final Method assignDecimalString = method("assignDecimalString", String.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method multiplyByPowerOfTen = method("multiplyByPowerOfTen", int.class);
+        final Method toHexString = method("toHexString");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 1);
+        assertEquals(toHexString.invoke(bignum), "3034");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 2);
+        assertEquals(toHexString.invoke(bignum), "1E208");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 3);
+        assertEquals(toHexString.invoke(bignum), "12D450");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 4);
+        assertEquals(toHexString.invoke(bignum), "BC4B20");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 5);
+        assertEquals(toHexString.invoke(bignum), "75AEF40");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 6);
+        assertEquals(toHexString.invoke(bignum), "498D5880");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 7);
+        assertEquals(toHexString.invoke(bignum), "2DF857500");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 8);
+        assertEquals(toHexString.invoke(bignum), "1CBB369200");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 9);
+        assertEquals(toHexString.invoke(bignum), "11F5021B400");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 10);
+        assertEquals(toHexString.invoke(bignum), "B3921510800");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 11);
+        assertEquals(toHexString.invoke(bignum), "703B4D2A5000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 12);
+        assertEquals(toHexString.invoke(bignum), "4625103A72000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 13);
+        assertEquals(toHexString.invoke(bignum), "2BD72A24874000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 14);
+        assertEquals(toHexString.invoke(bignum), "1B667A56D488000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 15);
+        assertEquals(toHexString.invoke(bignum), "11200C7644D50000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 16);
+        assertEquals(toHexString.invoke(bignum), "AB407C9EB0520000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 17);
+        assertEquals(toHexString.invoke(bignum), "6B084DE32E3340000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 18);
+        assertEquals(toHexString.invoke(bignum), "42E530ADFCE0080000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 19);
+        assertEquals(toHexString.invoke(bignum), "29CF3E6CBE0C0500000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 20);
+        assertEquals(toHexString.invoke(bignum), "1A218703F6C783200000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 21);
+        assertEquals(toHexString.invoke(bignum), "1054F4627A3CB1F400000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 22);
+        assertEquals(toHexString.invoke(bignum), "A3518BD8C65EF38800000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 23);
+        assertEquals(toHexString.invoke(bignum), "6612F7677BFB5835000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 24);
+        assertEquals(toHexString.invoke(bignum), "3FCBDAA0AD7D17212000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 25);
+        assertEquals(toHexString.invoke(bignum), "27DF68A46C6E2E74B4000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 26);
+        assertEquals(toHexString.invoke(bignum), "18EBA166C3C4DD08F08000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 27);
+        assertEquals(toHexString.invoke(bignum), "F9344E03A5B0A259650000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 28);
+        assertEquals(toHexString.invoke(bignum), "9BC0B0C2478E6577DF20000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 29);
+        assertEquals(toHexString.invoke(bignum), "61586E796CB8FF6AEB740000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 30);
+        assertEquals(toHexString.invoke(bignum), "3CD7450BE3F39FA2D32880000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 31);
+        assertEquals(toHexString.invoke(bignum), "26068B276E7843C5C3F9500000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 50);
+        assertEquals(toHexString.invoke(bignum), "149D1B4CFED03B23AB5F4E1196EF45C08000000000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 100);
+        assertEquals(toHexString.invoke(bignum),
+                "5827249F27165024FBC47DFCA9359BF316332D1B91ACEECF471FBAB06D9B2" +
+                "0000000000000000000000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 200);
+        assertEquals(toHexString.invoke(bignum),
+                "64C1F5C06C3816AFBF8DAFD5A3D756365BB0FD020E6F084E759C1F7C99E4F" +
+                "55B9ACC667CEC477EB958C2AEEB3C6C19BA35A1AD30B35C51EB72040920000" +
+                "0000000000000000000000000000000000000000000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 500);
+        assertEquals(toHexString.invoke(bignum),
+                "96741A625EB5D7C91039FEB5C5ACD6D9831EDA5B083D800E6019442C8C8223" +
+                "3EAFB3501FE2058062221E15121334928880827DEE1EC337A8B26489F3A40A" +
+                "CB440A2423734472D10BFCE886F41B3AF9F9503013D86D088929CA86EEB4D8" +
+                "B9C831D0BD53327B994A0326227CFD0ECBF2EB48B02387AAE2D4CCCDF1F1A1" +
+                "B8CC4F1FA2C56AD40D0E4DAA9C28CDBF0A549098EA13200000000000000000" +
+                "00000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000");
+
+        assignDecimalString.invoke(bignum, "1234");
+        multiplyByPowerOfTen.invoke(bignum, 1000);
+        assertEquals(toHexString.invoke(bignum),
+                "1258040F99B1CD1CC9819C676D413EA50E4A6A8F114BB0C65418C62D399B81" +
+                "6361466CA8E095193E1EE97173553597C96673AF67FAFE27A66E7EF2E5EF2E" +
+                "E3F5F5070CC17FE83BA53D40A66A666A02F9E00B0E11328D2224B8694C7372" +
+                "F3D536A0AD1985911BD361496F268E8B23112500EAF9B88A9BC67B2AB04D38" +
+                "7FEFACD00F5AF4F764F9ABC3ABCDE54612DE38CD90CB6647CA389EA0E86B16" +
+                "BF7A1F34086E05ADBE00BD1673BE00FAC4B34AF1091E8AD50BA675E0381440" +
+                "EA8E9D93E75D816BAB37C9844B1441C38FC65CF30ABB71B36433AF26DD97BD" +
+                "ABBA96C03B4919B8F3515B92826B85462833380DC193D79F69D20DD6038C99" +
+                "6114EF6C446F0BA28CC772ACBA58B81C04F8FFDE7B18C4E5A3ABC51E637FDF" +
+                "6E37FDFF04C940919390F4FF92000000000000000000000000000000000000" +
+                "00000000000000000000000000000000000000000000000000000000000000" +
+                "00000000000000000000000000000000000000000000000000000000000000" +
+                "00000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000");
+
+        assignHexString.invoke(bignum2,
+                "3DA774C07FB5DF54284D09C675A492165B830D5DAAEB2A7501" +
+                "DA17CF9DFA1CA2282269F92A25A97314296B717E3DCBB9FE17" +
+                "41A842FE2913F540F40796F2381155763502C58B15AF7A7F88" +
+                "6F744C9164FF409A28F7FA0C41F89ED79C1BE9F322C8578B97" +
+                "841F1CBAA17D901BE1230E3C00E1C643AF32638B5674E01FEA" +
+                "96FC90864E621B856A9E1CE56E6EB545B9C2F8F0CC10DDA88D" +
+                "CC6D282605F8DB67044F2DFD3695E7BA63877AE16701536AE6" +
+                "567C794D0BFE338DFBB42D92D4215AF3BB22BF0A8B283FDDC2" +
+                "C667A10958EA6D2");
+        assertEquals(toHexString.invoke(bignum2),
+                "3DA774C07FB5DF54284D09C675A492165B830D5DAAEB2A7501" +
+                "DA17CF9DFA1CA2282269F92A25A97314296B717E3DCBB9FE17" +
+                "41A842FE2913F540F40796F2381155763502C58B15AF7A7F88" +
+                "6F744C9164FF409A28F7FA0C41F89ED79C1BE9F322C8578B97" +
+                "841F1CBAA17D901BE1230E3C00E1C643AF32638B5674E01FEA" +
+                "96FC90864E621B856A9E1CE56E6EB545B9C2F8F0CC10DDA88D" +
+                "CC6D282605F8DB67044F2DFD3695E7BA63877AE16701536AE6" +
+                "567C794D0BFE338DFBB42D92D4215AF3BB22BF0A8B283FDDC2" +
+                "C667A10958EA6D2");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 1);
+
+        assertEquals(toHexString.invoke(bignum),
+                "2688A8F84FD1AB949930261C0986DB4DF931E85A8AD2FA8921284EE1C2BC51" +
+                "E55915823BBA5789E7EC99E326EEE69F543ECE890929DED9AC79489884BE57" +
+                "630AD569E121BB76ED8DAC8FB545A8AFDADF1F8860599AFC47A93B6346C191" +
+                "7237F5BD36B73EB29371F4A4EE7A116CB5E8E5808D1BEA4D7F7E3716090C13" +
+                "F29E5DDA53F0FD513362A2D20F6505314B9419DB967F8A8A89589FC43917C3" +
+                "BB892062B17CBE421DB0D47E34ACCCE060D422CFF60DCBD0277EE038BD509C" +
+                "7BC494D8D854F5B76696F927EA99BC00C4A5D7928434");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 2);
+
+        assertEquals(toHexString.invoke(bignum),
+                "1815699B31E30B3CDFBE17D185F44910BBBF313896C3DC95B4B9314D19B5B32" +
+                "F57AD71655476B630F3E02DF855502394A74115A5BA2B480BCBCD5F52F6F69D" +
+                "E6C5622CB5152A54788BD9D14B896DE8CB73B53C3800DDACC9C51E0C38FAE76" +
+                "2F9964232872F9C2738E7150C4AE3F1B18F70583172706FAEE26DC5A78C77A2" +
+                "FAA874769E52C01DA5C3499F233ECF3C90293E0FB69695D763DAA3AEDA5535B" +
+                "43DAEEDF6E9528E84CEE0EC000C3C8495C1F9C89F6218AF4C23765261CD5ADD" +
+                "0787351992A01E5BB8F2A015807AE7A6BB92A08");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 5);
+
+        assertEquals(toHexString.invoke(bignum),
+                "5E13A4863ADEE3E5C9FE8D0A73423D695D62D8450CED15A8C9F368952C6DC3" +
+                "F0EE7D82F3D1EFB7AF38A3B3920D410AFCAD563C8F5F39116E141A3C5C14B3" +
+                "58CD73077EA35AAD59F6E24AD98F10D5555ABBFBF33AC361EAF429FD5FBE94" +
+                "17DA9EF2F2956011F9F93646AA38048A681D984ED88127073443247CCC167C" +
+                "B354A32206EF5A733E73CF82D795A1AD598493211A6D613C39515E0E0F6304" +
+                "DCD9C810F3518C7F6A7CB6C81E99E02FCC65E8FDB7B7AE97306CC16A8631CE" +
+                "0A2AEF6568276BE4C176964A73C153FDE018E34CB4C2F40");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 10);
+
+        assertEquals(toHexString.invoke(bignum),
+                "8F8CB8EB51945A7E815809F6121EF2F4E61EF3405CD9432CAD2709749EEAFD" +
+                "1B81E843F14A3667A7BDCCC9E0BB795F63CDFDB62844AC7438976C885A0116" +
+                "29607DA54F9C023CC366570B7637ED0F855D931752038A614922D0923E382C" +
+                "B8E5F6C975672DB76E0DE471937BB9EDB11E28874F1C122D5E1EF38CECE9D0" +
+                "0723056BCBD4F964192B76830634B1D322B7EB0062F3267E84F5C824343A77" +
+                "4B7DCEE6DD464F01EBDC8C671BB18BB4EF4300A42474A6C77243F2A12B03BF" +
+                "0443C38A1C0D2701EDB393135AE0DEC94211F9D4EB51F990800");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 50);
+
+        assertEquals(toHexString.invoke(bignum),
+                "107A8BE345E24407372FC1DE442CBA696BC23C4FFD5B4BDFD9E5C39559815" +
+                "86628CF8472D2D589F2FC2BAD6E0816EC72CBF85CCA663D8A1EC6C51076D8" +
+                "2D247E6C26811B7EC4D4300FB1F91028DCB7B2C4E7A60C151161AA7E65E79" +
+                "B40917B12B2B5FBE7745984D4E8EFA31F9AE6062427B068B144A9CB155873" +
+                "E7C0C9F0115E5AC72DC5A73C4796DB970BF9205AB8C77A6996EB1B417F9D1" +
+                "6232431E6313C392203601B9C22CC10DDA88DCC6D282605F8DB67044F2DFD" +
+                "3695E7BA63877AE16701536AE6567C794D0BFE338DFBB42D924CF964BD2C0" +
+                "F586E03A2FCD35A408000000000000");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 100);
+
+        assertEquals(toHexString.invoke(bignum),
+                "46784A90ACD0ED3E7759CC585FB32D36EB6034A6F78D92604E3BAA5ED3D8B" +
+                "6E60E854439BE448897FB4B7EA5A3D873AA0FCB3CFFD80D0530880E45F511" +
+                "722A50CE7E058B5A6F5464DB7500E34984EE3202A9441F44FA1554C0CEA96" +
+                "B438A36F25E7C9D56D71AE2CD313EC37534DA299AC0854FC48591A7CF3171" +
+                "31265AA4AE62DE32344CE7BEEEF894AE686A2DAAFE5D6D9A10971FFD9C064" +
+                "5079B209E1048F58B5192D41D84336AC4C8C489EEF00939CFC9D55C122036" +
+                "01B9C22CC10DDA88DCC6D282605F8DB67044F2DFD3695E7BA3F67B96D3A32" +
+                "E11FB5561B68744C4035B0800DC166D49D98E3FD1D5BB2000000000000000" +
+                "0000000000");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 200);
+
+        assertEquals(toHexString.invoke(bignum),
+                "508BD351221DF139D72D88CDC0416845A53EE2D0E6B98352509A9AC312F8C" +
+                "6CB1A144889416201E0B6CE66EA3EBE259B5FD79ECFC1FD77963CE516CC7E" +
+                "2FE73D4B5B710C19F6BCB092C7A2FD76286543B8DBD2C596DFF2C896720BA" +
+                "DFF7BC9C366ACEA3A880AEC287C5E6207DF2739B5326FC19D773BD830B109" +
+                "ED36C7086544BF8FDB9D4B73719C2B5BC2F571A5937EC46876CD428281F6B" +
+                "F287E1E07F25C1B1D46BC37324FF657A8B2E0071DB83B86123CA34004F406" +
+                "001082D7945E90C6E8C9A9FEC2B44BE0DDA46E9F52B152E4D1336D2FCFBC9" +
+                "96E30CA0082256737365158FE36482AA7EB9DAF2AB128F10E7551A3CD5BE6" +
+                "0A922F3A7D5EED38B634A7EC95BCF7021BA6820A292000000000000000000" +
+                "00000000000000000000000000000000");
+
+        assignBignum.invoke(bignum, bignum2);
+        multiplyByPowerOfTen.invoke(bignum, 500);
+
+        assertEquals(toHexString.invoke(bignum),
+                "7845F900E475B5086885BAAAE67C8E85185ACFE4633727F82A4B06B5582AC" +
+                "BE933C53357DA0C98C20C5AC900C4D76A97247DF52B79F48F9E35840FB715" +
+                "D392CE303E22622B0CF82D9471B398457DD3196F639CEE8BBD2C146873841" +
+                "F0699E6C41F04FC7A54B48CEB995BEB6F50FE81DE9D87A8D7F849CC523553" +
+                "7B7BBBC1C7CAAFF6E9650BE03B308C6D31012AEF9580F70D3EE2083ADE126" +
+                "8940FA7D6308E239775DFD2F8C97FF7EBD525DAFA6512216F7047A62A93DC" +
+                "38A0165BDC67E250DCC96A0181DE935A70B38704DC71819F02FC5261FF7E1" +
+                "E5F11907678B0A3E519FF4C10A867B0C26CE02BE6960BA8621A87303C101C" +
+                "3F88798BB9F7739655946F8B5744E6B1EAF10B0C5621330F0079209033C69" +
+                "20DE2E2C8D324F0624463735D482BF291926C22A910F5B80FA25170B6B57D" +
+                "8D5928C7BCA3FE87461275F69BD5A1B83181DAAF43E05FC3C72C4E93111B6" +
+                "6205EBF49B28FEDFB7E7526CBDA658A332000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000");
+    }
+
+
+    @Test
+    public void testDivideModuloIntBignum() throws Exception {
+
+        final Object bignum = ctor.newInstance();
+        final Object other = ctor.newInstance();
+        final Object third = ctor.newInstance();
+
+        final Method addBignum = method("addBignum", Bignum);
+        final Method assignBignum = method("assignBignum", Bignum);
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method divideModuloIntBignum = method("divideModuloIntBignum", Bignum);
+        final Method multiplyByUInt32 = method("multiplyByUInt32", int.class);
+        final Method shiftLeft = method("shiftLeft", int.class);
+        final Method subtractBignum = method("subtractBignum", Bignum);
+        final Method toHexString = method("toHexString");
+
+        assignUInt16.invoke(bignum, (char) 10);
+        assignUInt16.invoke(other, (char) 2);
+        assertEquals((char) 5, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignUInt16.invoke(bignum, (char) 10);
+        shiftLeft.invoke(bignum, 500);
+        assignUInt16.invoke(other, (char) 2);
+        shiftLeft.invoke(other, 500);
+        assertEquals((char) 5, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "0");
+
+        assignUInt16.invoke(bignum, (char) 11);
+        assignUInt16.invoke(other, (char) 2);
+        assertEquals((char) 5, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "1");
+
+        assignUInt16.invoke(bignum, (char) 10);
+        shiftLeft.invoke(bignum, 500);
+        assignUInt16.invoke(other, (char) 1);
+        addBignum.invoke(bignum, other);
+        assignUInt16.invoke(other, (char) 2);
+        shiftLeft.invoke(other, 500);
+        assertEquals((char) 5, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "1");
+
+        assignUInt16.invoke(bignum, (char) 10);
+        shiftLeft.invoke(bignum, 500);
+        assignBignum.invoke(other, bignum);
+        multiplyByUInt32.invoke(bignum, 0x1234);
+        assignUInt16.invoke(third, (char) 0xFFF);
+        addBignum.invoke(bignum, third);
+        assertEquals((char) 0x1234, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "FFF");
+
+        assignUInt16.invoke(bignum, (char) 10);
+        assignHexString.invoke(other, "1234567890");
+        assertEquals((char) 0, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "A");
+
+        assignHexString.invoke(bignum, "12345678");
+        assignHexString.invoke(other, "3789012");
+        assertEquals((char) 5, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "D9861E");
+
+        assignHexString.invoke(bignum, "70000001");
+        assignHexString.invoke(other, "1FFFFFFF");
+        assertEquals((char) 3, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "10000004");
+
+        assignHexString.invoke(bignum, "28000000");
+        assignHexString.invoke(other, "12A05F20");
+        assertEquals((char) 2, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "2BF41C0");
+
+        assignUInt16.invoke(bignum, (char) 10);
+        shiftLeft.invoke(bignum, 500);
+        assignBignum.invoke(other, bignum);
+        multiplyByUInt32.invoke(bignum, 0x1234);
+        assignUInt16.invoke(third, (char) 0xFFF);
+        subtractBignum.invoke(other, third);
+        assertEquals((char) 0x1234, (char)  divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "1232DCC");
+        assertEquals((char) 0, (char) divideModuloIntBignum.invoke(bignum, other));
+        assertEquals(toHexString.invoke(bignum), "1232DCC");
+    }
+
+
+    @Test
+    public void testCompare() throws Exception {
+
+        final Object bignum1 = ctor.newInstance();
+        final Object bignum2 = ctor.newInstance();
+
+        final Method assignUInt16 = method("assignUInt16", char.class);
+        final Method assignHexString = method("assignHexString", String.class);
+        final Method compare = method("compare", Bignum, Bignum);
+        final Method equal = method("equal", Bignum, Bignum);
+        final Method less = method("less", Bignum, Bignum);
+        final Method lessEqual = method("lessEqual", Bignum, Bignum);
+        final Method shiftLeft = method("shiftLeft", int.class);
+
+        assignUInt16.invoke(bignum1, (char) 1);
+        assignUInt16.invoke(bignum2, (char) 1);
+        assertEquals(0, compare.invoke(null, bignum1, bignum2));
+        assertTrue((boolean) equal.invoke(null, bignum1, bignum2));
+        assertTrue((boolean) lessEqual.invoke(null, bignum1, bignum2));
+        assertTrue(!(boolean) less.invoke(null, bignum1, bignum2));
+
+        assignUInt16.invoke(bignum1, (char) 0);
+        assignUInt16.invoke(bignum2, (char) 1);
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+        assertTrue(!(boolean) equal.invoke(null, bignum1, bignum2));
+        assertTrue(!(boolean) equal.invoke(null, bignum2, bignum1));
+        assertTrue((boolean) lessEqual.invoke(null, bignum1, bignum2));
+        assertTrue(!(boolean) lessEqual.invoke(null, bignum2, bignum1));
+        assertTrue((boolean) less.invoke(null, bignum1, bignum2));
+        assertTrue(!(boolean) less.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "1234567890ABCDEF12345");
+        assignHexString.invoke(bignum2, "1234567890ABCDEF12345");
+        assertEquals(0, compare.invoke(null, bignum1, bignum2));
+
+        assignHexString.invoke(bignum1, "1234567890ABCDEF12345");
+        assignHexString.invoke(bignum2, "1234567890ABCDEF12346");
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "1234567890ABCDEF12345");
+        shiftLeft.invoke(bignum1, 500);
+        assignHexString.invoke(bignum2, "1234567890ABCDEF12345");
+        shiftLeft.invoke(bignum2, 500);
+        assertEquals(0, compare.invoke(null, bignum1, bignum2));
+
+        assignHexString.invoke(bignum1, "1234567890ABCDEF12345");
+        shiftLeft.invoke(bignum1, 500);
+        assignHexString.invoke(bignum2, "1234567890ABCDEF12346");
+        shiftLeft.invoke(bignum2, 500);
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignUInt16.invoke(bignum1, (char) 1);
+        shiftLeft.invoke(bignum1, 64);
+        assignHexString.invoke(bignum2, "10000000000000000");
+        assertEquals(0, compare.invoke(null, bignum1, bignum2));
+        assertEquals(0, compare.invoke(null, bignum2, bignum1));
+
+        assignUInt16.invoke(bignum1, (char) 1);
+        shiftLeft.invoke(bignum1, 64);
+        assignHexString.invoke(bignum2, "10000000000000001");
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignUInt16.invoke(bignum1, (char) 1);
+        shiftLeft.invoke(bignum1, 96);
+        assignHexString.invoke(bignum2, "10000000000000001");
+        shiftLeft.invoke(bignum2, 32);
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "FFFFFFFFFFFFFFFF");
+        assignUInt16.invoke(bignum2, (char) 1);
+        shiftLeft.invoke(bignum2, 64);
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "FFFFFFFFFFFFFFFF");
+        shiftLeft.invoke(bignum1, 32);
+        assignUInt16.invoke(bignum2, (char) 1);
+        shiftLeft.invoke(bignum2, 96);
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "FFFFFFFFFFFFFFFF");
+        shiftLeft.invoke(bignum1, 32);
+        assignUInt16.invoke(bignum2, (char) 1);
+        shiftLeft.invoke(bignum2, 95);
+        assertEquals(+1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(-1, compare.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "FFFFFFFFFFFFFFFF");
+        shiftLeft.invoke(bignum1, 32);
+        assignUInt16.invoke(bignum2, (char) 1);
+        shiftLeft.invoke(bignum2, 100);
+        assertEquals(-1, compare.invoke(null, bignum1, bignum2));
+        assertEquals(+1, compare.invoke(null, bignum2, bignum1));
+
+        assignHexString.invoke(bignum1, "100000000000000");
+        assignUInt16.invoke(bignum2, (char) 1);
+        shiftLeft.invoke(bignum2, 14*4);
+        assertEquals(0, compare.invoke(null, bignum1, bignum2));
+        assertEquals(0, compare.invoke(null, bignum2, bignum1));