1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 package org.mozilla.javascript; 8 9 import java.io.Serializable; 10 import java.lang.reflect.*; 11 import java.text.MessageFormat; 12 import java.util.Locale; 13 import java.util.ResourceBundle; 14 15 import org.mozilla.javascript.ast.FunctionNode; 16 import org.mozilla.javascript.v8dtoa.FastDtoa; 17 import org.mozilla.javascript.xml.XMLObject; 18 import org.mozilla.javascript.xml.XMLLib; 19 20 /** 21 * This is the class that implements the runtime. 22 * 23 * @author Norris Boyd 24 */ 25 26 public class ScriptRuntime { 27 28 /** 29 * No instances should be created. 30 */ ScriptRuntime()31 protected ScriptRuntime() { 32 } 33 34 35 /** 36 * Returns representation of the [[ThrowTypeError]] object. 37 * See ECMA 5 spec, 13.2.3 38 */ typeErrorThrower()39 public static BaseFunction typeErrorThrower() { 40 if (THROW_TYPE_ERROR == null) { 41 BaseFunction thrower = new BaseFunction() { 42 static final long serialVersionUID = -5891740962154902286L; 43 44 @Override 45 public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { 46 throw typeError0("msg.op.not.allowed"); 47 } 48 @Override 49 public int getLength() { 50 return 0; 51 } 52 }; 53 thrower.preventExtensions(); 54 THROW_TYPE_ERROR = thrower; 55 } 56 return THROW_TYPE_ERROR; 57 } 58 private static BaseFunction THROW_TYPE_ERROR = null; 59 60 static class NoSuchMethodShim implements Callable { 61 String methodName; 62 Callable noSuchMethodMethod; 63 NoSuchMethodShim(Callable noSuchMethodMethod, String methodName)64 NoSuchMethodShim(Callable noSuchMethodMethod, String methodName) 65 { 66 this.noSuchMethodMethod = noSuchMethodMethod; 67 this.methodName = methodName; 68 } 69 /** 70 * Perform the call. 71 * 72 * @param cx the current Context for this thread 73 * @param scope the scope to use to resolve properties. 74 * @param thisObj the JavaScript <code>this</code> object 75 * @param args the array of arguments 76 * @return the result of the call 77 */ call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)78 public Object call(Context cx, Scriptable scope, Scriptable thisObj, 79 Object[] args) 80 { 81 Object[] nestedArgs = new Object[2]; 82 83 nestedArgs[0] = methodName; 84 nestedArgs[1] = newArrayLiteral(args, null, cx, scope); 85 return noSuchMethodMethod.call(cx, scope, thisObj, nestedArgs); 86 } 87 88 } 89 /* 90 * There's such a huge space (and some time) waste for the Foo.class 91 * syntax: the compiler sticks in a test of a static field in the 92 * enclosing class for null and the code for creating the class value. 93 * It has to do this since the reference has to get pushed off until 94 * execution time (i.e. can't force an early load), but for the 95 * 'standard' classes - especially those in java.lang, we can trust 96 * that they won't cause problems by being loaded early. 97 */ 98 99 public final static Class<?> 100 BooleanClass = Kit.classOrNull("java.lang.Boolean"), 101 ByteClass = Kit.classOrNull("java.lang.Byte"), 102 CharacterClass = Kit.classOrNull("java.lang.Character"), 103 ClassClass = Kit.classOrNull("java.lang.Class"), 104 DoubleClass = Kit.classOrNull("java.lang.Double"), 105 FloatClass = Kit.classOrNull("java.lang.Float"), 106 IntegerClass = Kit.classOrNull("java.lang.Integer"), 107 LongClass = Kit.classOrNull("java.lang.Long"), 108 NumberClass = Kit.classOrNull("java.lang.Number"), 109 ObjectClass = Kit.classOrNull("java.lang.Object"), 110 ShortClass = Kit.classOrNull("java.lang.Short"), 111 StringClass = Kit.classOrNull("java.lang.String"), 112 DateClass = Kit.classOrNull("java.util.Date"); 113 114 public final static Class<?> 115 ContextClass 116 = Kit.classOrNull("org.mozilla.javascript.Context"), 117 ContextFactoryClass 118 = Kit.classOrNull("org.mozilla.javascript.ContextFactory"), 119 FunctionClass 120 = Kit.classOrNull("org.mozilla.javascript.Function"), 121 ScriptableObjectClass 122 = Kit.classOrNull("org.mozilla.javascript.ScriptableObject"); 123 public static final Class<Scriptable> ScriptableClass = 124 Scriptable.class; 125 126 // Locale object used to request locale-neutral operations. 127 public static Locale ROOT_LOCALE = new Locale(""); 128 129 private static final Object LIBRARY_SCOPE_KEY = "LIBRARY_SCOPE"; 130 isRhinoRuntimeType(Class<?> cl)131 public static boolean isRhinoRuntimeType(Class<?> cl) 132 { 133 if (cl.isPrimitive()) { 134 return (cl != Character.TYPE); 135 } else { 136 return (cl == StringClass || cl == BooleanClass 137 || NumberClass.isAssignableFrom(cl) 138 || ScriptableClass.isAssignableFrom(cl)); 139 } 140 } 141 initStandardObjects(Context cx, ScriptableObject scope, boolean sealed)142 public static ScriptableObject initStandardObjects(Context cx, 143 ScriptableObject scope, 144 boolean sealed) 145 { 146 if (scope == null) { 147 scope = new NativeObject(); 148 } 149 scope.associateValue(LIBRARY_SCOPE_KEY, scope); 150 (new ClassCache()).associate(scope); 151 152 BaseFunction.init(scope, sealed); 153 NativeObject.init(scope, sealed); 154 155 Scriptable objectProto = ScriptableObject.getObjectPrototype(scope); 156 157 // Function.prototype.__proto__ should be Object.prototype 158 Scriptable functionProto = ScriptableObject.getClassPrototype(scope, "Function"); 159 functionProto.setPrototype(objectProto); 160 161 // Set the prototype of the object passed in if need be 162 if (scope.getPrototype() == null) 163 scope.setPrototype(objectProto); 164 165 // must precede NativeGlobal since it's needed therein 166 NativeError.init(scope, sealed); 167 NativeGlobal.init(cx, scope, sealed); 168 169 NativeArray.init(scope, sealed); 170 if (cx.getOptimizationLevel() > 0) { 171 // When optimizing, attempt to fulfill all requests for new Array(N) 172 // with a higher threshold before switching to a sparse 173 // representation 174 NativeArray.setMaximumInitialCapacity(200000); 175 } 176 NativeString.init(scope, sealed); 177 NativeBoolean.init(scope, sealed); 178 NativeNumber.init(scope, sealed); 179 NativeDate.init(scope, sealed); 180 NativeMath.init(scope, sealed); 181 NativeJSON.init(scope, sealed); 182 183 NativeWith.init(scope, sealed); 184 NativeCall.init(scope, sealed); 185 NativeScript.init(scope, sealed); 186 187 NativeIterator.init(scope, sealed); // Also initializes NativeGenerator 188 189 boolean withXml = cx.hasFeature(Context.FEATURE_E4X) && 190 cx.getE4xImplementationFactory() != null; 191 192 // define lazy-loaded properties using their class name 193 new LazilyLoadedCtor(scope, "RegExp", 194 "org.mozilla.javascript.regexp.NativeRegExp", sealed, true); 195 new LazilyLoadedCtor(scope, "Packages", 196 "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); 197 new LazilyLoadedCtor(scope, "getClass", 198 "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); 199 new LazilyLoadedCtor(scope, "JavaAdapter", 200 "org.mozilla.javascript.JavaAdapter", sealed, true); 201 new LazilyLoadedCtor(scope, "JavaImporter", 202 "org.mozilla.javascript.ImporterTopLevel", sealed, true); 203 new LazilyLoadedCtor(scope, "Continuation", 204 "org.mozilla.javascript.NativeContinuation", sealed, true); 205 206 for (String packageName : getTopPackageNames()) { 207 new LazilyLoadedCtor(scope, packageName, 208 "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); 209 } 210 211 if (withXml) { 212 String xmlImpl = cx.getE4xImplementationFactory().getImplementationClassName(); 213 new LazilyLoadedCtor(scope, "XML", xmlImpl, sealed, true); 214 new LazilyLoadedCtor(scope, "XMLList", xmlImpl, sealed, true); 215 new LazilyLoadedCtor(scope, "Namespace", xmlImpl, sealed, true); 216 new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true); 217 } 218 219 if (scope instanceof TopLevel) { 220 ((TopLevel)scope).cacheBuiltins(); 221 } 222 223 return scope; 224 } 225 getTopPackageNames()226 static String[] getTopPackageNames() { 227 // Include "android" top package if running on Android 228 return "Dalvik".equals(System.getProperty("java.vm.name")) ? 229 new String[] { "java", "javax", "org", "com", "edu", "net", "android" } : 230 new String[] { "java", "javax", "org", "com", "edu", "net" }; 231 } 232 getLibraryScopeOrNull(Scriptable scope)233 public static ScriptableObject getLibraryScopeOrNull(Scriptable scope) 234 { 235 ScriptableObject libScope; 236 libScope = (ScriptableObject)ScriptableObject. 237 getTopScopeValue(scope, LIBRARY_SCOPE_KEY); 238 return libScope; 239 } 240 241 // It is public so NativeRegExp can access it. isJSLineTerminator(int c)242 public static boolean isJSLineTerminator(int c) 243 { 244 // Optimization for faster check for eol character: 245 // they do not have 0xDFD0 bits set 246 if ((c & 0xDFD0) != 0) { 247 return false; 248 } 249 return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; 250 } 251 isJSWhitespaceOrLineTerminator(int c)252 public static boolean isJSWhitespaceOrLineTerminator(int c) { 253 return (isStrWhiteSpaceChar(c) || isJSLineTerminator(c)); 254 } 255 256 /** 257 * Indicates if the character is a Str whitespace char according to ECMA spec: 258 * StrWhiteSpaceChar ::: 259 <TAB> 260 <SP> 261 <NBSP> 262 <FF> 263 <VT> 264 <CR> 265 <LF> 266 <LS> 267 <PS> 268 <USP> 269 <BOM> 270 */ isStrWhiteSpaceChar(int c)271 static boolean isStrWhiteSpaceChar(int c) 272 { 273 switch (c) { 274 case ' ': // <SP> 275 case '\n': // <LF> 276 case '\r': // <CR> 277 case '\t': // <TAB> 278 case '\u00A0': // <NBSP> 279 case '\u000C': // <FF> 280 case '\u000B': // <VT> 281 case '\u2028': // <LS> 282 case '\u2029': // <PS> 283 case '\uFEFF': // <BOM> 284 return true; 285 default: 286 return Character.getType(c) == Character.SPACE_SEPARATOR; 287 } 288 } 289 wrapBoolean(boolean b)290 public static Boolean wrapBoolean(boolean b) 291 { 292 return b ? Boolean.TRUE : Boolean.FALSE; 293 } 294 wrapInt(int i)295 public static Integer wrapInt(int i) 296 { 297 return Integer.valueOf(i); 298 } 299 wrapNumber(double x)300 public static Number wrapNumber(double x) 301 { 302 if (x != x) { 303 return ScriptRuntime.NaNobj; 304 } 305 return new Double(x); 306 } 307 308 /** 309 * Convert the value to a boolean. 310 * 311 * See ECMA 9.2. 312 */ toBoolean(Object val)313 public static boolean toBoolean(Object val) 314 { 315 for (;;) { 316 if (val instanceof Boolean) 317 return ((Boolean) val).booleanValue(); 318 if (val == null || val == Undefined.instance) 319 return false; 320 if (val instanceof CharSequence) 321 return ((CharSequence) val).length() != 0; 322 if (val instanceof Number) { 323 double d = ((Number) val).doubleValue(); 324 return (d == d && d != 0.0); 325 } 326 if (val instanceof Scriptable) { 327 if (val instanceof ScriptableObject && 328 ((ScriptableObject) val).avoidObjectDetection()) 329 { 330 return false; 331 } 332 if (Context.getContext().isVersionECMA1()) { 333 // pure ECMA 334 return true; 335 } 336 // ECMA extension 337 val = ((Scriptable) val).getDefaultValue(BooleanClass); 338 if (val instanceof Scriptable) 339 throw errorWithClassName("msg.primitive.expected", val); 340 continue; 341 } 342 warnAboutNonJSObject(val); 343 return true; 344 } 345 } 346 347 /** 348 * Convert the value to a number. 349 * 350 * See ECMA 9.3. 351 */ toNumber(Object val)352 public static double toNumber(Object val) 353 { 354 for (;;) { 355 if (val instanceof Number) 356 return ((Number) val).doubleValue(); 357 if (val == null) 358 return +0.0; 359 if (val == Undefined.instance) 360 return NaN; 361 if (val instanceof String) 362 return toNumber((String) val); 363 if (val instanceof CharSequence) 364 return toNumber(val.toString()); 365 if (val instanceof Boolean) 366 return ((Boolean) val).booleanValue() ? 1 : +0.0; 367 if (val instanceof Scriptable) { 368 val = ((Scriptable) val).getDefaultValue(NumberClass); 369 if (val instanceof Scriptable) 370 throw errorWithClassName("msg.primitive.expected", val); 371 continue; 372 } 373 warnAboutNonJSObject(val); 374 return NaN; 375 } 376 } 377 toNumber(Object[] args, int index)378 public static double toNumber(Object[] args, int index) { 379 return (index < args.length) ? toNumber(args[index]) : NaN; 380 } 381 382 // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM, 383 // versions 2.01 and 3.0P1, that causes some uses (returns at least) of 384 // Double.NaN to be converted to 1.0. 385 // So we use ScriptRuntime.NaN instead of Double.NaN. 386 public static final double 387 NaN = Double.longBitsToDouble(0x7ff8000000000000L); 388 389 // A similar problem exists for negative zero. 390 public static final double 391 negativeZero = Double.longBitsToDouble(0x8000000000000000L); 392 393 public static final Double NaNobj = new Double(NaN); 394 395 /* 396 * Helper function for toNumber, parseInt, and TokenStream.getToken. 397 */ stringToNumber(String s, int start, int radix)398 static double stringToNumber(String s, int start, int radix) { 399 char digitMax = '9'; 400 char lowerCaseBound = 'a'; 401 char upperCaseBound = 'A'; 402 int len = s.length(); 403 if (radix < 10) { 404 digitMax = (char) ('0' + radix - 1); 405 } 406 if (radix > 10) { 407 lowerCaseBound = (char) ('a' + radix - 10); 408 upperCaseBound = (char) ('A' + radix - 10); 409 } 410 int end; 411 double sum = 0.0; 412 for (end=start; end < len; end++) { 413 char c = s.charAt(end); 414 int newDigit; 415 if ('0' <= c && c <= digitMax) 416 newDigit = c - '0'; 417 else if ('a' <= c && c < lowerCaseBound) 418 newDigit = c - 'a' + 10; 419 else if ('A' <= c && c < upperCaseBound) 420 newDigit = c - 'A' + 10; 421 else 422 break; 423 sum = sum*radix + newDigit; 424 } 425 if (start == end) { 426 return NaN; 427 } 428 if (sum >= 9007199254740992.0) { 429 if (radix == 10) { 430 /* If we're accumulating a decimal number and the number 431 * is >= 2^53, then the result from the repeated multiply-add 432 * above may be inaccurate. Call Java to get the correct 433 * answer. 434 */ 435 try { 436 return Double.parseDouble(s.substring(start, end)); 437 } catch (NumberFormatException nfe) { 438 return NaN; 439 } 440 } else if (radix == 2 || radix == 4 || radix == 8 || 441 radix == 16 || radix == 32) 442 { 443 /* The number may also be inaccurate for one of these bases. 444 * This happens if the addition in value*radix + digit causes 445 * a round-down to an even least significant mantissa bit 446 * when the first dropped bit is a one. If any of the 447 * following digits in the number (which haven't been added 448 * in yet) are nonzero then the correct action would have 449 * been to round up instead of down. An example of this 450 * occurs when reading the number 0x1000000000000081, which 451 * rounds to 0x1000000000000000 instead of 0x1000000000000100. 452 */ 453 int bitShiftInChar = 1; 454 int digit = 0; 455 456 final int SKIP_LEADING_ZEROS = 0; 457 final int FIRST_EXACT_53_BITS = 1; 458 final int AFTER_BIT_53 = 2; 459 final int ZEROS_AFTER_54 = 3; 460 final int MIXED_AFTER_54 = 4; 461 462 int state = SKIP_LEADING_ZEROS; 463 int exactBitsLimit = 53; 464 double factor = 0.0; 465 boolean bit53 = false; 466 // bit54 is the 54th bit (the first dropped from the mantissa) 467 boolean bit54 = false; 468 469 for (;;) { 470 if (bitShiftInChar == 1) { 471 if (start == end) 472 break; 473 digit = s.charAt(start++); 474 if ('0' <= digit && digit <= '9') 475 digit -= '0'; 476 else if ('a' <= digit && digit <= 'z') 477 digit -= 'a' - 10; 478 else 479 digit -= 'A' - 10; 480 bitShiftInChar = radix; 481 } 482 bitShiftInChar >>= 1; 483 boolean bit = (digit & bitShiftInChar) != 0; 484 485 switch (state) { 486 case SKIP_LEADING_ZEROS: 487 if (bit) { 488 --exactBitsLimit; 489 sum = 1.0; 490 state = FIRST_EXACT_53_BITS; 491 } 492 break; 493 case FIRST_EXACT_53_BITS: 494 sum *= 2.0; 495 if (bit) 496 sum += 1.0; 497 --exactBitsLimit; 498 if (exactBitsLimit == 0) { 499 bit53 = bit; 500 state = AFTER_BIT_53; 501 } 502 break; 503 case AFTER_BIT_53: 504 bit54 = bit; 505 factor = 2.0; 506 state = ZEROS_AFTER_54; 507 break; 508 case ZEROS_AFTER_54: 509 if (bit) { 510 state = MIXED_AFTER_54; 511 } 512 // fallthrough 513 case MIXED_AFTER_54: 514 factor *= 2; 515 break; 516 } 517 } 518 switch (state) { 519 case SKIP_LEADING_ZEROS: 520 sum = 0.0; 521 break; 522 case FIRST_EXACT_53_BITS: 523 case AFTER_BIT_53: 524 // do nothing 525 break; 526 case ZEROS_AFTER_54: 527 // x1.1 -> x1 + 1 (round up) 528 // x0.1 -> x0 (round down) 529 if (bit54 & bit53) 530 sum += 1.0; 531 sum *= factor; 532 break; 533 case MIXED_AFTER_54: 534 // x.100...1.. -> x + 1 (round up) 535 // x.0anything -> x (round down) 536 if (bit54) 537 sum += 1.0; 538 sum *= factor; 539 break; 540 } 541 } 542 /* We don't worry about inaccurate numbers for any other base. */ 543 } 544 return sum; 545 } 546 547 548 /** 549 * ToNumber applied to the String type 550 * 551 * See ECMA 9.3.1 552 */ toNumber(String s)553 public static double toNumber(String s) { 554 int len = s.length(); 555 int start = 0; 556 char startChar; 557 for (;;) { 558 if (start == len) { 559 // Empty or contains only whitespace 560 return +0.0; 561 } 562 startChar = s.charAt(start); 563 if (!ScriptRuntime.isStrWhiteSpaceChar(startChar)) 564 break; 565 start++; 566 } 567 568 if (startChar == '0') { 569 if (start + 2 < len) { 570 int c1 = s.charAt(start + 1); 571 if (c1 == 'x' || c1 == 'X') { 572 // A hexadecimal number 573 return stringToNumber(s, start + 2, 16); 574 } 575 } 576 } else if (startChar == '+' || startChar == '-') { 577 if (start + 3 < len && s.charAt(start + 1) == '0') { 578 int c2 = s.charAt(start + 2); 579 if (c2 == 'x' || c2 == 'X') { 580 // A hexadecimal number with sign 581 double val = stringToNumber(s, start + 3, 16); 582 return startChar == '-' ? -val : val; 583 } 584 } 585 } 586 587 int end = len - 1; 588 char endChar; 589 while (ScriptRuntime.isStrWhiteSpaceChar(endChar = s.charAt(end))) 590 end--; 591 if (endChar == 'y') { 592 // check for "Infinity" 593 if (startChar == '+' || startChar == '-') 594 start++; 595 if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8)) 596 return startChar == '-' 597 ? Double.NEGATIVE_INFINITY 598 : Double.POSITIVE_INFINITY; 599 return NaN; 600 } 601 // A non-hexadecimal, non-infinity number: 602 // just try a normal floating point conversion 603 String sub = s.substring(start, end+1); 604 // Quick test to check string contains only valid characters because 605 // Double.parseDouble() can be slow and accept input we want to reject 606 for (int i = sub.length() - 1; i >= 0; i--) { 607 char c = sub.charAt(i); 608 if (('0' <= c && c <= '9') || c == '.' || 609 c == 'e' || c == 'E' || 610 c == '+' || c == '-') 611 continue; 612 return NaN; 613 } 614 try { 615 return Double.parseDouble(sub); 616 } catch (NumberFormatException ex) { 617 return NaN; 618 } 619 } 620 621 /** 622 * Helper function for builtin objects that use the varargs form. 623 * ECMA function formal arguments are undefined if not supplied; 624 * this function pads the argument array out to the expected 625 * length, if necessary. 626 */ padArguments(Object[] args, int count)627 public static Object[] padArguments(Object[] args, int count) { 628 if (count < args.length) 629 return args; 630 631 int i; 632 Object[] result = new Object[count]; 633 for (i = 0; i < args.length; i++) { 634 result[i] = args[i]; 635 } 636 637 for (; i < count; i++) { 638 result[i] = Undefined.instance; 639 } 640 641 return result; 642 } 643 escapeString(String s)644 public static String escapeString(String s) 645 { 646 return escapeString(s, '"'); 647 } 648 649 /** 650 * For escaping strings printed by object and array literals; not quite 651 * the same as 'escape.' 652 */ escapeString(String s, char escapeQuote)653 public static String escapeString(String s, char escapeQuote) 654 { 655 if (!(escapeQuote == '"' || escapeQuote == '\'')) Kit.codeBug(); 656 StringBuffer sb = null; 657 658 for(int i = 0, L = s.length(); i != L; ++i) { 659 int c = s.charAt(i); 660 661 if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { 662 // an ordinary print character (like C isprint()) and not " 663 // or \ . 664 if (sb != null) { 665 sb.append((char)c); 666 } 667 continue; 668 } 669 if (sb == null) { 670 sb = new StringBuffer(L + 3); 671 sb.append(s); 672 sb.setLength(i); 673 } 674 675 int escape = -1; 676 switch (c) { 677 case '\b': escape = 'b'; break; 678 case '\f': escape = 'f'; break; 679 case '\n': escape = 'n'; break; 680 case '\r': escape = 'r'; break; 681 case '\t': escape = 't'; break; 682 case 0xb: escape = 'v'; break; // Java lacks \v. 683 case ' ': escape = ' '; break; 684 case '\\': escape = '\\'; break; 685 } 686 if (escape >= 0) { 687 // an \escaped sort of character 688 sb.append('\\'); 689 sb.append((char)escape); 690 } else if (c == escapeQuote) { 691 sb.append('\\'); 692 sb.append(escapeQuote); 693 } else { 694 int hexSize; 695 if (c < 256) { 696 // 2-digit hex 697 sb.append("\\x"); 698 hexSize = 2; 699 } else { 700 // Unicode. 701 sb.append("\\u"); 702 hexSize = 4; 703 } 704 // append hexadecimal form of c left-padded with 0 705 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { 706 int digit = 0xf & (c >> shift); 707 int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; 708 sb.append((char)hc); 709 } 710 } 711 } 712 return (sb == null) ? s : sb.toString(); 713 } 714 isValidIdentifierName(String s)715 static boolean isValidIdentifierName(String s) 716 { 717 int L = s.length(); 718 if (L == 0) 719 return false; 720 if (!Character.isJavaIdentifierStart(s.charAt(0))) 721 return false; 722 for (int i = 1; i != L; ++i) { 723 if (!Character.isJavaIdentifierPart(s.charAt(i))) 724 return false; 725 } 726 return !TokenStream.isKeyword(s); 727 } 728 toCharSequence(Object val)729 public static CharSequence toCharSequence(Object val) { 730 if (val instanceof NativeString) { 731 return ((NativeString)val).toCharSequence(); 732 } 733 return val instanceof CharSequence ? (CharSequence) val : toString(val); 734 } 735 736 /** 737 * Convert the value to a string. 738 * 739 * See ECMA 9.8. 740 */ toString(Object val)741 public static String toString(Object val) { 742 for (;;) { 743 if (val == null) { 744 return "null"; 745 } 746 if (val == Undefined.instance) { 747 return "undefined"; 748 } 749 if (val instanceof String) { 750 return (String)val; 751 } 752 if (val instanceof CharSequence) { 753 return val.toString(); 754 } 755 if (val instanceof Number) { 756 // XXX should we just teach NativeNumber.stringValue() 757 // about Numbers? 758 return numberToString(((Number)val).doubleValue(), 10); 759 } 760 if (val instanceof Scriptable) { 761 val = ((Scriptable) val).getDefaultValue(StringClass); 762 if (val instanceof Scriptable) { 763 throw errorWithClassName("msg.primitive.expected", val); 764 } 765 continue; 766 } 767 return val.toString(); 768 } 769 } 770 defaultObjectToString(Scriptable obj)771 static String defaultObjectToString(Scriptable obj) 772 { 773 return "[object " + obj.getClassName() + ']'; 774 } 775 toString(Object[] args, int index)776 public static String toString(Object[] args, int index) 777 { 778 return (index < args.length) ? toString(args[index]) : "undefined"; 779 } 780 781 /** 782 * Optimized version of toString(Object) for numbers. 783 */ toString(double val)784 public static String toString(double val) { 785 return numberToString(val, 10); 786 } 787 numberToString(double d, int base)788 public static String numberToString(double d, int base) { 789 if (d != d) 790 return "NaN"; 791 if (d == Double.POSITIVE_INFINITY) 792 return "Infinity"; 793 if (d == Double.NEGATIVE_INFINITY) 794 return "-Infinity"; 795 if (d == 0.0) 796 return "0"; 797 798 if ((base < 2) || (base > 36)) { 799 throw Context.reportRuntimeError1( 800 "msg.bad.radix", Integer.toString(base)); 801 } 802 803 if (base != 10) { 804 return DToA.JS_dtobasestr(base, d); 805 } else { 806 // V8 FastDtoa can't convert all numbers, so try it first but 807 // fall back to old DToA in case it fails 808 String result = FastDtoa.numberToString(d); 809 if (result != null) { 810 return result; 811 } 812 StringBuilder buffer = new StringBuilder(); 813 DToA.JS_dtostr(buffer, DToA.DTOSTR_STANDARD, 0, d); 814 return buffer.toString(); 815 } 816 817 } 818 uneval(Context cx, Scriptable scope, Object value)819 static String uneval(Context cx, Scriptable scope, Object value) 820 { 821 if (value == null) { 822 return "null"; 823 } 824 if (value == Undefined.instance) { 825 return "undefined"; 826 } 827 if (value instanceof CharSequence) { 828 String escaped = escapeString(value.toString()); 829 StringBuffer sb = new StringBuffer(escaped.length() + 2); 830 sb.append('\"'); 831 sb.append(escaped); 832 sb.append('\"'); 833 return sb.toString(); 834 } 835 if (value instanceof Number) { 836 double d = ((Number)value).doubleValue(); 837 if (d == 0 && 1 / d < 0) { 838 return "-0"; 839 } 840 return toString(d); 841 } 842 if (value instanceof Boolean) { 843 return toString(value); 844 } 845 if (value instanceof Scriptable) { 846 Scriptable obj = (Scriptable)value; 847 // Wrapped Java objects won't have "toSource" and will report 848 // errors for get()s of nonexistent name, so use has() first 849 if (ScriptableObject.hasProperty(obj, "toSource")) { 850 Object v = ScriptableObject.getProperty(obj, "toSource"); 851 if (v instanceof Function) { 852 Function f = (Function)v; 853 return toString(f.call(cx, scope, obj, emptyArgs)); 854 } 855 } 856 return toString(value); 857 } 858 warnAboutNonJSObject(value); 859 return value.toString(); 860 } 861 defaultObjectToSource(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)862 static String defaultObjectToSource(Context cx, Scriptable scope, 863 Scriptable thisObj, Object[] args) 864 { 865 boolean toplevel, iterating; 866 if (cx.iterating == null) { 867 toplevel = true; 868 iterating = false; 869 cx.iterating = new ObjToIntMap(31); 870 } else { 871 toplevel = false; 872 iterating = cx.iterating.has(thisObj); 873 } 874 875 StringBuffer result = new StringBuffer(128); 876 if (toplevel) { 877 result.append("("); 878 } 879 result.append('{'); 880 881 // Make sure cx.iterating is set to null when done 882 // so we don't leak memory 883 try { 884 if (!iterating) { 885 cx.iterating.intern(thisObj); // stop recursion. 886 Object[] ids = thisObj.getIds(); 887 for (int i=0; i < ids.length; i++) { 888 Object id = ids[i]; 889 Object value; 890 if (id instanceof Integer) { 891 int intId = ((Integer)id).intValue(); 892 value = thisObj.get(intId, thisObj); 893 if (value == Scriptable.NOT_FOUND) 894 continue; // a property has been removed 895 if (i > 0) 896 result.append(", "); 897 result.append(intId); 898 } else { 899 String strId = (String)id; 900 value = thisObj.get(strId, thisObj); 901 if (value == Scriptable.NOT_FOUND) 902 continue; // a property has been removed 903 if (i > 0) 904 result.append(", "); 905 if (ScriptRuntime.isValidIdentifierName(strId)) { 906 result.append(strId); 907 } else { 908 result.append('\''); 909 result.append( 910 ScriptRuntime.escapeString(strId, '\'')); 911 result.append('\''); 912 } 913 } 914 result.append(':'); 915 result.append(ScriptRuntime.uneval(cx, scope, value)); 916 } 917 } 918 } finally { 919 if (toplevel) { 920 cx.iterating = null; 921 } 922 } 923 924 result.append('}'); 925 if (toplevel) { 926 result.append(')'); 927 } 928 return result.toString(); 929 } 930 toObject(Scriptable scope, Object val)931 public static Scriptable toObject(Scriptable scope, Object val) 932 { 933 if (val instanceof Scriptable) { 934 return (Scriptable)val; 935 } 936 return toObject(Context.getContext(), scope, val); 937 } 938 939 /** 940 * Warning: this doesn't allow to resolve primitive prototype properly when many top scopes are involved 941 */ toObjectOrNull(Context cx, Object obj)942 public static Scriptable toObjectOrNull(Context cx, Object obj) 943 { 944 if (obj instanceof Scriptable) { 945 return (Scriptable)obj; 946 } else if (obj != null && obj != Undefined.instance) { 947 return toObject(cx, getTopCallScope(cx), obj); 948 } 949 return null; 950 } 951 952 /** 953 * @param scope the scope that should be used to resolve primitive prototype 954 */ toObjectOrNull(Context cx, Object obj, final Scriptable scope)955 public static Scriptable toObjectOrNull(Context cx, Object obj, 956 final Scriptable scope) 957 { 958 if (obj instanceof Scriptable) { 959 return (Scriptable)obj; 960 } else if (obj != null && obj != Undefined.instance) { 961 return toObject(cx, scope, obj); 962 } 963 return null; 964 } 965 966 /** 967 * @deprecated Use {@link #toObject(Scriptable, Object)} instead. 968 */ toObject(Scriptable scope, Object val, Class<?> staticClass)969 public static Scriptable toObject(Scriptable scope, Object val, 970 Class<?> staticClass) 971 { 972 if (val instanceof Scriptable) { 973 return (Scriptable)val; 974 } 975 return toObject(Context.getContext(), scope, val); 976 } 977 978 /** 979 * Convert the value to an object. 980 * 981 * See ECMA 9.9. 982 */ toObject(Context cx, Scriptable scope, Object val)983 public static Scriptable toObject(Context cx, Scriptable scope, Object val) 984 { 985 if (val instanceof Scriptable) { 986 return (Scriptable) val; 987 } 988 if (val instanceof CharSequence) { 989 // FIXME we want to avoid toString() here, especially for concat() 990 NativeString result = new NativeString((CharSequence)val); 991 setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.String); 992 return result; 993 } 994 if (val instanceof Number) { 995 NativeNumber result = new NativeNumber(((Number)val).doubleValue()); 996 setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Number); 997 return result; 998 } 999 if (val instanceof Boolean) { 1000 NativeBoolean result = new NativeBoolean(((Boolean)val).booleanValue()); 1001 setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Boolean); 1002 return result; 1003 } 1004 if (val == null) { 1005 throw typeError0("msg.null.to.object"); 1006 } 1007 if (val == Undefined.instance) { 1008 throw typeError0("msg.undef.to.object"); 1009 } 1010 1011 // Extension: Wrap as a LiveConnect object. 1012 Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null); 1013 if (wrapped instanceof Scriptable) 1014 return (Scriptable) wrapped; 1015 throw errorWithClassName("msg.invalid.type", val); 1016 } 1017 1018 /** 1019 * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead. 1020 */ toObject(Context cx, Scriptable scope, Object val, Class<?> staticClass)1021 public static Scriptable toObject(Context cx, Scriptable scope, Object val, 1022 Class<?> staticClass) 1023 { 1024 return toObject(cx, scope, val); 1025 } 1026 1027 /** 1028 * @deprecated The method is only present for compatibility. 1029 */ call(Context cx, Object fun, Object thisArg, Object[] args, Scriptable scope)1030 public static Object call(Context cx, Object fun, Object thisArg, 1031 Object[] args, Scriptable scope) 1032 { 1033 if (!(fun instanceof Function)) { 1034 throw notFunctionError(toString(fun)); 1035 } 1036 Function function = (Function)fun; 1037 Scriptable thisObj = toObjectOrNull(cx, thisArg); 1038 if (thisObj == null) { 1039 throw undefCallError(thisObj, "function"); 1040 } 1041 return function.call(cx, scope, thisObj, args); 1042 } 1043 newObject(Context cx, Scriptable scope, String constructorName, Object[] args)1044 public static Scriptable newObject(Context cx, Scriptable scope, 1045 String constructorName, Object[] args) 1046 { 1047 scope = ScriptableObject.getTopLevelScope(scope); 1048 Function ctor = getExistingCtor(cx, scope, constructorName); 1049 if (args == null) { args = ScriptRuntime.emptyArgs; } 1050 return ctor.construct(cx, scope, args); 1051 } 1052 newBuiltinObject(Context cx, Scriptable scope, TopLevel.Builtins type, Object[] args)1053 public static Scriptable newBuiltinObject(Context cx, Scriptable scope, 1054 TopLevel.Builtins type, 1055 Object[] args) 1056 { 1057 scope = ScriptableObject.getTopLevelScope(scope); 1058 Function ctor = TopLevel.getBuiltinCtor(cx, scope, type); 1059 if (args == null) { args = ScriptRuntime.emptyArgs; } 1060 return ctor.construct(cx, scope, args); 1061 } 1062 1063 /** 1064 * 1065 * See ECMA 9.4. 1066 */ toInteger(Object val)1067 public static double toInteger(Object val) { 1068 return toInteger(toNumber(val)); 1069 } 1070 1071 // convenience method toInteger(double d)1072 public static double toInteger(double d) { 1073 // if it's NaN 1074 if (d != d) 1075 return +0.0; 1076 1077 if (d == 0.0 || 1078 d == Double.POSITIVE_INFINITY || 1079 d == Double.NEGATIVE_INFINITY) 1080 return d; 1081 1082 if (d > 0.0) 1083 return Math.floor(d); 1084 else 1085 return Math.ceil(d); 1086 } 1087 toInteger(Object[] args, int index)1088 public static double toInteger(Object[] args, int index) { 1089 return (index < args.length) ? toInteger(args[index]) : +0.0; 1090 } 1091 1092 /** 1093 * 1094 * See ECMA 9.5. 1095 */ toInt32(Object val)1096 public static int toInt32(Object val) 1097 { 1098 // short circuit for common integer values 1099 if (val instanceof Integer) 1100 return ((Integer)val).intValue(); 1101 1102 return toInt32(toNumber(val)); 1103 } 1104 toInt32(Object[] args, int index)1105 public static int toInt32(Object[] args, int index) { 1106 return (index < args.length) ? toInt32(args[index]) : 0; 1107 } 1108 toInt32(double d)1109 public static int toInt32(double d) { 1110 int id = (int)d; 1111 if (id == d) { 1112 // This covers -0.0 as well 1113 return id; 1114 } 1115 1116 if (d != d 1117 || d == Double.POSITIVE_INFINITY 1118 || d == Double.NEGATIVE_INFINITY) 1119 { 1120 return 0; 1121 } 1122 1123 d = (d >= 0) ? Math.floor(d) : Math.ceil(d); 1124 1125 double two32 = 4294967296.0; 1126 d = Math.IEEEremainder(d, two32); 1127 // (double)(long)d == d should hold here 1128 1129 long l = (long)d; 1130 // returning (int)d does not work as d can be outside int range 1131 // but the result must always be 32 lower bits of l 1132 return (int)l; 1133 } 1134 1135 /** 1136 * See ECMA 9.6. 1137 * @return long value representing 32 bits unsigned integer 1138 */ toUint32(double d)1139 public static long toUint32(double d) { 1140 long l = (long)d; 1141 if (l == d) { 1142 // This covers -0.0 as well 1143 return l & 0xffffffffL; 1144 } 1145 1146 if (d != d 1147 || d == Double.POSITIVE_INFINITY 1148 || d == Double.NEGATIVE_INFINITY) 1149 { 1150 return 0; 1151 } 1152 1153 d = (d >= 0) ? Math.floor(d) : Math.ceil(d); 1154 1155 // 0x100000000 gives me a numeric overflow... 1156 double two32 = 4294967296.0; 1157 l = (long)Math.IEEEremainder(d, two32); 1158 1159 return l & 0xffffffffL; 1160 } 1161 toUint32(Object val)1162 public static long toUint32(Object val) { 1163 return toUint32(toNumber(val)); 1164 } 1165 1166 /** 1167 * 1168 * See ECMA 9.7. 1169 */ toUint16(Object val)1170 public static char toUint16(Object val) { 1171 double d = toNumber(val); 1172 1173 int i = (int)d; 1174 if (i == d) { 1175 return (char)i; 1176 } 1177 1178 if (d != d 1179 || d == Double.POSITIVE_INFINITY 1180 || d == Double.NEGATIVE_INFINITY) 1181 { 1182 return 0; 1183 } 1184 1185 d = (d >= 0) ? Math.floor(d) : Math.ceil(d); 1186 1187 int int16 = 0x10000; 1188 i = (int)Math.IEEEremainder(d, int16); 1189 1190 return (char)i; 1191 } 1192 1193 // XXX: this is until setDefaultNamespace will learn how to store NS 1194 // properly and separates namespace form Scriptable.get etc. 1195 private static final String DEFAULT_NS_TAG = "__default_namespace__"; 1196 setDefaultNamespace(Object namespace, Context cx)1197 public static Object setDefaultNamespace(Object namespace, Context cx) 1198 { 1199 Scriptable scope = cx.currentActivationCall; 1200 if (scope == null) { 1201 scope = getTopCallScope(cx); 1202 } 1203 1204 XMLLib xmlLib = currentXMLLib(cx); 1205 Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace); 1206 1207 // XXX : this should be in separated namesapce from Scriptable.get/put 1208 if (!scope.has(DEFAULT_NS_TAG, scope)) { 1209 // XXX: this is racy of cause 1210 ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns, 1211 ScriptableObject.PERMANENT 1212 | ScriptableObject.DONTENUM); 1213 } else { 1214 scope.put(DEFAULT_NS_TAG, scope, ns); 1215 } 1216 1217 return Undefined.instance; 1218 } 1219 searchDefaultNamespace(Context cx)1220 public static Object searchDefaultNamespace(Context cx) 1221 { 1222 Scriptable scope = cx.currentActivationCall; 1223 if (scope == null) { 1224 scope = getTopCallScope(cx); 1225 } 1226 Object nsObject; 1227 for (;;) { 1228 Scriptable parent = scope.getParentScope(); 1229 if (parent == null) { 1230 nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG); 1231 if (nsObject == Scriptable.NOT_FOUND) { 1232 return null; 1233 } 1234 break; 1235 } 1236 nsObject = scope.get(DEFAULT_NS_TAG, scope); 1237 if (nsObject != Scriptable.NOT_FOUND) { 1238 break; 1239 } 1240 scope = parent; 1241 } 1242 return nsObject; 1243 } 1244 getTopLevelProp(Scriptable scope, String id)1245 public static Object getTopLevelProp(Scriptable scope, String id) { 1246 scope = ScriptableObject.getTopLevelScope(scope); 1247 return ScriptableObject.getProperty(scope, id); 1248 } 1249 getExistingCtor(Context cx, Scriptable scope, String constructorName)1250 static Function getExistingCtor(Context cx, Scriptable scope, 1251 String constructorName) 1252 { 1253 Object ctorVal = ScriptableObject.getProperty(scope, constructorName); 1254 if (ctorVal instanceof Function) { 1255 return (Function)ctorVal; 1256 } 1257 if (ctorVal == Scriptable.NOT_FOUND) { 1258 throw Context.reportRuntimeError1( 1259 "msg.ctor.not.found", constructorName); 1260 } else { 1261 throw Context.reportRuntimeError1( 1262 "msg.not.ctor", constructorName); 1263 } 1264 } 1265 1266 /** 1267 * Return -1L if str is not an index, or the index value as lower 32 1268 * bits of the result. Note that the result needs to be cast to an int 1269 * in order to produce the actual index, which may be negative. 1270 */ indexFromString(String str)1271 public static long indexFromString(String str) 1272 { 1273 // The length of the decimal string representation of 1274 // Integer.MAX_VALUE, 2147483647 1275 final int MAX_VALUE_LENGTH = 10; 1276 1277 int len = str.length(); 1278 if (len > 0) { 1279 int i = 0; 1280 boolean negate = false; 1281 int c = str.charAt(0); 1282 if (c == '-') { 1283 if (len > 1) { 1284 c = str.charAt(1); 1285 if (c == '0') return -1L; // "-0" is not an index 1286 i = 1; 1287 negate = true; 1288 } 1289 } 1290 c -= '0'; 1291 if (0 <= c && c <= 9 1292 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) 1293 { 1294 // Use negative numbers to accumulate index to handle 1295 // Integer.MIN_VALUE that is greater by 1 in absolute value 1296 // then Integer.MAX_VALUE 1297 int index = -c; 1298 int oldIndex = 0; 1299 i++; 1300 if (index != 0) { 1301 // Note that 00, 01, 000 etc. are not indexes 1302 while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9) 1303 { 1304 oldIndex = index; 1305 index = 10 * index - c; 1306 i++; 1307 } 1308 } 1309 // Make sure all characters were consumed and that it couldn't 1310 // have overflowed. 1311 if (i == len && 1312 (oldIndex > (Integer.MIN_VALUE / 10) || 1313 (oldIndex == (Integer.MIN_VALUE / 10) && 1314 c <= (negate ? -(Integer.MIN_VALUE % 10) 1315 : (Integer.MAX_VALUE % 10))))) 1316 { 1317 return 0xFFFFFFFFL & (negate ? index : -index); 1318 } 1319 } 1320 } 1321 return -1L; 1322 } 1323 1324 /** 1325 * If str is a decimal presentation of Uint32 value, return it as long. 1326 * Othewise return -1L; 1327 */ testUint32String(String str)1328 public static long testUint32String(String str) 1329 { 1330 // The length of the decimal string representation of 1331 // UINT32_MAX_VALUE, 4294967296 1332 final int MAX_VALUE_LENGTH = 10; 1333 1334 int len = str.length(); 1335 if (1 <= len && len <= MAX_VALUE_LENGTH) { 1336 int c = str.charAt(0); 1337 c -= '0'; 1338 if (c == 0) { 1339 // Note that 00,01 etc. are not valid Uint32 presentations 1340 return (len == 1) ? 0L : -1L; 1341 } 1342 if (1 <= c && c <= 9) { 1343 long v = c; 1344 for (int i = 1; i != len; ++i) { 1345 c = str.charAt(i) - '0'; 1346 if (!(0 <= c && c <= 9)) { 1347 return -1; 1348 } 1349 v = 10 * v + c; 1350 } 1351 // Check for overflow 1352 if ((v >>> 32) == 0) { 1353 return v; 1354 } 1355 } 1356 } 1357 return -1; 1358 } 1359 1360 /** 1361 * If s represents index, then return index value wrapped as Integer 1362 * and othewise return s. 1363 */ getIndexObject(String s)1364 static Object getIndexObject(String s) 1365 { 1366 long indexTest = indexFromString(s); 1367 if (indexTest >= 0) { 1368 return Integer.valueOf((int)indexTest); 1369 } 1370 return s; 1371 } 1372 1373 /** 1374 * If d is exact int value, return its value wrapped as Integer 1375 * and othewise return d converted to String. 1376 */ getIndexObject(double d)1377 static Object getIndexObject(double d) 1378 { 1379 int i = (int)d; 1380 if (i == d) { 1381 return Integer.valueOf(i); 1382 } 1383 return toString(d); 1384 } 1385 1386 /** 1387 * If toString(id) is a decimal presentation of int32 value, then id 1388 * is index. In this case return null and make the index available 1389 * as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id). 1390 */ toStringIdOrIndex(Context cx, Object id)1391 static String toStringIdOrIndex(Context cx, Object id) 1392 { 1393 if (id instanceof Number) { 1394 double d = ((Number)id).doubleValue(); 1395 int index = (int)d; 1396 if (index == d) { 1397 storeIndexResult(cx, index); 1398 return null; 1399 } 1400 return toString(id); 1401 } else { 1402 String s; 1403 if (id instanceof String) { 1404 s = (String)id; 1405 } else { 1406 s = toString(id); 1407 } 1408 long indexTest = indexFromString(s); 1409 if (indexTest >= 0) { 1410 storeIndexResult(cx, (int)indexTest); 1411 return null; 1412 } 1413 return s; 1414 } 1415 } 1416 1417 /** 1418 * Call obj.[[Get]](id) 1419 */ getObjectElem(Object obj, Object elem, Context cx)1420 public static Object getObjectElem(Object obj, Object elem, Context cx) 1421 { 1422 return getObjectElem(obj, elem, cx, getTopCallScope(cx)); 1423 } 1424 1425 /** 1426 * Call obj.[[Get]](id) 1427 */ getObjectElem(Object obj, Object elem, Context cx, final Scriptable scope)1428 public static Object getObjectElem(Object obj, Object elem, Context cx, final Scriptable scope) 1429 { 1430 Scriptable sobj = toObjectOrNull(cx, obj, scope); 1431 if (sobj == null) { 1432 throw undefReadError(obj, elem); 1433 } 1434 return getObjectElem(sobj, elem, cx); 1435 } 1436 getObjectElem(Scriptable obj, Object elem, Context cx)1437 public static Object getObjectElem(Scriptable obj, Object elem, 1438 Context cx) 1439 { 1440 1441 Object result; 1442 1443 if (obj instanceof XMLObject) { 1444 result = ((XMLObject)obj).get(cx, elem); 1445 } else { 1446 String s = toStringIdOrIndex(cx, elem); 1447 if (s == null) { 1448 int index = lastIndexResult(cx); 1449 result = ScriptableObject.getProperty(obj, index); 1450 } else { 1451 result = ScriptableObject.getProperty(obj, s); 1452 } 1453 } 1454 1455 if (result == Scriptable.NOT_FOUND) { 1456 result = Undefined.instance; 1457 } 1458 1459 return result; 1460 } 1461 1462 /** 1463 * Version of getObjectElem when elem is a valid JS identifier name. 1464 */ getObjectProp(Object obj, String property, Context cx)1465 public static Object getObjectProp(Object obj, String property, 1466 Context cx) 1467 { 1468 Scriptable sobj = toObjectOrNull(cx, obj); 1469 if (sobj == null) { 1470 throw undefReadError(obj, property); 1471 } 1472 return getObjectProp(sobj, property, cx); 1473 } 1474 1475 /** 1476 * @param scope the scope that should be used to resolve primitive prototype 1477 */ getObjectProp(Object obj, String property, Context cx, final Scriptable scope)1478 public static Object getObjectProp(Object obj, String property, 1479 Context cx, final Scriptable scope) 1480 { 1481 Scriptable sobj = toObjectOrNull(cx, obj, scope); 1482 if (sobj == null) { 1483 throw undefReadError(obj, property); 1484 } 1485 return getObjectProp(sobj, property, cx); 1486 } 1487 getObjectProp(Scriptable obj, String property, Context cx)1488 public static Object getObjectProp(Scriptable obj, String property, 1489 Context cx) 1490 { 1491 1492 Object result = ScriptableObject.getProperty(obj, property); 1493 if (result == Scriptable.NOT_FOUND) { 1494 if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) { 1495 Context.reportWarning(ScriptRuntime.getMessage1( 1496 "msg.ref.undefined.prop", property)); 1497 } 1498 result = Undefined.instance; 1499 } 1500 1501 return result; 1502 } 1503 getObjectPropNoWarn(Object obj, String property, Context cx)1504 public static Object getObjectPropNoWarn(Object obj, String property, 1505 Context cx) 1506 { 1507 Scriptable sobj = toObjectOrNull(cx, obj); 1508 if (sobj == null) { 1509 throw undefReadError(obj, property); 1510 } 1511 Object result = ScriptableObject.getProperty(sobj, property); 1512 if (result == Scriptable.NOT_FOUND) { 1513 return Undefined.instance; 1514 } 1515 return result; 1516 } 1517 1518 /* 1519 * A cheaper and less general version of the above for well-known argument 1520 * types. 1521 */ getObjectIndex(Object obj, double dblIndex, Context cx)1522 public static Object getObjectIndex(Object obj, double dblIndex, 1523 Context cx) 1524 { 1525 Scriptable sobj = toObjectOrNull(cx, obj); 1526 if (sobj == null) { 1527 throw undefReadError(obj, toString(dblIndex)); 1528 } 1529 1530 int index = (int)dblIndex; 1531 if (index == dblIndex) { 1532 return getObjectIndex(sobj, index, cx); 1533 } else { 1534 String s = toString(dblIndex); 1535 return getObjectProp(sobj, s, cx); 1536 } 1537 } 1538 getObjectIndex(Scriptable obj, int index, Context cx)1539 public static Object getObjectIndex(Scriptable obj, int index, 1540 Context cx) 1541 { 1542 1543 Object result = ScriptableObject.getProperty(obj, index); 1544 if (result == Scriptable.NOT_FOUND) { 1545 result = Undefined.instance; 1546 } 1547 1548 return result; 1549 } 1550 1551 /* 1552 * Call obj.[[Put]](id, value) 1553 */ setObjectElem(Object obj, Object elem, Object value, Context cx)1554 public static Object setObjectElem(Object obj, Object elem, Object value, 1555 Context cx) 1556 { 1557 Scriptable sobj = toObjectOrNull(cx, obj); 1558 if (sobj == null) { 1559 throw undefWriteError(obj, elem, value); 1560 } 1561 return setObjectElem(sobj, elem, value, cx); 1562 } 1563 setObjectElem(Scriptable obj, Object elem, Object value, Context cx)1564 public static Object setObjectElem(Scriptable obj, Object elem, 1565 Object value, Context cx) 1566 { 1567 if (obj instanceof XMLObject) { 1568 ((XMLObject)obj).put(cx, elem, value); 1569 } else { 1570 String s = toStringIdOrIndex(cx, elem); 1571 if (s == null) { 1572 int index = lastIndexResult(cx); 1573 ScriptableObject.putProperty(obj, index, value); 1574 } else { 1575 ScriptableObject.putProperty(obj, s, value); 1576 } 1577 } 1578 1579 return value; 1580 } 1581 1582 /** 1583 * Version of setObjectElem when elem is a valid JS identifier name. 1584 */ setObjectProp(Object obj, String property, Object value, Context cx)1585 public static Object setObjectProp(Object obj, String property, 1586 Object value, Context cx) 1587 { 1588 Scriptable sobj = toObjectOrNull(cx, obj); 1589 if (sobj == null) { 1590 throw undefWriteError(obj, property, value); 1591 } 1592 return setObjectProp(sobj, property, value, cx); 1593 } 1594 setObjectProp(Scriptable obj, String property, Object value, Context cx)1595 public static Object setObjectProp(Scriptable obj, String property, 1596 Object value, Context cx) 1597 { 1598 ScriptableObject.putProperty(obj, property, value); 1599 return value; 1600 } 1601 1602 /* 1603 * A cheaper and less general version of the above for well-known argument 1604 * types. 1605 */ setObjectIndex(Object obj, double dblIndex, Object value, Context cx)1606 public static Object setObjectIndex(Object obj, double dblIndex, 1607 Object value, Context cx) 1608 { 1609 Scriptable sobj = toObjectOrNull(cx, obj); 1610 if (sobj == null) { 1611 throw undefWriteError(obj, String.valueOf(dblIndex), value); 1612 } 1613 1614 int index = (int)dblIndex; 1615 if (index == dblIndex) { 1616 return setObjectIndex(sobj, index, value, cx); 1617 } else { 1618 String s = toString(dblIndex); 1619 return setObjectProp(sobj, s, value, cx); 1620 } 1621 } 1622 setObjectIndex(Scriptable obj, int index, Object value, Context cx)1623 public static Object setObjectIndex(Scriptable obj, int index, Object value, 1624 Context cx) 1625 { 1626 ScriptableObject.putProperty(obj, index, value); 1627 return value; 1628 } 1629 deleteObjectElem(Scriptable target, Object elem, Context cx)1630 public static boolean deleteObjectElem(Scriptable target, Object elem, 1631 Context cx) 1632 { 1633 String s = toStringIdOrIndex(cx, elem); 1634 if (s == null) { 1635 int index = lastIndexResult(cx); 1636 target.delete(index); 1637 return !target.has(index, target); 1638 } else { 1639 target.delete(s); 1640 return !target.has(s, target); 1641 } 1642 } 1643 hasObjectElem(Scriptable target, Object elem, Context cx)1644 public static boolean hasObjectElem(Scriptable target, Object elem, 1645 Context cx) 1646 { 1647 boolean result; 1648 1649 String s = toStringIdOrIndex(cx, elem); 1650 if (s == null) { 1651 int index = lastIndexResult(cx); 1652 result = ScriptableObject.hasProperty(target, index); 1653 } else { 1654 result = ScriptableObject.hasProperty(target, s); 1655 } 1656 1657 return result; 1658 } 1659 refGet(Ref ref, Context cx)1660 public static Object refGet(Ref ref, Context cx) 1661 { 1662 return ref.get(cx); 1663 } 1664 refSet(Ref ref, Object value, Context cx)1665 public static Object refSet(Ref ref, Object value, Context cx) 1666 { 1667 return ref.set(cx, value); 1668 } 1669 refDel(Ref ref, Context cx)1670 public static Object refDel(Ref ref, Context cx) 1671 { 1672 return wrapBoolean(ref.delete(cx)); 1673 } 1674 isSpecialProperty(String s)1675 static boolean isSpecialProperty(String s) 1676 { 1677 return s.equals("__proto__") || s.equals("__parent__"); 1678 } 1679 specialRef(Object obj, String specialProperty, Context cx)1680 public static Ref specialRef(Object obj, String specialProperty, 1681 Context cx) 1682 { 1683 return SpecialRef.createSpecial(cx, obj, specialProperty); 1684 } 1685 1686 /** 1687 * @deprecated 1688 */ delete(Object obj, Object id, Context cx)1689 public static Object delete(Object obj, Object id, Context cx) 1690 { 1691 return delete(obj, id, cx, false); 1692 } 1693 1694 /** 1695 * The delete operator 1696 * 1697 * See ECMA 11.4.1 1698 * 1699 * In ECMA 0.19, the description of the delete operator (11.4.1) 1700 * assumes that the [[Delete]] method returns a value. However, 1701 * the definition of the [[Delete]] operator (8.6.2.5) does not 1702 * define a return value. Here we assume that the [[Delete]] 1703 * method doesn't return a value. 1704 */ delete(Object obj, Object id, Context cx, boolean isName)1705 public static Object delete(Object obj, Object id, Context cx, boolean isName) 1706 { 1707 Scriptable sobj = toObjectOrNull(cx, obj); 1708 if (sobj == null) { 1709 if (isName) { 1710 return Boolean.TRUE; 1711 } 1712 String idStr = (id == null) ? "null" : id.toString(); 1713 throw typeError2("msg.undef.prop.delete", toString(obj), idStr); 1714 } 1715 boolean result = deleteObjectElem(sobj, id, cx); 1716 return wrapBoolean(result); 1717 } 1718 1719 /** 1720 * Looks up a name in the scope chain and returns its value. 1721 */ name(Context cx, Scriptable scope, String name)1722 public static Object name(Context cx, Scriptable scope, String name) 1723 { 1724 Scriptable parent = scope.getParentScope(); 1725 if (parent == null) { 1726 Object result = topScopeName(cx, scope, name); 1727 if (result == Scriptable.NOT_FOUND) { 1728 throw notFoundError(scope, name); 1729 } 1730 return result; 1731 } 1732 1733 return nameOrFunction(cx, scope, parent, name, false); 1734 } 1735 nameOrFunction(Context cx, Scriptable scope, Scriptable parentScope, String name, boolean asFunctionCall)1736 private static Object nameOrFunction(Context cx, Scriptable scope, 1737 Scriptable parentScope, String name, 1738 boolean asFunctionCall) 1739 { 1740 Object result; 1741 Scriptable thisObj = scope; // It is used only if asFunctionCall==true. 1742 1743 XMLObject firstXMLObject = null; 1744 for (;;) { 1745 if (scope instanceof NativeWith) { 1746 Scriptable withObj = scope.getPrototype(); 1747 if (withObj instanceof XMLObject) { 1748 XMLObject xmlObj = (XMLObject)withObj; 1749 if (xmlObj.has(name, xmlObj)) { 1750 // function this should be the target object of with 1751 thisObj = xmlObj; 1752 result = xmlObj.get(name, xmlObj); 1753 break; 1754 } 1755 if (firstXMLObject == null) { 1756 firstXMLObject = xmlObj; 1757 } 1758 } else { 1759 result = ScriptableObject.getProperty(withObj, name); 1760 if (result != Scriptable.NOT_FOUND) { 1761 // function this should be the target object of with 1762 thisObj = withObj; 1763 break; 1764 } 1765 } 1766 } else if (scope instanceof NativeCall) { 1767 // NativeCall does not prototype chain and Scriptable.get 1768 // can be called directly. 1769 result = scope.get(name, scope); 1770 if (result != Scriptable.NOT_FOUND) { 1771 if (asFunctionCall) { 1772 // ECMA 262 requires that this for nested funtions 1773 // should be top scope 1774 thisObj = ScriptableObject. 1775 getTopLevelScope(parentScope); 1776 } 1777 break; 1778 } 1779 } else { 1780 // Can happen if Rhino embedding decided that nested 1781 // scopes are useful for what ever reasons. 1782 result = ScriptableObject.getProperty(scope, name); 1783 if (result != Scriptable.NOT_FOUND) { 1784 thisObj = scope; 1785 break; 1786 } 1787 } 1788 scope = parentScope; 1789 parentScope = parentScope.getParentScope(); 1790 if (parentScope == null) { 1791 result = topScopeName(cx, scope, name); 1792 if (result == Scriptable.NOT_FOUND) { 1793 if (firstXMLObject == null || asFunctionCall) { 1794 throw notFoundError(scope, name); 1795 } 1796 // The name was not found, but we did find an XML 1797 // object in the scope chain and we are looking for name, 1798 // not function. The result should be an empty XMLList 1799 // in name context. 1800 result = firstXMLObject.get(name, firstXMLObject); 1801 } 1802 // For top scope thisObj for functions is always scope itself. 1803 thisObj = scope; 1804 break; 1805 } 1806 } 1807 1808 if (asFunctionCall) { 1809 if (!(result instanceof Callable)) { 1810 throw notFunctionError(result, name); 1811 } 1812 storeScriptable(cx, thisObj); 1813 } 1814 1815 return result; 1816 } 1817 topScopeName(Context cx, Scriptable scope, String name)1818 private static Object topScopeName(Context cx, Scriptable scope, 1819 String name) 1820 { 1821 if (cx.useDynamicScope) { 1822 scope = checkDynamicScope(cx.topCallScope, scope); 1823 } 1824 return ScriptableObject.getProperty(scope, name); 1825 } 1826 1827 1828 /** 1829 * Returns the object in the scope chain that has a given property. 1830 * 1831 * The order of evaluation of an assignment expression involves 1832 * evaluating the lhs to a reference, evaluating the rhs, and then 1833 * modifying the reference with the rhs value. This method is used 1834 * to 'bind' the given name to an object containing that property 1835 * so that the side effects of evaluating the rhs do not affect 1836 * which property is modified. 1837 * Typically used in conjunction with setName. 1838 * 1839 * See ECMA 10.1.4 1840 */ bind(Context cx, Scriptable scope, String id)1841 public static Scriptable bind(Context cx, Scriptable scope, String id) 1842 { 1843 Scriptable firstXMLObject = null; 1844 Scriptable parent = scope.getParentScope(); 1845 childScopesChecks: if (parent != null) { 1846 // Check for possibly nested "with" scopes first 1847 while (scope instanceof NativeWith) { 1848 Scriptable withObj = scope.getPrototype(); 1849 if (withObj instanceof XMLObject) { 1850 XMLObject xmlObject = (XMLObject)withObj; 1851 if (xmlObject.has(cx, id)) { 1852 return xmlObject; 1853 } 1854 if (firstXMLObject == null) { 1855 firstXMLObject = xmlObject; 1856 } 1857 } else { 1858 if (ScriptableObject.hasProperty(withObj, id)) { 1859 return withObj; 1860 } 1861 } 1862 scope = parent; 1863 parent = parent.getParentScope(); 1864 if (parent == null) { 1865 break childScopesChecks; 1866 } 1867 } 1868 for (;;) { 1869 if (ScriptableObject.hasProperty(scope, id)) { 1870 return scope; 1871 } 1872 scope = parent; 1873 parent = parent.getParentScope(); 1874 if (parent == null) { 1875 break childScopesChecks; 1876 } 1877 } 1878 } 1879 // scope here is top scope 1880 if (cx.useDynamicScope) { 1881 scope = checkDynamicScope(cx.topCallScope, scope); 1882 } 1883 if (ScriptableObject.hasProperty(scope, id)) { 1884 return scope; 1885 } 1886 // Nothing was found, but since XML objects always bind 1887 // return one if found 1888 return firstXMLObject; 1889 } 1890 setName(Scriptable bound, Object value, Context cx, Scriptable scope, String id)1891 public static Object setName(Scriptable bound, Object value, 1892 Context cx, Scriptable scope, String id) 1893 { 1894 if (bound != null) { 1895 // TODO: we used to special-case XMLObject here, but putProperty 1896 // seems to work for E4X and it's better to optimize the common case 1897 ScriptableObject.putProperty(bound, id, value); 1898 } else { 1899 // "newname = 7;", where 'newname' has not yet 1900 // been defined, creates a new property in the 1901 // top scope unless strict mode is specified. 1902 if (cx.hasFeature(Context.FEATURE_STRICT_MODE) || 1903 cx.hasFeature(Context.FEATURE_STRICT_VARS)) 1904 { 1905 Context.reportWarning( 1906 ScriptRuntime.getMessage1("msg.assn.create.strict", id)); 1907 } 1908 // Find the top scope by walking up the scope chain. 1909 bound = ScriptableObject.getTopLevelScope(scope); 1910 if (cx.useDynamicScope) { 1911 bound = checkDynamicScope(cx.topCallScope, bound); 1912 } 1913 bound.put(id, bound, value); 1914 } 1915 return value; 1916 } 1917 strictSetName(Scriptable bound, Object value, Context cx, Scriptable scope, String id)1918 public static Object strictSetName(Scriptable bound, Object value, 1919 Context cx, Scriptable scope, String id) { 1920 if (bound != null) { 1921 // TODO: The LeftHandSide also may not be a reference to a 1922 // data property with the attribute value {[[Writable]]:false}, 1923 // to an accessor property with the attribute value 1924 // {[[Put]]:undefined}, nor to a non-existent property of an 1925 // object whose [[Extensible]] internal property has the value 1926 // false. In these cases a TypeError exception is thrown (11.13.1). 1927 // TODO: we used to special-case XMLObject here, but putProperty 1928 // seems to work for E4X and we should optimize the common case 1929 ScriptableObject.putProperty(bound, id, value); 1930 return value; 1931 } else { 1932 // See ES5 8.7.2 1933 String msg = "Assignment to undefined \"" + id + "\" in strict mode"; 1934 throw constructError("ReferenceError", msg); 1935 } 1936 } 1937 setConst(Scriptable bound, Object value, Context cx, String id)1938 public static Object setConst(Scriptable bound, Object value, 1939 Context cx, String id) 1940 { 1941 if (bound instanceof XMLObject) { 1942 bound.put(id, bound, value); 1943 } else { 1944 ScriptableObject.putConstProperty(bound, id, value); 1945 } 1946 return value; 1947 } 1948 1949 /** 1950 * This is the enumeration needed by the for..in statement. 1951 * 1952 * See ECMA 12.6.3. 1953 * 1954 * IdEnumeration maintains a ObjToIntMap to make sure a given 1955 * id is enumerated only once across multiple objects in a 1956 * prototype chain. 1957 * 1958 * XXX - ECMA delete doesn't hide properties in the prototype, 1959 * but js/ref does. This means that the js/ref for..in can 1960 * avoid maintaining a hash table and instead perform lookups 1961 * to see if a given property has already been enumerated. 1962 * 1963 */ 1964 private static class IdEnumeration implements Serializable 1965 { 1966 private static final long serialVersionUID = 1L; 1967 Scriptable obj; 1968 Object[] ids; 1969 int index; 1970 ObjToIntMap used; 1971 Object currentId; 1972 int enumType; /* one of ENUM_INIT_KEYS, ENUM_INIT_VALUES, 1973 ENUM_INIT_ARRAY */ 1974 1975 // if true, integer ids will be returned as numbers rather than strings 1976 boolean enumNumbers; 1977 1978 Scriptable iterator; 1979 } 1980 toIterator(Context cx, Scriptable scope, Scriptable obj, boolean keyOnly)1981 public static Scriptable toIterator(Context cx, Scriptable scope, 1982 Scriptable obj, boolean keyOnly) 1983 { 1984 if (ScriptableObject.hasProperty(obj, 1985 NativeIterator.ITERATOR_PROPERTY_NAME)) 1986 { 1987 Object v = ScriptableObject.getProperty(obj, 1988 NativeIterator.ITERATOR_PROPERTY_NAME); 1989 if (!(v instanceof Callable)) { 1990 throw typeError0("msg.invalid.iterator"); 1991 } 1992 Callable f = (Callable) v; 1993 Object[] args = new Object[] { keyOnly ? Boolean.TRUE 1994 : Boolean.FALSE }; 1995 v = f.call(cx, scope, obj, args); 1996 if (!(v instanceof Scriptable)) { 1997 throw typeError0("msg.iterator.primitive"); 1998 } 1999 return (Scriptable) v; 2000 } 2001 return null; 2002 } 2003 2004 // for backwards compatibility with generated class files enumInit(Object value, Context cx, boolean enumValues)2005 public static Object enumInit(Object value, Context cx, boolean enumValues) 2006 { 2007 return enumInit(value, cx, enumValues ? ENUMERATE_VALUES 2008 : ENUMERATE_KEYS); 2009 } 2010 2011 public static final int ENUMERATE_KEYS = 0; 2012 public static final int ENUMERATE_VALUES = 1; 2013 public static final int ENUMERATE_ARRAY = 2; 2014 public static final int ENUMERATE_KEYS_NO_ITERATOR = 3; 2015 public static final int ENUMERATE_VALUES_NO_ITERATOR = 4; 2016 public static final int ENUMERATE_ARRAY_NO_ITERATOR = 5; 2017 enumInit(Object value, Context cx, int enumType)2018 public static Object enumInit(Object value, Context cx, int enumType) 2019 { 2020 IdEnumeration x = new IdEnumeration(); 2021 x.obj = toObjectOrNull(cx, value); 2022 if (x.obj == null) { 2023 // null or undefined do not cause errors but rather lead to empty 2024 // "for in" loop 2025 return x; 2026 } 2027 x.enumType = enumType; 2028 x.iterator = null; 2029 if (enumType != ENUMERATE_KEYS_NO_ITERATOR && 2030 enumType != ENUMERATE_VALUES_NO_ITERATOR && 2031 enumType != ENUMERATE_ARRAY_NO_ITERATOR) 2032 { 2033 x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj, 2034 enumType == ScriptRuntime.ENUMERATE_KEYS); 2035 } 2036 if (x.iterator == null) { 2037 // enumInit should read all initial ids before returning 2038 // or "for (a.i in a)" would wrongly enumerate i in a as well 2039 enumChangeObject(x); 2040 } 2041 2042 return x; 2043 } 2044 setEnumNumbers(Object enumObj, boolean enumNumbers)2045 public static void setEnumNumbers(Object enumObj, boolean enumNumbers) { 2046 ((IdEnumeration)enumObj).enumNumbers = enumNumbers; 2047 } 2048 enumNext(Object enumObj)2049 public static Boolean enumNext(Object enumObj) 2050 { 2051 IdEnumeration x = (IdEnumeration)enumObj; 2052 if (x.iterator != null) { 2053 Object v = ScriptableObject.getProperty(x.iterator, "next"); 2054 if (!(v instanceof Callable)) 2055 return Boolean.FALSE; 2056 Callable f = (Callable) v; 2057 Context cx = Context.getContext(); 2058 try { 2059 x.currentId = f.call(cx, x.iterator.getParentScope(), 2060 x.iterator, emptyArgs); 2061 return Boolean.TRUE; 2062 } catch (JavaScriptException e) { 2063 if (e.getValue() instanceof NativeIterator.StopIteration) { 2064 return Boolean.FALSE; 2065 } 2066 throw e; 2067 } 2068 } 2069 for (;;) { 2070 if (x.obj == null) { 2071 return Boolean.FALSE; 2072 } 2073 if (x.index == x.ids.length) { 2074 x.obj = x.obj.getPrototype(); 2075 enumChangeObject(x); 2076 continue; 2077 } 2078 Object id = x.ids[x.index++]; 2079 if (x.used != null && x.used.has(id)) { 2080 continue; 2081 } 2082 if (id instanceof String) { 2083 String strId = (String)id; 2084 if (!x.obj.has(strId, x.obj)) 2085 continue; // must have been deleted 2086 x.currentId = strId; 2087 } else { 2088 int intId = ((Number)id).intValue(); 2089 if (!x.obj.has(intId, x.obj)) 2090 continue; // must have been deleted 2091 x.currentId = x.enumNumbers ? (Object) (Integer.valueOf(intId)) 2092 : String.valueOf(intId); 2093 } 2094 return Boolean.TRUE; 2095 } 2096 } 2097 enumId(Object enumObj, Context cx)2098 public static Object enumId(Object enumObj, Context cx) 2099 { 2100 IdEnumeration x = (IdEnumeration)enumObj; 2101 if (x.iterator != null) { 2102 return x.currentId; 2103 } 2104 switch (x.enumType) { 2105 case ENUMERATE_KEYS: 2106 case ENUMERATE_KEYS_NO_ITERATOR: 2107 return x.currentId; 2108 case ENUMERATE_VALUES: 2109 case ENUMERATE_VALUES_NO_ITERATOR: 2110 return enumValue(enumObj, cx); 2111 case ENUMERATE_ARRAY: 2112 case ENUMERATE_ARRAY_NO_ITERATOR: 2113 Object[] elements = { x.currentId, enumValue(enumObj, cx) }; 2114 return cx.newArray(ScriptableObject.getTopLevelScope(x.obj), elements); 2115 default: 2116 throw Kit.codeBug(); 2117 } 2118 } 2119 enumValue(Object enumObj, Context cx)2120 public static Object enumValue(Object enumObj, Context cx) { 2121 IdEnumeration x = (IdEnumeration)enumObj; 2122 2123 Object result; 2124 2125 String s = toStringIdOrIndex(cx, x.currentId); 2126 if (s == null) { 2127 int index = lastIndexResult(cx); 2128 result = x.obj.get(index, x.obj); 2129 } else { 2130 result = x.obj.get(s, x.obj); 2131 } 2132 2133 return result; 2134 } 2135 enumChangeObject(IdEnumeration x)2136 private static void enumChangeObject(IdEnumeration x) 2137 { 2138 Object[] ids = null; 2139 while (x.obj != null) { 2140 ids = x.obj.getIds(); 2141 if (ids.length != 0) { 2142 break; 2143 } 2144 x.obj = x.obj.getPrototype(); 2145 } 2146 if (x.obj != null && x.ids != null) { 2147 Object[] previous = x.ids; 2148 int L = previous.length; 2149 if (x.used == null) { 2150 x.used = new ObjToIntMap(L); 2151 } 2152 for (int i = 0; i != L; ++i) { 2153 x.used.intern(previous[i]); 2154 } 2155 } 2156 x.ids = ids; 2157 x.index = 0; 2158 } 2159 2160 /** 2161 * Prepare for calling name(...): return function corresponding to 2162 * name and make current top scope available 2163 * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. 2164 * The caller must call ScriptRuntime.lastStoredScriptable() immediately 2165 * after calling this method. 2166 */ getNameFunctionAndThis(String name, Context cx, Scriptable scope)2167 public static Callable getNameFunctionAndThis(String name, 2168 Context cx, 2169 Scriptable scope) 2170 { 2171 Scriptable parent = scope.getParentScope(); 2172 if (parent == null) { 2173 Object result = topScopeName(cx, scope, name); 2174 if (!(result instanceof Callable)) { 2175 if (result == Scriptable.NOT_FOUND) { 2176 throw notFoundError(scope, name); 2177 } else { 2178 throw notFunctionError(result, name); 2179 } 2180 } 2181 // Top scope is not NativeWith or NativeCall => thisObj == scope 2182 Scriptable thisObj = scope; 2183 storeScriptable(cx, thisObj); 2184 return (Callable)result; 2185 } 2186 2187 // name will call storeScriptable(cx, thisObj); 2188 return (Callable)nameOrFunction(cx, scope, parent, name, true); 2189 } 2190 2191 /** 2192 * Prepare for calling obj[id](...): return function corresponding to 2193 * obj[id] and make obj properly converted to Scriptable available 2194 * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. 2195 * The caller must call ScriptRuntime.lastStoredScriptable() immediately 2196 * after calling this method. 2197 */ getElemFunctionAndThis(Object obj, Object elem, Context cx)2198 public static Callable getElemFunctionAndThis(Object obj, 2199 Object elem, 2200 Context cx) 2201 { 2202 String str = toStringIdOrIndex(cx, elem); 2203 if (str != null) { 2204 return getPropFunctionAndThis(obj, str, cx); 2205 } 2206 int index = lastIndexResult(cx); 2207 2208 Scriptable thisObj = toObjectOrNull(cx, obj); 2209 if (thisObj == null) { 2210 throw undefCallError(obj, String.valueOf(index)); 2211 } 2212 2213 Object value = ScriptableObject.getProperty(thisObj, index); 2214 if (!(value instanceof Callable)) { 2215 throw notFunctionError(value, elem); 2216 } 2217 2218 storeScriptable(cx, thisObj); 2219 return (Callable)value; 2220 } 2221 2222 /** 2223 * Prepare for calling obj.property(...): return function corresponding to 2224 * obj.property and make obj properly converted to Scriptable available 2225 * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. 2226 * The caller must call ScriptRuntime.lastStoredScriptable() immediately 2227 * after calling this method. 2228 * Warning: this doesn't allow to resolve primitive prototype properly when 2229 * many top scopes are involved. 2230 */ getPropFunctionAndThis(Object obj, String property, Context cx)2231 public static Callable getPropFunctionAndThis(Object obj, 2232 String property, 2233 Context cx) 2234 { 2235 Scriptable thisObj = toObjectOrNull(cx, obj); 2236 return getPropFunctionAndThisHelper(obj, property, cx, thisObj); 2237 } 2238 2239 /** 2240 * Prepare for calling obj.property(...): return function corresponding to 2241 * obj.property and make obj properly converted to Scriptable available 2242 * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. 2243 * The caller must call ScriptRuntime.lastStoredScriptable() immediately 2244 * after calling this method. 2245 */ getPropFunctionAndThis(Object obj, String property, Context cx, final Scriptable scope)2246 public static Callable getPropFunctionAndThis(Object obj, 2247 String property, 2248 Context cx, final Scriptable scope) 2249 { 2250 Scriptable thisObj = toObjectOrNull(cx, obj, scope); 2251 return getPropFunctionAndThisHelper(obj, property, cx, thisObj); 2252 } 2253 getPropFunctionAndThisHelper(Object obj, String property, Context cx, Scriptable thisObj)2254 private static Callable getPropFunctionAndThisHelper(Object obj, 2255 String property, Context cx, Scriptable thisObj) 2256 { 2257 if (thisObj == null) { 2258 throw undefCallError(obj, property); 2259 } 2260 2261 Object value = ScriptableObject.getProperty(thisObj, property); 2262 if (!(value instanceof Callable)) { 2263 Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__"); 2264 if (noSuchMethod instanceof Callable) 2265 value = new NoSuchMethodShim((Callable)noSuchMethod, property); 2266 } 2267 2268 if (!(value instanceof Callable)) { 2269 throw notFunctionError(thisObj, value, property); 2270 } 2271 2272 storeScriptable(cx, thisObj); 2273 return (Callable)value; 2274 } 2275 2276 /** 2277 * Prepare for calling <expression>(...): return function corresponding to 2278 * <expression> and make parent scope of the function available 2279 * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. 2280 * The caller must call ScriptRuntime.lastStoredScriptable() immediately 2281 * after calling this method. 2282 */ getValueFunctionAndThis(Object value, Context cx)2283 public static Callable getValueFunctionAndThis(Object value, Context cx) 2284 { 2285 if (!(value instanceof Callable)) { 2286 throw notFunctionError(value); 2287 } 2288 2289 Callable f = (Callable)value; 2290 Scriptable thisObj = null; 2291 if (f instanceof Scriptable) { 2292 thisObj = ((Scriptable)f).getParentScope(); 2293 } 2294 if (thisObj == null) { 2295 if (cx.topCallScope == null) throw new IllegalStateException(); 2296 thisObj = cx.topCallScope; 2297 } 2298 if (thisObj.getParentScope() != null) { 2299 if (thisObj instanceof NativeWith) { 2300 // functions defined inside with should have with target 2301 // as their thisObj 2302 } else if (thisObj instanceof NativeCall) { 2303 // nested functions should have top scope as their thisObj 2304 thisObj = ScriptableObject.getTopLevelScope(thisObj); 2305 } 2306 } 2307 storeScriptable(cx, thisObj); 2308 return f; 2309 } 2310 2311 /** 2312 * Perform function call in reference context. Should always 2313 * return value that can be passed to 2314 * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)} 2315 * arbitrary number of times. 2316 * The args array reference should not be stored in any object that is 2317 * can be GC-reachable after this method returns. If this is necessary, 2318 * store args.clone(), not args array itself. 2319 */ callRef(Callable function, Scriptable thisObj, Object[] args, Context cx)2320 public static Ref callRef(Callable function, Scriptable thisObj, 2321 Object[] args, Context cx) 2322 { 2323 if (function instanceof RefCallable) { 2324 RefCallable rfunction = (RefCallable)function; 2325 Ref ref = rfunction.refCall(cx, thisObj, args); 2326 if (ref == null) { 2327 throw new IllegalStateException(rfunction.getClass().getName()+".refCall() returned null"); 2328 } 2329 return ref; 2330 } 2331 // No runtime support for now 2332 String msg = getMessage1("msg.no.ref.from.function", 2333 toString(function)); 2334 throw constructError("ReferenceError", msg); 2335 } 2336 2337 /** 2338 * Operator new. 2339 * 2340 * See ECMA 11.2.2 2341 */ newObject(Object fun, Context cx, Scriptable scope, Object[] args)2342 public static Scriptable newObject(Object fun, Context cx, 2343 Scriptable scope, Object[] args) 2344 { 2345 if (!(fun instanceof Function)) { 2346 throw notFunctionError(fun); 2347 } 2348 Function function = (Function)fun; 2349 return function.construct(cx, scope, args); 2350 } 2351 callSpecial(Context cx, Callable fun, Scriptable thisObj, Object[] args, Scriptable scope, Scriptable callerThis, int callType, String filename, int lineNumber)2352 public static Object callSpecial(Context cx, Callable fun, 2353 Scriptable thisObj, 2354 Object[] args, Scriptable scope, 2355 Scriptable callerThis, int callType, 2356 String filename, int lineNumber) 2357 { 2358 if (callType == Node.SPECIALCALL_EVAL) { 2359 if (thisObj.getParentScope() == null && NativeGlobal.isEvalFunction(fun)) { 2360 return evalSpecial(cx, scope, callerThis, args, 2361 filename, lineNumber); 2362 } 2363 } else if (callType == Node.SPECIALCALL_WITH) { 2364 if (NativeWith.isWithFunction(fun)) { 2365 throw Context.reportRuntimeError1("msg.only.from.new", 2366 "With"); 2367 } 2368 } else { 2369 throw Kit.codeBug(); 2370 } 2371 2372 return fun.call(cx, scope, thisObj, args); 2373 } 2374 newSpecial(Context cx, Object fun, Object[] args, Scriptable scope, int callType)2375 public static Object newSpecial(Context cx, Object fun, 2376 Object[] args, Scriptable scope, 2377 int callType) 2378 { 2379 if (callType == Node.SPECIALCALL_EVAL) { 2380 if (NativeGlobal.isEvalFunction(fun)) { 2381 throw typeError1("msg.not.ctor", "eval"); 2382 } 2383 } else if (callType == Node.SPECIALCALL_WITH) { 2384 if (NativeWith.isWithFunction(fun)) { 2385 return NativeWith.newWithSpecial(cx, scope, args); 2386 } 2387 } else { 2388 throw Kit.codeBug(); 2389 } 2390 2391 return newObject(fun, cx, scope, args); 2392 } 2393 2394 /** 2395 * Function.prototype.apply and Function.prototype.call 2396 * 2397 * See Ecma 15.3.4.[34] 2398 */ applyOrCall(boolean isApply, Context cx, Scriptable scope, Scriptable thisObj, Object[] args)2399 public static Object applyOrCall(boolean isApply, 2400 Context cx, Scriptable scope, 2401 Scriptable thisObj, Object[] args) 2402 { 2403 int L = args.length; 2404 Callable function = getCallable(thisObj); 2405 2406 Scriptable callThis = null; 2407 if (L != 0) { 2408 callThis = toObjectOrNull(cx, args[0]); 2409 } 2410 if (callThis == null) { 2411 // This covers the case of args[0] == (null|undefined) as well. 2412 callThis = getTopCallScope(cx); 2413 } 2414 2415 Object[] callArgs; 2416 if (isApply) { 2417 // Follow Ecma 15.3.4.3 2418 callArgs = L <= 1 ? ScriptRuntime.emptyArgs : 2419 getApplyArguments(cx, args[1]); 2420 } else { 2421 // Follow Ecma 15.3.4.4 2422 if (L <= 1) { 2423 callArgs = ScriptRuntime.emptyArgs; 2424 } else { 2425 callArgs = new Object[L - 1]; 2426 System.arraycopy(args, 1, callArgs, 0, L - 1); 2427 } 2428 } 2429 2430 return function.call(cx, scope, callThis, callArgs); 2431 } 2432 getApplyArguments(Context cx, Object arg1)2433 static Object[] getApplyArguments(Context cx, Object arg1) 2434 { 2435 if (arg1 == null || arg1 == Undefined.instance) { 2436 return ScriptRuntime.emptyArgs; 2437 } else if (arg1 instanceof NativeArray || arg1 instanceof Arguments) { 2438 return cx.getElements((Scriptable) arg1); 2439 } else { 2440 throw ScriptRuntime.typeError0("msg.arg.isnt.array"); 2441 } 2442 } 2443 getCallable(Scriptable thisObj)2444 static Callable getCallable(Scriptable thisObj) 2445 { 2446 Callable function; 2447 if (thisObj instanceof Callable) { 2448 function = (Callable)thisObj; 2449 } else { 2450 Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass); 2451 if (!(value instanceof Callable)) { 2452 throw ScriptRuntime.notFunctionError(value, thisObj); 2453 } 2454 function = (Callable)value; 2455 } 2456 return function; 2457 } 2458 2459 /** 2460 * The eval function property of the global object. 2461 * 2462 * See ECMA 15.1.2.1 2463 */ evalSpecial(Context cx, Scriptable scope, Object thisArg, Object[] args, String filename, int lineNumber)2464 public static Object evalSpecial(Context cx, Scriptable scope, 2465 Object thisArg, Object[] args, 2466 String filename, int lineNumber) 2467 { 2468 if (args.length < 1) 2469 return Undefined.instance; 2470 Object x = args[0]; 2471 if (!(x instanceof CharSequence)) { 2472 if (cx.hasFeature(Context.FEATURE_STRICT_MODE) || 2473 cx.hasFeature(Context.FEATURE_STRICT_EVAL)) 2474 { 2475 throw Context.reportRuntimeError0("msg.eval.nonstring.strict"); 2476 } 2477 String message = ScriptRuntime.getMessage0("msg.eval.nonstring"); 2478 Context.reportWarning(message); 2479 return x; 2480 } 2481 if (filename == null) { 2482 int[] linep = new int[1]; 2483 filename = Context.getSourcePositionFromStack(linep); 2484 if (filename != null) { 2485 lineNumber = linep[0]; 2486 } else { 2487 filename = ""; 2488 } 2489 } 2490 String sourceName = ScriptRuntime. 2491 makeUrlForGeneratedScript(true, filename, lineNumber); 2492 2493 ErrorReporter reporter; 2494 reporter = DefaultErrorReporter.forEval(cx.getErrorReporter()); 2495 2496 Evaluator evaluator = Context.createInterpreter(); 2497 if (evaluator == null) { 2498 throw new JavaScriptException("Interpreter not present", 2499 filename, lineNumber); 2500 } 2501 2502 // Compile with explicit interpreter instance to force interpreter 2503 // mode. 2504 Script script = cx.compileString(x.toString(), evaluator, 2505 reporter, sourceName, 1, null); 2506 evaluator.setEvalScriptFlag(script); 2507 Callable c = (Callable)script; 2508 return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs); 2509 } 2510 2511 /** 2512 * The typeof operator 2513 */ typeof(Object value)2514 public static String typeof(Object value) 2515 { 2516 if (value == null) 2517 return "object"; 2518 if (value == Undefined.instance) 2519 return "undefined"; 2520 if (value instanceof ScriptableObject) 2521 return ((ScriptableObject) value).getTypeOf(); 2522 if (value instanceof Scriptable) 2523 return (value instanceof Callable) ? "function" : "object"; 2524 if (value instanceof CharSequence) 2525 return "string"; 2526 if (value instanceof Number) 2527 return "number"; 2528 if (value instanceof Boolean) 2529 return "boolean"; 2530 throw errorWithClassName("msg.invalid.type", value); 2531 } 2532 2533 /** 2534 * The typeof operator that correctly handles the undefined case 2535 */ typeofName(Scriptable scope, String id)2536 public static String typeofName(Scriptable scope, String id) 2537 { 2538 Context cx = Context.getContext(); 2539 Scriptable val = bind(cx, scope, id); 2540 if (val == null) 2541 return "undefined"; 2542 return typeof(getObjectProp(val, id, cx)); 2543 } 2544 2545 // neg: 2546 // implement the '-' operator inline in the caller 2547 // as "-toNumber(val)" 2548 2549 // not: 2550 // implement the '!' operator inline in the caller 2551 // as "!toBoolean(val)" 2552 2553 // bitnot: 2554 // implement the '~' operator inline in the caller 2555 // as "~toInt32(val)" 2556 add(Object val1, Object val2, Context cx)2557 public static Object add(Object val1, Object val2, Context cx) 2558 { 2559 if(val1 instanceof Number && val2 instanceof Number) { 2560 return wrapNumber(((Number)val1).doubleValue() + 2561 ((Number)val2).doubleValue()); 2562 } 2563 if (val1 instanceof XMLObject) { 2564 Object test = ((XMLObject)val1).addValues(cx, true, val2); 2565 if (test != Scriptable.NOT_FOUND) { 2566 return test; 2567 } 2568 } 2569 if (val2 instanceof XMLObject) { 2570 Object test = ((XMLObject)val2).addValues(cx, false, val1); 2571 if (test != Scriptable.NOT_FOUND) { 2572 return test; 2573 } 2574 } 2575 if (val1 instanceof Scriptable) 2576 val1 = ((Scriptable) val1).getDefaultValue(null); 2577 if (val2 instanceof Scriptable) 2578 val2 = ((Scriptable) val2).getDefaultValue(null); 2579 if (!(val1 instanceof CharSequence) && !(val2 instanceof CharSequence)) 2580 if ((val1 instanceof Number) && (val2 instanceof Number)) 2581 return wrapNumber(((Number)val1).doubleValue() + 2582 ((Number)val2).doubleValue()); 2583 else 2584 return wrapNumber(toNumber(val1) + toNumber(val2)); 2585 return new ConsString(toCharSequence(val1), toCharSequence(val2)); 2586 } 2587 add(CharSequence val1, Object val2)2588 public static CharSequence add(CharSequence val1, Object val2) { 2589 return new ConsString(val1, toCharSequence(val2)); 2590 } 2591 add(Object val1, CharSequence val2)2592 public static CharSequence add(Object val1, CharSequence val2) { 2593 return new ConsString(toCharSequence(val1), val2); 2594 } 2595 2596 /** 2597 * @deprecated The method is only present for compatibility. 2598 */ nameIncrDecr(Scriptable scopeChain, String id, int incrDecrMask)2599 public static Object nameIncrDecr(Scriptable scopeChain, String id, 2600 int incrDecrMask) 2601 { 2602 return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask); 2603 } 2604 nameIncrDecr(Scriptable scopeChain, String id, Context cx, int incrDecrMask)2605 public static Object nameIncrDecr(Scriptable scopeChain, String id, 2606 Context cx, int incrDecrMask) 2607 { 2608 Scriptable target; 2609 Object value; 2610 search: { 2611 do { 2612 if (cx.useDynamicScope && scopeChain.getParentScope() == null) { 2613 scopeChain = checkDynamicScope(cx.topCallScope, scopeChain); 2614 } 2615 target = scopeChain; 2616 do { 2617 if (target instanceof NativeWith && 2618 target.getPrototype() instanceof XMLObject) { 2619 break; 2620 } 2621 value = target.get(id, scopeChain); 2622 if (value != Scriptable.NOT_FOUND) { 2623 break search; 2624 } 2625 target = target.getPrototype(); 2626 } while (target != null); 2627 scopeChain = scopeChain.getParentScope(); 2628 } while (scopeChain != null); 2629 throw notFoundError(scopeChain, id); 2630 } 2631 return doScriptableIncrDecr(target, id, scopeChain, value, 2632 incrDecrMask); 2633 } 2634 propIncrDecr(Object obj, String id, Context cx, int incrDecrMask)2635 public static Object propIncrDecr(Object obj, String id, 2636 Context cx, int incrDecrMask) 2637 { 2638 Scriptable start = toObjectOrNull(cx, obj); 2639 if (start == null) { 2640 throw undefReadError(obj, id); 2641 } 2642 2643 Scriptable target = start; 2644 Object value; 2645 search: { 2646 do { 2647 value = target.get(id, start); 2648 if (value != Scriptable.NOT_FOUND) { 2649 break search; 2650 } 2651 target = target.getPrototype(); 2652 } while (target != null); 2653 start.put(id, start, NaNobj); 2654 return NaNobj; 2655 } 2656 return doScriptableIncrDecr(target, id, start, value, 2657 incrDecrMask); 2658 } 2659 doScriptableIncrDecr(Scriptable target, String id, Scriptable protoChainStart, Object value, int incrDecrMask)2660 private static Object doScriptableIncrDecr(Scriptable target, 2661 String id, 2662 Scriptable protoChainStart, 2663 Object value, 2664 int incrDecrMask) 2665 { 2666 boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); 2667 double number; 2668 if (value instanceof Number) { 2669 number = ((Number)value).doubleValue(); 2670 } else { 2671 number = toNumber(value); 2672 if (post) { 2673 // convert result to number 2674 value = wrapNumber(number); 2675 } 2676 } 2677 if ((incrDecrMask & Node.DECR_FLAG) == 0) { 2678 ++number; 2679 } else { 2680 --number; 2681 } 2682 Number result = wrapNumber(number); 2683 target.put(id, protoChainStart, result); 2684 if (post) { 2685 return value; 2686 } else { 2687 return result; 2688 } 2689 } 2690 elemIncrDecr(Object obj, Object index, Context cx, int incrDecrMask)2691 public static Object elemIncrDecr(Object obj, Object index, 2692 Context cx, int incrDecrMask) 2693 { 2694 Object value = getObjectElem(obj, index, cx); 2695 boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); 2696 double number; 2697 if (value instanceof Number) { 2698 number = ((Number)value).doubleValue(); 2699 } else { 2700 number = toNumber(value); 2701 if (post) { 2702 // convert result to number 2703 value = wrapNumber(number); 2704 } 2705 } 2706 if ((incrDecrMask & Node.DECR_FLAG) == 0) { 2707 ++number; 2708 } else { 2709 --number; 2710 } 2711 Number result = wrapNumber(number); 2712 setObjectElem(obj, index, result, cx); 2713 if (post) { 2714 return value; 2715 } else { 2716 return result; 2717 } 2718 } 2719 refIncrDecr(Ref ref, Context cx, int incrDecrMask)2720 public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask) 2721 { 2722 Object value = ref.get(cx); 2723 boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); 2724 double number; 2725 if (value instanceof Number) { 2726 number = ((Number)value).doubleValue(); 2727 } else { 2728 number = toNumber(value); 2729 if (post) { 2730 // convert result to number 2731 value = wrapNumber(number); 2732 } 2733 } 2734 if ((incrDecrMask & Node.DECR_FLAG) == 0) { 2735 ++number; 2736 } else { 2737 --number; 2738 } 2739 Number result = wrapNumber(number); 2740 ref.set(cx, result); 2741 if (post) { 2742 return value; 2743 } else { 2744 return result; 2745 } 2746 } 2747 toPrimitive(Object val)2748 public static Object toPrimitive(Object val) { 2749 return toPrimitive(val, null); 2750 } 2751 toPrimitive(Object val, Class<?> typeHint)2752 public static Object toPrimitive(Object val, Class<?> typeHint) 2753 { 2754 if (!(val instanceof Scriptable)) { 2755 return val; 2756 } 2757 Scriptable s = (Scriptable)val; 2758 Object result = s.getDefaultValue(typeHint); 2759 if (result instanceof Scriptable) 2760 throw typeError0("msg.bad.default.value"); 2761 return result; 2762 } 2763 2764 /** 2765 * Equality 2766 * 2767 * See ECMA 11.9 2768 */ eq(Object x, Object y)2769 public static boolean eq(Object x, Object y) 2770 { 2771 if (x == null || x == Undefined.instance) { 2772 if (y == null || y == Undefined.instance) { 2773 return true; 2774 } 2775 if (y instanceof ScriptableObject) { 2776 Object test = ((ScriptableObject)y).equivalentValues(x); 2777 if (test != Scriptable.NOT_FOUND) { 2778 return ((Boolean)test).booleanValue(); 2779 } 2780 } 2781 return false; 2782 } else if (x instanceof Number) { 2783 return eqNumber(((Number)x).doubleValue(), y); 2784 } else if (x == y) { 2785 return true; 2786 } else if (x instanceof CharSequence) { 2787 return eqString((CharSequence)x, y); 2788 } else if (x instanceof Boolean) { 2789 boolean b = ((Boolean)x).booleanValue(); 2790 if (y instanceof Boolean) { 2791 return b == ((Boolean)y).booleanValue(); 2792 } 2793 if (y instanceof ScriptableObject) { 2794 Object test = ((ScriptableObject)y).equivalentValues(x); 2795 if (test != Scriptable.NOT_FOUND) { 2796 return ((Boolean)test).booleanValue(); 2797 } 2798 } 2799 return eqNumber(b ? 1.0 : 0.0, y); 2800 } else if (x instanceof Scriptable) { 2801 if (y instanceof Scriptable) { 2802 if (x instanceof ScriptableObject) { 2803 Object test = ((ScriptableObject)x).equivalentValues(y); 2804 if (test != Scriptable.NOT_FOUND) { 2805 return ((Boolean)test).booleanValue(); 2806 } 2807 } 2808 if (y instanceof ScriptableObject) { 2809 Object test = ((ScriptableObject)y).equivalentValues(x); 2810 if (test != Scriptable.NOT_FOUND) { 2811 return ((Boolean)test).booleanValue(); 2812 } 2813 } 2814 if (x instanceof Wrapper && y instanceof Wrapper) { 2815 // See bug 413838. Effectively an extension to ECMA for 2816 // the LiveConnect case. 2817 Object unwrappedX = ((Wrapper)x).unwrap(); 2818 Object unwrappedY = ((Wrapper)y).unwrap(); 2819 return unwrappedX == unwrappedY || 2820 (isPrimitive(unwrappedX) && 2821 isPrimitive(unwrappedY) && 2822 eq(unwrappedX, unwrappedY)); 2823 } 2824 return false; 2825 } else if (y instanceof Boolean) { 2826 if (x instanceof ScriptableObject) { 2827 Object test = ((ScriptableObject)x).equivalentValues(y); 2828 if (test != Scriptable.NOT_FOUND) { 2829 return ((Boolean)test).booleanValue(); 2830 } 2831 } 2832 double d = ((Boolean)y).booleanValue() ? 1.0 : 0.0; 2833 return eqNumber(d, x); 2834 } else if (y instanceof Number) { 2835 return eqNumber(((Number)y).doubleValue(), x); 2836 } else if (y instanceof CharSequence) { 2837 return eqString((CharSequence)y, x); 2838 } 2839 // covers the case when y == Undefined.instance as well 2840 return false; 2841 } else { 2842 warnAboutNonJSObject(x); 2843 return x == y; 2844 } 2845 } 2846 isPrimitive(Object obj)2847 public static boolean isPrimitive(Object obj) { 2848 return obj == null || obj == Undefined.instance || 2849 (obj instanceof Number) || (obj instanceof String) || 2850 (obj instanceof Boolean); 2851 } 2852 eqNumber(double x, Object y)2853 static boolean eqNumber(double x, Object y) 2854 { 2855 for (;;) { 2856 if (y == null || y == Undefined.instance) { 2857 return false; 2858 } else if (y instanceof Number) { 2859 return x == ((Number)y).doubleValue(); 2860 } else if (y instanceof CharSequence) { 2861 return x == toNumber(y); 2862 } else if (y instanceof Boolean) { 2863 return x == (((Boolean)y).booleanValue() ? 1.0 : +0.0); 2864 } else if (y instanceof Scriptable) { 2865 if (y instanceof ScriptableObject) { 2866 Object xval = wrapNumber(x); 2867 Object test = ((ScriptableObject)y).equivalentValues(xval); 2868 if (test != Scriptable.NOT_FOUND) { 2869 return ((Boolean)test).booleanValue(); 2870 } 2871 } 2872 y = toPrimitive(y); 2873 } else { 2874 warnAboutNonJSObject(y); 2875 return false; 2876 } 2877 } 2878 } 2879 eqString(CharSequence x, Object y)2880 private static boolean eqString(CharSequence x, Object y) 2881 { 2882 for (;;) { 2883 if (y == null || y == Undefined.instance) { 2884 return false; 2885 } else if (y instanceof CharSequence) { 2886 CharSequence c = (CharSequence)y; 2887 return x.length() == c.length() && x.toString().equals(c.toString()); 2888 } else if (y instanceof Number) { 2889 return toNumber(x.toString()) == ((Number)y).doubleValue(); 2890 } else if (y instanceof Boolean) { 2891 return toNumber(x.toString()) == (((Boolean)y).booleanValue() ? 1.0 : 0.0); 2892 } else if (y instanceof Scriptable) { 2893 if (y instanceof ScriptableObject) { 2894 Object test = ((ScriptableObject)y).equivalentValues(x.toString()); 2895 if (test != Scriptable.NOT_FOUND) { 2896 return ((Boolean)test).booleanValue(); 2897 } 2898 } 2899 y = toPrimitive(y); 2900 continue; 2901 } else { 2902 warnAboutNonJSObject(y); 2903 return false; 2904 } 2905 } 2906 } shallowEq(Object x, Object y)2907 public static boolean shallowEq(Object x, Object y) 2908 { 2909 if (x == y) { 2910 if (!(x instanceof Number)) { 2911 return true; 2912 } 2913 // NaN check 2914 double d = ((Number)x).doubleValue(); 2915 return d == d; 2916 } 2917 if (x == null || x == Undefined.instance) { 2918 return false; 2919 } else if (x instanceof Number) { 2920 if (y instanceof Number) { 2921 return ((Number)x).doubleValue() == ((Number)y).doubleValue(); 2922 } 2923 } else if (x instanceof CharSequence) { 2924 if (y instanceof CharSequence) { 2925 return x.toString().equals(y.toString()); 2926 } 2927 } else if (x instanceof Boolean) { 2928 if (y instanceof Boolean) { 2929 return x.equals(y); 2930 } 2931 } else if (x instanceof Scriptable) { 2932 if (x instanceof Wrapper && y instanceof Wrapper) { 2933 return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap(); 2934 } 2935 } else { 2936 warnAboutNonJSObject(x); 2937 return x == y; 2938 } 2939 return false; 2940 } 2941 2942 /** 2943 * The instanceof operator. 2944 * 2945 * @return a instanceof b 2946 */ instanceOf(Object a, Object b, Context cx)2947 public static boolean instanceOf(Object a, Object b, Context cx) 2948 { 2949 // Check RHS is an object 2950 if (! (b instanceof Scriptable)) { 2951 throw typeError0("msg.instanceof.not.object"); 2952 } 2953 2954 // for primitive values on LHS, return false 2955 if (! (a instanceof Scriptable)) 2956 return false; 2957 2958 return ((Scriptable)b).hasInstance((Scriptable)a); 2959 } 2960 2961 /** 2962 * Delegates to 2963 * 2964 * @return true iff rhs appears in lhs' proto chain 2965 */ jsDelegatesTo(Scriptable lhs, Scriptable rhs)2966 public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) { 2967 Scriptable proto = lhs.getPrototype(); 2968 2969 while (proto != null) { 2970 if (proto.equals(rhs)) return true; 2971 proto = proto.getPrototype(); 2972 } 2973 2974 return false; 2975 } 2976 2977 /** 2978 * The in operator. 2979 * 2980 * This is a new JS 1.3 language feature. The in operator mirrors 2981 * the operation of the for .. in construct, and tests whether the 2982 * rhs has the property given by the lhs. It is different from the 2983 * for .. in construct in that: 2984 * <BR> - it doesn't perform ToObject on the right hand side 2985 * <BR> - it returns true for DontEnum properties. 2986 * @param a the left hand operand 2987 * @param b the right hand operand 2988 * 2989 * @return true if property name or element number a is a property of b 2990 */ in(Object a, Object b, Context cx)2991 public static boolean in(Object a, Object b, Context cx) 2992 { 2993 if (!(b instanceof Scriptable)) { 2994 throw typeError0("msg.in.not.object"); 2995 } 2996 2997 return hasObjectElem((Scriptable)b, a, cx); 2998 } 2999 cmp_LT(Object val1, Object val2)3000 public static boolean cmp_LT(Object val1, Object val2) 3001 { 3002 double d1, d2; 3003 if (val1 instanceof Number && val2 instanceof Number) { 3004 d1 = ((Number)val1).doubleValue(); 3005 d2 = ((Number)val2).doubleValue(); 3006 } else { 3007 if (val1 instanceof Scriptable) 3008 val1 = ((Scriptable) val1).getDefaultValue(NumberClass); 3009 if (val2 instanceof Scriptable) 3010 val2 = ((Scriptable) val2).getDefaultValue(NumberClass); 3011 if (val1 instanceof CharSequence && val2 instanceof CharSequence) { 3012 return val1.toString().compareTo(val2.toString()) < 0; 3013 } 3014 d1 = toNumber(val1); 3015 d2 = toNumber(val2); 3016 } 3017 return d1 < d2; 3018 } 3019 cmp_LE(Object val1, Object val2)3020 public static boolean cmp_LE(Object val1, Object val2) 3021 { 3022 double d1, d2; 3023 if (val1 instanceof Number && val2 instanceof Number) { 3024 d1 = ((Number)val1).doubleValue(); 3025 d2 = ((Number)val2).doubleValue(); 3026 } else { 3027 if (val1 instanceof Scriptable) 3028 val1 = ((Scriptable) val1).getDefaultValue(NumberClass); 3029 if (val2 instanceof Scriptable) 3030 val2 = ((Scriptable) val2).getDefaultValue(NumberClass); 3031 if (val1 instanceof CharSequence && val2 instanceof CharSequence) { 3032 return val1.toString().compareTo(val2.toString()) <= 0; 3033 } 3034 d1 = toNumber(val1); 3035 d2 = toNumber(val2); 3036 } 3037 return d1 <= d2; 3038 } 3039 3040 // ------------------ 3041 // Statements 3042 // ------------------ 3043 getGlobal(Context cx)3044 public static ScriptableObject getGlobal(Context cx) { 3045 final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global"; 3046 Class<?> globalClass = Kit.classOrNull(GLOBAL_CLASS); 3047 if (globalClass != null) { 3048 try { 3049 Class<?>[] parm = { ScriptRuntime.ContextClass }; 3050 Constructor<?> globalClassCtor = globalClass.getConstructor(parm); 3051 Object[] arg = { cx }; 3052 return (ScriptableObject) globalClassCtor.newInstance(arg); 3053 } 3054 catch (RuntimeException e) { 3055 throw e; 3056 } 3057 catch (Exception e) { 3058 // fall through... 3059 } 3060 } 3061 return new ImporterTopLevel(cx); 3062 } 3063 hasTopCall(Context cx)3064 public static boolean hasTopCall(Context cx) 3065 { 3066 return (cx.topCallScope != null); 3067 } 3068 getTopCallScope(Context cx)3069 public static Scriptable getTopCallScope(Context cx) 3070 { 3071 Scriptable scope = cx.topCallScope; 3072 if (scope == null) { 3073 throw new IllegalStateException(); 3074 } 3075 return scope; 3076 } 3077 doTopCall(Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args)3078 public static Object doTopCall(Callable callable, 3079 Context cx, Scriptable scope, 3080 Scriptable thisObj, Object[] args) 3081 { 3082 if (scope == null) 3083 throw new IllegalArgumentException(); 3084 if (cx.topCallScope != null) throw new IllegalStateException(); 3085 3086 Object result; 3087 cx.topCallScope = ScriptableObject.getTopLevelScope(scope); 3088 cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE); 3089 ContextFactory f = cx.getFactory(); 3090 try { 3091 result = f.doTopCall(callable, cx, scope, thisObj, args); 3092 } finally { 3093 cx.topCallScope = null; 3094 // Cleanup cached references 3095 cx.cachedXMLLib = null; 3096 3097 if (cx.currentActivationCall != null) { 3098 // Function should always call exitActivationFunction 3099 // if it creates activation record 3100 throw new IllegalStateException(); 3101 } 3102 } 3103 return result; 3104 } 3105 3106 /** 3107 * Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt> 3108 * is present on its prototype chain and return <tt>staticTopScope</tt> 3109 * otherwise. 3110 * Should only be called when <tt>staticTopScope</tt> is top scope. 3111 */ checkDynamicScope(Scriptable possibleDynamicScope, Scriptable staticTopScope)3112 static Scriptable checkDynamicScope(Scriptable possibleDynamicScope, 3113 Scriptable staticTopScope) 3114 { 3115 // Return cx.topCallScope if scope 3116 if (possibleDynamicScope == staticTopScope) { 3117 return possibleDynamicScope; 3118 } 3119 Scriptable proto = possibleDynamicScope; 3120 for (;;) { 3121 proto = proto.getPrototype(); 3122 if (proto == staticTopScope) { 3123 return possibleDynamicScope; 3124 } 3125 if (proto == null) { 3126 return staticTopScope; 3127 } 3128 } 3129 } 3130 addInstructionCount(Context cx, int instructionsToAdd)3131 public static void addInstructionCount(Context cx, int instructionsToAdd) 3132 { 3133 cx.instructionCount += instructionsToAdd; 3134 if (cx.instructionCount > cx.instructionThreshold) 3135 { 3136 cx.observeInstructionCount(cx.instructionCount); 3137 cx.instructionCount = 0; 3138 } 3139 } 3140 initScript(NativeFunction funObj, Scriptable thisObj, Context cx, Scriptable scope, boolean evalScript)3141 public static void initScript(NativeFunction funObj, Scriptable thisObj, 3142 Context cx, Scriptable scope, 3143 boolean evalScript) 3144 { 3145 if (cx.topCallScope == null) 3146 throw new IllegalStateException(); 3147 3148 int varCount = funObj.getParamAndVarCount(); 3149 if (varCount != 0) { 3150 3151 Scriptable varScope = scope; 3152 // Never define any variables from var statements inside with 3153 // object. See bug 38590. 3154 while (varScope instanceof NativeWith) { 3155 varScope = varScope.getParentScope(); 3156 } 3157 3158 for (int i = varCount; i-- != 0;) { 3159 String name = funObj.getParamOrVarName(i); 3160 boolean isConst = funObj.getParamOrVarConst(i); 3161 // Don't overwrite existing def if already defined in object 3162 // or prototypes of object. 3163 if (!ScriptableObject.hasProperty(scope, name)) { 3164 if (!evalScript) { 3165 // Global var definitions are supposed to be DONTDELETE 3166 if (isConst) 3167 ScriptableObject.defineConstProperty(varScope, name); 3168 else 3169 ScriptableObject.defineProperty( 3170 varScope, name, Undefined.instance, 3171 ScriptableObject.PERMANENT); 3172 } else { 3173 varScope.put(name, varScope, Undefined.instance); 3174 } 3175 } else { 3176 ScriptableObject.redefineProperty(scope, name, isConst); 3177 } 3178 } 3179 } 3180 } 3181 createFunctionActivation(NativeFunction funObj, Scriptable scope, Object[] args)3182 public static Scriptable createFunctionActivation(NativeFunction funObj, 3183 Scriptable scope, 3184 Object[] args) 3185 { 3186 return new NativeCall(funObj, scope, args); 3187 } 3188 3189 enterActivationFunction(Context cx, Scriptable scope)3190 public static void enterActivationFunction(Context cx, 3191 Scriptable scope) 3192 { 3193 if (cx.topCallScope == null) 3194 throw new IllegalStateException(); 3195 NativeCall call = (NativeCall)scope; 3196 call.parentActivationCall = cx.currentActivationCall; 3197 cx.currentActivationCall = call; 3198 } 3199 exitActivationFunction(Context cx)3200 public static void exitActivationFunction(Context cx) 3201 { 3202 NativeCall call = cx.currentActivationCall; 3203 cx.currentActivationCall = call.parentActivationCall; 3204 call.parentActivationCall = null; 3205 } 3206 findFunctionActivation(Context cx, Function f)3207 static NativeCall findFunctionActivation(Context cx, Function f) 3208 { 3209 NativeCall call = cx.currentActivationCall; 3210 while (call != null) { 3211 if (call.function == f) 3212 return call; 3213 call = call.parentActivationCall; 3214 } 3215 return null; 3216 } 3217 newCatchScope(Throwable t, Scriptable lastCatchScope, String exceptionName, Context cx, Scriptable scope)3218 public static Scriptable newCatchScope(Throwable t, 3219 Scriptable lastCatchScope, 3220 String exceptionName, 3221 Context cx, Scriptable scope) 3222 { 3223 Object obj; 3224 boolean cacheObj; 3225 3226 getObj: 3227 if (t instanceof JavaScriptException) { 3228 cacheObj = false; 3229 obj = ((JavaScriptException)t).getValue(); 3230 } else { 3231 cacheObj = true; 3232 3233 // Create wrapper object unless it was associated with 3234 // the previous scope object 3235 3236 if (lastCatchScope != null) { 3237 NativeObject last = (NativeObject)lastCatchScope; 3238 obj = last.getAssociatedValue(t); 3239 if (obj == null) Kit.codeBug(); 3240 break getObj; 3241 } 3242 3243 RhinoException re; 3244 String errorName; 3245 String errorMsg; 3246 Throwable javaException = null; 3247 3248 if (t instanceof EcmaError) { 3249 EcmaError ee = (EcmaError)t; 3250 re = ee; 3251 errorName = ee.getName(); 3252 errorMsg = ee.getErrorMessage(); 3253 } else if (t instanceof WrappedException) { 3254 WrappedException we = (WrappedException)t; 3255 re = we; 3256 javaException = we.getWrappedException(); 3257 errorName = "JavaException"; 3258 errorMsg = javaException.getClass().getName() 3259 +": "+javaException.getMessage(); 3260 } else if (t instanceof EvaluatorException) { 3261 // Pure evaluator exception, nor WrappedException instance 3262 EvaluatorException ee = (EvaluatorException)t; 3263 re = ee; 3264 errorName = "InternalError"; 3265 errorMsg = ee.getMessage(); 3266 } else if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) { 3267 // With FEATURE_ENHANCED_JAVA_ACCESS, scripts can catch 3268 // all exception types 3269 re = new WrappedException(t); 3270 errorName = "JavaException"; 3271 errorMsg = t.toString(); 3272 } else { 3273 // Script can catch only instances of JavaScriptException, 3274 // EcmaError and EvaluatorException 3275 throw Kit.codeBug(); 3276 } 3277 3278 String sourceUri = re.sourceName(); 3279 if (sourceUri == null) { 3280 sourceUri = ""; 3281 } 3282 int line = re.lineNumber(); 3283 Object args[]; 3284 if (line > 0) { 3285 args = new Object[] { errorMsg, sourceUri, Integer.valueOf(line) }; 3286 } else { 3287 args = new Object[] { errorMsg, sourceUri }; 3288 } 3289 3290 Scriptable errorObject = cx.newObject(scope, errorName, args); 3291 ScriptableObject.putProperty(errorObject, "name", errorName); 3292 // set exception in Error objects to enable non-ECMA "stack" property 3293 if (errorObject instanceof NativeError) { 3294 ((NativeError) errorObject).setStackProvider(re); 3295 } 3296 3297 if (javaException != null && isVisible(cx, javaException)) { 3298 Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, 3299 null); 3300 ScriptableObject.defineProperty( 3301 errorObject, "javaException", wrap, 3302 ScriptableObject.PERMANENT | ScriptableObject.READONLY); 3303 } 3304 if (isVisible(cx, re)) { 3305 Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null); 3306 ScriptableObject.defineProperty( 3307 errorObject, "rhinoException", wrap, 3308 ScriptableObject.PERMANENT | ScriptableObject.READONLY); 3309 } 3310 obj = errorObject; 3311 } 3312 3313 NativeObject catchScopeObject = new NativeObject(); 3314 // See ECMA 12.4 3315 catchScopeObject.defineProperty( 3316 exceptionName, obj, ScriptableObject.PERMANENT); 3317 3318 if (isVisible(cx, t)) { 3319 // Add special Rhino object __exception__ defined in the catch 3320 // scope that can be used to retrieve the Java exception associated 3321 // with the JavaScript exception (to get stack trace info, etc.) 3322 catchScopeObject.defineProperty( 3323 "__exception__", Context.javaToJS(t, scope), 3324 ScriptableObject.PERMANENT|ScriptableObject.DONTENUM); 3325 } 3326 3327 if (cacheObj) { 3328 catchScopeObject.associateValue(t, obj); 3329 } 3330 return catchScopeObject; 3331 } 3332 isVisible(Context cx, Object obj)3333 private static boolean isVisible(Context cx, Object obj) { 3334 ClassShutter shutter = cx.getClassShutter(); 3335 return shutter == null || 3336 shutter.visibleToScripts(obj.getClass().getName()); 3337 } 3338 enterWith(Object obj, Context cx, Scriptable scope)3339 public static Scriptable enterWith(Object obj, Context cx, 3340 Scriptable scope) 3341 { 3342 Scriptable sobj = toObjectOrNull(cx, obj); 3343 if (sobj == null) { 3344 throw typeError1("msg.undef.with", toString(obj)); 3345 } 3346 if (sobj instanceof XMLObject) { 3347 XMLObject xmlObject = (XMLObject)sobj; 3348 return xmlObject.enterWith(scope); 3349 } 3350 return new NativeWith(scope, sobj); 3351 } 3352 leaveWith(Scriptable scope)3353 public static Scriptable leaveWith(Scriptable scope) 3354 { 3355 NativeWith nw = (NativeWith)scope; 3356 return nw.getParentScope(); 3357 } 3358 enterDotQuery(Object value, Scriptable scope)3359 public static Scriptable enterDotQuery(Object value, Scriptable scope) 3360 { 3361 if (!(value instanceof XMLObject)) { 3362 throw notXmlError(value); 3363 } 3364 XMLObject object = (XMLObject)value; 3365 return object.enterDotQuery(scope); 3366 } 3367 updateDotQuery(boolean value, Scriptable scope)3368 public static Object updateDotQuery(boolean value, Scriptable scope) 3369 { 3370 // Return null to continue looping 3371 NativeWith nw = (NativeWith)scope; 3372 return nw.updateDotQuery(value); 3373 } 3374 leaveDotQuery(Scriptable scope)3375 public static Scriptable leaveDotQuery(Scriptable scope) 3376 { 3377 NativeWith nw = (NativeWith)scope; 3378 return nw.getParentScope(); 3379 } 3380 setFunctionProtoAndParent(BaseFunction fn, Scriptable scope)3381 public static void setFunctionProtoAndParent(BaseFunction fn, 3382 Scriptable scope) 3383 { 3384 fn.setParentScope(scope); 3385 fn.setPrototype(ScriptableObject.getFunctionPrototype(scope)); 3386 } 3387 setObjectProtoAndParent(ScriptableObject object, Scriptable scope)3388 public static void setObjectProtoAndParent(ScriptableObject object, 3389 Scriptable scope) 3390 { 3391 // Compared with function it always sets the scope to top scope 3392 scope = ScriptableObject.getTopLevelScope(scope); 3393 object.setParentScope(scope); 3394 Scriptable proto 3395 = ScriptableObject.getClassPrototype(scope, object.getClassName()); 3396 object.setPrototype(proto); 3397 } 3398 setBuiltinProtoAndParent(ScriptableObject object, Scriptable scope, TopLevel.Builtins type)3399 public static void setBuiltinProtoAndParent(ScriptableObject object, 3400 Scriptable scope, 3401 TopLevel.Builtins type) 3402 { 3403 scope = ScriptableObject.getTopLevelScope(scope); 3404 object.setParentScope(scope); 3405 object.setPrototype(TopLevel.getBuiltinPrototype(scope, type)); 3406 } 3407 3408 initFunction(Context cx, Scriptable scope, NativeFunction function, int type, boolean fromEvalCode)3409 public static void initFunction(Context cx, Scriptable scope, 3410 NativeFunction function, int type, 3411 boolean fromEvalCode) 3412 { 3413 if (type == FunctionNode.FUNCTION_STATEMENT) { 3414 String name = function.getFunctionName(); 3415 if (name != null && name.length() != 0) { 3416 if (!fromEvalCode) { 3417 // ECMA specifies that functions defined in global and 3418 // function scope outside eval should have DONTDELETE set. 3419 ScriptableObject.defineProperty 3420 (scope, name, function, ScriptableObject.PERMANENT); 3421 } else { 3422 scope.put(name, scope, function); 3423 } 3424 } 3425 } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { 3426 String name = function.getFunctionName(); 3427 if (name != null && name.length() != 0) { 3428 // Always put function expression statements into initial 3429 // activation object ignoring the with statement to follow 3430 // SpiderMonkey 3431 while (scope instanceof NativeWith) { 3432 scope = scope.getParentScope(); 3433 } 3434 scope.put(name, scope, function); 3435 } 3436 } else { 3437 throw Kit.codeBug(); 3438 } 3439 } 3440 newArrayLiteral(Object[] objects, int[] skipIndices, Context cx, Scriptable scope)3441 public static Scriptable newArrayLiteral(Object[] objects, 3442 int[] skipIndices, 3443 Context cx, Scriptable scope) 3444 { 3445 final int SKIP_DENSITY = 2; 3446 int count = objects.length; 3447 int skipCount = 0; 3448 if (skipIndices != null) { 3449 skipCount = skipIndices.length; 3450 } 3451 int length = count + skipCount; 3452 if (length > 1 && skipCount * SKIP_DENSITY < length) { 3453 // If not too sparse, create whole array for constructor 3454 Object[] sparse; 3455 if (skipCount == 0) { 3456 sparse = objects; 3457 } else { 3458 sparse = new Object[length]; 3459 int skip = 0; 3460 for (int i = 0, j = 0; i != length; ++i) { 3461 if (skip != skipCount && skipIndices[skip] == i) { 3462 sparse[i] = Scriptable.NOT_FOUND; 3463 ++skip; 3464 continue; 3465 } 3466 sparse[i] = objects[j]; 3467 ++j; 3468 } 3469 } 3470 return cx.newArray(scope, sparse); 3471 } 3472 3473 Scriptable array = cx.newArray(scope, length); 3474 3475 int skip = 0; 3476 for (int i = 0, j = 0; i != length; ++i) { 3477 if (skip != skipCount && skipIndices[skip] == i) { 3478 ++skip; 3479 continue; 3480 } 3481 ScriptableObject.putProperty(array, i, objects[j]); 3482 ++j; 3483 } 3484 return array; 3485 } 3486 3487 /** 3488 * This method is here for backward compat with existing compiled code. It 3489 * is called when an object literal is compiled. The next instance will be 3490 * the version called from new code. 3491 * @deprecated This method only present for compatibility. 3492 */ newObjectLiteral(Object[] propertyIds, Object[] propertyValues, Context cx, Scriptable scope)3493 public static Scriptable newObjectLiteral(Object[] propertyIds, 3494 Object[] propertyValues, 3495 Context cx, Scriptable scope) 3496 { 3497 // Passing null for getterSetters means no getters or setters 3498 return newObjectLiteral(propertyIds, propertyValues, null, cx, scope); 3499 3500 } 3501 newObjectLiteral(Object[] propertyIds, Object[] propertyValues, int [] getterSetters, Context cx, Scriptable scope)3502 public static Scriptable newObjectLiteral(Object[] propertyIds, 3503 Object[] propertyValues, 3504 int [] getterSetters, 3505 Context cx, Scriptable scope) 3506 { 3507 Scriptable object = cx.newObject(scope); 3508 for (int i = 0, end = propertyIds.length; i != end; ++i) { 3509 Object id = propertyIds[i]; 3510 int getterSetter = getterSetters == null ? 0 : getterSetters[i]; 3511 Object value = propertyValues[i]; 3512 if (id instanceof String) { 3513 if (getterSetter == 0) { 3514 if (isSpecialProperty((String)id)) { 3515 specialRef(object, (String)id, cx).set(cx, value); 3516 } else { 3517 object.put((String)id, object, value); 3518 } 3519 } else { 3520 ScriptableObject so = (ScriptableObject)object; 3521 Callable getterOrSetter = (Callable)value; 3522 boolean isSetter = getterSetter == 1; 3523 so.setGetterOrSetter((String)id, 0, getterOrSetter, isSetter); 3524 } 3525 } else { 3526 int index = ((Integer)id).intValue(); 3527 object.put(index, object, value); 3528 } 3529 } 3530 return object; 3531 } 3532 isArrayObject(Object obj)3533 public static boolean isArrayObject(Object obj) 3534 { 3535 return obj instanceof NativeArray || obj instanceof Arguments; 3536 } 3537 getArrayElements(Scriptable object)3538 public static Object[] getArrayElements(Scriptable object) 3539 { 3540 Context cx = Context.getContext(); 3541 long longLen = NativeArray.getLengthProperty(cx, object); 3542 if (longLen > Integer.MAX_VALUE) { 3543 // arrays beyond MAX_INT is not in Java in any case 3544 throw new IllegalArgumentException(); 3545 } 3546 int len = (int) longLen; 3547 if (len == 0) { 3548 return ScriptRuntime.emptyArgs; 3549 } else { 3550 Object[] result = new Object[len]; 3551 for (int i=0; i < len; i++) { 3552 Object elem = ScriptableObject.getProperty(object, i); 3553 result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance 3554 : elem; 3555 } 3556 return result; 3557 } 3558 } 3559 checkDeprecated(Context cx, String name)3560 static void checkDeprecated(Context cx, String name) { 3561 int version = cx.getLanguageVersion(); 3562 if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) { 3563 String msg = getMessage1("msg.deprec.ctor", name); 3564 if (version == Context.VERSION_DEFAULT) 3565 Context.reportWarning(msg); 3566 else 3567 throw Context.reportRuntimeError(msg); 3568 } 3569 } 3570 getMessage0(String messageId)3571 public static String getMessage0(String messageId) 3572 { 3573 return getMessage(messageId, null); 3574 } 3575 getMessage1(String messageId, Object arg1)3576 public static String getMessage1(String messageId, Object arg1) 3577 { 3578 Object[] arguments = {arg1}; 3579 return getMessage(messageId, arguments); 3580 } 3581 getMessage2( String messageId, Object arg1, Object arg2)3582 public static String getMessage2( 3583 String messageId, Object arg1, Object arg2) 3584 { 3585 Object[] arguments = {arg1, arg2}; 3586 return getMessage(messageId, arguments); 3587 } 3588 getMessage3( String messageId, Object arg1, Object arg2, Object arg3)3589 public static String getMessage3( 3590 String messageId, Object arg1, Object arg2, Object arg3) 3591 { 3592 Object[] arguments = {arg1, arg2, arg3}; 3593 return getMessage(messageId, arguments); 3594 } 3595 getMessage4( String messageId, Object arg1, Object arg2, Object arg3, Object arg4)3596 public static String getMessage4( 3597 String messageId, Object arg1, Object arg2, Object arg3, Object arg4) 3598 { 3599 Object[] arguments = {arg1, arg2, arg3, arg4}; 3600 return getMessage(messageId, arguments); 3601 } 3602 3603 /** 3604 * This is an interface defining a message provider. Create your 3605 * own implementation to override the default error message provider. 3606 * 3607 * @author Mike Harm 3608 */ 3609 public interface MessageProvider { 3610 3611 /** 3612 * Returns a textual message identified by the given messageId, 3613 * parameterized by the given arguments. 3614 * 3615 * @param messageId the identifier of the message 3616 * @param arguments the arguments to fill into the message 3617 */ getMessage(String messageId, Object[] arguments)3618 String getMessage(String messageId, Object[] arguments); 3619 } 3620 3621 public static MessageProvider messageProvider = new DefaultMessageProvider(); 3622 getMessage(String messageId, Object[] arguments)3623 public static String getMessage(String messageId, Object[] arguments) 3624 { 3625 return messageProvider.getMessage(messageId, arguments); 3626 } 3627 3628 /* OPT there's a noticable delay for the first error! Maybe it'd 3629 * make sense to use a ListResourceBundle instead of a properties 3630 * file to avoid (synchronized) text parsing. 3631 */ 3632 private static class DefaultMessageProvider implements MessageProvider { getMessage(String messageId, Object[] arguments)3633 public String getMessage(String messageId, Object[] arguments) { 3634 final String defaultResource 3635 = "org.mozilla.javascript.resources.Messages"; 3636 3637 Context cx = Context.getCurrentContext(); 3638 Locale locale = cx != null ? cx.getLocale() : Locale.getDefault(); 3639 3640 // ResourceBundle does caching. 3641 ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale); 3642 3643 String formatString; 3644 try { 3645 formatString = rb.getString(messageId); 3646 } catch (java.util.MissingResourceException mre) { 3647 throw new RuntimeException 3648 ("no message resource found for message property "+ messageId); 3649 } 3650 3651 /* 3652 * It's OK to format the string, even if 'arguments' is null; 3653 * we need to format it anyway, to make double ''s collapse to 3654 * single 's. 3655 */ 3656 MessageFormat formatter = new MessageFormat(formatString); 3657 return formatter.format(arguments); 3658 } 3659 } 3660 constructError(String error, String message)3661 public static EcmaError constructError(String error, String message) 3662 { 3663 int[] linep = new int[1]; 3664 String filename = Context.getSourcePositionFromStack(linep); 3665 return constructError(error, message, filename, linep[0], null, 0); 3666 } 3667 constructError(String error, String message, int lineNumberDelta)3668 public static EcmaError constructError(String error, 3669 String message, 3670 int lineNumberDelta) 3671 { 3672 int[] linep = new int[1]; 3673 String filename = Context.getSourcePositionFromStack(linep); 3674 if (linep[0] != 0) { 3675 linep[0] += lineNumberDelta; 3676 } 3677 return constructError(error, message, filename, linep[0], null, 0); 3678 } 3679 constructError(String error, String message, String sourceName, int lineNumber, String lineSource, int columnNumber)3680 public static EcmaError constructError(String error, 3681 String message, 3682 String sourceName, 3683 int lineNumber, 3684 String lineSource, 3685 int columnNumber) 3686 { 3687 return new EcmaError(error, message, sourceName, 3688 lineNumber, lineSource, columnNumber); 3689 } 3690 typeError(String message)3691 public static EcmaError typeError(String message) 3692 { 3693 return constructError("TypeError", message); 3694 } 3695 typeError0(String messageId)3696 public static EcmaError typeError0(String messageId) 3697 { 3698 String msg = getMessage0(messageId); 3699 return typeError(msg); 3700 } 3701 typeError1(String messageId, String arg1)3702 public static EcmaError typeError1(String messageId, String arg1) 3703 { 3704 String msg = getMessage1(messageId, arg1); 3705 return typeError(msg); 3706 } 3707 typeError2(String messageId, String arg1, String arg2)3708 public static EcmaError typeError2(String messageId, String arg1, 3709 String arg2) 3710 { 3711 String msg = getMessage2(messageId, arg1, arg2); 3712 return typeError(msg); 3713 } 3714 typeError3(String messageId, String arg1, String arg2, String arg3)3715 public static EcmaError typeError3(String messageId, String arg1, 3716 String arg2, String arg3) 3717 { 3718 String msg = getMessage3(messageId, arg1, arg2, arg3); 3719 return typeError(msg); 3720 } 3721 undefReadError(Object object, Object id)3722 public static RuntimeException undefReadError(Object object, Object id) 3723 { 3724 String idStr = (id == null) ? "null" : id.toString(); 3725 return typeError2("msg.undef.prop.read", toString(object), idStr); 3726 } 3727 undefCallError(Object object, Object id)3728 public static RuntimeException undefCallError(Object object, Object id) 3729 { 3730 String idStr = (id == null) ? "null" : id.toString(); 3731 return typeError2("msg.undef.method.call", toString(object), idStr); 3732 } 3733 undefWriteError(Object object, Object id, Object value)3734 public static RuntimeException undefWriteError(Object object, 3735 Object id, 3736 Object value) 3737 { 3738 String idStr = (id == null) ? "null" : id.toString(); 3739 String valueStr = (value instanceof Scriptable) 3740 ? value.toString() : toString(value); 3741 return typeError3("msg.undef.prop.write", toString(object), idStr, 3742 valueStr); 3743 } 3744 notFoundError(Scriptable object, String property)3745 public static RuntimeException notFoundError(Scriptable object, 3746 String property) 3747 { 3748 // XXX: use object to improve the error message 3749 String msg = getMessage1("msg.is.not.defined", property); 3750 throw constructError("ReferenceError", msg); 3751 } 3752 notFunctionError(Object value)3753 public static RuntimeException notFunctionError(Object value) 3754 { 3755 return notFunctionError(value, value); 3756 } 3757 notFunctionError(Object value, Object messageHelper)3758 public static RuntimeException notFunctionError(Object value, 3759 Object messageHelper) 3760 { 3761 // Use value for better error reporting 3762 String msg = (messageHelper == null) 3763 ? "null" : messageHelper.toString(); 3764 if (value == Scriptable.NOT_FOUND) { 3765 return typeError1("msg.function.not.found", msg); 3766 } 3767 return typeError2("msg.isnt.function", msg, typeof(value)); 3768 } 3769 notFunctionError(Object obj, Object value, String propertyName)3770 public static RuntimeException notFunctionError(Object obj, Object value, 3771 String propertyName) 3772 { 3773 // Use obj and value for better error reporting 3774 String objString = toString(obj); 3775 if (obj instanceof NativeFunction) { 3776 // Omit function body in string representations of functions 3777 int curly = objString.indexOf('{'); 3778 if (curly > -1) { 3779 objString = objString.substring(0, curly + 1) + "...}"; 3780 } 3781 } 3782 if (value == Scriptable.NOT_FOUND) { 3783 return typeError2("msg.function.not.found.in", propertyName, 3784 objString); 3785 } 3786 return typeError3("msg.isnt.function.in", propertyName, objString, 3787 typeof(value)); 3788 } 3789 notXmlError(Object value)3790 private static RuntimeException notXmlError(Object value) 3791 { 3792 throw typeError1("msg.isnt.xml.object", toString(value)); 3793 } 3794 warnAboutNonJSObject(Object nonJSObject)3795 private static void warnAboutNonJSObject(Object nonJSObject) 3796 { 3797 String message = 3798 "RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n" 3799 +"Rhino runtime detected object "+nonJSObject+" of class "+nonJSObject.getClass().getName()+" where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call."; 3800 Context.reportWarning(message); 3801 // Just to be sure that it would be noticed 3802 System.err.println(message); 3803 } 3804 getRegExpProxy(Context cx)3805 public static RegExpProxy getRegExpProxy(Context cx) 3806 { 3807 return cx.getRegExpProxy(); 3808 } 3809 setRegExpProxy(Context cx, RegExpProxy proxy)3810 public static void setRegExpProxy(Context cx, RegExpProxy proxy) 3811 { 3812 if (proxy == null) throw new IllegalArgumentException(); 3813 cx.regExpProxy = proxy; 3814 } 3815 checkRegExpProxy(Context cx)3816 public static RegExpProxy checkRegExpProxy(Context cx) 3817 { 3818 RegExpProxy result = getRegExpProxy(cx); 3819 if (result == null) { 3820 throw Context.reportRuntimeError0("msg.no.regexp"); 3821 } 3822 return result; 3823 } 3824 wrapRegExp(Context cx, Scriptable scope, Object compiled)3825 public static Scriptable wrapRegExp(Context cx, Scriptable scope, 3826 Object compiled) { 3827 return cx.getRegExpProxy().wrapRegExp(cx, scope, compiled); 3828 } 3829 currentXMLLib(Context cx)3830 private static XMLLib currentXMLLib(Context cx) 3831 { 3832 // Scripts should be running to access this 3833 if (cx.topCallScope == null) 3834 throw new IllegalStateException(); 3835 3836 XMLLib xmlLib = cx.cachedXMLLib; 3837 if (xmlLib == null) { 3838 xmlLib = XMLLib.extractFromScope(cx.topCallScope); 3839 if (xmlLib == null) 3840 throw new IllegalStateException(); 3841 cx.cachedXMLLib = xmlLib; 3842 } 3843 3844 return xmlLib; 3845 } 3846 3847 /** 3848 * Escapes the reserved characters in a value of an attribute 3849 * 3850 * @param value Unescaped text 3851 * @return The escaped text 3852 */ escapeAttributeValue(Object value, Context cx)3853 public static String escapeAttributeValue(Object value, Context cx) 3854 { 3855 XMLLib xmlLib = currentXMLLib(cx); 3856 return xmlLib.escapeAttributeValue(value); 3857 } 3858 3859 /** 3860 * Escapes the reserved characters in a value of a text node 3861 * 3862 * @param value Unescaped text 3863 * @return The escaped text 3864 */ escapeTextValue(Object value, Context cx)3865 public static String escapeTextValue(Object value, Context cx) 3866 { 3867 XMLLib xmlLib = currentXMLLib(cx); 3868 return xmlLib.escapeTextValue(value); 3869 } 3870 memberRef(Object obj, Object elem, Context cx, int memberTypeFlags)3871 public static Ref memberRef(Object obj, Object elem, 3872 Context cx, int memberTypeFlags) 3873 { 3874 if (!(obj instanceof XMLObject)) { 3875 throw notXmlError(obj); 3876 } 3877 XMLObject xmlObject = (XMLObject)obj; 3878 return xmlObject.memberRef(cx, elem, memberTypeFlags); 3879 } 3880 memberRef(Object obj, Object namespace, Object elem, Context cx, int memberTypeFlags)3881 public static Ref memberRef(Object obj, Object namespace, Object elem, 3882 Context cx, int memberTypeFlags) 3883 { 3884 if (!(obj instanceof XMLObject)) { 3885 throw notXmlError(obj); 3886 } 3887 XMLObject xmlObject = (XMLObject)obj; 3888 return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags); 3889 } 3890 nameRef(Object name, Context cx, Scriptable scope, int memberTypeFlags)3891 public static Ref nameRef(Object name, Context cx, 3892 Scriptable scope, int memberTypeFlags) 3893 { 3894 XMLLib xmlLib = currentXMLLib(cx); 3895 return xmlLib.nameRef(cx, name, scope, memberTypeFlags); 3896 } 3897 nameRef(Object namespace, Object name, Context cx, Scriptable scope, int memberTypeFlags)3898 public static Ref nameRef(Object namespace, Object name, Context cx, 3899 Scriptable scope, int memberTypeFlags) 3900 { 3901 XMLLib xmlLib = currentXMLLib(cx); 3902 return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags); 3903 } 3904 storeIndexResult(Context cx, int index)3905 private static void storeIndexResult(Context cx, int index) 3906 { 3907 cx.scratchIndex = index; 3908 } 3909 lastIndexResult(Context cx)3910 static int lastIndexResult(Context cx) 3911 { 3912 return cx.scratchIndex; 3913 } 3914 storeUint32Result(Context cx, long value)3915 public static void storeUint32Result(Context cx, long value) 3916 { 3917 if ((value >>> 32) != 0) 3918 throw new IllegalArgumentException(); 3919 cx.scratchUint32 = value; 3920 } 3921 lastUint32Result(Context cx)3922 public static long lastUint32Result(Context cx) 3923 { 3924 long value = cx.scratchUint32; 3925 if ((value >>> 32) != 0) 3926 throw new IllegalStateException(); 3927 return value; 3928 } 3929 storeScriptable(Context cx, Scriptable value)3930 private static void storeScriptable(Context cx, Scriptable value) 3931 { 3932 // The previously stored scratchScriptable should be consumed 3933 if (cx.scratchScriptable != null) 3934 throw new IllegalStateException(); 3935 cx.scratchScriptable = value; 3936 } 3937 lastStoredScriptable(Context cx)3938 public static Scriptable lastStoredScriptable(Context cx) 3939 { 3940 Scriptable result = cx.scratchScriptable; 3941 cx.scratchScriptable = null; 3942 return result; 3943 } 3944 makeUrlForGeneratedScript(boolean isEval, String masterScriptUrl, int masterScriptLine)3945 static String makeUrlForGeneratedScript 3946 (boolean isEval, String masterScriptUrl, int masterScriptLine) 3947 { 3948 if (isEval) { 3949 return masterScriptUrl+'#'+masterScriptLine+"(eval)"; 3950 } else { 3951 return masterScriptUrl+'#'+masterScriptLine+"(Function)"; 3952 } 3953 } 3954 isGeneratedScript(String sourceUrl)3955 static boolean isGeneratedScript(String sourceUrl) { 3956 // ALERT: this may clash with a valid URL containing (eval) or 3957 // (Function) 3958 return sourceUrl.indexOf("(eval)") >= 0 3959 || sourceUrl.indexOf("(Function)") >= 0; 3960 } 3961 errorWithClassName(String msg, Object val)3962 private static RuntimeException errorWithClassName(String msg, Object val) 3963 { 3964 return Context.reportRuntimeError1(msg, val.getClass().getName()); 3965 } 3966 3967 /** 3968 * Equivalent to executing "new Error(message)" from JavaScript. 3969 * @param cx the current context 3970 * @param scope the current scope 3971 * @param message the message 3972 * @return a JavaScriptException you should throw 3973 */ throwError(Context cx, Scriptable scope, String message)3974 public static JavaScriptException throwError(Context cx, Scriptable scope, 3975 String message) { 3976 int[] linep = { 0 }; 3977 String filename = Context.getSourcePositionFromStack(linep); 3978 final Scriptable error = newBuiltinObject(cx, scope, 3979 TopLevel.Builtins.Error, new Object[] { message, filename, Integer.valueOf(linep[0]) }); 3980 return new JavaScriptException(error, filename, linep[0]); 3981 } 3982 3983 public static final Object[] emptyArgs = new Object[0]; 3984 public static final String[] emptyStrings = new String[0]; 3985 3986 } 3987