2003/05/12 05:10:30
[org.ibex.core.git] / src / org / mozilla / javascript / ScriptRuntime.java
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
2  *\r
3  * The contents of this file are subject to the Netscape Public\r
4  * License Version 1.1 (the "License"); you may not use this file\r
5  * except in compliance with the License. You may obtain a copy of\r
6  * the License at http://www.mozilla.org/NPL/\r
7  *\r
8  * Software distributed under the License is distributed on an "AS\r
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
10  * implied. See the License for the specific language governing\r
11  * rights and limitations under the License.\r
12  *\r
13  * The Original Code is Rhino code, released\r
14  * May 6, 1999.\r
15  *\r
16  * The Initial Developer of the Original Code is Netscape\r
17  * Communications Corporation.  Portions created by Netscape are\r
18  * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
19  * Rights Reserved.\r
20  *\r
21  * Contributor(s):\r
22  * Patrick Beard\r
23  * Norris Boyd\r
24  * Igor Bukanov \r
25  * Roger Lawrence\r
26  * Frank Mitchell\r
27  * Andrew Wason\r
28  *\r
29  * Alternatively, the contents of this file may be used under the\r
30  * terms of the GNU Public License (the "GPL"), in which case the\r
31  * provisions of the GPL are applicable instead of those above.\r
32  * If you wish to allow use of your version of this file only\r
33  * under the terms of the GPL and not to allow others to use your\r
34  * version of this file under the NPL, indicate your decision by\r
35  * deleting the provisions above and replace them with the notice\r
36  * and other provisions required by the GPL.  If you do not delete\r
37  * the provisions above, a recipient may use your version of this\r
38  * file under either the NPL or the GPL.\r
39  */\r
40 \r
41 package org.mozilla.javascript;\r
42 \r
43 import java.util.*;\r
44 import java.lang.reflect.*;\r
45 //import org.mozilla.classfile.DefiningClassLoader;\r
46 \r
47 /**\r
48  * This is the class that implements the runtime.\r
49  *\r
50  * @author Norris Boyd\r
51  */\r
52 \r
53 public class ScriptRuntime {\r
54 \r
55     /**\r
56      * No instances should be created.\r
57      */\r
58     protected ScriptRuntime() {\r
59     }\r
60 \r
61     /*\r
62      * There's such a huge space (and some time) waste for the Foo.class\r
63      * syntax: the compiler sticks in a test of a static field in the\r
64      * enclosing class for null and the code for creating the class value.\r
65      * It has to do this since the reference has to get pushed off til\r
66      * executiontime (i.e. can't force an early load), but for the\r
67      * 'standard' classes - especially those in java.lang, we can trust\r
68      * that they won't cause problems by being loaded early.\r
69      */\r
70 \r
71     public final static Class UndefinedClass = Undefined.class;\r
72     public final static Class ScriptableClass = Scriptable.class;\r
73     public final static Class StringClass = String.class;\r
74     public final static Class NumberClass = Number.class;\r
75     public final static Class BooleanClass = Boolean.class;\r
76     public final static Class ByteClass = Byte.class;\r
77     public final static Class ShortClass = Short.class;\r
78     public final static Class IntegerClass = Integer.class;\r
79     public final static Class LongClass = Long.class;\r
80     public final static Class FloatClass = Float.class;\r
81     public final static Class DoubleClass = Double.class;\r
82     public final static Class CharacterClass = Character.class;\r
83     public final static Class ObjectClass = Object.class;\r
84     public final static Class FunctionClass = Function.class;\r
85     public final static Class ClassClass = Class.class;\r
86 \r
87     /**\r
88      * Convert the value to a boolean.\r
89      *\r
90      * See ECMA 9.2.\r
91      */\r
92     public static boolean toBoolean(Object val) {\r
93         if (val == null)\r
94             return false;\r
95         if (val instanceof Scriptable) {\r
96             if (Context.getContext().isVersionECMA1()) {\r
97                 // pure ECMA\r
98                 return val != Undefined.instance;\r
99             }\r
100             // ECMA extension\r
101             val = ((Scriptable) val).getDefaultValue(BooleanClass);\r
102             if (val instanceof Scriptable)\r
103                 throw errorWithClassName("msg.primitive.expected", val);\r
104             // fall through\r
105         }\r
106         if (val instanceof String)\r
107             return ((String) val).length() != 0;\r
108         if (val instanceof Number) {\r
109             double d = ((Number) val).doubleValue();\r
110             return (d == d && d != 0.0);\r
111         }\r
112         if (val instanceof Boolean)\r
113             return ((Boolean) val).booleanValue();\r
114         throw errorWithClassName("msg.invalid.type", val);\r
115     }\r
116 \r
117     public static boolean toBoolean(Object[] args, int index) {\r
118         return (index < args.length) ? toBoolean(args[index]) : false;\r
119     }\r
120     /**\r
121      * Convert the value to a number.\r
122      *\r
123      * See ECMA 9.3.\r
124      */\r
125     public static double toNumber(Object val) {\r
126         if (val == null) \r
127             return +0.0;\r
128         if (val instanceof Scriptable) {\r
129             val = ((Scriptable) val).getDefaultValue(NumberClass);\r
130             if (val != null && val instanceof Scriptable)\r
131                 throw errorWithClassName("msg.primitive.expected", val);\r
132             // fall through\r
133         }\r
134         if (val instanceof String)\r
135             return toNumber((String) val);\r
136         if (val instanceof Number)\r
137             return ((Number) val).doubleValue();\r
138         if (val instanceof Boolean)\r
139             return ((Boolean) val).booleanValue() ? 1 : +0.0;\r
140         throw errorWithClassName("msg.invalid.type", val);\r
141     }\r
142 \r
143     public static double toNumber(Object[] args, int index) {\r
144         return (index < args.length) ? toNumber(args[index]) : NaN;\r
145     }\r
146     \r
147     // This definition of NaN is identical to that in java.lang.Double\r
148     // except that it is not final. This is a workaround for a bug in\r
149     // the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses\r
150     // (returns at least) of Double.NaN to be converted to 1.0.\r
151     // So we use ScriptRuntime.NaN instead of Double.NaN.\r
152     public static double NaN = 0.0d / 0.0;\r
153     public static Double NaNobj = new Double(0.0d / 0.0);\r
154 \r
155     // A similar problem exists for negative zero.\r
156     public static double negativeZero = -0.0;\r
157 \r
158     /*\r
159      * Helper function for toNumber, parseInt, and TokenStream.getToken.\r
160      */\r
161     static double stringToNumber(String s, int start, int radix) {\r
162         char digitMax = '9';\r
163         char lowerCaseBound = 'a';\r
164         char upperCaseBound = 'A';\r
165         int len = s.length();\r
166         if (radix < 10) {\r
167             digitMax = (char) ('0' + radix - 1);\r
168         }\r
169         if (radix > 10) {\r
170             lowerCaseBound = (char) ('a' + radix - 10);\r
171             upperCaseBound = (char) ('A' + radix - 10);\r
172         }\r
173         int end;\r
174         double sum = 0.0;\r
175         for (end=start; end < len; end++) {\r
176             char c = s.charAt(end);\r
177             int newDigit;\r
178             if ('0' <= c && c <= digitMax)\r
179                 newDigit = c - '0';\r
180             else if ('a' <= c && c < lowerCaseBound)\r
181                 newDigit = c - 'a' + 10;\r
182             else if ('A' <= c && c < upperCaseBound)\r
183                 newDigit = c - 'A' + 10;\r
184             else\r
185                 break;\r
186             sum = sum*radix + newDigit;\r
187         }\r
188         if (start == end) {\r
189             return NaN;\r
190         }\r
191         if (sum >= 9007199254740992.0) {\r
192             if (radix == 10) {\r
193                 /* If we're accumulating a decimal number and the number\r
194                  * is >= 2^53, then the result from the repeated multiply-add\r
195                  * above may be inaccurate.  Call Java to get the correct\r
196                  * answer.\r
197                  */\r
198                 try {\r
199                     return Double.valueOf(s.substring(start, end)).doubleValue();\r
200                 } catch (NumberFormatException nfe) {\r
201                     return NaN;\r
202                 }\r
203             } else if (radix == 2 || radix == 4 || radix == 8 ||\r
204                        radix == 16 || radix == 32)\r
205             {\r
206                 /* The number may also be inaccurate for one of these bases.\r
207                  * This happens if the addition in value*radix + digit causes\r
208                  * a round-down to an even least significant mantissa bit\r
209                  * when the first dropped bit is a one.  If any of the\r
210                  * following digits in the number (which haven't been added\r
211                  * in yet) are nonzero then the correct action would have\r
212                  * been to round up instead of down.  An example of this\r
213                  * occurs when reading the number 0x1000000000000081, which\r
214                  * rounds to 0x1000000000000000 instead of 0x1000000000000100.\r
215                  */\r
216                 BinaryDigitReader bdr = new BinaryDigitReader(radix, s, start, end);\r
217                 int bit;\r
218                 sum = 0.0;\r
219 \r
220                 /* Skip leading zeros. */\r
221                 do {\r
222                     bit = bdr.getNextBinaryDigit();\r
223                 } while (bit == 0);\r
224 \r
225                 if (bit == 1) {\r
226                     /* Gather the 53 significant bits (including the leading 1) */\r
227                     sum = 1.0;\r
228                     for (int j = 52; j != 0; j--) {\r
229                         bit = bdr.getNextBinaryDigit();\r
230                         if (bit < 0)\r
231                             return sum;\r
232                         sum = sum*2 + bit;\r
233                     }\r
234                     /* bit54 is the 54th bit (the first dropped from the mantissa) */\r
235                     int bit54 = bdr.getNextBinaryDigit();\r
236                     if (bit54 >= 0) {\r
237                         double factor = 2.0;\r
238                         int sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */\r
239                         int bit3;\r
240 \r
241                         while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {\r
242                             sticky |= bit3;\r
243                             factor *= 2;\r
244                         }\r
245                         sum += bit54 & (bit | sticky);\r
246                         sum *= factor;\r
247                     }\r
248                 }\r
249             }\r
250             /* We don't worry about inaccurate numbers for any other base. */\r
251         }\r
252         return sum;\r
253     }\r
254 \r
255 \r
256     /**\r
257      * ToNumber applied to the String type\r
258      *\r
259      * See ECMA 9.3.1\r
260      */\r
261     public static double toNumber(String s) {\r
262         int len = s.length();\r
263         int start = 0;\r
264         char startChar;\r
265         for (;;) {\r
266             if (start == len) {\r
267                 // Empty or contains only whitespace\r
268                 return +0.0;\r
269             }\r
270             startChar = s.charAt(start);\r
271             if (!Character.isWhitespace(startChar))\r
272                 break;\r
273             start++;\r
274         }\r
275 \r
276         if (startChar == '0' && start+2 < len &&\r
277             Character.toLowerCase(s.charAt(start+1)) == 'x')\r
278             // A hexadecimal number\r
279             return stringToNumber(s, start + 2, 16);\r
280 \r
281         if ((startChar == '+' || startChar == '-') && start+3 < len &&\r
282             s.charAt(start+1) == '0' &&\r
283             Character.toLowerCase(s.charAt(start+2)) == 'x') {\r
284             // A hexadecimal number\r
285             double val = stringToNumber(s, start + 3, 16);\r
286             return startChar == '-' ? -val : val;\r
287         }\r
288 \r
289         int end = len - 1;\r
290         char endChar;\r
291         while (Character.isWhitespace(endChar = s.charAt(end)))\r
292             end--;\r
293         if (endChar == 'y') {\r
294             // check for "Infinity"\r
295             if (startChar == '+' || startChar == '-')\r
296                 start++;\r
297             if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))\r
298                 return startChar == '-'\r
299                     ? Double.NEGATIVE_INFINITY\r
300                     : Double.POSITIVE_INFINITY;\r
301             return NaN;\r
302         }\r
303         // A non-hexadecimal, non-infinity number:\r
304         // just try a normal floating point conversion\r
305         String sub = s.substring(start, end+1);\r
306         if (MSJVM_BUG_WORKAROUNDS) {\r
307             // The MS JVM will accept non-conformant strings\r
308             // rather than throwing a NumberFormatException\r
309             // as it should.\r
310             for (int i=sub.length()-1; i >= 0; i--) {\r
311                 char c = sub.charAt(i);\r
312                 if (('0' <= c && c <= '9') || c == '.' ||\r
313                     c == 'e' || c == 'E'  ||\r
314                     c == '+' || c == '-')\r
315                     continue;\r
316                 return NaN;\r
317             }\r
318         }\r
319         try {\r
320             return Double.valueOf(sub).doubleValue();\r
321         } catch (NumberFormatException ex) {\r
322             return NaN;\r
323         }\r
324     }\r
325 \r
326     /**\r
327      * Helper function for builtin objects that use the varargs form.\r
328      * ECMA function formal arguments are undefined if not supplied;\r
329      * this function pads the argument array out to the expected\r
330      * length, if necessary.\r
331      */\r
332     public static Object[] padArguments(Object[] args, int count) {\r
333         if (count < args.length)\r
334             return args;\r
335 \r
336         int i;\r
337         Object[] result = new Object[count];\r
338         for (i = 0; i < args.length; i++) {\r
339             result[i] = args[i];\r
340         }\r
341 \r
342         for (; i < count; i++) {\r
343             result[i] = Undefined.instance;\r
344         }\r
345 \r
346         return result;\r
347     }\r
348 \r
349     /* Work around Microsoft Java VM bugs. */\r
350     private final static boolean MSJVM_BUG_WORKAROUNDS = true;\r
351 \r
352     /**\r
353      * For escaping strings printed by object and array literals; not quite\r
354      * the same as 'escape.'\r
355      */\r
356     public static String escapeString(String s) {\r
357         // ack!  Java lacks \v.\r
358         String escapeMap = "\bb\ff\nn\rr\tt\u000bv\"\"''";\r
359         StringBuffer result = new StringBuffer(s.length());\r
360 \r
361         for(int i=0; i < s.length(); i++) {\r
362             char c = s.charAt(i);\r
363 \r
364             // an ordinary print character\r
365             if (c >= ' ' && c <= '~'     // string.h isprint()\r
366                 && c != '"')\r
367             {\r
368                 result.append(c);\r
369                 continue;\r
370             }\r
371 \r
372             // an \escaped sort of character\r
373             int index;\r
374             if ((index = escapeMap.indexOf(c)) >= 0) {\r
375                 result.append("\\");\r
376                 result.append(escapeMap.charAt(index + 1));\r
377                 continue;\r
378             }\r
379 \r
380             // 2-digit hex?\r
381             if (c < 256) {\r
382                 String hex = Integer.toHexString((int) c);\r
383                 if (hex.length() == 1) {\r
384                     result.append("\\x0");\r
385                     result.append(hex);\r
386                 } else {\r
387                     result.append("\\x");\r
388                     result.append(hex);\r
389                 }\r
390                 continue;\r
391             }\r
392 \r
393             // nope.  Unicode.\r
394             String hex = Integer.toHexString((int) c);\r
395             // cool idiom courtesy Shaver.\r
396             result.append("\\u");\r
397             for (int l = hex.length(); l < 4; l++)\r
398                 result.append('0');\r
399             result.append(hex);\r
400         }\r
401 \r
402         return result.toString();\r
403     }\r
404 \r
405 \r
406     /**\r
407      * Convert the value to a string.\r
408      *\r
409      * See ECMA 9.8.\r
410      */\r
411     public static String toString(Object val) {\r
412         for (;;) {\r
413             if (val == null)\r
414                 return "null";\r
415             if (val instanceof Scriptable) {\r
416                 val = ((Scriptable) val).getDefaultValue(StringClass);\r
417                 if (val != Undefined.instance && val instanceof Scriptable) {\r
418                     throw errorWithClassName("msg.primitive.expected", val);\r
419                 }\r
420                 continue;\r
421             }\r
422             if (val instanceof Number) {\r
423                 // XXX should we just teach NativeNumber.stringValue()\r
424                 // about Numbers?\r
425                 return numberToString(((Number) val).doubleValue(), 10);\r
426             }\r
427             return val.toString();\r
428         }\r
429     }\r
430 \r
431     public static String toString(Object[] args, int index) {\r
432         return (index < args.length) ? toString(args[index]) : "undefined";\r
433     }\r
434 \r
435     /**\r
436      * Optimized version of toString(Object) for numbers.\r
437      */\r
438     public static String toString(double val) {\r
439         return numberToString(val, 10);\r
440     }\r
441     \r
442     public static String numberToString(double d, int base) {\r
443         if (d != d)\r
444             return "NaN";\r
445         if (d == Double.POSITIVE_INFINITY)\r
446             return "Infinity";\r
447         if (d == Double.NEGATIVE_INFINITY)\r
448             return "-Infinity";\r
449         if (d == 0.0)\r
450             return "0";\r
451 \r
452         if ((base < 2) || (base > 36)) {\r
453             throw Context.reportRuntimeError1(\r
454                 "msg.bad.radix", Integer.toString(base));\r
455         }\r
456 \r
457         if (base != 10) {\r
458             return DToA.JS_dtobasestr(base, d);\r
459         } else {\r
460             StringBuffer result = new StringBuffer();\r
461             DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);\r
462             return result.toString();\r
463         }\r
464     \r
465     }\r
466 \r
467     /**\r
468      * Convert the value to an object.\r
469      *\r
470      * See ECMA 9.9.\r
471      */\r
472     public static Scriptable toObject(Scriptable scope, Object val) {\r
473         return toObject(scope, val, null);\r
474     }\r
475 \r
476     public static Scriptable toObject(Scriptable scope, Object val,\r
477                                       Class staticClass)\r
478     {\r
479         if (val == null) {\r
480             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
481         }\r
482         if (val instanceof Scriptable) {\r
483             if (val == Undefined.instance) {\r
484                 throw NativeGlobal.typeError0("msg.undef.to.object", scope);\r
485             }\r
486             return (Scriptable) val;\r
487         }\r
488         String className = val instanceof String ? "String" :\r
489                            val instanceof Number ? "Number" :\r
490                            val instanceof Boolean ? "Boolean" :\r
491                            null;\r
492 \r
493         if (className == null) {\r
494             // Extension: Wrap as a LiveConnect object.\r
495             Object wrapped = NativeJavaObject.wrap(scope, val, staticClass);\r
496             if (wrapped instanceof Scriptable)\r
497                 return (Scriptable) wrapped;\r
498             throw errorWithClassName("msg.invalid.type", val);\r
499         }\r
500 \r
501         Object[] args = { val };\r
502         scope = ScriptableObject.getTopLevelScope(scope);\r
503         Scriptable result = newObject(Context.getContext(), org.xwt.util.JSObject.defaultObjects, className, args);\r
504         return result;\r
505     }\r
506 \r
507     public static Scriptable newObject(Context cx, Scriptable scope,\r
508                                        String constructorName, Object[] args)\r
509     {\r
510         Exception re = null;\r
511         try {\r
512             return cx.newObject(scope, constructorName, args);\r
513         }\r
514         catch (NotAFunctionException e) {\r
515             re = e;\r
516         }\r
517         catch (PropertyException e) {\r
518             re = e;\r
519         }\r
520         catch (JavaScriptException e) {\r
521             re = e;\r
522         }\r
523         throw cx.reportRuntimeError(re.getMessage());\r
524     }\r
525 \r
526     /**\r
527      *\r
528      * See ECMA 9.4.\r
529      */\r
530     public static double toInteger(Object val) {\r
531         return toInteger(toNumber(val));\r
532     }\r
533 \r
534     // convenience method\r
535     public static double toInteger(double d) {\r
536         // if it's NaN\r
537         if (d != d)\r
538             return +0.0;\r
539 \r
540         if (d == 0.0 ||\r
541             d == Double.POSITIVE_INFINITY ||\r
542             d == Double.NEGATIVE_INFINITY)\r
543             return d;\r
544 \r
545         if (d > 0.0)\r
546             return Math.floor(d);\r
547         else\r
548             return Math.ceil(d);\r
549     }\r
550 \r
551     public static double toInteger(Object[] args, int index) {\r
552         return (index < args.length) ? toInteger(args[index]) : +0.0;\r
553     }\r
554 \r
555     /**\r
556      *\r
557      * See ECMA 9.5.\r
558      */\r
559     public static int toInt32(Object val) {\r
560         // 0x100000000 gives me a numeric overflow...\r
561         double two32 = 4294967296.0;\r
562         double two31 = 2147483648.0;\r
563 \r
564         // short circuit for common small values; TokenStream\r
565         // returns them as Bytes.\r
566         if (val instanceof Byte)\r
567             return ((Number)val).intValue();\r
568 \r
569         double d = toNumber(val);\r
570         if (d != d || d == 0.0 ||\r
571             d == Double.POSITIVE_INFINITY ||\r
572             d == Double.NEGATIVE_INFINITY)\r
573             return 0;\r
574 \r
575         d = Math.IEEEremainder(d, two32);\r
576 \r
577         d = d >= 0\r
578             ? d\r
579             : d + two32;\r
580 \r
581         if (d >= two31)\r
582             return (int)(d - two32);\r
583         else\r
584             return (int)d;\r
585     }\r
586 \r
587     public static int toInt32(Object[] args, int index) {\r
588         return (index < args.length) ? toInt32(args[index]) : 0;\r
589     }\r
590 \r
591     public static int toInt32(double d) {\r
592         // 0x100000000 gives me a numeric overflow...\r
593         double two32 = 4294967296.0;\r
594         double two31 = 2147483648.0;\r
595 \r
596         if (d != d || d == 0.0 ||\r
597             d == Double.POSITIVE_INFINITY ||\r
598             d == Double.NEGATIVE_INFINITY)\r
599             return 0;\r
600 \r
601         d = Math.IEEEremainder(d, two32);\r
602 \r
603         d = d >= 0\r
604             ? d\r
605             : d + two32;\r
606 \r
607         if (d >= two31)\r
608             return (int)(d - two32);\r
609         else\r
610             return (int)d;\r
611     }\r
612 \r
613     /**\r
614      *\r
615      * See ECMA 9.6.\r
616      */\r
617 \r
618     // must return long to hold an _unsigned_ int\r
619     public static long toUint32(double d) {\r
620         // 0x100000000 gives me a numeric overflow...\r
621         double two32 = 4294967296.0;\r
622 \r
623         if (d != d || d == 0.0 ||\r
624             d == Double.POSITIVE_INFINITY ||\r
625             d == Double.NEGATIVE_INFINITY)\r
626             return 0;\r
627 \r
628         if (d > 0.0)\r
629             d = Math.floor(d);\r
630         else\r
631             d = Math.ceil(d);\r
632 \r
633     d = Math.IEEEremainder(d, two32);\r
634 \r
635     d = d >= 0\r
636             ? d\r
637             : d + two32;\r
638 \r
639         return (long) Math.floor(d);\r
640     }\r
641 \r
642     public static long toUint32(Object val) {\r
643         return toUint32(toNumber(val));\r
644     }\r
645 \r
646     /**\r
647      *\r
648      * See ECMA 9.7.\r
649      */\r
650     public static char toUint16(Object val) {\r
651     long int16 = 0x10000;\r
652 \r
653     double d = toNumber(val);\r
654     if (d != d || d == 0.0 ||\r
655         d == Double.POSITIVE_INFINITY ||\r
656         d == Double.NEGATIVE_INFINITY)\r
657     {\r
658         return 0;\r
659     }\r
660 \r
661     d = Math.IEEEremainder(d, int16);\r
662 \r
663     d = d >= 0\r
664             ? d\r
665         : d + int16;\r
666 \r
667     return (char) Math.floor(d);\r
668     }\r
669 \r
670     /**\r
671      * Unwrap a JavaScriptException.  Sleight of hand so that we don't\r
672      * javadoc JavaScriptException.getRuntimeValue().\r
673      */\r
674     public static Object unwrapJavaScriptException(JavaScriptException jse) {\r
675         return jse.value;\r
676     }\r
677 \r
678     /**\r
679      * Check a WrappedException. Unwrap a JavaScriptException and return\r
680      * the value, otherwise rethrow.\r
681      */\r
682     public static Object unwrapWrappedException(WrappedException we) {\r
683         Throwable t = we.getWrappedException();\r
684         if (t instanceof JavaScriptException)\r
685             return ((JavaScriptException) t).value;\r
686         throw we;\r
687     }\r
688 \r
689     public static Object getProp(Object obj, String id, Scriptable scope) {\r
690         Scriptable start;\r
691         if (obj instanceof Scriptable) {\r
692             start = (Scriptable) obj;\r
693         } else {\r
694             start = toObject(scope, obj);\r
695         }\r
696         if (start == null || start == Undefined.instance) {\r
697             String msg = start == null ? "msg.null.to.object"\r
698                                        : "msg.undefined";\r
699             throw NativeGlobal.constructError(\r
700                         Context.getContext(), "ConversionError",\r
701                         ScriptRuntime.getMessage0(msg),\r
702                         scope);\r
703         }\r
704         Scriptable m = start;\r
705         do {\r
706             Object result = m.get(id, start);\r
707             if (result != Scriptable.NOT_FOUND)\r
708                 return result;\r
709             m = m.getPrototype();\r
710         } while (m != null);\r
711         return Undefined.instance;\r
712     }\r
713 \r
714     public static Object getTopLevelProp(Scriptable scope, String id) {\r
715         Scriptable s = ScriptableObject.getTopLevelScope(scope);\r
716         Object v;\r
717         do {\r
718             v = s.get(id, s);\r
719             if (v != Scriptable.NOT_FOUND)\r
720                 return v;\r
721             s = s.getPrototype();\r
722         } while (s != null);\r
723         return v;\r
724     }\r
725 \r
726 \r
727 \r
728 /***********************************************************************/\r
729 \r
730     public static Scriptable getProto(Object obj, Scriptable scope) {\r
731         Scriptable s;\r
732         if (obj instanceof Scriptable) {\r
733             s = (Scriptable) obj;\r
734         } else {\r
735             s = toObject(scope, obj);\r
736         }\r
737         if (s == null) {\r
738             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
739         }\r
740         return s.getPrototype();\r
741     }\r
742 \r
743     public static Scriptable getParent(Object obj) {\r
744         Scriptable s;\r
745         try {\r
746             s = (Scriptable) obj;\r
747         }\r
748         catch (ClassCastException e) {\r
749             return null;\r
750         }\r
751         if (s == null) {\r
752             return null;\r
753         }\r
754         return getThis(s.getParentScope());\r
755     }\r
756 \r
757    public static Scriptable getParent(Object obj, Scriptable scope) {\r
758         Scriptable s;\r
759         if (obj instanceof Scriptable) {\r
760             s = (Scriptable) obj;\r
761         } else {\r
762             s = toObject(scope, obj);\r
763         }\r
764         if (s == null) {\r
765             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
766         }\r
767         return s.getParentScope();\r
768     }\r
769 \r
770     public static Object setProto(Object obj, Object value, Scriptable scope) {\r
771         Scriptable start;\r
772         if (obj instanceof Scriptable) {\r
773             start = (Scriptable) obj;\r
774         } else {\r
775             start = toObject(scope, obj);\r
776         }\r
777         Scriptable result = value == null ? null : toObject(scope, value);\r
778         Scriptable s = result;\r
779         while (s != null) {\r
780             if (s == start) {\r
781                 throw Context.reportRuntimeError1(\r
782                     "msg.cyclic.value", "__proto__");\r
783             }\r
784             s = s.getPrototype();\r
785         }\r
786         if (start == null) {\r
787             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
788         }\r
789         start.setPrototype(result);\r
790         return result;\r
791     }\r
792 \r
793     public static Object setParent(Object obj, Object value, Scriptable scope) {\r
794         Scriptable start;\r
795         if (obj instanceof Scriptable) {\r
796             start = (Scriptable) obj;\r
797         } else {\r
798             start = toObject(scope, obj);\r
799         }\r
800         Scriptable result = value == null ? null : toObject(scope, value);\r
801         Scriptable s = result;\r
802         while (s != null) {\r
803             if (s == start) {\r
804                 throw Context.reportRuntimeError1(\r
805                     "msg.cyclic.value", "__parent__");\r
806             }\r
807             s = s.getParentScope();\r
808         }\r
809         if (start == null) {\r
810             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
811         }\r
812         start.setParentScope(result);\r
813         return result;\r
814     }\r
815 \r
816     public static Object setProp(Object obj, String id, Object value,\r
817                                  Scriptable scope)\r
818     {\r
819         Scriptable start;\r
820         if (obj instanceof Scriptable) {\r
821             start = (Scriptable) obj;\r
822         } else {\r
823             start = toObject(scope, obj);\r
824         }\r
825         if (start == null) {\r
826             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
827         }\r
828         Scriptable m = start;\r
829         do {\r
830             if (m.has(id, start)) {\r
831                 m.put(id, start, value);\r
832                 return value;\r
833             }\r
834             m = m.getPrototype();\r
835         } while (m != null);\r
836 \r
837         start.put(id, start, value);\r
838         return value;\r
839     }\r
840 \r
841     // Return -1L if str is not an index or the index value as lower 32 \r
842     // bits of the result\r
843     private static long indexFromString(String str) {\r
844         // It must be a string.\r
845 \r
846         // The length of the decimal string representation of \r
847         //  Integer.MAX_VALUE, 2147483647\r
848         final int MAX_VALUE_LENGTH = 10;\r
849         \r
850         int len = str.length();\r
851         if (len > 0) {\r
852             int i = 0;\r
853             boolean negate = false;\r
854             int c = str.charAt(0);\r
855             if (c == '-') {\r
856                 if (len > 1) {\r
857                     c = str.charAt(1); \r
858                     i = 1; \r
859                     negate = true;\r
860                 }\r
861             }\r
862             c -= '0';\r
863             if (0 <= c && c <= 9 \r
864                 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))\r
865             {\r
866                 // Use negative numbers to accumulate index to handle\r
867                 // Integer.MIN_VALUE that is greater by 1 in absolute value\r
868                 // then Integer.MAX_VALUE\r
869                 int index = -c;\r
870                 int oldIndex = 0;\r
871                 i++;\r
872                 if (index != 0) {\r
873                     // Note that 00, 01, 000 etc. are not indexes\r
874                     while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)\r
875                     {\r
876                         oldIndex = index;\r
877                         index = 10 * index - c;\r
878                         i++;\r
879                     }\r
880                 }\r
881                 // Make sure all characters were consumed and that it couldn't\r
882                 // have overflowed.\r
883                 if (i == len &&\r
884                     (oldIndex > (Integer.MIN_VALUE / 10) ||\r
885                      (oldIndex == (Integer.MIN_VALUE / 10) &&\r
886                       c <= (negate ? -(Integer.MIN_VALUE % 10) \r
887                                    : (Integer.MAX_VALUE % 10)))))\r
888                 {\r
889                     return 0xFFFFFFFFL & (negate ? index : -index);\r
890                 }\r
891             }\r
892         }\r
893         return -1L;\r
894     }\r
895 \r
896     static String getStringId(Object id) {\r
897         if (id instanceof Number) {\r
898             double d = ((Number) id).doubleValue();\r
899             int index = (int) d;\r
900             if (((double) index) == d)\r
901                 return null;\r
902             return toString(id);\r
903         }\r
904         String s = toString(id);\r
905         long indexTest = indexFromString(s);\r
906         if (indexTest >= 0)\r
907             return null;\r
908         return s;\r
909     }\r
910 \r
911     static int getIntId(Object id) {\r
912         if (id instanceof Number) {\r
913             double d = ((Number) id).doubleValue();\r
914             int index = (int) d;\r
915             if (((double) index) == d)\r
916                 return index;\r
917             return 0;\r
918         }\r
919         String s = toString(id);\r
920         long indexTest = indexFromString(s);\r
921         if (indexTest >= 0)\r
922             return (int)indexTest;\r
923         return 0;\r
924     }\r
925 \r
926     public static Object getElem(Object obj, Object id, Scriptable scope) {\r
927         int index;\r
928         String s;\r
929         if (id instanceof Number) {\r
930             double d = ((Number) id).doubleValue();\r
931             index = (int) d;\r
932             s = ((double) index) == d ? null : toString(id);\r
933         } else {\r
934             s = toString(id);\r
935             long indexTest = indexFromString(s);\r
936             if (indexTest >= 0) {\r
937                 index = (int)indexTest;\r
938                 s = null;\r
939             } else {\r
940                 index = 0;\r
941             }                \r
942         }\r
943         Scriptable start = obj instanceof Scriptable\r
944                            ? (Scriptable) obj\r
945                            : toObject(scope, obj);\r
946         Scriptable m = start;\r
947         if (s != null) {\r
948             if (s.equals("__proto__"))\r
949                 return start.getPrototype();\r
950             if (s.equals("__parent__"))\r
951                 return start.getParentScope();\r
952             while (m != null) {\r
953                 Object result = m.get(s, start);\r
954                 if (result != Scriptable.NOT_FOUND)\r
955                     return result;\r
956                 m = m.getPrototype();\r
957             }\r
958             return Undefined.instance;\r
959         }\r
960         while (m != null) {\r
961             Object result = m.get(index, start);\r
962             if (result != Scriptable.NOT_FOUND)\r
963                 return result;\r
964             m = m.getPrototype();\r
965         }\r
966         return Undefined.instance;\r
967     }\r
968 \r
969 \r
970     /*\r
971      * A cheaper and less general version of the above for well-known argument\r
972      * types.\r
973      */\r
974     public static Object getElem(Scriptable obj, int index)\r
975     {\r
976         Scriptable m = obj;\r
977         while (m != null) {\r
978             Object result = m.get(index, obj);\r
979             if (result != Scriptable.NOT_FOUND)\r
980                 return result;\r
981             m = m.getPrototype();\r
982         }\r
983         return Undefined.instance;\r
984     }\r
985 \r
986     public static Object setElem(Object obj, Object id, Object value,\r
987                                  Scriptable scope)\r
988     {\r
989         int index;\r
990         String s;\r
991         if (id instanceof Number) {\r
992             double d = ((Number) id).doubleValue();\r
993             index = (int) d;\r
994             s = ((double) index) == d ? null : toString(id);\r
995         } else {\r
996             s = toString(id);\r
997             long indexTest = indexFromString(s);\r
998             if (indexTest >= 0) {\r
999                 index = (int)indexTest;\r
1000                 s = null;\r
1001             } else {\r
1002                 index = 0;\r
1003             }\r
1004         }\r
1005 \r
1006         Scriptable start = obj instanceof Scriptable\r
1007                      ? (Scriptable) obj\r
1008                      : toObject(scope, obj);\r
1009         Scriptable m = start;\r
1010         if (s != null) {\r
1011             if (s.equals("__proto__"))\r
1012                 return setProto(obj, value, scope);\r
1013             if (s.equals("__parent__"))\r
1014                 return setParent(obj, value, scope);\r
1015 \r
1016             do {\r
1017                 if (m.has(s, start)) {\r
1018                     m.put(s, start, value);\r
1019                     return value;\r
1020                 }\r
1021                 m = m.getPrototype();\r
1022             } while (m != null);\r
1023             start.put(s, start, value);\r
1024             return value;\r
1025        }\r
1026 \r
1027         do {\r
1028             if (m.has(index, start)) {\r
1029                 m.put(index, start, value);\r
1030                 return value;\r
1031             }\r
1032             m = m.getPrototype();\r
1033         } while (m != null);\r
1034         start.put(index, start, value);\r
1035         return value;\r
1036     }\r
1037 \r
1038     /*\r
1039      * A cheaper and less general version of the above for well-known argument\r
1040      * types.\r
1041      */\r
1042     public static Object setElem(Scriptable obj, int index, Object value)\r
1043     {\r
1044         Scriptable m = obj;\r
1045         do {\r
1046             if (m.has(index, obj)) {\r
1047                 m.put(index, obj, value);\r
1048                 return value;\r
1049             }\r
1050             m = m.getPrototype();\r
1051         } while (m != null);\r
1052         obj.put(index, obj, value);\r
1053         return value;\r
1054     }\r
1055 \r
1056     /**\r
1057      * The delete operator\r
1058      *\r
1059      * See ECMA 11.4.1\r
1060      *\r
1061      * In ECMA 0.19, the description of the delete operator (11.4.1)\r
1062      * assumes that the [[Delete]] method returns a value. However,\r
1063      * the definition of the [[Delete]] operator (8.6.2.5) does not\r
1064      * define a return value. Here we assume that the [[Delete]]\r
1065      * method doesn't return a value.\r
1066      */\r
1067     public static Object delete(Object obj, Object id) {\r
1068         String s = getStringId(id);\r
1069         boolean result = s != null\r
1070             ? ScriptableObject.deleteProperty((Scriptable) obj, s)\r
1071             : ScriptableObject.deleteProperty((Scriptable) obj, getIntId(id));\r
1072         return result ? Boolean.TRUE : Boolean.FALSE;\r
1073     }\r
1074 \r
1075     /**\r
1076      * Looks up a name in the scope chain and returns its value.\r
1077      */\r
1078     public static Object name(Scriptable scopeChain, String id) {\r
1079         Scriptable obj = scopeChain;\r
1080         Object prop;\r
1081         while (obj != null) {\r
1082             Scriptable m = obj;\r
1083             do {\r
1084                 Object result = m.get(id, obj);\r
1085                 if (result != Scriptable.NOT_FOUND)\r
1086                     return result;\r
1087                 m = m.getPrototype();\r
1088             } while (m != null);\r
1089             obj = obj.getParentScope();\r
1090         }\r
1091         throw NativeGlobal.constructError\r
1092             (Context.getContext(), "ReferenceError",\r
1093              ScriptRuntime.getMessage1("msg.is.not.defined", id.toString()),\r
1094                         scopeChain);\r
1095     }\r
1096 \r
1097     /**\r
1098      * Returns the object in the scope chain that has a given property.\r
1099      *\r
1100      * The order of evaluation of an assignment expression involves\r
1101      * evaluating the lhs to a reference, evaluating the rhs, and then\r
1102      * modifying the reference with the rhs value. This method is used\r
1103      * to 'bind' the given name to an object containing that property\r
1104      * so that the side effects of evaluating the rhs do not affect\r
1105      * which property is modified.\r
1106      * Typically used in conjunction with setName.\r
1107      *\r
1108      * See ECMA 10.1.4\r
1109      */\r
1110     public static Scriptable bind(Scriptable scope, String id) {\r
1111         Scriptable obj = scope;\r
1112         Object prop;\r
1113         while (obj != null) {\r
1114             Scriptable m = obj;\r
1115             do {\r
1116                 if (m.has(id, obj))\r
1117                     return obj;\r
1118                 m = m.getPrototype();\r
1119             } while (m != null);\r
1120             obj = obj.getParentScope();\r
1121         }\r
1122         return null;\r
1123     }\r
1124 \r
1125     public static Scriptable getBase(Scriptable scope, String id) {\r
1126         Scriptable obj = scope;\r
1127         Object prop;\r
1128         while (obj != null) {\r
1129             Scriptable m = obj;\r
1130             do {\r
1131                 if (m.get(id, obj) != Scriptable.NOT_FOUND)\r
1132                     return obj;\r
1133                 m = m.getPrototype();\r
1134             } while (m != null);\r
1135             obj = obj.getParentScope();\r
1136         }\r
1137         throw NativeGlobal.constructError(\r
1138                     Context.getContext(), "ReferenceError",\r
1139                     ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
1140                     scope);\r
1141     }\r
1142 \r
1143     public static Scriptable getThis(Scriptable base) {\r
1144         while (base instanceof NativeWith)\r
1145             base = base.getPrototype();\r
1146         if (base instanceof NativeCall)\r
1147             base = ScriptableObject.getTopLevelScope(base);\r
1148         return base;\r
1149     }\r
1150 \r
1151     public static Object setName(Scriptable bound, Object value,\r
1152                                  Scriptable scope, String id)\r
1153     {\r
1154         if (bound == null) {\r
1155             // "newname = 7;", where 'newname' has not yet\r
1156             // been defined, creates a new property in the\r
1157             // global object. Find the global object by\r
1158             // walking up the scope chain.\r
1159             Scriptable next = scope;\r
1160             do {\r
1161                 bound = next;\r
1162                 next = bound.getParentScope();\r
1163             } while (next != null);\r
1164 \r
1165             bound.put(id, bound, value);\r
1166             /*\r
1167             This code is causing immense performance problems in\r
1168             scripts that assign to the variables as a way of creating them.\r
1169             XXX need strict mode\r
1170             String message = getMessage1("msg.assn.create", id);\r
1171             Context.reportWarning(message);\r
1172             */\r
1173             return value;\r
1174         }\r
1175         return setProp(bound, id, value, scope);\r
1176     }\r
1177 \r
1178     public static Enumeration initEnum(Object value, Scriptable scope) {\r
1179         Scriptable m = toObject(scope, value);\r
1180         return new IdEnumeration(m);\r
1181     }\r
1182 \r
1183     public static Object nextEnum(Enumeration enum) {\r
1184         // OPT this could be more efficient; should junk the Enumeration\r
1185         // interface\r
1186         if (!enum.hasMoreElements())\r
1187             return null;\r
1188         return enum.nextElement();\r
1189     }\r
1190 \r
1191     // Form used by class files generated by 1.4R3 and earlier.\r
1192     public static Object call(Context cx, Object fun, Object thisArg,\r
1193                               Object[] args)\r
1194         throws JavaScriptException\r
1195     {\r
1196         Scriptable scope = null;\r
1197         if (fun instanceof Scriptable) \r
1198             scope = ((Scriptable) fun).getParentScope();\r
1199         return call(cx, fun, thisArg, args, scope);\r
1200     }\r
1201     \r
1202     public static Object call(Context cx, Object fun, Object thisArg,\r
1203                               Object[] args, Scriptable scope)\r
1204         throws JavaScriptException\r
1205     {\r
1206         Function function;\r
1207         try {\r
1208             function = (Function) fun;\r
1209         }\r
1210         catch (ClassCastException e) {\r
1211             throw NativeGlobal.typeError1\r
1212                 ("msg.isnt.function", toString(fun), scope);\r
1213         }\r
1214 \r
1215         Scriptable thisObj;\r
1216         if (thisArg instanceof Scriptable || thisArg == null) {\r
1217             thisObj = (Scriptable) thisArg;\r
1218         } else {\r
1219             thisObj = ScriptRuntime.toObject(scope, thisArg);\r
1220         }\r
1221         if (function == null) throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
1222         return function.call(cx, scope, thisObj, args);\r
1223     }\r
1224 \r
1225     private static Object callOrNewSpecial(Context cx, Scriptable scope,\r
1226                                            Object fun, Object jsThis, \r
1227                                            Object thisArg,\r
1228                                            Object[] args, boolean isCall,\r
1229                                            String filename, int lineNumber)\r
1230         throws JavaScriptException\r
1231     {\r
1232         if (fun instanceof IdFunction) {\r
1233             IdFunction f = (IdFunction)fun;\r
1234             String name = f.getFunctionName();\r
1235             if (name.length() == 4) {\r
1236                 if (name.equals("eval")) {\r
1237                     if (f.master.getClass() == NativeGlobal.class) {\r
1238                         return NativeGlobal.evalSpecial(cx, scope, \r
1239                                                         thisArg, args,\r
1240                                                         filename, lineNumber);\r
1241                     }\r
1242                 }\r
1243                 else if (name.equals("With")) {\r
1244                     if (f.master.getClass() == NativeWith.class) {\r
1245                         return NativeWith.newWithSpecial(cx, args, f, !isCall);\r
1246                     }\r
1247                 }\r
1248                 else if (name.equals("exec")) {\r
1249                     if (f.master.getClass() == NativeScript.class) {\r
1250                         return ((NativeScript)jsThis).\r
1251                             exec(cx, ScriptableObject.getTopLevelScope(scope));\r
1252                     }\r
1253                     else {\r
1254                         RegExpProxy proxy = cx.getRegExpProxy();\r
1255                         if (proxy != null && proxy.isRegExp(jsThis)) {\r
1256                             return call(cx, fun, jsThis, args, scope);\r
1257                         }\r
1258                     }\r
1259                 }\r
1260             }\r
1261         }\r
1262         else    // could've been <java>.XXX.exec() that was re-directed here\r
1263             if (fun instanceof NativeJavaMethod)\r
1264                 return call(cx, fun, jsThis, args, scope);\r
1265 \r
1266         if (isCall)\r
1267             return call(cx, fun, thisArg, args, scope);\r
1268         return newObject(cx, fun, args, scope);\r
1269     }\r
1270 \r
1271     public static Object callSpecial(Context cx, Object fun,\r
1272                                      Object thisArg, Object[] args,\r
1273                                      Scriptable enclosingThisArg,\r
1274                                      Scriptable scope, String filename,\r
1275                                      int lineNumber)\r
1276         throws JavaScriptException\r
1277     {\r
1278         return callOrNewSpecial(cx, scope, fun, thisArg,\r
1279                                 enclosingThisArg, args, true,\r
1280                                 filename, lineNumber);\r
1281     }\r
1282 \r
1283     /**\r
1284      * Operator new.\r
1285      *\r
1286      * See ECMA 11.2.2\r
1287      */\r
1288     public static Scriptable newObject(Context cx, Object fun,\r
1289                                        Object[] args, Scriptable scope)\r
1290         throws JavaScriptException\r
1291     {\r
1292         Function f;\r
1293         try {\r
1294             f = (Function) fun;\r
1295             if (f != null) {\r
1296                 return f.construct(cx, scope, args);\r
1297            }\r
1298             // else fall through to error\r
1299         } catch (ClassCastException e) {\r
1300             // fall through to error\r
1301         }\r
1302         throw NativeGlobal.typeError1\r
1303             ("msg.isnt.function", toString(fun), scope);\r
1304     }\r
1305 \r
1306     public static Scriptable newObjectSpecial(Context cx, Object fun,\r
1307                                               Object[] args, Scriptable scope)\r
1308         throws JavaScriptException\r
1309     {\r
1310         return (Scriptable) callOrNewSpecial(cx, scope, fun, null, null, args,\r
1311                                              false, null, -1);\r
1312     }\r
1313 \r
1314     /**\r
1315      * The typeof operator\r
1316      */\r
1317     public static String typeof(Object value) {\r
1318         if (value == Undefined.instance)\r
1319             return "undefined";\r
1320         if (value == null)\r
1321             return "object";\r
1322         if (value instanceof Scriptable)\r
1323             return (value instanceof Function) ? "function" : "object";\r
1324         if (value instanceof String)\r
1325             return "string";\r
1326         if (value instanceof Number)\r
1327             return "number";\r
1328         if (value instanceof Boolean)\r
1329             return "boolean";\r
1330         throw errorWithClassName("msg.invalid.type", value);\r
1331     }\r
1332 \r
1333     /**\r
1334      * The typeof operator that correctly handles the undefined case\r
1335      */\r
1336     public static String typeofName(Scriptable scope, String id) {\r
1337         Object val = bind(scope, id);\r
1338         if (val == null)\r
1339             return "undefined";\r
1340         return typeof(getProp(val, id, scope));\r
1341     }\r
1342 \r
1343     // neg:\r
1344     // implement the '-' operator inline in the caller\r
1345     // as "-toNumber(val)"\r
1346 \r
1347     // not:\r
1348     // implement the '!' operator inline in the caller\r
1349     // as "!toBoolean(val)"\r
1350 \r
1351     // bitnot:\r
1352     // implement the '~' operator inline in the caller\r
1353     // as "~toInt32(val)"\r
1354 \r
1355     public static Object add(Object val1, Object val2) {\r
1356         if (val1 instanceof Scriptable)\r
1357             val1 = ((Scriptable) val1).getDefaultValue(null);\r
1358         if (val2 instanceof Scriptable)\r
1359             val2 = ((Scriptable) val2).getDefaultValue(null);\r
1360         if (!(val1 instanceof String) && !(val2 instanceof String))\r
1361             if ((val1 instanceof Number) && (val2 instanceof Number))\r
1362                 return new Double(((Number)val1).doubleValue() +\r
1363                                   ((Number)val2).doubleValue());\r
1364             else\r
1365                 return new Double(toNumber(val1) + toNumber(val2));\r
1366         return toString(val1) + toString(val2);\r
1367     }\r
1368 \r
1369     public static Object postIncrement(Object value) {\r
1370         if (value instanceof Number)\r
1371             value = new Double(((Number)value).doubleValue() + 1.0);\r
1372         else\r
1373             value = new Double(toNumber(value) + 1.0);\r
1374         return value;\r
1375     }\r
1376 \r
1377     public static Object postIncrement(Scriptable scopeChain, String id) {\r
1378         Scriptable obj = scopeChain;\r
1379         Object prop;\r
1380         while (obj != null) {\r
1381             Scriptable m = obj;\r
1382             do {\r
1383                 Object result = m.get(id, obj);\r
1384                 if (result != Scriptable.NOT_FOUND) {\r
1385                     Object newValue = result;\r
1386                     if (newValue instanceof Number) {\r
1387                         newValue = new Double(\r
1388                                     ((Number)newValue).doubleValue() + 1.0);\r
1389                         m.put(id, obj, newValue);\r
1390                         return result;\r
1391                     }\r
1392                     else {\r
1393                         newValue = new Double(toNumber(newValue) + 1.0);\r
1394                         m.put(id, obj, newValue);\r
1395                         return new Double(toNumber(result));\r
1396                     }\r
1397                 }\r
1398                 m = m.getPrototype();\r
1399             } while (m != null);\r
1400             obj = obj.getParentScope();\r
1401         }\r
1402         throw NativeGlobal.constructError\r
1403             (Context.getContext(), "ReferenceError",\r
1404              ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
1405                     scopeChain);\r
1406     }\r
1407 \r
1408     public static Object postIncrement(Object obj, String id, Scriptable scope) {\r
1409         Scriptable start;\r
1410         if (obj instanceof Scriptable) {\r
1411             start = (Scriptable) obj;\r
1412         } else {\r
1413             start = toObject(scope, obj);\r
1414         }\r
1415         if (start == null) {\r
1416             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
1417         }\r
1418         Scriptable m = start;\r
1419         do {\r
1420             Object result = m.get(id, start);\r
1421             if (result != Scriptable.NOT_FOUND) {\r
1422                 Object newValue = result;\r
1423                 if (newValue instanceof Number) {\r
1424                     newValue = new Double(\r
1425                                 ((Number)newValue).doubleValue() + 1.0);\r
1426                     m.put(id, start, newValue);\r
1427                     return result;\r
1428                 }\r
1429                 else {\r
1430                     newValue = new Double(toNumber(newValue) + 1.0);\r
1431                     m.put(id, start, newValue);\r
1432                     return new Double(toNumber(result));\r
1433                 }\r
1434             }\r
1435             m = m.getPrototype();\r
1436         } while (m != null);\r
1437         return Undefined.instance;\r
1438     }\r
1439 \r
1440     public static Object postIncrementElem(Object obj,\r
1441                                             Object index, Scriptable scope) {\r
1442         Object oldValue = getElem(obj, index, scope);\r
1443         if (oldValue == Undefined.instance)\r
1444             return Undefined.instance;\r
1445         double resultValue = toNumber(oldValue);\r
1446         Double newValue = new Double(resultValue + 1.0);\r
1447         setElem(obj, index, newValue, scope);\r
1448         return new Double(resultValue);\r
1449     }\r
1450 \r
1451     public static Object postDecrementElem(Object obj,\r
1452                                             Object index, Scriptable scope) {\r
1453         Object oldValue = getElem(obj, index, scope);\r
1454         if (oldValue == Undefined.instance)\r
1455             return Undefined.instance;\r
1456         double resultValue = toNumber(oldValue);\r
1457         Double newValue = new Double(resultValue - 1.0);\r
1458         setElem(obj, index, newValue, scope);\r
1459         return new Double(resultValue);\r
1460     }\r
1461 \r
1462     public static Object postDecrement(Object value) {\r
1463         if (value instanceof Number)\r
1464             value = new Double(((Number)value).doubleValue() - 1.0);\r
1465         else\r
1466             value = new Double(toNumber(value) - 1.0);\r
1467         return value;\r
1468     }\r
1469 \r
1470     public static Object postDecrement(Scriptable scopeChain, String id) {\r
1471         Scriptable obj = scopeChain;\r
1472         Object prop;\r
1473         while (obj != null) {\r
1474             Scriptable m = obj;\r
1475             do {\r
1476                 Object result = m.get(id, obj);\r
1477                 if (result != Scriptable.NOT_FOUND) {\r
1478                     Object newValue = result;\r
1479                     if (newValue instanceof Number) {\r
1480                         newValue = new Double(\r
1481                                     ((Number)newValue).doubleValue() - 1.0);\r
1482                         m.put(id, obj, newValue);\r
1483                         return result;\r
1484                     }\r
1485                     else {\r
1486                         newValue = new Double(toNumber(newValue) - 1.0);\r
1487                         m.put(id, obj, newValue);\r
1488                         return new Double(toNumber(result));\r
1489                     }\r
1490                 }\r
1491                 m = m.getPrototype();\r
1492             } while (m != null);\r
1493             obj = obj.getParentScope();\r
1494         }\r
1495         throw NativeGlobal.constructError\r
1496             (Context.getContext(), "ReferenceError",\r
1497              ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
1498                     scopeChain);\r
1499     }\r
1500 \r
1501     public static Object postDecrement(Object obj, String id, Scriptable scope) {\r
1502         Scriptable start;\r
1503         if (obj instanceof Scriptable) {\r
1504             start = (Scriptable) obj;\r
1505         } else {\r
1506             start = toObject(scope, obj);\r
1507         }\r
1508         if (start == null) {\r
1509             throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
1510         }\r
1511         Scriptable m = start;\r
1512         do {\r
1513             Object result = m.get(id, start);\r
1514             if (result != Scriptable.NOT_FOUND) {\r
1515                 Object newValue = result;\r
1516                 if (newValue instanceof Number) {\r
1517                     newValue = new Double(\r
1518                                 ((Number)newValue).doubleValue() - 1.0);\r
1519                     m.put(id, start, newValue);\r
1520                     return result;\r
1521                 }\r
1522                 else {\r
1523                     newValue = new Double(toNumber(newValue) - 1.0);\r
1524                     m.put(id, start, newValue);\r
1525                     return new Double(toNumber(result));\r
1526                 }\r
1527             }\r
1528             m = m.getPrototype();\r
1529         } while (m != null);\r
1530         return Undefined.instance;\r
1531     }\r
1532 \r
1533     public static Object toPrimitive(Object val) {\r
1534         if (val == null || !(val instanceof Scriptable)) {\r
1535             return val;\r
1536         }\r
1537         Object result = ((Scriptable) val).getDefaultValue(null);\r
1538         if (result != null && result instanceof Scriptable)\r
1539             throw NativeGlobal.typeError0("msg.bad.default.value", val);\r
1540         return result;\r
1541     }\r
1542 \r
1543     private static Class getTypeOfValue(Object obj) {\r
1544         if (obj == null)\r
1545             return ScriptableClass;\r
1546         if (obj == Undefined.instance)\r
1547             return UndefinedClass;\r
1548         if (obj instanceof Scriptable)\r
1549             return ScriptableClass;\r
1550         if (obj instanceof Number)\r
1551             return NumberClass;\r
1552         return obj.getClass();\r
1553     }\r
1554 \r
1555     /**\r
1556      * Equality\r
1557      *\r
1558      * See ECMA 11.9\r
1559      */\r
1560     public static boolean eq(Object x, Object y) {\r
1561         Object xCopy = x;                                       // !!! JIT bug in Cafe 2.1\r
1562         Object yCopy = y;                                       // need local copies, otherwise their values get blown below\r
1563         for (;;) {\r
1564             Class typeX = getTypeOfValue(x);\r
1565             Class typeY = getTypeOfValue(y);\r
1566             if (typeX == typeY) {\r
1567                 if (typeX == UndefinedClass)\r
1568                     return true;\r
1569                 if (typeX == NumberClass)\r
1570                     return ((Number) x).doubleValue() ==\r
1571                            ((Number) y).doubleValue();\r
1572                 if (typeX == StringClass || typeX == BooleanClass)\r
1573                     return xCopy.equals(yCopy);                                 // !!! JIT bug in Cafe 2.1\r
1574                 if (typeX == ScriptableClass) {\r
1575                     if (x == y)\r
1576                         return true;\r
1577                     if (x instanceof Wrapper &&\r
1578                         y instanceof Wrapper)\r
1579                     {\r
1580                         return ((Wrapper) x).unwrap() ==\r
1581                                ((Wrapper) y).unwrap();\r
1582                     }\r
1583                     return false;\r
1584                 }\r
1585                 throw new RuntimeException(); // shouldn't get here\r
1586             }\r
1587             if (x == null && y == Undefined.instance)\r
1588                 return true;\r
1589             if (x == Undefined.instance && y == null)\r
1590                 return true;\r
1591             if (typeX == NumberClass &&\r
1592                 typeY == StringClass)\r
1593             {\r
1594                 return ((Number) x).doubleValue() == toNumber(y);\r
1595             }\r
1596             if (typeX == StringClass &&\r
1597                 typeY == NumberClass)\r
1598             {\r
1599                 return toNumber(x) == ((Number) y).doubleValue();\r
1600             }\r
1601             if (typeX == BooleanClass) {\r
1602                 x = new Double(toNumber(x));\r
1603                 xCopy = x;                                 // !!! JIT bug in Cafe 2.1\r
1604                 continue;\r
1605             }\r
1606             if (typeY == BooleanClass) {\r
1607                 y = new Double(toNumber(y));\r
1608                 yCopy = y;                                 // !!! JIT bug in Cafe 2.1\r
1609                 continue;\r
1610             }\r
1611             if ((typeX == StringClass ||\r
1612                  typeX == NumberClass) &&\r
1613                 typeY == ScriptableClass && y != null)\r
1614             {\r
1615                 y = toPrimitive(y);\r
1616                 yCopy = y;                                 // !!! JIT bug in Cafe 2.1\r
1617                 continue;\r
1618             }\r
1619             if (typeX == ScriptableClass && x != null &&\r
1620                 (typeY == StringClass ||\r
1621                  typeY == NumberClass))\r
1622             {\r
1623                 x = toPrimitive(x);\r
1624                 xCopy = x;                                 // !!! JIT bug in Cafe 2.1\r
1625                 continue;\r
1626             }\r
1627             return false;\r
1628         }\r
1629     }\r
1630 \r
1631     public static Boolean eqB(Object x, Object y) {\r
1632         if (eq(x,y))\r
1633             return Boolean.TRUE;\r
1634         else\r
1635             return Boolean.FALSE;\r
1636     }\r
1637 \r
1638     public static Boolean neB(Object x, Object y) {\r
1639         if (eq(x,y))\r
1640             return Boolean.FALSE;\r
1641         else\r
1642             return Boolean.TRUE;\r
1643     }\r
1644 \r
1645     public static boolean shallowEq(Object x, Object y) {\r
1646         Class type = getTypeOfValue(x);\r
1647         if (type != getTypeOfValue(y))\r
1648             return false;\r
1649         if (type == StringClass || type == BooleanClass)\r
1650             return x.equals(y);\r
1651         if (type == NumberClass)\r
1652             return ((Number) x).doubleValue() ==\r
1653                    ((Number) y).doubleValue();\r
1654         if (type == ScriptableClass) {\r
1655             if (x == y)\r
1656                 return true;\r
1657             if (x instanceof Wrapper && y instanceof Wrapper)\r
1658                 return ((Wrapper) x).unwrap() ==\r
1659                        ((Wrapper) y).unwrap();\r
1660             return false;\r
1661         }\r
1662         if (type == UndefinedClass)\r
1663             return true;\r
1664         return false;\r
1665     }\r
1666 \r
1667     public static Boolean seqB(Object x, Object y) {\r
1668         if (shallowEq(x,y))\r
1669             return Boolean.TRUE;\r
1670         else\r
1671             return Boolean.FALSE;\r
1672     }\r
1673 \r
1674     public static Boolean sneB(Object x, Object y) {\r
1675         if (shallowEq(x,y))\r
1676             return Boolean.FALSE;\r
1677         else\r
1678             return Boolean.TRUE;\r
1679     }\r
1680 \r
1681     /**\r
1682      * The instanceof operator.\r
1683      *\r
1684      * @return a instanceof b\r
1685      */\r
1686     public static boolean instanceOf(Scriptable scope, Object a, Object b) {\r
1687         // Check RHS is an object\r
1688         if (! (b instanceof Scriptable)) {\r
1689             throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);\r
1690         }\r
1691 \r
1692         // for primitive values on LHS, return false\r
1693         // XXX we may want to change this so that\r
1694         // 5 instanceof Number == true\r
1695         if (! (a instanceof Scriptable))\r
1696             return false;\r
1697 \r
1698         return ((Scriptable)b).hasInstance((Scriptable)a);\r
1699     }\r
1700 \r
1701     /**\r
1702      * Delegates to\r
1703      *\r
1704      * @return true iff rhs appears in lhs' proto chain\r
1705      */\r
1706     protected static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {\r
1707         Scriptable proto = lhs.getPrototype();\r
1708 \r
1709         while (proto != null) {\r
1710             if (proto.equals(rhs)) return true;\r
1711             proto = proto.getPrototype();\r
1712         }\r
1713 \r
1714         return false;\r
1715     }\r
1716 \r
1717     /**\r
1718      * The in operator.\r
1719      *\r
1720      * This is a new JS 1.3 language feature.  The in operator mirrors\r
1721      * the operation of the for .. in construct, and tests whether the\r
1722      * rhs has the property given by the lhs.  It is different from the \r
1723      * for .. in construct in that:\r
1724      * <BR> - it doesn't perform ToObject on the right hand side\r
1725      * <BR> - it returns true for DontEnum properties.\r
1726      * @param a the left hand operand\r
1727      * @param b the right hand operand\r
1728      *\r
1729      * @return true if property name or element number a is a property of b\r
1730      */\r
1731     public static boolean in(Object a, Object b, Scriptable scope) {\r
1732         if (!(b instanceof Scriptable)) {\r
1733             throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);\r
1734         }\r
1735         String s = getStringId(a);\r
1736         return s != null\r
1737             ? ScriptableObject.hasProperty((Scriptable) b, s)\r
1738             : ScriptableObject.hasProperty((Scriptable) b, getIntId(a));\r
1739     }\r
1740 \r
1741     public static Boolean cmp_LTB(Object val1, Object val2) {\r
1742         if (cmp_LT(val1, val2) == 1)\r
1743             return Boolean.TRUE;\r
1744         else\r
1745             return Boolean.FALSE;\r
1746     }\r
1747 \r
1748     public static int cmp_LT(Object val1, Object val2) {\r
1749         if (val1 instanceof Scriptable)\r
1750             val1 = ((Scriptable) val1).getDefaultValue(NumberClass);\r
1751         if (val2 instanceof Scriptable)\r
1752             val2 = ((Scriptable) val2).getDefaultValue(NumberClass);\r
1753         if (!(val1 instanceof String) || !(val2 instanceof String)) {\r
1754             double d1 = toNumber(val1);\r
1755             if (d1 != d1)\r
1756                 return 0;\r
1757             double d2 = toNumber(val2);\r
1758             if (d2 != d2)\r
1759                 return 0;\r
1760             return d1 < d2 ? 1 : 0;\r
1761         }\r
1762         return toString(val1).compareTo(toString(val2)) < 0 ? 1 : 0;\r
1763     }\r
1764 \r
1765     public static Boolean cmp_LEB(Object val1, Object val2) {\r
1766         if (cmp_LE(val1, val2) == 1)\r
1767             return Boolean.TRUE;\r
1768         else\r
1769             return Boolean.FALSE;\r
1770     }\r
1771 \r
1772     public static int cmp_LE(Object val1, Object val2) {\r
1773         if (val1 instanceof Scriptable)\r
1774             val1 = ((Scriptable) val1).getDefaultValue(NumberClass);\r
1775         if (val2 instanceof Scriptable)\r
1776             val2 = ((Scriptable) val2).getDefaultValue(NumberClass);\r
1777         if (!(val1 instanceof String) || !(val2 instanceof String)) {\r
1778             double d1 = toNumber(val1);\r
1779             if (d1 != d1)\r
1780                 return 0;\r
1781             double d2 = toNumber(val2);\r
1782             if (d2 != d2)\r
1783                 return 0;\r
1784             return d1 <= d2 ? 1 : 0;\r
1785         }\r
1786         return toString(val1).compareTo(toString(val2)) <= 0 ? 1 : 0;\r
1787     }\r
1788 \r
1789     // lt:\r
1790     // implement the '<' operator inline in the caller\r
1791     // as "compare(val1, val2) == 1"\r
1792 \r
1793     // le:\r
1794     // implement the '<=' operator inline in the caller\r
1795     // as "compare(val2, val1) == 0"\r
1796 \r
1797     // gt:\r
1798     // implement the '>' operator inline in the caller\r
1799     // as "compare(val2, val1) == 1"\r
1800 \r
1801     // ge:\r
1802     // implement the '>=' operator inline in the caller\r
1803     // as "compare(val1, val2) == 0"\r
1804 \r
1805     // ------------------\r
1806     // Statements\r
1807     // ------------------\r
1808 \r
1809     private static final String GLOBAL_CLASS = \r
1810         "org.mozilla.javascript.tools.shell.Global";\r
1811 \r
1812     private static ScriptableObject getGlobal(Context cx) {\r
1813         try {\r
1814             Class globalClass = loadClassName(GLOBAL_CLASS);\r
1815             Class[] parm = { Context.class };\r
1816             Constructor globalClassCtor = globalClass.getConstructor(parm);\r
1817             Object[] arg = { cx };\r
1818             return (ScriptableObject) globalClassCtor.newInstance(arg);\r
1819         } catch (ClassNotFoundException e) {\r
1820             // fall through...\r
1821         } catch (NoSuchMethodException e) {\r
1822             // fall through...\r
1823         } catch (InvocationTargetException e) {\r
1824             // fall through...\r
1825         } catch (IllegalAccessException e) {\r
1826             // fall through...\r
1827         } catch (InstantiationException e) {\r
1828             // fall through...\r
1829         }\r
1830         return new ImporterTopLevel(cx);\r
1831     }\r
1832 \r
1833     public static void main(String scriptClassName, String[] args)\r
1834         throws JavaScriptException\r
1835     {\r
1836         Context cx = Context.enter();\r
1837         ScriptableObject global = getGlobal(cx);\r
1838 \r
1839         // get the command line arguments and define "arguments" \r
1840         // array in the top-level object\r
1841         Scriptable argsObj = cx.newArray(global, args);\r
1842         global.defineProperty("arguments", argsObj,\r
1843                               ScriptableObject.DONTENUM);\r
1844         \r
1845         try {\r
1846             Class cl = loadClassName(scriptClassName);\r
1847             Script script = (Script) cl.newInstance();\r
1848             script.exec(cx, global);\r
1849             return;\r
1850         }\r
1851         catch (ClassNotFoundException e) {\r
1852         }\r
1853         catch (InstantiationException e) {\r
1854         }\r
1855         catch (IllegalAccessException e) {\r
1856         }\r
1857         finally {\r
1858             Context.exit();\r
1859         }\r
1860         throw new RuntimeException("Error creating script object");\r
1861     }\r
1862 \r
1863     public static Scriptable initScript(Context cx, Scriptable scope,\r
1864                                         NativeFunction funObj,\r
1865                                         Scriptable thisObj,\r
1866                                         boolean fromEvalCode)\r
1867     {\r
1868         String[] argNames = funObj.argNames;\r
1869         if (argNames != null) {\r
1870             ScriptableObject so;\r
1871             try {\r
1872                 /* Global var definitions are supposed to be DONTDELETE\r
1873                  * so we try to create them that way by hoping that the\r
1874                  * scope is a ScriptableObject which provides access to\r
1875                  * setting the attributes.\r
1876                  */\r
1877                 so = (ScriptableObject) scope;\r
1878             } catch (ClassCastException x) {\r
1879                 // oh well, we tried.\r
1880                 so = null;\r
1881             }\r
1882 \r
1883             Scriptable varScope = scope;\r
1884             if (fromEvalCode) {\r
1885                 // When executing an eval() inside a with statement,\r
1886                 // define any variables resulting from var statements\r
1887                 // in the first non-with scope. See bug 38590.\r
1888                 varScope = scope;\r
1889                 while (varScope instanceof NativeWith)\r
1890                     varScope = varScope.getParentScope();\r
1891             }\r
1892             for (int i = argNames.length; i-- != 0;) {\r
1893                 String name = argNames[i];\r
1894                 // Don't overwrite existing def if already defined in object\r
1895                 // or prototypes of object.\r
1896                 if (!hasProp(scope, name)) {\r
1897                     if (so != null && !fromEvalCode)\r
1898                         so.defineProperty(name, Undefined.instance,\r
1899                                           ScriptableObject.PERMANENT);\r
1900                     else \r
1901                         varScope.put(name, varScope, Undefined.instance);\r
1902                 }\r
1903             }\r
1904         }\r
1905 \r
1906         return scope;\r
1907     }\r
1908 \r
1909     public static Scriptable runScript(Script script) {\r
1910         Context cx = Context.enter();\r
1911         ScriptableObject global = getGlobal(cx);\r
1912         try {\r
1913             script.exec(cx, global);\r
1914         } catch (JavaScriptException e) {\r
1915             throw new Error(e.toString());\r
1916         } finally {\r
1917             Context.exit();\r
1918         }\r
1919         return global;\r
1920     }\r
1921 \r
1922     public static Scriptable initVarObj(Context cx, Scriptable scope,\r
1923                                         NativeFunction funObj,\r
1924                                         Scriptable thisObj, Object[] args)\r
1925     {\r
1926         NativeCall result = new NativeCall(cx, scope, funObj, thisObj, args);\r
1927         String[] argNames = funObj.argNames;\r
1928         if (argNames != null) {\r
1929             for (int i = funObj.argCount; i != argNames.length; i++) {\r
1930                 String name = argNames[i];\r
1931                 result.put(name, result, Undefined.instance);\r
1932             }\r
1933         }\r
1934         return result;\r
1935     }\r
1936 \r
1937     public static void popActivation(Context cx) {\r
1938         NativeCall current = cx.currentActivation;\r
1939         if (current != null) {\r
1940             cx.currentActivation = current.caller;\r
1941             current.caller = null;\r
1942         }\r
1943     }\r
1944 \r
1945     public static Scriptable newScope() {\r
1946         return new NativeObject();\r
1947     }\r
1948 \r
1949     public static Scriptable enterWith(Object value, Scriptable scope) {\r
1950         return new NativeWith(scope, toObject(scope, value));\r
1951     }\r
1952 \r
1953     public static Scriptable leaveWith(Scriptable scope) {\r
1954         return scope.getParentScope();\r
1955     }\r
1956 \r
1957     public static NativeFunction initFunction(NativeFunction fn,\r
1958                                               Scriptable scope,\r
1959                                               String fnName,\r
1960                                               Context cx,\r
1961                                               boolean doSetName)\r
1962     {\r
1963         fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
1964         fn.setParentScope(scope);\r
1965         if (doSetName)\r
1966             setName(scope, fn, scope, fnName);\r
1967         return fn;\r
1968     }\r
1969 \r
1970     public static NativeFunction createFunctionObject(Scriptable scope,\r
1971                                                       Class functionClass,\r
1972                                                       Context cx,\r
1973                                                       boolean setName)\r
1974     {\r
1975         Constructor[] ctors = functionClass.getConstructors();\r
1976 \r
1977         NativeFunction result = null;\r
1978         Object[] initArgs = { scope, cx };\r
1979         try {\r
1980             result = (NativeFunction) ctors[0].newInstance(initArgs);\r
1981         }\r
1982         catch (InstantiationException e) {\r
1983             throw WrappedException.wrapException(e);\r
1984         }\r
1985         catch (IllegalAccessException e) {\r
1986             throw WrappedException.wrapException(e);\r
1987         }\r
1988         catch (IllegalArgumentException e) {\r
1989             throw WrappedException.wrapException(e);\r
1990         }\r
1991         catch (InvocationTargetException e) {\r
1992             throw WrappedException.wrapException(e);\r
1993         }\r
1994 \r
1995         result.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
1996         result.setParentScope(scope);\r
1997 \r
1998         String fnName = result.getFunctionName();\r
1999         if (setName && fnName != null && fnName.length() != 0 && \r
2000             !fnName.equals("anonymous"))\r
2001         {\r
2002             setProp(scope, fnName, result, scope);\r
2003         }\r
2004 \r
2005         return result;\r
2006     }\r
2007 \r
2008     static void checkDeprecated(Context cx, String name) {\r
2009         int version = cx.getLanguageVersion();\r
2010         if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {\r
2011             String msg = getMessage1("msg.deprec.ctor", name);\r
2012             if (version == Context.VERSION_DEFAULT)\r
2013                 Context.reportWarning(msg);\r
2014             else\r
2015                 throw Context.reportRuntimeError(msg);\r
2016         }\r
2017     }\r
2018 \r
2019     public static String getMessage0(String messageId) {\r
2020         return Context.getMessage0(messageId);\r
2021     }\r
2022 \r
2023     public static String getMessage1(String messageId, Object arg1) {\r
2024         return Context.getMessage1(messageId, arg1);\r
2025     }\r
2026 \r
2027     public static String getMessage2\r
2028         (String messageId, Object arg1, Object arg2) \r
2029     {\r
2030         return Context.getMessage2(messageId, arg1, arg2);\r
2031     }\r
2032 \r
2033     public static String getMessage(String messageId, Object[] arguments) {\r
2034         return Context.getMessage(messageId, arguments);\r
2035     }\r
2036 \r
2037     public static RegExpProxy getRegExpProxy(Context cx) {\r
2038         return cx.getRegExpProxy();\r
2039     }\r
2040 \r
2041     public static NativeCall getCurrentActivation(Context cx) {\r
2042         return cx.currentActivation;\r
2043     }\r
2044 \r
2045     public static void setCurrentActivation(Context cx,\r
2046                                             NativeCall activation)\r
2047     {\r
2048         cx.currentActivation = activation;\r
2049     }\r
2050 \r
2051     public static Class loadClassName(String className) \r
2052         throws ClassNotFoundException\r
2053     {\r
2054         /*\r
2055         try {\r
2056             ClassLoader cl = DefiningClassLoader.getContextClassLoader();\r
2057             if (cl != null)\r
2058                 return cl.loadClass(className);\r
2059         } catch (SecurityException e) {\r
2060             // fall through...\r
2061         } catch (ClassNotFoundException e) {\r
2062             // Rather than just letting the exception propagate\r
2063             // we'll try Class.forName as well. The results could be\r
2064             // different if this class was loaded on a different\r
2065             // thread than the current thread.\r
2066             // So fall through...\r
2067         }\r
2068         */\r
2069         return Class.forName(className);                \r
2070     }\r
2071 \r
2072     static boolean hasProp(Scriptable start, String name) {\r
2073         Scriptable m = start;\r
2074         do {\r
2075             if (m.has(name, start))\r
2076                 return true;\r
2077             m = m.getPrototype();\r
2078         } while (m != null);\r
2079         return false;\r
2080     }\r
2081 \r
2082     private static RuntimeException errorWithClassName(String msg, Object val)\r
2083     {\r
2084         return Context.reportRuntimeError1(msg, val.getClass().getName());\r
2085     }\r
2086 \r
2087     public static final Object[] emptyArgs = new Object[0];\r
2088 \r
2089 }\r
2090 \r
2091 \r
2092 /**\r
2093  * This is the enumeration needed by the for..in statement.\r
2094  *\r
2095  * See ECMA 12.6.3.\r
2096  *\r
2097  * IdEnumeration maintains a Hashtable to make sure a given\r
2098  * id is enumerated only once across multiple objects in a\r
2099  * prototype chain.\r
2100  *\r
2101  * XXX - ECMA delete doesn't hide properties in the prototype,\r
2102  * but js/ref does. This means that the js/ref for..in can\r
2103  * avoid maintaining a hash table and instead perform lookups\r
2104  * to see if a given property has already been enumerated.\r
2105  *\r
2106  */\r
2107 class IdEnumeration implements Enumeration {\r
2108     IdEnumeration(Scriptable m) {\r
2109         used = new Hashtable(27);\r
2110         changeObject(m);\r
2111         next = getNext();\r
2112     }\r
2113 \r
2114     public boolean hasMoreElements() {\r
2115         return next != null;\r
2116     }\r
2117 \r
2118     public Object nextElement() {\r
2119         Object result = next;\r
2120 \r
2121         // only key used; 'next' as value for convenience\r
2122         used.put(next, next);\r
2123 \r
2124         next = getNext();\r
2125         return result;\r
2126     }\r
2127 \r
2128     private void changeObject(Scriptable m) {\r
2129         obj = m;\r
2130         if (obj != null) {\r
2131             array = m.getIds();\r
2132             if (array.length == 0)\r
2133                 changeObject(obj.getPrototype());\r
2134         }\r
2135         index = 0;\r
2136     }\r
2137 \r
2138     private Object getNext() {\r
2139         if (obj == null)\r
2140             return null;\r
2141         Object result;\r
2142         for (;;) {\r
2143             if (index == array.length) {\r
2144                 changeObject(obj.getPrototype());\r
2145                 if (obj == null)\r
2146                     return null;\r
2147             }\r
2148             result = array[index++];\r
2149             if (result instanceof String) {\r
2150                 if (!obj.has((String) result, obj))\r
2151                     continue;   // must have been deleted\r
2152             } else {\r
2153                 if (!obj.has(((Number) result).intValue(), obj))\r
2154                     continue;   // must have been deleted\r
2155             }\r
2156             if (!used.containsKey(result)) {\r
2157                 break;\r
2158             }\r
2159         }\r
2160         return ScriptRuntime.toString(result);\r
2161     }\r
2162 \r
2163     private Object next;\r
2164     private Scriptable obj;\r
2165     private int index;\r
2166     private Object[] array;\r
2167     private Hashtable used;\r
2168 }\r