1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.lookup.Lookup.MH; 29 30 import java.lang.invoke.MethodHandle; 31 import java.lang.invoke.MethodHandles; 32 import java.util.Locale; 33 34 /** 35 * Utilities used by Global class. 36 */ 37 public final class GlobalFunctions { 38 39 /** Methodhandle to implementation of ECMA 15.1.2.2, parseInt */ 40 public static final MethodHandle PARSEINT = findOwnMH("parseInt", double.class, Object.class, Object.class, Object.class); 41 42 /** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */ 43 public static final MethodHandle PARSEINT_OI = findOwnMH("parseInt", double.class, Object.class, Object.class, int.class); 44 45 /** ParseInt - NaN for booleans (thru string conversion to number conversion) */ 46 public static final MethodHandle PARSEINT_Z = MH.dropArguments(MH.dropArguments(MH.constant(double.class, Double.NaN), 0, boolean.class), 0, Object.class); 47 48 /** ParseInt - identity for ints */ 49 public static final MethodHandle PARSEINT_I = MH.dropArguments(MH.identity(int.class), 0, Object.class); 50 51 /** ParseInt - identity for longs */ 52 public static final MethodHandle PARSEINT_J = MH.dropArguments(MH.identity(long.class), 0, Object.class); 53 54 /** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */ 55 public static final MethodHandle PARSEINT_O = findOwnMH("parseInt", double.class, Object.class, Object.class); 56 57 /** Methodhandle to implementation of ECMA 15.1.2.3, parseFloat */ 58 public static final MethodHandle PARSEFLOAT = findOwnMH("parseFloat", double.class, Object.class, Object.class); 59 60 /** isNan for integers - always false */ 61 public static final MethodHandle IS_NAN_I = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class); 62 63 /** isNan for longs - always false */ 64 public static final MethodHandle IS_NAN_J = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class); 65 66 /** IsNan for doubles - use Double.isNaN */ 67 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); 68 69 /** Methodhandle to implementation of ECMA 15.1.2.4, isNaN */ 70 public static final MethodHandle IS_NAN = findOwnMH("isNaN", boolean.class, Object.class, Object.class); 71 72 /** Methodhandle to implementation of ECMA 15.1.2.5, isFinite */ 73 public static final MethodHandle IS_FINITE = findOwnMH("isFinite", boolean.class, Object.class, Object.class); 74 75 /** Methodhandle to implementation of ECMA 15.1.3.3, encodeURI */ 76 public static final MethodHandle ENCODE_URI = findOwnMH("encodeURI", Object.class, Object.class, Object.class); 77 78 /** Methodhandle to implementation of ECMA 15.1.3.4, encodeURIComponent */ 79 public static final MethodHandle ENCODE_URICOMPONENT = findOwnMH("encodeURIComponent", Object.class, Object.class, Object.class); 80 81 /** Methodhandle to implementation of ECMA 15.1.3.1, decodeURI */ 82 public static final MethodHandle DECODE_URI = findOwnMH("decodeURI", Object.class, Object.class, Object.class); 83 84 /** Methodhandle to implementation of ECMA 15.1.3.2, decodeURIComponent */ 85 public static final MethodHandle DECODE_URICOMPONENT = findOwnMH("decodeURIComponent", Object.class, Object.class, Object.class); 86 87 /** Methodhandle to implementation of ECMA B.2.1, escape */ 88 public static final MethodHandle ESCAPE = findOwnMH("escape", String.class, Object.class, Object.class); 89 90 /** Methodhandle to implementation of ECMA B.2.2, unescape */ 91 public static final MethodHandle UNESCAPE = findOwnMH("unescape", String.class, Object.class, Object.class); 92 93 /** Methodhandle to implementation of ECMA 15.3.4, "anonymous" - Properties of the Function Prototype Object. */ 94 public static final MethodHandle ANONYMOUS = findOwnMH("anonymous", Object.class, Object.class); 95 96 private static final String UNESCAPED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"; 97 98 private GlobalFunctions() { 99 } 100 101 /** 102 * ECMA 15.1.2.2 parseInt implementation 103 * 104 * @param self self reference 105 * @param string string to parse 106 * @param rad radix 107 * 108 * @return numeric type representing string contents as an int 109 */ 110 public static double parseInt(final Object self, final Object string, final Object rad) { 111 return parseIntInternal(JSType.trimLeft(JSType.toString(string)), JSType.toInt32(rad)); 112 } 113 114 /** 115 * ECMA 15.1.2.2 parseInt implementation specialized for int radix 116 * 117 * @param self self reference 118 * @param string string to parse 119 * @param rad radix 120 * 121 * @return numeric type representing string contents as an int 122 */ 123 public static double parseInt(final Object self, final Object string, final int rad) { 124 return parseIntInternal(JSType.trimLeft(JSType.toString(string)), rad); 125 } 126 127 /** 128 * ECMA 15.1.2.2 parseInt implementation specialized for no radix argument 129 * 130 * @param self self reference 131 * @param string string to parse 132 * 133 * @return numeric type representing string contents as an int 134 */ 135 public static double parseInt(final Object self, final Object string) { 136 return parseIntInternal(JSType.trimLeft(JSType.toString(string)), 0); 137 } 138 139 private static double parseIntInternal(final String str, final int rad) { 140 final int length = str.length(); 141 int radix = rad; 142 143 // empty string is not valid 144 if (length == 0) { 145 return Double.NaN; 146 } 147 148 boolean negative = false; 149 int idx = 0; 150 151 // checking for the sign character 152 final char firstChar = str.charAt(idx); 153 if (firstChar < '0') { 154 // Possible leading "+" or "-" 155 if (firstChar == '-') { 156 negative = true; 157 } else if (firstChar != '+') { 158 return Double.NaN; 159 } 160 // skip the sign character 161 idx++; 162 } 163 164 boolean stripPrefix = true; 165 166 if (radix != 0) { 167 if (radix < 2 || radix > 36) { 168 return Double.NaN; 169 } 170 if (radix != 16) { 171 stripPrefix = false; 172 } 173 } else { 174 // default radix 175 radix = 10; 176 } 177 // strip "0x" or "0X" and treat radix as 16 178 if (stripPrefix && ((idx + 1) < length)) { 179 final char c1 = str.charAt(idx); 180 final char c2 = str.charAt(idx + 1); 181 if (c1 == '0' && (c2 == 'x' || c2 == 'X')) { 182 radix = 16; 183 // skip "0x" or "0X" 184 idx += 2; 185 } 186 } 187 188 double result = 0.0; 189 int digit; 190 // we should see at least one valid digit 191 boolean entered = false; 192 while (idx < length) { 193 digit = fastDigit(str.charAt(idx++), radix); 194 if (digit < 0) { 195 break; 196 } 197 // we have seen at least one valid digit in the specified radix 198 entered = true; 199 result *= radix; 200 result += digit; 201 } 202 203 return entered ? (negative ? -result : result) : Double.NaN; 204 } 205 206 /** 207 * ECMA 15.1.2.3 parseFloat implementation 208 * 209 * @param self self reference 210 * @param string string to parse 211 * 212 * @return numeric type representing string contents 213 */ 214 public static double parseFloat(final Object self, final Object string) { 215 final String str = JSType.trimLeft(JSType.toString(string)); 216 final int length = str.length(); 217 218 // empty string is not valid 219 if (length == 0) { 220 return Double.NaN; 221 } 222 223 int start = 0; 224 boolean negative = false; 225 char ch = str.charAt(0); 226 227 if (ch == '-') { 228 start++; 229 negative = true; 230 } else if (ch == '+') { 231 start++; 232 } else if (ch == 'N') { 233 if (str.startsWith("NaN")) { 234 return Double.NaN; 235 } 236 } 237 238 if (start == length) { 239 // just the sign character 240 return Double.NaN; 241 } 242 243 ch = str.charAt(start); 244 if (ch == 'I') { 245 if (str.substring(start).startsWith("Infinity")) { 246 return negative? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; 247 } 248 } 249 250 boolean dotSeen = false; 251 boolean exponentOk = false; 252 int exponentOffset = -1; 253 int end; 254 255 loop: 256 for (end = start; end < length; end++) { 257 ch = str.charAt(end); 258 259 switch (ch) { 260 case '.': 261 // dot allowed only once 262 if (exponentOffset != -1 || dotSeen) { 263 break loop; 264 } 265 dotSeen = true; 266 break; 267 268 case 'e': 269 case 'E': 270 // 'e'/'E' allow only once 271 if (exponentOffset != -1) { 272 break loop; 273 } 274 exponentOffset = end; 275 break; 276 277 case '+': 278 case '-': 279 // Sign of the exponent. But allowed only if the 280 // previous char in the string was 'e' or 'E'. 281 if (exponentOffset != end - 1) { 282 break loop; 283 } 284 break; 285 286 case '0': 287 case '1': 288 case '2': 289 case '3': 290 case '4': 291 case '5': 292 case '6': 293 case '7': 294 case '8': 295 case '9': 296 if (exponentOffset != -1) { 297 // seeing digit after 'e' or 'E' 298 exponentOk = true; 299 } 300 break; 301 302 default: // ignore garbage at the end 303 break loop; 304 } 305 } 306 307 // ignore 'e'/'E' followed by '+/-' if not real exponent found 308 if (exponentOffset != -1 && !exponentOk) { 309 end = exponentOffset; 310 } 311 312 if (start == end) { 313 return Double.NaN; 314 } 315 316 try { 317 final double result = Double.valueOf(str.substring(start, end)); 318 return negative ? -result : result; 319 } catch (final NumberFormatException e) { 320 return Double.NaN; 321 } 322 } 323 324 /** 325 * ECMA 15.1.2.4, isNaN implementation 326 * 327 * @param self self reference 328 * @param number number to check 329 * 330 * @return true if number is NaN 331 */ 332 public static boolean isNaN(final Object self, final Object number) { 333 return Double.isNaN(JSType.toNumber(number)); 334 } 335 336 /** 337 * ECMA 15.1.2.5, isFinite implementation 338 * 339 * @param self self reference 340 * @param number number to check 341 * 342 * @return true if number is infinite 343 */ 344 public static boolean isFinite(final Object self, final Object number) { 345 final double value = JSType.toNumber(number); 346 return ! (Double.isInfinite(value) || Double.isNaN(value)); 347 } 348 349 350 /** 351 * ECMA 15.1.3.3, encodeURI implementation 352 * 353 * @param self self reference 354 * @param uri URI to encode 355 * 356 * @return encoded URI 357 */ 358 public static Object encodeURI(final Object self, final Object uri) { 359 return URIUtils.encodeURI(self, JSType.toString(uri)); 360 } 361 362 /** 363 * ECMA 15.1.3.4, encodeURIComponent implementation 364 * 365 * @param self self reference 366 * @param uri URI component to encode 367 * 368 * @return encoded URIComponent 369 */ 370 public static Object encodeURIComponent(final Object self, final Object uri) { 371 return URIUtils.encodeURIComponent(self, JSType.toString(uri)); 372 } 373 374 /** 375 * ECMA 15.1.3.1, decodeURI implementation 376 * 377 * @param self self reference 378 * @param uri URI to decode 379 * 380 * @return decoded URI 381 */ 382 public static Object decodeURI(final Object self, final Object uri) { 383 return URIUtils.decodeURI(self, JSType.toString(uri)); 384 } 385 386 /** 387 * ECMA 15.1.3.2, decodeURIComponent implementation 388 * 389 * @param self self reference 390 * @param uri URI component to encode 391 * 392 * @return decoded URI 393 */ 394 public static Object decodeURIComponent(final Object self, final Object uri) { 395 return URIUtils.decodeURIComponent(self, JSType.toString(uri)); 396 } 397 398 /** 399 * ECMA B.2.1, escape implementation 400 * 401 * @param self self reference 402 * @param string string to escape 403 * 404 * @return escaped string 405 */ 406 public static String escape(final Object self, final Object string) { 407 final String str = JSType.toString(string); 408 final int length = str.length(); 409 410 if (length == 0) { 411 return str; 412 } 413 414 final StringBuilder sb = new StringBuilder(); 415 for (int k = 0; k < length; k++) { 416 final char ch = str.charAt(k); 417 if (UNESCAPED.indexOf(ch) != -1) { 418 sb.append(ch); 419 } else if (ch < 256) { 420 sb.append('%'); 421 if (ch < 16) { 422 sb.append('0'); 423 } 424 sb.append(Integer.toHexString(ch).toUpperCase(Locale.ENGLISH)); 425 } else { 426 sb.append("%u"); 427 if (ch < 4096) { 428 sb.append('0'); 429 } 430 sb.append(Integer.toHexString(ch).toUpperCase(Locale.ENGLISH)); 431 } 432 } 433 434 return sb.toString(); 435 } 436 437 /** 438 * ECMA B.2.2, unescape implementation 439 * 440 * @param self self reference 441 * @param string string to unescape 442 * 443 * @return unescaped string 444 */ 445 public static String unescape(final Object self, final Object string) { 446 final String str = JSType.toString(string); 447 final int length = str.length(); 448 449 if (length == 0) { 450 return str; 451 } 452 453 final StringBuilder sb = new StringBuilder(); 454 for (int k = 0; k < length; k++) { 455 char ch = str.charAt(k); 456 if (ch != '%') { 457 sb.append(ch); 458 } else { 459 if (k < (length - 5)) { 460 if (str.charAt(k + 1) == 'u') { 461 try { 462 ch = (char) Integer.parseInt(str.substring(k + 2, k + 6), 16); 463 sb.append(ch); 464 k += 5; 465 continue; 466 } catch (final NumberFormatException e) { 467 //ignored 468 } 469 } 470 } 471 472 if (k < (length - 2)) { 473 try { 474 ch = (char) Integer.parseInt(str.substring(k + 1, k + 3), 16); 475 sb.append(ch); 476 k += 2; 477 continue; 478 } catch (final NumberFormatException e) { 479 //ignored 480 } 481 } 482 483 // everything fails 484 sb.append(ch); 485 } 486 } 487 488 return sb.toString(); 489 } 490 491 492 /** 493 * ECMA 15.3.4 Properties of the Function Prototype Object. 494 * The Function prototype object is itself a Function object 495 * (its [[Class]] is "Function") that, when invoked, accepts 496 * any arguments and returns undefined. This method is used to 497 * implement that anonymous function. 498 * 499 * @param self self reference 500 * 501 * @return undefined 502 */ 503 public static Object anonymous(final Object self) { 504 return ScriptRuntime.UNDEFINED; 505 } 506 507 private static int fastDigit(final int ch, final int radix) { 508 int n = -1; 509 if (ch >= '0' && ch <= '9') { 510 n = ch - '0'; 511 } else if (radix > 10) { 512 if (ch >= 'a' && ch <= 'z') { 513 n = ch - 'a' + 10; 514 } else if (ch >= 'A' && ch <= 'Z') { 515 n = ch - 'A' + 10; 516 } 517 } 518 return n < radix ? n : -1; 519 } 520 521 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 522 return MH.findStatic(MethodHandles.lookup(), GlobalFunctions.class, name, MH.type(rtype, types)); 523 } 524 }