bring back in JSGlobal (default impl)
[org.ibex.js.git] / src / org / ibex / js / JSScope.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.js; 
6
7 /** Implementation of a JavaScript Scope */
8 class JSScope {
9
10     private final int base;
11     private final JS[] vars;
12     final JSScope parent;
13
14     public static class Top extends JSScope {
15         private final JS global;
16         public Top(JS global) { super(null,0,0); this.global = global; }
17         JS get(int i) throws JSExn { throw new JSExn("scope index out of range"); }
18         void put(int i, JS o) throws JSExn { throw new JSExn("scope index out of range"); }
19         JS getGlobal() { return global; }
20     };
21         
22     // NOTE: We can't just set base to parent.base + parent.vars.length
23     // sometimes we only access part of a parent's scope
24     public JSScope(JSScope parent, int base, int size) {
25         this.parent = parent;
26         this.base = base;
27         this.vars = new JS[size];
28     }
29     
30     final JS get(JS i) throws JSExn {
31         if(i==null) throw new NullPointerException();
32         try {
33             return get(JSU.toInt(i));
34         } catch(ArrayIndexOutOfBoundsException e) { 
35             throw new JSExn("scope index out of range");
36         }
37     }
38     final void put(JS i, JS o) throws JSExn {
39         if(i==null) throw new NullPointerException();
40         try {
41             put(JSU.toInt(i), o);
42         } catch(ArrayIndexOutOfBoundsException e) { 
43             throw new JSExn("scope index out of range");
44         }
45     }
46     JS get(int i) throws JSExn { return i < base ? parent.get(i) : vars[i-base]; }
47     void put(int i, JS o) throws JSExn { if(i < base) parent.put(i,o); else vars[i-base] = o; }
48     
49     JS getGlobal() { return parent.getGlobal(); }
50
51     public static class Global extends JS.Immutable {
52         private final static JS NaN = JSU.N(Double.NaN);
53         private final static JS POSITIVE_INFINITY = JSU.N(Double.POSITIVE_INFINITY);
54         private final static JS.Method METHOD = new JS.Method();
55
56         public Global() { }
57         public JS get(JS key) throws JSExn {
58             //#jsswitch(key)
59             case "NaN": return NaN;
60             case "Infinity": return POSITIVE_INFINITY;
61             case "undefined": return null;
62             case "stringFromCharCode": return METHOD;
63             case "parseInt": return METHOD;
64             case "isNaN": return METHOD;
65             case "isFinite": return METHOD;
66             case "decodeURI": return METHOD;
67             case "decodeURIComponent": return METHOD;
68             case "encodeURI": return METHOD;
69             case "encodeURIComponent": return METHOD;
70             case "escape": return METHOD;
71             case "unescape": return METHOD;
72             case "parseInt": return METHOD;
73             //#end
74             return super.get(key);
75         }
76
77         public JS call(JS method, JS[] args) throws JSExn {
78             switch(args.length) {
79                 case 0: {
80                     //#jsswitch(method)
81                     case "stringFromCharCode":
82                         char buf[] = new char[args.length];
83                         for(int i=0; i<args.length; i++) buf[i] = (char)(JSU.toInt(args[i]) & 0xffff);
84                         return JSU.S(new String(buf));
85                     //#end
86                     break;
87                 }
88                 case 1: {
89                     //#jsswitch(method)
90                     case "parseInt": return parseInt(args[0], JSU.N(0));
91                     case "isNaN": { double d = JSU.toDouble(args[0]); return d == d ? JSU.F : JSU.T; }
92                     case "isFinite": { double d = JSU.toDouble(args[0]); return (d==d && !Double.isInfinite(d)) ? JSU.T : JSU.F; }
93                     case "decodeURI": throw new JSExn("unimplemented");
94                     case "decodeURIComponent": throw new JSExn("unimplemented");
95                     case "encodeURI": throw new JSExn("unimplemented");
96                     case "encodeURIComponent": throw new JSExn("unimplemented");
97                     case "escape": throw new JSExn("unimplemented");
98                     case "unescape": throw new JSExn("unimplemented");
99                     //#end
100                     break;
101                 }
102                 case 2: {
103                     //#jsswitch(method)
104                     case "parseInt": return parseInt(args[0], args[1]);
105                     //#end
106                     break;
107                 }
108             }
109             return super.call(method, args);
110         }
111
112         private JS parseInt(JS arg, JS r) throws JSExn {
113             int radix = JSU.toInt(r);
114             String s = JSU.toString(arg);
115             int start = 0;
116             int length = s.length();
117             int sign = 1;
118             long n = 0;
119             if (radix != 0 && (radix < 2 || radix > 36)) return NaN;
120             while (start < length && Character.isWhitespace(s.charAt(start))) start++;
121             if ((length >= start+1) && (s.charAt(start) == '+' || s.charAt(start) == '-')) {
122                 sign = s.charAt(start) == '+' ? 1 : -1;
123                 start++;
124             }
125             if(radix == 0 && length >= start+1 && s.charAt(start) == '0') {
126                 start++;
127                 if(length >= start+1 && (s.charAt(start) == 'x' || s.charAt(start) == 'X')) {
128                     start++;
129                     radix = 16;
130                 } else {
131                     radix = 8;
132                     if(length == start || Character.digit(s.charAt(start+1),8)==-1) return JSU.ZERO;
133                 }
134             }
135             if(radix == 0) radix = 10;
136             if(length == start || Character.digit(s.charAt(start),radix) == -1) return NaN;
137             // try the fast way first
138             try {
139                 String s2 = start == 0 ? s : s.substring(start);
140                 return JSU.N(sign*Integer.parseInt(s2,radix));
141             } catch(NumberFormatException e) { }
142             // fall through to a slower but emca-compliant method
143             for(int i=start;i<length;i++) {
144                 int digit = Character.digit(s.charAt(i),radix);
145                 if(digit < 0) break;
146                 n = n*radix + digit;
147                 if(n < 0) return NaN; // overflow;
148             }
149             if(n <= Integer.MAX_VALUE) return JSU.N(sign*(int)n);
150             return JSU.N((long)sign*n);
151         }
152
153         private JS parseFloat(JS arg) throws JSExn {
154             String s = JSU.toString(arg);
155             int start = 0;
156             int length = s.length();
157             while(start < length && Character.isWhitespace(s.charAt(0))) start++;
158             int end = length;
159             // as long as the string has no trailing garbage,this is fast, its slow with
160             // trailing garbage
161             while(start < end) {
162                 try {
163                     return JSU.N(Float.parseFloat(s.substring(start,length)));
164                 } catch(NumberFormatException e) { }
165                 end--;
166             }
167             return NaN;
168         }
169     }
170 }