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