1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
\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
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
13 * The Original Code is Rhino code, released
\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
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
41 package org.mozilla.javascript;
\r
44 import java.lang.reflect.*;
\r
45 //import org.mozilla.classfile.DefiningClassLoader;
\r
48 * This is the class that implements the runtime.
\r
50 * @author Norris Boyd
\r
53 public class ScriptRuntime {
\r
56 * No instances should be created.
\r
58 protected ScriptRuntime() {
\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
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
88 * Convert the value to a boolean.
\r
92 public static boolean toBoolean(Object val) {
\r
95 if (val instanceof Scriptable) {
\r
96 if (Context.getContext().isVersionECMA1()) {
\r
98 return val != Undefined.instance;
\r
101 val = ((Scriptable) val).getDefaultValue(BooleanClass);
\r
102 if (val instanceof Scriptable)
\r
103 throw errorWithClassName("msg.primitive.expected", val);
\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
112 if (val instanceof Boolean)
\r
113 return ((Boolean) val).booleanValue();
\r
114 throw errorWithClassName("msg.invalid.type", val);
\r
117 public static boolean toBoolean(Object[] args, int index) {
\r
118 return (index < args.length) ? toBoolean(args[index]) : false;
\r
121 * Convert the value to a number.
\r
125 public static double toNumber(Object val) {
\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
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
143 public static double toNumber(Object[] args, int index) {
\r
144 return (index < args.length) ? toNumber(args[index]) : NaN;
\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
155 // A similar problem exists for negative zero.
\r
156 public static double negativeZero = -0.0;
\r
159 * Helper function for toNumber, parseInt, and TokenStream.getToken.
\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
167 digitMax = (char) ('0' + radix - 1);
\r
170 lowerCaseBound = (char) ('a' + radix - 10);
\r
171 upperCaseBound = (char) ('A' + radix - 10);
\r
175 for (end=start; end < len; end++) {
\r
176 char c = s.charAt(end);
\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
186 sum = sum*radix + newDigit;
\r
188 if (start == end) {
\r
191 if (sum >= 9007199254740992.0) {
\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
199 return Double.valueOf(s.substring(start, end)).doubleValue();
\r
200 } catch (NumberFormatException nfe) {
\r
203 } else if (radix == 2 || radix == 4 || radix == 8 ||
\r
204 radix == 16 || radix == 32)
\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
216 BinaryDigitReader bdr = new BinaryDigitReader(radix, s, start, end);
\r
220 /* Skip leading zeros. */
\r
222 bit = bdr.getNextBinaryDigit();
\r
223 } while (bit == 0);
\r
226 /* Gather the 53 significant bits (including the leading 1) */
\r
228 for (int j = 52; j != 0; j--) {
\r
229 bit = bdr.getNextBinaryDigit();
\r
234 /* bit54 is the 54th bit (the first dropped from the mantissa) */
\r
235 int bit54 = bdr.getNextBinaryDigit();
\r
237 double factor = 2.0;
\r
238 int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
\r
241 while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {
\r
245 sum += bit54 & (bit | sticky);
\r
250 /* We don't worry about inaccurate numbers for any other base. */
\r
257 * ToNumber applied to the String type
\r
261 public static double toNumber(String s) {
\r
262 int len = s.length();
\r
266 if (start == len) {
\r
267 // Empty or contains only whitespace
\r
270 startChar = s.charAt(start);
\r
271 if (!Character.isWhitespace(startChar))
\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
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
291 while (Character.isWhitespace(endChar = s.charAt(end)))
\r
293 if (endChar == 'y') {
\r
294 // check for "Infinity"
\r
295 if (startChar == '+' || startChar == '-')
\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
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
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
320 return Double.valueOf(sub).doubleValue();
\r
321 } catch (NumberFormatException ex) {
\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
332 public static Object[] padArguments(Object[] args, int count) {
\r
333 if (count < args.length)
\r
337 Object[] result = new Object[count];
\r
338 for (i = 0; i < args.length; i++) {
\r
339 result[i] = args[i];
\r
342 for (; i < count; i++) {
\r
343 result[i] = Undefined.instance;
\r
349 /* Work around Microsoft Java VM bugs. */
\r
350 private final static boolean MSJVM_BUG_WORKAROUNDS = true;
\r
353 * For escaping strings printed by object and array literals; not quite
\r
354 * the same as 'escape.'
\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
361 for(int i=0; i < s.length(); i++) {
\r
362 char c = s.charAt(i);
\r
364 // an ordinary print character
\r
365 if (c >= ' ' && c <= '~' // string.h isprint()
\r
372 // an \escaped sort of character
\r
374 if ((index = escapeMap.indexOf(c)) >= 0) {
\r
375 result.append("\\");
\r
376 result.append(escapeMap.charAt(index + 1));
\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
387 result.append("\\x");
\r
388 result.append(hex);
\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
402 return result.toString();
\r
407 * Convert the value to a string.
\r
411 public static String toString(Object val) {
\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
422 if (val instanceof Number) {
\r
423 // XXX should we just teach NativeNumber.stringValue()
\r
425 return numberToString(((Number) val).doubleValue(), 10);
\r
427 return val.toString();
\r
431 public static String toString(Object[] args, int index) {
\r
432 return (index < args.length) ? toString(args[index]) : "undefined";
\r
436 * Optimized version of toString(Object) for numbers.
\r
438 public static String toString(double val) {
\r
439 return numberToString(val, 10);
\r
442 public static String numberToString(double d, int base) {
\r
445 if (d == Double.POSITIVE_INFINITY)
\r
447 if (d == Double.NEGATIVE_INFINITY)
\r
448 return "-Infinity";
\r
452 if ((base < 2) || (base > 36)) {
\r
453 throw Context.reportRuntimeError1(
\r
454 "msg.bad.radix", Integer.toString(base));
\r
458 return DToA.JS_dtobasestr(base, d);
\r
460 StringBuffer result = new StringBuffer();
\r
461 DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
\r
462 return result.toString();
\r
468 * Convert the value to an object.
\r
472 public static Scriptable toObject(Scriptable scope, Object val) {
\r
473 return toObject(scope, val, null);
\r
476 public static Scriptable toObject(Scriptable scope, Object val,
\r
480 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
482 if (val instanceof Scriptable) {
\r
483 if (val == Undefined.instance) {
\r
484 throw NativeGlobal.typeError0("msg.undef.to.object", scope);
\r
486 return (Scriptable) val;
\r
488 String className = val instanceof String ? "String" :
\r
489 val instanceof Number ? "Number" :
\r
490 val instanceof Boolean ? "Boolean" :
\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
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
507 public static Scriptable newObject(Context cx, Scriptable scope,
\r
508 String constructorName, Object[] args)
\r
510 Exception re = null;
\r
512 return cx.newObject(scope, constructorName, args);
\r
514 catch (NotAFunctionException e) {
\r
517 catch (PropertyException e) {
\r
520 catch (JavaScriptException e) {
\r
523 throw cx.reportRuntimeError(re.getMessage());
\r
530 public static double toInteger(Object val) {
\r
531 return toInteger(toNumber(val));
\r
534 // convenience method
\r
535 public static double toInteger(double d) {
\r
541 d == Double.POSITIVE_INFINITY ||
\r
542 d == Double.NEGATIVE_INFINITY)
\r
546 return Math.floor(d);
\r
548 return Math.ceil(d);
\r
551 public static double toInteger(Object[] args, int index) {
\r
552 return (index < args.length) ? toInteger(args[index]) : +0.0;
\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
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
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
575 d = Math.IEEEremainder(d, two32);
\r
582 return (int)(d - two32);
\r
587 public static int toInt32(Object[] args, int index) {
\r
588 return (index < args.length) ? toInt32(args[index]) : 0;
\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
596 if (d != d || d == 0.0 ||
\r
597 d == Double.POSITIVE_INFINITY ||
\r
598 d == Double.NEGATIVE_INFINITY)
\r
601 d = Math.IEEEremainder(d, two32);
\r
608 return (int)(d - two32);
\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
623 if (d != d || d == 0.0 ||
\r
624 d == Double.POSITIVE_INFINITY ||
\r
625 d == Double.NEGATIVE_INFINITY)
\r
633 d = Math.IEEEremainder(d, two32);
\r
639 return (long) Math.floor(d);
\r
642 public static long toUint32(Object val) {
\r
643 return toUint32(toNumber(val));
\r
650 public static char toUint16(Object val) {
\r
651 long int16 = 0x10000;
\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
661 d = Math.IEEEremainder(d, int16);
\r
667 return (char) Math.floor(d);
\r
671 * Unwrap a JavaScriptException. Sleight of hand so that we don't
\r
672 * javadoc JavaScriptException.getRuntimeValue().
\r
674 public static Object unwrapJavaScriptException(JavaScriptException jse) {
\r
679 * Check a WrappedException. Unwrap a JavaScriptException and return
\r
680 * the value, otherwise rethrow.
\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
689 public static Object getProp(Object obj, String id, Scriptable scope) {
\r
691 if (obj instanceof Scriptable) {
\r
692 start = (Scriptable) obj;
\r
694 start = toObject(scope, obj);
\r
696 if (start == null || start == Undefined.instance) {
\r
697 String msg = start == null ? "msg.null.to.object"
\r
699 throw NativeGlobal.constructError(
\r
700 Context.getContext(), "ConversionError",
\r
701 ScriptRuntime.getMessage0(msg),
\r
704 Scriptable m = start;
\r
706 Object result = m.get(id, start);
\r
707 if (result != Scriptable.NOT_FOUND)
\r
709 m = m.getPrototype();
\r
710 } while (m != null);
\r
711 return Undefined.instance;
\r
714 public static Object getTopLevelProp(Scriptable scope, String id) {
\r
715 Scriptable s = ScriptableObject.getTopLevelScope(scope);
\r
719 if (v != Scriptable.NOT_FOUND)
\r
721 s = s.getPrototype();
\r
722 } while (s != null);
\r
728 /***********************************************************************/
\r
730 public static Scriptable getProto(Object obj, Scriptable scope) {
\r
732 if (obj instanceof Scriptable) {
\r
733 s = (Scriptable) obj;
\r
735 s = toObject(scope, obj);
\r
738 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
740 return s.getPrototype();
\r
743 public static Scriptable getParent(Object obj) {
\r
746 s = (Scriptable) obj;
\r
748 catch (ClassCastException e) {
\r
754 return getThis(s.getParentScope());
\r
757 public static Scriptable getParent(Object obj, Scriptable scope) {
\r
759 if (obj instanceof Scriptable) {
\r
760 s = (Scriptable) obj;
\r
762 s = toObject(scope, obj);
\r
765 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
767 return s.getParentScope();
\r
770 public static Object setProto(Object obj, Object value, Scriptable scope) {
\r
772 if (obj instanceof Scriptable) {
\r
773 start = (Scriptable) obj;
\r
775 start = toObject(scope, obj);
\r
777 Scriptable result = value == null ? null : toObject(scope, value);
\r
778 Scriptable s = result;
\r
779 while (s != null) {
\r
781 throw Context.reportRuntimeError1(
\r
782 "msg.cyclic.value", "__proto__");
\r
784 s = s.getPrototype();
\r
786 if (start == null) {
\r
787 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
789 start.setPrototype(result);
\r
793 public static Object setParent(Object obj, Object value, Scriptable scope) {
\r
795 if (obj instanceof Scriptable) {
\r
796 start = (Scriptable) obj;
\r
798 start = toObject(scope, obj);
\r
800 Scriptable result = value == null ? null : toObject(scope, value);
\r
801 Scriptable s = result;
\r
802 while (s != null) {
\r
804 throw Context.reportRuntimeError1(
\r
805 "msg.cyclic.value", "__parent__");
\r
807 s = s.getParentScope();
\r
809 if (start == null) {
\r
810 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
812 start.setParentScope(result);
\r
816 public static Object setProp(Object obj, String id, Object value,
\r
820 if (obj instanceof Scriptable) {
\r
821 start = (Scriptable) obj;
\r
823 start = toObject(scope, obj);
\r
825 if (start == null) {
\r
826 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
828 Scriptable m = start;
\r
830 if (m.has(id, start)) {
\r
831 m.put(id, start, value);
\r
834 m = m.getPrototype();
\r
835 } while (m != null);
\r
837 start.put(id, start, value);
\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
846 // The length of the decimal string representation of
\r
847 // Integer.MAX_VALUE, 2147483647
\r
848 final int MAX_VALUE_LENGTH = 10;
\r
850 int len = str.length();
\r
853 boolean negate = false;
\r
854 int c = str.charAt(0);
\r
857 c = str.charAt(1);
\r
863 if (0 <= c && c <= 9
\r
864 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))
\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
873 // Note that 00, 01, 000 etc. are not indexes
\r
874 while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)
\r
877 index = 10 * index - c;
\r
881 // Make sure all characters were consumed and that it couldn't
\r
882 // have overflowed.
\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
889 return 0xFFFFFFFFL & (negate ? index : -index);
\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
902 return toString(id);
\r
904 String s = toString(id);
\r
905 long indexTest = indexFromString(s);
\r
906 if (indexTest >= 0)
\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
919 String s = toString(id);
\r
920 long indexTest = indexFromString(s);
\r
921 if (indexTest >= 0)
\r
922 return (int)indexTest;
\r
926 public static Object getElem(Object obj, Object id, Scriptable scope) {
\r
929 if (id instanceof Number) {
\r
930 double d = ((Number) id).doubleValue();
\r
932 s = ((double) index) == d ? null : toString(id);
\r
935 long indexTest = indexFromString(s);
\r
936 if (indexTest >= 0) {
\r
937 index = (int)indexTest;
\r
943 Scriptable start = obj instanceof Scriptable
\r
945 : toObject(scope, obj);
\r
946 Scriptable m = start;
\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
956 m = m.getPrototype();
\r
958 return Undefined.instance;
\r
960 while (m != null) {
\r
961 Object result = m.get(index, start);
\r
962 if (result != Scriptable.NOT_FOUND)
\r
964 m = m.getPrototype();
\r
966 return Undefined.instance;
\r
971 * A cheaper and less general version of the above for well-known argument
\r
974 public static Object getElem(Scriptable obj, int index)
\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
981 m = m.getPrototype();
\r
983 return Undefined.instance;
\r
986 public static Object setElem(Object obj, Object id, Object value,
\r
991 if (id instanceof Number) {
\r
992 double d = ((Number) id).doubleValue();
\r
994 s = ((double) index) == d ? null : toString(id);
\r
997 long indexTest = indexFromString(s);
\r
998 if (indexTest >= 0) {
\r
999 index = (int)indexTest;
\r
1006 Scriptable start = obj instanceof Scriptable
\r
1007 ? (Scriptable) obj
\r
1008 : toObject(scope, obj);
\r
1009 Scriptable m = start;
\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
1017 if (m.has(s, start)) {
\r
1018 m.put(s, start, value);
\r
1021 m = m.getPrototype();
\r
1022 } while (m != null);
\r
1023 start.put(s, start, value);
\r
1028 if (m.has(index, start)) {
\r
1029 m.put(index, start, value);
\r
1032 m = m.getPrototype();
\r
1033 } while (m != null);
\r
1034 start.put(index, start, value);
\r
1039 * A cheaper and less general version of the above for well-known argument
\r
1042 public static Object setElem(Scriptable obj, int index, Object value)
\r
1044 Scriptable m = obj;
\r
1046 if (m.has(index, obj)) {
\r
1047 m.put(index, obj, value);
\r
1050 m = m.getPrototype();
\r
1051 } while (m != null);
\r
1052 obj.put(index, obj, value);
\r
1057 * The delete operator
\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
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
1076 * Looks up a name in the scope chain and returns its value.
\r
1078 public static Object name(Scriptable scopeChain, String id) {
\r
1079 Scriptable obj = scopeChain;
\r
1081 while (obj != null) {
\r
1082 Scriptable m = obj;
\r
1084 Object result = m.get(id, obj);
\r
1085 if (result != Scriptable.NOT_FOUND)
\r
1087 m = m.getPrototype();
\r
1088 } while (m != null);
\r
1089 obj = obj.getParentScope();
\r
1091 throw NativeGlobal.constructError
\r
1092 (Context.getContext(), "ReferenceError",
\r
1093 ScriptRuntime.getMessage1("msg.is.not.defined", id.toString()),
\r
1098 * Returns the object in the scope chain that has a given property.
\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
1110 public static Scriptable bind(Scriptable scope, String id) {
\r
1111 Scriptable obj = scope;
\r
1113 while (obj != null) {
\r
1114 Scriptable m = obj;
\r
1116 if (m.has(id, obj))
\r
1118 m = m.getPrototype();
\r
1119 } while (m != null);
\r
1120 obj = obj.getParentScope();
\r
1125 public static Scriptable getBase(Scriptable scope, String id) {
\r
1126 Scriptable obj = scope;
\r
1128 while (obj != null) {
\r
1129 Scriptable m = obj;
\r
1131 if (m.get(id, obj) != Scriptable.NOT_FOUND)
\r
1133 m = m.getPrototype();
\r
1134 } while (m != null);
\r
1135 obj = obj.getParentScope();
\r
1137 throw NativeGlobal.constructError(
\r
1138 Context.getContext(), "ReferenceError",
\r
1139 ScriptRuntime.getMessage1("msg.is.not.defined", id),
\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
1151 public static Object setName(Scriptable bound, Object value,
\r
1152 Scriptable scope, String id)
\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
1162 next = bound.getParentScope();
\r
1163 } while (next != null);
\r
1165 bound.put(id, bound, value);
\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
1175 return setProp(bound, id, value, scope);
\r
1178 public static Enumeration initEnum(Object value, Scriptable scope) {
\r
1179 Scriptable m = toObject(scope, value);
\r
1180 return new IdEnumeration(m);
\r
1183 public static Object nextEnum(Enumeration enum) {
\r
1184 // OPT this could be more efficient; should junk the Enumeration
\r
1186 if (!enum.hasMoreElements())
\r
1188 return enum.nextElement();
\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
1194 throws JavaScriptException
\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
1202 public static Object call(Context cx, Object fun, Object thisArg,
\r
1203 Object[] args, Scriptable scope)
\r
1204 throws JavaScriptException
\r
1206 Function function;
\r
1208 function = (Function) fun;
\r
1210 catch (ClassCastException e) {
\r
1211 throw NativeGlobal.typeError1
\r
1212 ("msg.isnt.function", toString(fun), scope);
\r
1215 Scriptable thisObj;
\r
1216 if (thisArg instanceof Scriptable || thisArg == null) {
\r
1217 thisObj = (Scriptable) thisArg;
\r
1219 thisObj = ScriptRuntime.toObject(scope, thisArg);
\r
1221 if (function == null) throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
1222 return function.call(cx, scope, thisObj, args);
\r
1225 private static Object callOrNewSpecial(Context cx, Scriptable scope,
\r
1226 Object fun, Object jsThis,
\r
1228 Object[] args, boolean isCall,
\r
1229 String filename, int lineNumber)
\r
1230 throws JavaScriptException
\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
1240 filename, lineNumber);
\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
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
1254 RegExpProxy proxy = cx.getRegExpProxy();
\r
1255 if (proxy != null && proxy.isRegExp(jsThis)) {
\r
1256 return call(cx, fun, jsThis, args, scope);
\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
1267 return call(cx, fun, thisArg, args, scope);
\r
1268 return newObject(cx, fun, args, scope);
\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
1276 throws JavaScriptException
\r
1278 return callOrNewSpecial(cx, scope, fun, thisArg,
\r
1279 enclosingThisArg, args, true,
\r
1280 filename, lineNumber);
\r
1288 public static Scriptable newObject(Context cx, Object fun,
\r
1289 Object[] args, Scriptable scope)
\r
1290 throws JavaScriptException
\r
1294 f = (Function) fun;
\r
1296 return f.construct(cx, scope, args);
\r
1298 // else fall through to error
\r
1299 } catch (ClassCastException e) {
\r
1300 // fall through to error
\r
1302 throw NativeGlobal.typeError1
\r
1303 ("msg.isnt.function", toString(fun), scope);
\r
1306 public static Scriptable newObjectSpecial(Context cx, Object fun,
\r
1307 Object[] args, Scriptable scope)
\r
1308 throws JavaScriptException
\r
1310 return (Scriptable) callOrNewSpecial(cx, scope, fun, null, null, args,
\r
1315 * The typeof operator
\r
1317 public static String typeof(Object value) {
\r
1318 if (value == Undefined.instance)
\r
1319 return "undefined";
\r
1320 if (value == null)
\r
1322 if (value instanceof Scriptable)
\r
1323 return (value instanceof Function) ? "function" : "object";
\r
1324 if (value instanceof String)
\r
1326 if (value instanceof Number)
\r
1328 if (value instanceof Boolean)
\r
1330 throw errorWithClassName("msg.invalid.type", value);
\r
1334 * The typeof operator that correctly handles the undefined case
\r
1336 public static String typeofName(Scriptable scope, String id) {
\r
1337 Object val = bind(scope, id);
\r
1339 return "undefined";
\r
1340 return typeof(getProp(val, id, scope));
\r
1344 // implement the '-' operator inline in the caller
\r
1345 // as "-toNumber(val)"
\r
1348 // implement the '!' operator inline in the caller
\r
1349 // as "!toBoolean(val)"
\r
1352 // implement the '~' operator inline in the caller
\r
1353 // as "~toInt32(val)"
\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
1365 return new Double(toNumber(val1) + toNumber(val2));
\r
1366 return toString(val1) + toString(val2);
\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
1373 value = new Double(toNumber(value) + 1.0);
\r
1377 public static Object postIncrement(Scriptable scopeChain, String id) {
\r
1378 Scriptable obj = scopeChain;
\r
1380 while (obj != null) {
\r
1381 Scriptable m = obj;
\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
1393 newValue = new Double(toNumber(newValue) + 1.0);
\r
1394 m.put(id, obj, newValue);
\r
1395 return new Double(toNumber(result));
\r
1398 m = m.getPrototype();
\r
1399 } while (m != null);
\r
1400 obj = obj.getParentScope();
\r
1402 throw NativeGlobal.constructError
\r
1403 (Context.getContext(), "ReferenceError",
\r
1404 ScriptRuntime.getMessage1("msg.is.not.defined", id),
\r
1408 public static Object postIncrement(Object obj, String id, Scriptable scope) {
\r
1410 if (obj instanceof Scriptable) {
\r
1411 start = (Scriptable) obj;
\r
1413 start = toObject(scope, obj);
\r
1415 if (start == null) {
\r
1416 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
1418 Scriptable m = start;
\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
1430 newValue = new Double(toNumber(newValue) + 1.0);
\r
1431 m.put(id, start, newValue);
\r
1432 return new Double(toNumber(result));
\r
1435 m = m.getPrototype();
\r
1436 } while (m != null);
\r
1437 return Undefined.instance;
\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
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
1462 public static Object postDecrement(Object value) {
\r
1463 if (value instanceof Number)
\r
1464 value = new Double(((Number)value).doubleValue() - 1.0);
\r
1466 value = new Double(toNumber(value) - 1.0);
\r
1470 public static Object postDecrement(Scriptable scopeChain, String id) {
\r
1471 Scriptable obj = scopeChain;
\r
1473 while (obj != null) {
\r
1474 Scriptable m = obj;
\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
1486 newValue = new Double(toNumber(newValue) - 1.0);
\r
1487 m.put(id, obj, newValue);
\r
1488 return new Double(toNumber(result));
\r
1491 m = m.getPrototype();
\r
1492 } while (m != null);
\r
1493 obj = obj.getParentScope();
\r
1495 throw NativeGlobal.constructError
\r
1496 (Context.getContext(), "ReferenceError",
\r
1497 ScriptRuntime.getMessage1("msg.is.not.defined", id),
\r
1501 public static Object postDecrement(Object obj, String id, Scriptable scope) {
\r
1503 if (obj instanceof Scriptable) {
\r
1504 start = (Scriptable) obj;
\r
1506 start = toObject(scope, obj);
\r
1508 if (start == null) {
\r
1509 throw NativeGlobal.typeError0("msg.null.to.object", scope);
\r
1511 Scriptable m = start;
\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
1523 newValue = new Double(toNumber(newValue) - 1.0);
\r
1524 m.put(id, start, newValue);
\r
1525 return new Double(toNumber(result));
\r
1528 m = m.getPrototype();
\r
1529 } while (m != null);
\r
1530 return Undefined.instance;
\r
1533 public static Object toPrimitive(Object val) {
\r
1534 if (val == null || !(val instanceof Scriptable)) {
\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
1543 private static Class getTypeOfValue(Object obj) {
\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
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
1564 Class typeX = getTypeOfValue(x);
\r
1565 Class typeY = getTypeOfValue(y);
\r
1566 if (typeX == typeY) {
\r
1567 if (typeX == UndefinedClass)
\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
1577 if (x instanceof Wrapper &&
\r
1578 y instanceof Wrapper)
\r
1580 return ((Wrapper) x).unwrap() ==
\r
1581 ((Wrapper) y).unwrap();
\r
1585 throw new RuntimeException(); // shouldn't get here
\r
1587 if (x == null && y == Undefined.instance)
\r
1589 if (x == Undefined.instance && y == null)
\r
1591 if (typeX == NumberClass &&
\r
1592 typeY == StringClass)
\r
1594 return ((Number) x).doubleValue() == toNumber(y);
\r
1596 if (typeX == StringClass &&
\r
1597 typeY == NumberClass)
\r
1599 return toNumber(x) == ((Number) y).doubleValue();
\r
1601 if (typeX == BooleanClass) {
\r
1602 x = new Double(toNumber(x));
\r
1603 xCopy = x; // !!! JIT bug in Cafe 2.1
\r
1606 if (typeY == BooleanClass) {
\r
1607 y = new Double(toNumber(y));
\r
1608 yCopy = y; // !!! JIT bug in Cafe 2.1
\r
1611 if ((typeX == StringClass ||
\r
1612 typeX == NumberClass) &&
\r
1613 typeY == ScriptableClass && y != null)
\r
1615 y = toPrimitive(y);
\r
1616 yCopy = y; // !!! JIT bug in Cafe 2.1
\r
1619 if (typeX == ScriptableClass && x != null &&
\r
1620 (typeY == StringClass ||
\r
1621 typeY == NumberClass))
\r
1623 x = toPrimitive(x);
\r
1624 xCopy = x; // !!! JIT bug in Cafe 2.1
\r
1631 public static Boolean eqB(Object x, Object y) {
\r
1633 return Boolean.TRUE;
\r
1635 return Boolean.FALSE;
\r
1638 public static Boolean neB(Object x, Object y) {
\r
1640 return Boolean.FALSE;
\r
1642 return Boolean.TRUE;
\r
1645 public static boolean shallowEq(Object x, Object y) {
\r
1646 Class type = getTypeOfValue(x);
\r
1647 if (type != getTypeOfValue(y))
\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
1657 if (x instanceof Wrapper && y instanceof Wrapper)
\r
1658 return ((Wrapper) x).unwrap() ==
\r
1659 ((Wrapper) y).unwrap();
\r
1662 if (type == UndefinedClass)
\r
1667 public static Boolean seqB(Object x, Object y) {
\r
1668 if (shallowEq(x,y))
\r
1669 return Boolean.TRUE;
\r
1671 return Boolean.FALSE;
\r
1674 public static Boolean sneB(Object x, Object y) {
\r
1675 if (shallowEq(x,y))
\r
1676 return Boolean.FALSE;
\r
1678 return Boolean.TRUE;
\r
1682 * The instanceof operator.
\r
1684 * @return a instanceof b
\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
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
1698 return ((Scriptable)b).hasInstance((Scriptable)a);
\r
1704 * @return true iff rhs appears in lhs' proto chain
\r
1706 protected static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
\r
1707 Scriptable proto = lhs.getPrototype();
\r
1709 while (proto != null) {
\r
1710 if (proto.equals(rhs)) return true;
\r
1711 proto = proto.getPrototype();
\r
1718 * The in operator.
\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
1729 * @return true if property name or element number a is a property of b
\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
1735 String s = getStringId(a);
\r
1737 ? ScriptableObject.hasProperty((Scriptable) b, s)
\r
1738 : ScriptableObject.hasProperty((Scriptable) b, getIntId(a));
\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
1745 return Boolean.FALSE;
\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
1757 double d2 = toNumber(val2);
\r
1760 return d1 < d2 ? 1 : 0;
\r
1762 return toString(val1).compareTo(toString(val2)) < 0 ? 1 : 0;
\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
1769 return Boolean.FALSE;
\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
1781 double d2 = toNumber(val2);
\r
1784 return d1 <= d2 ? 1 : 0;
\r
1786 return toString(val1).compareTo(toString(val2)) <= 0 ? 1 : 0;
\r
1790 // implement the '<' operator inline in the caller
\r
1791 // as "compare(val1, val2) == 1"
\r
1794 // implement the '<=' operator inline in the caller
\r
1795 // as "compare(val2, val1) == 0"
\r
1798 // implement the '>' operator inline in the caller
\r
1799 // as "compare(val2, val1) == 1"
\r
1802 // implement the '>=' operator inline in the caller
\r
1803 // as "compare(val1, val2) == 0"
\r
1805 // ------------------
\r
1807 // ------------------
\r
1809 private static final String GLOBAL_CLASS =
\r
1810 "org.mozilla.javascript.tools.shell.Global";
\r
1812 private static ScriptableObject getGlobal(Context cx) {
\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
1830 return new ImporterTopLevel(cx);
\r
1833 public static void main(String scriptClassName, String[] args)
\r
1834 throws JavaScriptException
\r
1836 Context cx = Context.enter();
\r
1837 ScriptableObject global = getGlobal(cx);
\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
1846 Class cl = loadClassName(scriptClassName);
\r
1847 Script script = (Script) cl.newInstance();
\r
1848 script.exec(cx, global);
\r
1851 catch (ClassNotFoundException e) {
\r
1853 catch (InstantiationException e) {
\r
1855 catch (IllegalAccessException e) {
\r
1860 throw new RuntimeException("Error creating script object");
\r
1863 public static Scriptable initScript(Context cx, Scriptable scope,
\r
1864 NativeFunction funObj,
\r
1865 Scriptable thisObj,
\r
1866 boolean fromEvalCode)
\r
1868 String[] argNames = funObj.argNames;
\r
1869 if (argNames != null) {
\r
1870 ScriptableObject so;
\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
1877 so = (ScriptableObject) scope;
\r
1878 } catch (ClassCastException x) {
\r
1879 // oh well, we tried.
\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
1889 while (varScope instanceof NativeWith)
\r
1890 varScope = varScope.getParentScope();
\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
1901 varScope.put(name, varScope, Undefined.instance);
\r
1909 public static Scriptable runScript(Script script) {
\r
1910 Context cx = Context.enter();
\r
1911 ScriptableObject global = getGlobal(cx);
\r
1913 script.exec(cx, global);
\r
1914 } catch (JavaScriptException e) {
\r
1915 throw new Error(e.toString());
\r
1922 public static Scriptable initVarObj(Context cx, Scriptable scope,
\r
1923 NativeFunction funObj,
\r
1924 Scriptable thisObj, Object[] args)
\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
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
1945 public static Scriptable newScope() {
\r
1946 return new NativeObject();
\r
1949 public static Scriptable enterWith(Object value, Scriptable scope) {
\r
1950 return new NativeWith(scope, toObject(scope, value));
\r
1953 public static Scriptable leaveWith(Scriptable scope) {
\r
1954 return scope.getParentScope();
\r
1957 public static NativeFunction initFunction(NativeFunction fn,
\r
1961 boolean doSetName)
\r
1963 fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));
\r
1964 fn.setParentScope(scope);
\r
1966 setName(scope, fn, scope, fnName);
\r
1970 public static NativeFunction createFunctionObject(Scriptable scope,
\r
1971 Class functionClass,
\r
1975 Constructor[] ctors = functionClass.getConstructors();
\r
1977 NativeFunction result = null;
\r
1978 Object[] initArgs = { scope, cx };
\r
1980 result = (NativeFunction) ctors[0].newInstance(initArgs);
\r
1982 catch (InstantiationException e) {
\r
1983 throw WrappedException.wrapException(e);
\r
1985 catch (IllegalAccessException e) {
\r
1986 throw WrappedException.wrapException(e);
\r
1988 catch (IllegalArgumentException e) {
\r
1989 throw WrappedException.wrapException(e);
\r
1991 catch (InvocationTargetException e) {
\r
1992 throw WrappedException.wrapException(e);
\r
1995 result.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));
\r
1996 result.setParentScope(scope);
\r
1998 String fnName = result.getFunctionName();
\r
1999 if (setName && fnName != null && fnName.length() != 0 &&
\r
2000 !fnName.equals("anonymous"))
\r
2002 setProp(scope, fnName, result, scope);
\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
2015 throw Context.reportRuntimeError(msg);
\r
2019 public static String getMessage0(String messageId) {
\r
2020 return Context.getMessage0(messageId);
\r
2023 public static String getMessage1(String messageId, Object arg1) {
\r
2024 return Context.getMessage1(messageId, arg1);
\r
2027 public static String getMessage2
\r
2028 (String messageId, Object arg1, Object arg2)
\r
2030 return Context.getMessage2(messageId, arg1, arg2);
\r
2033 public static String getMessage(String messageId, Object[] arguments) {
\r
2034 return Context.getMessage(messageId, arguments);
\r
2037 public static RegExpProxy getRegExpProxy(Context cx) {
\r
2038 return cx.getRegExpProxy();
\r
2041 public static NativeCall getCurrentActivation(Context cx) {
\r
2042 return cx.currentActivation;
\r
2045 public static void setCurrentActivation(Context cx,
\r
2046 NativeCall activation)
\r
2048 cx.currentActivation = activation;
\r
2051 public static Class loadClassName(String className)
\r
2052 throws ClassNotFoundException
\r
2056 ClassLoader cl = DefiningClassLoader.getContextClassLoader();
\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
2069 return Class.forName(className);
\r
2072 static boolean hasProp(Scriptable start, String name) {
\r
2073 Scriptable m = start;
\r
2075 if (m.has(name, start))
\r
2077 m = m.getPrototype();
\r
2078 } while (m != null);
\r
2082 private static RuntimeException errorWithClassName(String msg, Object val)
\r
2084 return Context.reportRuntimeError1(msg, val.getClass().getName());
\r
2087 public static final Object[] emptyArgs = new Object[0];
\r
2093 * This is the enumeration needed by the for..in statement.
\r
2095 * See ECMA 12.6.3.
\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
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
2107 class IdEnumeration implements Enumeration {
\r
2108 IdEnumeration(Scriptable m) {
\r
2109 used = new Hashtable(27);
\r
2114 public boolean hasMoreElements() {
\r
2115 return next != null;
\r
2118 public Object nextElement() {
\r
2119 Object result = next;
\r
2121 // only key used; 'next' as value for convenience
\r
2122 used.put(next, next);
\r
2128 private void changeObject(Scriptable m) {
\r
2130 if (obj != null) {
\r
2131 array = m.getIds();
\r
2132 if (array.length == 0)
\r
2133 changeObject(obj.getPrototype());
\r
2138 private Object getNext() {
\r
2143 if (index == array.length) {
\r
2144 changeObject(obj.getPrototype());
\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
2153 if (!obj.has(((Number) result).intValue(), obj))
\r
2154 continue; // must have been deleted
\r
2156 if (!used.containsKey(result)) {
\r
2160 return ScriptRuntime.toString(result);
\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