~ubuntu-branches/ubuntu/karmic/rhino/karmic

« back to all changes in this revision

Viewing changes to src/org/mozilla/javascript/ScriptRuntime.java

  • Committer: Bazaar Package Importer
  • Author(s): Jerry Haltom
  • Date: 2005-03-19 16:56:07 UTC
  • mto: (11.1.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20050319165607-geu3j3fnqlkpqkh1
Tags: upstream-1.6.R1
ImportĀ upstreamĀ versionĀ 1.6.R1

Show diffs side-by-side

added added

removed removed

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