1    // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] 
2    package org.xwt.js; 
3    
4    import org.xwt.util.*; 
5    import java.io.*;
6    import java.util.*;
7    
8    // FIXME: should allow parentScope to be a JS, not a JSScope
9    /** Implementation of a JavaScript Scope */
10   public class JSScope extends JS { 
11   
12       private JSScope parentScope;
13   
14       private static final Object NULL_PLACEHOLDER = new Object();
15   
16       public JSScope(JSScope parentScope) { this.parentScope = parentScope; }
17       public void declare(String s) throws JSExn { super.put(s, NULL_PLACEHOLDER); }
18       public JSScope getParentScope() { return parentScope; }
19   
20       public Object get(Object key) throws JSExn {
21           Object o = super.get(key);
22           if (o != null) return o == NULL_PLACEHOLDER ? null : o;
23           else return parentScope == null ? null : parentScope.get(key);
24       }
25   
26       public boolean has(Object key) throws JSExn { return super.get(key) != null; }
27       public void put(Object key, Object val) throws JSExn {
28           if (parentScope != null && !has(key)) parentScope.put(key, val);
29           else super.put(key, val == null ? NULL_PLACEHOLDER : val);
30       }
31       
32       public JSScope top() { 
33           JSScope s = this;
34           while(s.parentScope != null) s = s.parentScope;
35           return s;
36       }
37   
38       public static class Global extends JSScope {
39           private final static Double NaN = new Double(Double.NaN);
40           private final static Double POSITIVE_INFINITY = new Double(Double.POSITIVE_INFINITY);
41   
42           public Global() { super(null); }
43           public Object get(Object key) throws JSExn {
44               //#switch(key)
45               case "NaN": return NaN;
46               case "Infinity": return POSITIVE_INFINITY;
47               case "undefined": return null;
48               case "stringFromCharCode": return METHOD;
49               case "parseInt": return METHOD;
50               case "isNaN": return METHOD;
51               case "isFinite": return METHOD;
52               case "decodeURI": return METHOD;
53               case "decodeURIComponent": return METHOD;
54               case "encodeURI": return METHOD;
55               case "encodeURIComponent": return METHOD;
56               case "escape": return METHOD;
57               case "unescape": return METHOD;
58               case "parseInt": return METHOD;
59               //#end
60               return super.get(key);
61           }
62   
63           public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
64               switch(nargs) {
65                   case 0: {
66                       //#switch(method)
67                       case "stringFromCharCode":
68                           char buf[] = new char[nargs];
69                           for(int i=0; i<nargs; i++) buf[i] = (char)(JS.toInt(i==0?a0:i==1?a1:i==2?a2:rest[i-3]) & 0xffff);
70                           return new String(buf);
71                       //#end
72                       break;
73                   }
74                   case 1: {
75                       //#switch(method)
76                       case "parseInt": return parseInt(a0, N(0));
77                       case "isNaN": { double d = toDouble(a0); return d == d ? F : T; }
78                       case "isFinite": { double d = toDouble(a0); return (d == d && !Double.isInfinite(d)) ? T : F; }
79                       case "decodeURI": throw new JSExn("unimplemented");
80                       case "decodeURIComponent": throw new JSExn("unimplemented");
81                       case "encodeURI": throw new JSExn("unimplemented");
82                       case "encodeURIComponent": throw new JSExn("unimplemented");
83                       case "escape": throw new JSExn("unimplemented");
84                       case "unescape": throw new JSExn("unimplemented");
85                       //#end
86                       break;
87                   }
88                   case 2: {
89                       //#switch(method)
90                       case "parseInt": return parseInt(a0, a1);
91                       //#end
92                       break;
93                   }
94               }
95               return super.callMethod(method, a0, a1, a2, rest, nargs);
96           }
97   
98           private Object parseInt(Object arg, Object r) {
99               int radix = JS.toInt(r);
100              String s = (String)arg;
101              int start = 0;
102              int length = s.length();
103              int sign = 1;
104              long n = 0;
105              if(radix != 0 && (radix < 2 || radix > 36)) return NaN;
106              while(start < length && Character.isWhitespace(s.charAt(start))) start++;
107              if((length >= start+1) && (s.charAt(start) == '+' || s.charAt(start) == '-')) {
108                  sign = s.charAt(start) == '+' ? 1 : -1;
109                  start++;
110              }
111              if(radix == 0 && length >= start+1 && s.charAt(start) == '0') {
112                  start++;
113                  if(length >= start+1 && (s.charAt(start) == 'x' || s.charAt(start) == 'X')) {
114                      start++;
115                      radix = 16;
116                  } else {
117                      radix = 8;
118                      if(length == start || Character.digit(s.charAt(start+1),8)==-1) return JS.ZERO;
119                  }
120              }
121              if(radix == 0) radix = 10;
122              if(length == start || Character.digit(s.charAt(start),radix) == -1) return NaN;
123              // try the fast way first
124              try {
125                  String s2 = start == 0 ? s : s.substring(start);
126                  return JS.N(sign*Integer.parseInt(s2,radix));
127              } catch(NumberFormatException e) { }
128              // fall through to a slower but emca-compliant method
129              for(int i=start;i<length;i++) {
130                  int digit = Character.digit(s.charAt(i),radix);
131                  if(digit < 0) break;
132                  n = n*radix + digit;
133                  if(n < 0) return NaN; // overflow;
134              }
135              if(n <= Integer.MAX_VALUE) return JS.N(sign*(int)n);
136              return JS.N((long)sign*n);
137          }
138  
139          private Object parseFloat(Object arg) {
140              String s = (String)arg;
141              int start = 0;
142              int length = s.length();
143              while(start < length && Character.isWhitespace(s.charAt(0))) start++;
144              int end = length;
145              // as long as the string has no trailing garbage,this is fast, its slow with
146              // trailing garbage
147              while(start < end) {
148                  try {
149                      return JS.N(s.substring(start,length));
150                  } catch(NumberFormatException e) { }
151                  end--;
152              }
153              return NaN;
154          }
155      }
156  }
157  
158