NativeNumber.java revision 1551:f3b883bec2d0
1227569Sphilip/*
2227569Sphilip * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3227569Sphilip * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4227569Sphilip *
5227569Sphilip * This code is free software; you can redistribute it and/or modify it
6227569Sphilip * under the terms of the GNU General Public License version 2 only, as
7227569Sphilip * published by the Free Software Foundation.  Oracle designates this
8227569Sphilip * particular file as subject to the "Classpath" exception as provided
9227569Sphilip * by Oracle in the LICENSE file that accompanied this code.
10227569Sphilip *
11227569Sphilip * This code is distributed in the hope that it will be useful, but WITHOUT
12227569Sphilip * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13227569Sphilip * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14227569Sphilip * version 2 for more details (a copy is included in the LICENSE file that
15227569Sphilip * accompanied this code).
16227569Sphilip *
17227569Sphilip * You should have received a copy of the GNU General Public License version
18227569Sphilip * 2 along with this work; if not, write to the Free Software Foundation,
19227569Sphilip * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20227569Sphilip *
21227569Sphilip * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22227569Sphilip * or visit www.oracle.com if you need additional information or have any
23227569Sphilip * questions.
24227569Sphilip */
25227569Sphilip
26228100Sphilippackage jdk.nashorn.internal.objects;
27228100Sphilip
28228100Sphilipimport static jdk.nashorn.internal.lookup.Lookup.MH;
29227569Sphilipimport static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
30227569Sphilipimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
31227569Sphilipimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
32227569Sphilip
33227569Sphilipimport java.lang.invoke.MethodHandle;
34227569Sphilipimport java.lang.invoke.MethodHandles;
35227569Sphilipimport java.lang.invoke.MethodType;
36227569Sphilipimport java.math.RoundingMode;
37227569Sphilipimport java.text.NumberFormat;
38227569Sphilipimport java.util.Locale;
39227569Sphilipimport jdk.dynalink.linker.GuardedInvocation;
40227569Sphilipimport jdk.dynalink.linker.LinkRequest;
41227569Sphilipimport jdk.nashorn.internal.objects.annotations.Attribute;
42227569Sphilipimport jdk.nashorn.internal.objects.annotations.Constructor;
43227569Sphilipimport jdk.nashorn.internal.objects.annotations.Function;
44227569Sphilipimport jdk.nashorn.internal.objects.annotations.Property;
45227569Sphilipimport jdk.nashorn.internal.objects.annotations.ScriptClass;
46227569Sphilipimport jdk.nashorn.internal.objects.annotations.SpecializedFunction;
47227569Sphilipimport jdk.nashorn.internal.objects.annotations.Where;
48227569Sphilipimport jdk.nashorn.internal.runtime.JSType;
49227569Sphilipimport jdk.nashorn.internal.runtime.PropertyMap;
50227569Sphilipimport jdk.nashorn.internal.runtime.ScriptObject;
51227569Sphilipimport jdk.nashorn.internal.runtime.ScriptRuntime;
52227569Sphilipimport jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
53227569Sphilipimport jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
54227569Sphilip
55227569Sphilip/**
56227569Sphilip * ECMA 15.7 Number Objects.
57227569Sphilip *
58227569Sphilip */
59227569Sphilip@ScriptClass("Number")
60227569Sphilippublic final class NativeNumber extends ScriptObject {
61227569Sphilip
62227569Sphilip    /** Method handle to create an object wrapper for a primitive number. */
63227569Sphilip    static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class));
64227569Sphilip    /** Method handle to retrieve the Number prototype object. */
65227569Sphilip    private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
66227569Sphilip
67227569Sphilip    /** ECMA 15.7.3.2 largest positive finite value */
68227569Sphilip    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR)
69227569Sphilip    public static final double MAX_VALUE = Double.MAX_VALUE;
70227569Sphilip
71227569Sphilip    /** ECMA 15.7.3.3 smallest positive finite value */
72227569Sphilip    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR)
73227569Sphilip    public static final double MIN_VALUE = Double.MIN_VALUE;
74227569Sphilip
75227569Sphilip    /** ECMA 15.7.3.4 NaN */
76227569Sphilip    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR)
77227569Sphilip    public static final double NaN = Double.NaN;
78227569Sphilip
79227569Sphilip    /** ECMA 15.7.3.5 negative infinity */
80227569Sphilip    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR)
81227569Sphilip    public static final double NEGATIVE_INFINITY = Double.NEGATIVE_INFINITY;
82227569Sphilip
83227569Sphilip    /** ECMA 15.7.3.5 positive infinity */
84227569Sphilip    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR)
85227569Sphilip    public static final double POSITIVE_INFINITY = Double.POSITIVE_INFINITY;
86227569Sphilip
87227569Sphilip    private final double  value;
88227569Sphilip
89227569Sphilip    // initialized by nasgen
90227569Sphilip    private static PropertyMap $nasgenmap$;
91227569Sphilip
92227569Sphilip    private NativeNumber(final double value, final ScriptObject proto, final PropertyMap map) {
93227569Sphilip        super(proto, map);
94227569Sphilip        this.value = value;
95227569Sphilip    }
96227569Sphilip
97227569Sphilip    NativeNumber(final double value, final Global global) {
98227569Sphilip        this(value, global.getNumberPrototype(), $nasgenmap$);
99227569Sphilip    }
100227569Sphilip
101227569Sphilip    private NativeNumber(final double value) {
102227569Sphilip        this(value, Global.instance());
103227569Sphilip    }
104227569Sphilip
105227569Sphilip
106227569Sphilip    @Override
107227569Sphilip    public String safeToString() {
108227569Sphilip        return "[Number " + toString() + "]";
109227569Sphilip    }
110227569Sphilip
111227569Sphilip    @Override
112227569Sphilip    public String toString() {
113227569Sphilip        return Double.toString(getValue());
114227569Sphilip    }
115227569Sphilip
116227569Sphilip    /**
117227569Sphilip     * Get the value of this Number
118227569Sphilip     * @return a {@code double} representing the Number value
119227569Sphilip     */
120227569Sphilip    public double getValue() {
121227569Sphilip        return doubleValue();
122227569Sphilip    }
123227569Sphilip
124227569Sphilip    /**
125227569Sphilip     * Get the value of this Number
126227569Sphilip     * @return a {@code double} representing the Number value
127227569Sphilip     */
128227569Sphilip    public double doubleValue() {
129227569Sphilip        return value;
130227569Sphilip    }
131227569Sphilip
132227569Sphilip    @Override
133227569Sphilip    public String getClassName() {
134227569Sphilip        return "Number";
135227569Sphilip    }
136227569Sphilip
137227569Sphilip    /**
138227569Sphilip     * ECMA 15.7.2 - The Number constructor
139227569Sphilip     *
140227569Sphilip     * @param newObj is this Number instantiated with the new operator
141227569Sphilip     * @param self   self reference
142227569Sphilip     * @param args   value of number
143227569Sphilip     * @return the Number instance (internally represented as a {@code NativeNumber})
144227569Sphilip     */
145227569Sphilip    @Constructor(arity = 1)
146227569Sphilip    public static Object constructor(final boolean newObj, final Object self, final Object... args) {
147227569Sphilip        final double num = (args.length > 0) ? JSType.toNumber(args[0]) : 0.0;
148227569Sphilip
149227569Sphilip        return newObj? new NativeNumber(num) : num;
150227569Sphilip    }
151227569Sphilip
152227569Sphilip    /**
153227569Sphilip     * ECMA 15.7.4.5 Number.prototype.toFixed (fractionDigits)
154227569Sphilip     *
155227569Sphilip     * @param self           self reference
156227569Sphilip     * @param fractionDigits how many digits should be after the decimal point, 0 if undefined
157227569Sphilip     *
158227569Sphilip     * @return number in decimal fixed point notation
159227569Sphilip     */
160227569Sphilip    @Function(attributes = Attribute.NOT_ENUMERABLE)
161227569Sphilip    public static String toFixed(final Object self, final Object fractionDigits) {
162227569Sphilip        return toFixed(self, JSType.toInteger(fractionDigits));
163227569Sphilip    }
164227569Sphilip
165227569Sphilip    /**
166227569Sphilip     * ECMA 15.7.4.5 Number.prototype.toFixed (fractionDigits) specialized for int fractionDigits
167227569Sphilip     *
168227569Sphilip     * @param self           self reference
169227569Sphilip     * @param fractionDigits how many digits should be after the decimal point, 0 if undefined
170227569Sphilip     *
171227569Sphilip     * @return number in decimal fixed point notation
172227569Sphilip     */
173227569Sphilip    @SpecializedFunction
174227569Sphilip    public static String toFixed(final Object self, final int fractionDigits) {
175227569Sphilip        if (fractionDigits < 0 || fractionDigits > 20) {
176227569Sphilip            throw rangeError("invalid.fraction.digits", "toFixed");
177227569Sphilip        }
178227569Sphilip
179227569Sphilip        final double x = getNumberValue(self);
180227569Sphilip        if (Double.isNaN(x)) {
181227569Sphilip            return "NaN";
182227569Sphilip        }
183227569Sphilip
184227569Sphilip        if (Math.abs(x) >= 1e21) {
185227569Sphilip            return JSType.toString(x);
186227569Sphilip        }
187227569Sphilip
188227569Sphilip        return DoubleConversion.toFixed(x, fractionDigits);
189227569Sphilip    }
190227569Sphilip
191227569Sphilip    /**
192227569Sphilip     * ECMA 15.7.4.6 Number.prototype.toExponential (fractionDigits)
193227569Sphilip     *
194227569Sphilip     * @param self           self reference
195227569Sphilip     * @param fractionDigits how many digital should be after the significand's decimal point. If undefined, use as many as necessary to uniquely specify number.
196227569Sphilip     *
197227569Sphilip     * @return number in decimal exponential notation
198227569Sphilip     */
199227569Sphilip    @Function(attributes = Attribute.NOT_ENUMERABLE)
200227569Sphilip    public static String toExponential(final Object self, final Object fractionDigits) {
201227569Sphilip        final double  x         = getNumberValue(self);
202227569Sphilip        final boolean trimZeros = fractionDigits == UNDEFINED;
203227569Sphilip        final int     f         = trimZeros ? 16 : JSType.toInteger(fractionDigits);
204227569Sphilip
205227569Sphilip        if (Double.isNaN(x)) {
206227569Sphilip            return "NaN";
207227569Sphilip        } else if (Double.isInfinite(x)) {
208227569Sphilip            return x > 0? "Infinity" : "-Infinity";
209227569Sphilip        }
210227569Sphilip
211227569Sphilip        if (fractionDigits != UNDEFINED && (f < 0 || f > 20)) {
212227569Sphilip            throw rangeError("invalid.fraction.digits", "toExponential");
213227569Sphilip        }
214227569Sphilip
215227569Sphilip        final String res = String.format(Locale.US, "%1." + f + "e", x);
216227569Sphilip        return fixExponent(res, trimZeros);
217227569Sphilip    }
218227569Sphilip
219227569Sphilip    /**
220227569Sphilip     * ECMA 15.7.4.7 Number.prototype.toPrecision (precision)
221227569Sphilip     *
222227569Sphilip     * @param self      self reference
223227569Sphilip     * @param precision use {@code precision - 1} digits after the significand's decimal point or call {@link JSType#toString} if undefined
224227569Sphilip     *
225227569Sphilip     * @return number in decimal exponentiation notation or decimal fixed notation depending on {@code precision}
226227569Sphilip     */
227227569Sphilip    @Function(attributes = Attribute.NOT_ENUMERABLE)
228227569Sphilip    public static String toPrecision(final Object self, final Object precision) {
229227569Sphilip        final double x = getNumberValue(self);
230227569Sphilip        if (precision == UNDEFINED) {
231227569Sphilip            return JSType.toString(x);
232227569Sphilip        }
233227569Sphilip        return (toPrecision(x, JSType.toInteger(precision)));
234227569Sphilip    }
235227569Sphilip
236227569Sphilip    /**
237227569Sphilip     * ECMA 15.7.4.7 Number.prototype.toPrecision (precision) specialized f
238227569Sphilip     *
239227569Sphilip     * @param self      self reference
240227569Sphilip     * @param precision use {@code precision - 1} digits after the significand's decimal point.
241227569Sphilip     *
242227569Sphilip     * @return number in decimal exponentiation notation or decimal fixed notation depending on {@code precision}
243227569Sphilip     */
244227569Sphilip    @SpecializedFunction
245227569Sphilip    public static String toPrecision(final Object self, final int precision) {
246227569Sphilip        return toPrecision(getNumberValue(self), precision);
247227569Sphilip    }
248227569Sphilip
249227569Sphilip    private static String toPrecision(final double x, final int p) {
250227569Sphilip        if (Double.isNaN(x)) {
251227569Sphilip            return "NaN";
252227569Sphilip        } else if (Double.isInfinite(x)) {
253227569Sphilip            return x > 0? "Infinity" : "-Infinity";
254227569Sphilip        }
255227569Sphilip
256227569Sphilip        if (p < 1 || p > 21) {
257227569Sphilip            throw rangeError("invalid.precision");
258227569Sphilip        }
259227569Sphilip
260227569Sphilip        // workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6469160
261227569Sphilip        if (x == 0.0 && p <= 1) {
262227569Sphilip            return "0";
263227569Sphilip        }
264227569Sphilip
265227569Sphilip        return DoubleConversion.toPrecision(x, p);
266227569Sphilip    }
267227569Sphilip
268227569Sphilip    /**
269227569Sphilip     * ECMA 15.7.4.2 Number.prototype.toString ( [ radix ] )
270227569Sphilip     *
271227569Sphilip     * @param self  self reference
272227569Sphilip     * @param radix radix to use for string conversion
273227569Sphilip     * @return string representation of this Number in the given radix
274227569Sphilip     */
275227569Sphilip    @Function(attributes = Attribute.NOT_ENUMERABLE)
276227569Sphilip    public static String toString(final Object self, final Object radix) {
277227569Sphilip        if (radix != UNDEFINED) {
278227569Sphilip            final int intRadix = JSType.toInteger(radix);
279227569Sphilip            if (intRadix != 10) {
280227569Sphilip                if (intRadix < 2 || intRadix > 36) {
281227569Sphilip                    throw rangeError("invalid.radix");
282227569Sphilip                }
283227569Sphilip                return JSType.toString(getNumberValue(self), intRadix);
284227569Sphilip            }
285227569Sphilip        }
286227569Sphilip
287227569Sphilip        return JSType.toString(getNumberValue(self));
288227569Sphilip    }
289227569Sphilip
290227569Sphilip    /**
291227569Sphilip     * ECMA 15.7.4.3 Number.prototype.toLocaleString()
292227569Sphilip     *
293227569Sphilip     * @param self self reference
294227569Sphilip     * @return localized string for this Number
295227569Sphilip     */
296227569Sphilip    @Function(attributes = Attribute.NOT_ENUMERABLE)
297227569Sphilip    public static String toLocaleString(final Object self) {
298227569Sphilip        return JSType.toString(getNumberValue(self));
299227569Sphilip    }
300227569Sphilip
301227569Sphilip
302227569Sphilip    /**
303227569Sphilip     * ECMA 15.7.4.4 Number.prototype.valueOf ( )
304227569Sphilip     *
305227569Sphilip     * @param self self reference
306227569Sphilip     * @return number value for this Number
307227569Sphilip     */
308227569Sphilip    @Function(attributes = Attribute.NOT_ENUMERABLE)
309227569Sphilip    public static double valueOf(final Object self) {
310227569Sphilip        return getNumberValue(self);
311227569Sphilip    }
312227569Sphilip
313227569Sphilip    /**
314227569Sphilip     * Lookup the appropriate method for an invoke dynamic call.
315227569Sphilip     * @param request  The link request
316227569Sphilip     * @param receiver receiver of call
317227569Sphilip     * @return Link to be invoked at call site.
318227569Sphilip     */
319227569Sphilip    public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
320227569Sphilip        return PrimitiveLookup.lookupPrimitive(request, Number.class, new NativeNumber(((Number)receiver).doubleValue()), WRAPFILTER, PROTOFILTER);
321227569Sphilip    }
322227569Sphilip
323227569Sphilip    @SuppressWarnings("unused")
324227569Sphilip    private static NativeNumber wrapFilter(final Object receiver) {
325227569Sphilip        return new NativeNumber(((Number)receiver).doubleValue());
326227569Sphilip    }
327227569Sphilip
328227569Sphilip    @SuppressWarnings("unused")
329227569Sphilip    private static Object protoFilter(final Object object) {
330227569Sphilip        return Global.instance().getNumberPrototype();
331227569Sphilip    }
332227569Sphilip
333227569Sphilip    private static double getNumberValue(final Object self) {
334227569Sphilip        if (self instanceof Number) {
335227569Sphilip            return ((Number)self).doubleValue();
336227569Sphilip        } else if (self instanceof NativeNumber) {
337227569Sphilip            return ((NativeNumber)self).getValue();
338227569Sphilip        } else if (self != null && self == Global.instance().getNumberPrototype()) {
339227569Sphilip            return 0.0;
340227569Sphilip        } else {
341227569Sphilip            throw typeError("not.a.number", ScriptRuntime.safeToString(self));
342227569Sphilip        }
343227569Sphilip    }
344227569Sphilip
345227569Sphilip    // Exponent of Java "e" or "E" formatter is always 2 digits and zero
346227569Sphilip    // padded if needed (e+01, e+00, e+12 etc.) JS expects exponent to contain
347227569Sphilip    // exact number of digits e+1, e+0, e+12 etc. Fix the exponent here.
348227569Sphilip    //
349227569Sphilip    // Additionally, if trimZeros is true, this cuts trailing zeros in the
350227569Sphilip    // fraction part for calls to toExponential() with undefined fractionDigits
351227569Sphilip    // argument.
352227569Sphilip    private static String fixExponent(final String str, final boolean trimZeros) {
353227569Sphilip        final int index = str.indexOf('e');
354227569Sphilip        if (index < 1) {
355227569Sphilip            // no exponent, do nothing..
356227569Sphilip            return str;
357227569Sphilip        }
358227569Sphilip
359227569Sphilip        // check if character after e+ or e- is 0
360227569Sphilip        final int expPadding = str.charAt(index + 2) == '0' ? 3 : 2;
361227569Sphilip        // check if there are any trailing zeroes we should remove
362227569Sphilip
363227569Sphilip        int fractionOffset = index;
364227569Sphilip        if (trimZeros) {
365227569Sphilip            assert fractionOffset > 0;
366227569Sphilip            char c = str.charAt(fractionOffset - 1);
367227569Sphilip            while (fractionOffset > 1 && (c == '0' || c == '.')) {
368227569Sphilip                c = str.charAt(--fractionOffset - 1);
369227569Sphilip            }
370227569Sphilip
371227569Sphilip        }
372227569Sphilip        // if anything needs to be done compose a new string
373227569Sphilip        if (fractionOffset < index || expPadding == 3) {
374227569Sphilip            return str.substring(0, fractionOffset)
375227569Sphilip                    + str.substring(index, index + 2)
376227569Sphilip                    + str.substring(index + expPadding);
377227569Sphilip        }
378227569Sphilip        return str;
379227569Sphilip    }
380227569Sphilip
381227569Sphilip    private static MethodHandle findOwnMH(final String name, final MethodType type) {
382227569Sphilip        return MH.findStatic(MethodHandles.lookup(), NativeNumber.class, name, type);
383227569Sphilip    }
384227569Sphilip}
385227569Sphilip