/*
 * 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 static jdk.nashorn.internal.lookup.Lookup.MH;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Locale;

Utilities used by Global class.
/** * Utilities used by Global class. */
public final class GlobalFunctions {
Methodhandle to implementation of ECMA 15.1.2.2, parseInt
/** Methodhandle to implementation of ECMA 15.1.2.2, parseInt */
public static final MethodHandle PARSEINT = findOwnMH("parseInt", double.class, Object.class, Object.class, Object.class);
Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt
/** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */
public static final MethodHandle PARSEINT_OI = findOwnMH("parseInt", double.class, Object.class, Object.class, int.class);
ParseInt - NaN for booleans (thru string conversion to number conversion)
/** ParseInt - NaN for booleans (thru string conversion to number conversion) */
public static final MethodHandle PARSEINT_Z = MH.dropArguments(MH.dropArguments(MH.constant(double.class, Double.NaN), 0, boolean.class), 0, Object.class);
ParseInt - identity for ints
/** ParseInt - identity for ints */
public static final MethodHandle PARSEINT_I = MH.dropArguments(MH.identity(int.class), 0, Object.class);
Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt
/** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */
public static final MethodHandle PARSEINT_O = findOwnMH("parseInt", double.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.1.2.3, parseFloat
/** Methodhandle to implementation of ECMA 15.1.2.3, parseFloat */
public static final MethodHandle PARSEFLOAT = findOwnMH("parseFloat", double.class, Object.class, Object.class);
isNan for integers - always false
/** isNan for integers - always false */
public static final MethodHandle IS_NAN_I = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class);
isNan for longs - always false
/** isNan for longs - always false */
public static final MethodHandle IS_NAN_J = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class);
IsNan for doubles - use Double.isNaN
/** IsNan for doubles - use Double.isNaN */
public static final MethodHandle IS_NAN_D = MH.dropArguments(MH.findStatic(MethodHandles.lookup(), Double.class, "isNaN", MH.type(boolean.class, double.class)), 0, Object.class);
Methodhandle to implementation of ECMA 15.1.2.4, isNaN
/** Methodhandle to implementation of ECMA 15.1.2.4, isNaN */
public static final MethodHandle IS_NAN = findOwnMH("isNaN", boolean.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.1.2.5, isFinite
/** Methodhandle to implementation of ECMA 15.1.2.5, isFinite */
public static final MethodHandle IS_FINITE = findOwnMH("isFinite", boolean.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.1.3.3, encodeURI
/** Methodhandle to implementation of ECMA 15.1.3.3, encodeURI */
public static final MethodHandle ENCODE_URI = findOwnMH("encodeURI", Object.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.1.3.4, encodeURIComponent
/** Methodhandle to implementation of ECMA 15.1.3.4, encodeURIComponent */
public static final MethodHandle ENCODE_URICOMPONENT = findOwnMH("encodeURIComponent", Object.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.1.3.1, decodeURI
/** Methodhandle to implementation of ECMA 15.1.3.1, decodeURI */
public static final MethodHandle DECODE_URI = findOwnMH("decodeURI", Object.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.1.3.2, decodeURIComponent
/** Methodhandle to implementation of ECMA 15.1.3.2, decodeURIComponent */
public static final MethodHandle DECODE_URICOMPONENT = findOwnMH("decodeURIComponent", Object.class, Object.class, Object.class);
Methodhandle to implementation of ECMA B.2.1, escape
/** Methodhandle to implementation of ECMA B.2.1, escape */
public static final MethodHandle ESCAPE = findOwnMH("escape", String.class, Object.class, Object.class);
Methodhandle to implementation of ECMA B.2.2, unescape
/** Methodhandle to implementation of ECMA B.2.2, unescape */
public static final MethodHandle UNESCAPE = findOwnMH("unescape", String.class, Object.class, Object.class);
Methodhandle to implementation of ECMA 15.3.4, "anonymous" - Properties of the Function Prototype Object.
/** Methodhandle to implementation of ECMA 15.3.4, "anonymous" - Properties of the Function Prototype Object. */
public static final MethodHandle ANONYMOUS = findOwnMH("anonymous", Object.class, Object.class); private static final String UNESCAPED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"; private GlobalFunctions() { }
ECMA 15.1.2.2 parseInt implementation
Params:
  • self – self reference
  • string – string to parse
  • rad – radix
Returns:numeric type representing string contents as an int
/** * ECMA 15.1.2.2 parseInt implementation * * @param self self reference * @param string string to parse * @param rad radix * * @return numeric type representing string contents as an int */
public static double parseInt(final Object self, final Object string, final Object rad) { return parseIntInternal(JSType.trimLeft(JSType.toString(string)), JSType.toInt32(rad)); }
ECMA 15.1.2.2 parseInt implementation specialized for int radix
Params:
  • self – self reference
  • string – string to parse
  • rad – radix
Returns:numeric type representing string contents as an int
/** * ECMA 15.1.2.2 parseInt implementation specialized for int radix * * @param self self reference * @param string string to parse * @param rad radix * * @return numeric type representing string contents as an int */
public static double parseInt(final Object self, final Object string, final int rad) { return parseIntInternal(JSType.trimLeft(JSType.toString(string)), rad); }
ECMA 15.1.2.2 parseInt implementation specialized for no radix argument
Params:
  • self – self reference
  • string – string to parse
Returns:numeric type representing string contents as an int
/** * ECMA 15.1.2.2 parseInt implementation specialized for no radix argument * * @param self self reference * @param string string to parse * * @return numeric type representing string contents as an int */
public static double parseInt(final Object self, final Object string) { return parseIntInternal(JSType.trimLeft(JSType.toString(string)), 0); } private static double parseIntInternal(final String str, final int rad) { final int length = str.length(); int radix = rad; // empty string is not valid if (length == 0) { return Double.NaN; } boolean negative = false; int idx = 0; // checking for the sign character final char firstChar = str.charAt(idx); if (firstChar < '0') { // Possible leading "+" or "-" if (firstChar == '-') { negative = true; } else if (firstChar != '+') { return Double.NaN; } // skip the sign character idx++; } boolean stripPrefix = true; if (radix != 0) { if (radix < 2 || radix > 36) { return Double.NaN; } if (radix != 16) { stripPrefix = false; } } else { // default radix radix = 10; } // strip "0x" or "0X" and treat radix as 16 if (stripPrefix && ((idx + 1) < length)) { final char c1 = str.charAt(idx); final char c2 = str.charAt(idx + 1); if (c1 == '0' && (c2 == 'x' || c2 == 'X')) { radix = 16; // skip "0x" or "0X" idx += 2; } } double result = 0.0; int digit; // we should see at least one valid digit boolean entered = false; while (idx < length) { digit = fastDigit(str.charAt(idx++), radix); if (digit < 0) { break; } // we have seen at least one valid digit in the specified radix entered = true; result *= radix; result += digit; } return entered ? (negative ? -result : result) : Double.NaN; }
ECMA 15.1.2.3 parseFloat implementation
Params:
  • self – self reference
  • string – string to parse
Returns:numeric type representing string contents
/** * ECMA 15.1.2.3 parseFloat implementation * * @param self self reference * @param string string to parse * * @return numeric type representing string contents */
public static double parseFloat(final Object self, final Object string) { final String str = JSType.trimLeft(JSType.toString(string)); final int length = str.length(); // empty string is not valid if (length == 0) { return Double.NaN; } int start = 0; boolean negative = false; char ch = str.charAt(0); if (ch == '-') { start++; negative = true; } else if (ch == '+') { start++; } else if (ch == 'N') { if (str.startsWith("NaN")) { return Double.NaN; } } if (start == length) { // just the sign character return Double.NaN; } ch = str.charAt(start); if (ch == 'I') { if (str.substring(start).startsWith("Infinity")) { return negative? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } } boolean dotSeen = false; boolean exponentOk = false; int exponentOffset = -1; int end; loop: for (end = start; end < length; end++) { ch = str.charAt(end); switch (ch) { case '.': // dot allowed only once if (exponentOffset != -1 || dotSeen) { break loop; } dotSeen = true; break; case 'e': case 'E': // 'e'/'E' allow only once if (exponentOffset != -1) { break loop; } exponentOffset = end; break; case '+': case '-': // Sign of the exponent. But allowed only if the // previous char in the string was 'e' or 'E'. if (exponentOffset != end - 1) { break loop; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (exponentOffset != -1) { // seeing digit after 'e' or 'E' exponentOk = true; } break; default: // ignore garbage at the end break loop; } } // ignore 'e'/'E' followed by '+/-' if not real exponent found if (exponentOffset != -1 && !exponentOk) { end = exponentOffset; } if (start == end) { return Double.NaN; } try { final double result = Double.valueOf(str.substring(start, end)); return negative ? -result : result; } catch (final NumberFormatException e) { return Double.NaN; } }
ECMA 15.1.2.4, isNaN implementation
Params:
  • self – self reference
  • number – number to check
Returns:true if number is NaN
/** * ECMA 15.1.2.4, isNaN implementation * * @param self self reference * @param number number to check * * @return true if number is NaN */
public static boolean isNaN(final Object self, final Object number) { return Double.isNaN(JSType.toNumber(number)); }
ECMA 15.1.2.5, isFinite implementation
Params:
  • self – self reference
  • number – number to check
Returns:true if number is infinite
/** * ECMA 15.1.2.5, isFinite implementation * * @param self self reference * @param number number to check * * @return true if number is infinite */
public static boolean isFinite(final Object self, final Object number) { final double value = JSType.toNumber(number); return ! (Double.isInfinite(value) || Double.isNaN(value)); }
ECMA 15.1.3.3, encodeURI implementation
Params:
  • self – self reference
  • uri – URI to encode
Returns:encoded URI
/** * ECMA 15.1.3.3, encodeURI implementation * * @param self self reference * @param uri URI to encode * * @return encoded URI */
public static Object encodeURI(final Object self, final Object uri) { return URIUtils.encodeURI(self, JSType.toString(uri)); }
ECMA 15.1.3.4, encodeURIComponent implementation
Params:
  • self – self reference
  • uri – URI component to encode
Returns:encoded URIComponent
/** * ECMA 15.1.3.4, encodeURIComponent implementation * * @param self self reference * @param uri URI component to encode * * @return encoded URIComponent */
public static Object encodeURIComponent(final Object self, final Object uri) { return URIUtils.encodeURIComponent(self, JSType.toString(uri)); }
ECMA 15.1.3.1, decodeURI implementation
Params:
  • self – self reference
  • uri – URI to decode
Returns:decoded URI
/** * ECMA 15.1.3.1, decodeURI implementation * * @param self self reference * @param uri URI to decode * * @return decoded URI */
public static Object decodeURI(final Object self, final Object uri) { return URIUtils.decodeURI(self, JSType.toString(uri)); }
ECMA 15.1.3.2, decodeURIComponent implementation
Params:
  • self – self reference
  • uri – URI component to encode
Returns:decoded URI
/** * ECMA 15.1.3.2, decodeURIComponent implementation * * @param self self reference * @param uri URI component to encode * * @return decoded URI */
public static Object decodeURIComponent(final Object self, final Object uri) { return URIUtils.decodeURIComponent(self, JSType.toString(uri)); }
ECMA B.2.1, escape implementation
Params:
  • self – self reference
  • string – string to escape
Returns:escaped string
/** * ECMA B.2.1, escape implementation * * @param self self reference * @param string string to escape * * @return escaped string */
public static String escape(final Object self, final Object string) { final String str = JSType.toString(string); final int length = str.length(); if (length == 0) { return str; } final StringBuilder sb = new StringBuilder(); for (int k = 0; k < length; k++) { final char ch = str.charAt(k); if (UNESCAPED.indexOf(ch) != -1) { sb.append(ch); } else if (ch < 256) { sb.append('%'); if (ch < 16) { sb.append('0'); } sb.append(Integer.toHexString(ch).toUpperCase(Locale.ENGLISH)); } else { sb.append("%u"); if (ch < 4096) { sb.append('0'); } sb.append(Integer.toHexString(ch).toUpperCase(Locale.ENGLISH)); } } return sb.toString(); }
ECMA B.2.2, unescape implementation
Params:
  • self – self reference
  • string – string to unescape
Returns:unescaped string
/** * ECMA B.2.2, unescape implementation * * @param self self reference * @param string string to unescape * * @return unescaped string */
public static String unescape(final Object self, final Object string) { final String str = JSType.toString(string); final int length = str.length(); if (length == 0) { return str; } final StringBuilder sb = new StringBuilder(); for (int k = 0; k < length; k++) { char ch = str.charAt(k); if (ch != '%') { sb.append(ch); } else { if (k < (length - 5)) { if (str.charAt(k + 1) == 'u') { try { ch = (char) Integer.parseInt(str.substring(k + 2, k + 6), 16); sb.append(ch); k += 5; continue; } catch (final NumberFormatException e) { //ignored } } } if (k < (length - 2)) { try { ch = (char) Integer.parseInt(str.substring(k + 1, k + 3), 16); sb.append(ch); k += 2; continue; } catch (final NumberFormatException e) { //ignored } } // everything fails sb.append(ch); } } return sb.toString(); }
ECMA 15.3.4 Properties of the Function Prototype Object. The Function prototype object is itself a Function object (its [[Class]] is "Function") that, when invoked, accepts any arguments and returns undefined. This method is used to implement that anonymous function.
Params:
  • self – self reference
Returns:undefined
/** * ECMA 15.3.4 Properties of the Function Prototype Object. * The Function prototype object is itself a Function object * (its [[Class]] is "Function") that, when invoked, accepts * any arguments and returns undefined. This method is used to * implement that anonymous function. * * @param self self reference * * @return undefined */
public static Object anonymous(final Object self) { return ScriptRuntime.UNDEFINED; } private static int fastDigit(final int ch, final int radix) { int n = -1; if (ch >= '0' && ch <= '9') { n = ch - '0'; } else if (radix > 10) { if (ch >= 'a' && ch <= 'z') { n = ch - 'a' + 10; } else if (ch >= 'A' && ch <= 'Z') { n = ch - 'A' + 10; } } return n < radix ? n : -1; } private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { return MH.findStatic(MethodHandles.lookup(), GlobalFunctions.class, name, MH.type(rtype, types)); } }