2003/05/12 05:10:30
[org.ibex.core.git] / src / org / mozilla / javascript / NativeGlobal.java
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
2  *\r
3  * The contents of this file are subject to the Netscape Public\r
4  * License Version 1.1 (the "License"); you may not use this file\r
5  * except in compliance with the License. You may obtain a copy of\r
6  * the License at http://www.mozilla.org/NPL/\r
7  *\r
8  * Software distributed under the License is distributed on an "AS\r
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
10  * implied. See the License for the specific language governing\r
11  * rights and limitations under the License.\r
12  *\r
13  * The Original Code is Rhino code, released\r
14  * May 6, 1999.\r
15  *\r
16  * The Initial Developer of the Original Code is Netscape\r
17  * Communications Corporation.  Portions created by Netscape are\r
18  * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
19  * Rights Reserved.\r
20  *\r
21  * Contributor(s):\r
22  * Norris Boyd\r
23  * Igor Bukanov\r
24  * Mike McCabe\r
25  *\r
26  * Alternatively, the contents of this file may be used under the\r
27  * terms of the GNU Public License (the "GPL"), in which case the\r
28  * provisions of the GPL are applicable instead of those above.\r
29  * If you wish to allow use of your version of this file only\r
30  * under the terms of the GPL and not to allow others to use your\r
31  * version of this file under the NPL, indicate your decision by\r
32  * deleting the provisions above and replace them with the notice\r
33  * and other provisions required by the GPL.  If you do not delete\r
34  * the provisions above, a recipient may use your version of this\r
35  * file under either the NPL or the GPL.\r
36  */\r
37 \r
38 package org.mozilla.javascript;\r
39 \r
40 import java.io.StringReader;\r
41 import java.io.IOException;\r
42 import java.lang.reflect.Method;\r
43 \r
44 /**\r
45  * This class implements the global native object (function and value\r
46  * properties only).\r
47  *\r
48  * See ECMA 15.1.[12].\r
49  *\r
50  * @author Mike Shaver\r
51  */\r
52 \r
53 public class NativeGlobal implements IdFunctionMaster {\r
54 \r
55     public static void init(Context cx, Scriptable scope, boolean sealed) {\r
56         NativeGlobal obj = new NativeGlobal();\r
57         obj.scopeSlaveFlag = true;\r
58 \r
59         for (int id = 1; id <= LAST_SCOPE_FUNCTION_ID; ++id) {\r
60             String name = getMethodName(id);\r
61             IdFunction f = new IdFunction(obj, name, id);\r
62             f.setParentScope(scope);\r
63             if (sealed) { f.sealObject(); }\r
64             ScriptableObject.defineProperty(scope, name, f,\r
65                                             ScriptableObject.DONTENUM);\r
66         }\r
67 \r
68         ScriptableObject.defineProperty(scope, "NaN",\r
69                                         ScriptRuntime.NaNobj,\r
70                                         ScriptableObject.DONTENUM);\r
71         ScriptableObject.defineProperty(scope, "Infinity",\r
72                                         new Double(Double.POSITIVE_INFINITY),\r
73                                         ScriptableObject.DONTENUM);\r
74         /*\r
75         ScriptableObject.defineProperty(scope, "undefined",\r
76                                         Undefined.instance,\r
77                                         ScriptableObject.DONTENUM);\r
78         */\r
79 \r
80         String[] errorMethods = { "ConversionError",\r
81                                   "EvalError",\r
82                                   "RangeError",\r
83                                   "ReferenceError",\r
84                                   "SyntaxError",\r
85                                   "TypeError",\r
86                                   "URIError"\r
87                                 };\r
88 \r
89         /*\r
90             Each error constructor gets its own Error object as a prototype,\r
91             with the 'name' property set to the name of the error.\r
92         */\r
93         for (int i = 0; i < errorMethods.length; i++) {\r
94             String name = errorMethods[i];\r
95             IdFunction ctor = new IdFunction(obj, name, Id_new_CommonError);\r
96             ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);\r
97             ctor.setParentScope(scope);\r
98             ScriptableObject.defineProperty(scope, name, ctor,\r
99                                             ScriptableObject.DONTENUM);\r
100 \r
101             Scriptable errorProto = ScriptRuntime.newObject\r
102                 (cx, scope, "Error", ScriptRuntime.emptyArgs);\r
103 \r
104             errorProto.put("name", errorProto, name);\r
105             ctor.put("prototype", ctor, errorProto);\r
106             if (sealed) {\r
107                 ctor.sealObject();\r
108                 if (errorProto instanceof ScriptableObject) {\r
109                     ((ScriptableObject)errorProto).sealObject();\r
110                 }\r
111             }\r
112         }\r
113     }\r
114 \r
115     public Object execMethod(int methodId, IdFunction function, Context cx,\r
116                              Scriptable scope, Scriptable thisObj,\r
117                              Object[] args)\r
118         throws JavaScriptException\r
119     {\r
120         if (scopeSlaveFlag) {\r
121             switch (methodId) {\r
122                 case Id_decodeURI:\r
123                     return js_decodeURI(cx, args);\r
124 \r
125                 case Id_decodeURIComponent:\r
126                     return js_decodeURIComponent(cx, args);\r
127 \r
128                 case Id_encodeURI:\r
129                     return js_encodeURI(cx, args);\r
130 \r
131                 case Id_encodeURIComponent:\r
132                     return js_encodeURIComponent(cx, args);\r
133 \r
134                 case Id_escape:\r
135                     return js_escape(cx, args);\r
136 \r
137                 case Id_eval:\r
138                     return js_eval(cx, scope, args);\r
139 \r
140                 case Id_isFinite:\r
141                     return js_isFinite(cx, args);\r
142 \r
143                 case Id_isNaN:\r
144                     return js_isNaN(cx, args);\r
145 \r
146                 case Id_parseFloat:\r
147                     return js_parseFloat(cx, args);\r
148 \r
149                 case Id_parseInt:\r
150                     return js_parseInt(cx, args);\r
151 \r
152                 case Id_unescape:\r
153                     return js_unescape(cx, args);\r
154 \r
155                 case Id_new_CommonError:\r
156                     return new_CommonError(function, cx, scope, args);\r
157             }\r
158         }\r
159         throw IdFunction.onBadMethodId(this, methodId);\r
160     }\r
161 \r
162     public int methodArity(int methodId) {\r
163         if (scopeSlaveFlag) {\r
164             switch (methodId) {\r
165                 case Id_decodeURI:           return 1;\r
166                 case Id_decodeURIComponent:  return 1;\r
167                 case Id_encodeURI:           return 1;\r
168                 case Id_encodeURIComponent:  return 1;\r
169                 case Id_escape:              return 1;\r
170                 case Id_eval:                return 1;\r
171                 case Id_isFinite:            return 1;\r
172                 case Id_isNaN:               return 1;\r
173                 case Id_parseFloat:          return 1;\r
174                 case Id_parseInt:            return 2;\r
175                 case Id_unescape:            return 1;\r
176 \r
177                 case Id_new_CommonError:     return 1;\r
178             }\r
179         }\r
180         return -1;\r
181     }\r
182 \r
183     private static String getMethodName(int methodId) {\r
184         switch (methodId) {\r
185             case Id_decodeURI:           return "decodeURI";\r
186             case Id_decodeURIComponent:  return "decodeURIComponent";\r
187             case Id_encodeURI:           return "encodeURI";\r
188             case Id_encodeURIComponent:  return "encodeURIComponent";\r
189             case Id_escape:              return "escape";\r
190             case Id_eval:                return "eval";\r
191             case Id_isFinite:            return "isFinite";\r
192             case Id_isNaN:               return "isNaN";\r
193             case Id_parseFloat:          return "parseFloat";\r
194             case Id_parseInt:            return "parseInt";\r
195             case Id_unescape:            return "unescape";\r
196         }\r
197         return null;\r
198     }\r
199 \r
200     /**\r
201      * The global method parseInt, as per ECMA-262 15.1.2.2.\r
202      */\r
203     private Object js_parseInt(Context cx, Object[] args) {\r
204         String s = ScriptRuntime.toString(args, 0);\r
205         int radix = ScriptRuntime.toInt32(args, 1);\r
206 \r
207         int len = s.length();\r
208         if (len == 0)\r
209             return ScriptRuntime.NaNobj;\r
210 \r
211         boolean negative = false;\r
212         int start = 0;\r
213         char c;\r
214         do {\r
215             c = s.charAt(start);\r
216             if (!Character.isWhitespace(c))\r
217                 break;\r
218             start++;\r
219         } while (start < len);\r
220 \r
221         if (c == '+' || (negative = (c == '-')))\r
222             start++;\r
223 \r
224         final int NO_RADIX = -1;\r
225         if (radix == 0) {\r
226             radix = NO_RADIX;\r
227         } else if (radix < 2 || radix > 36) {\r
228             return ScriptRuntime.NaNobj;\r
229         } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {\r
230             c = s.charAt(start+1);\r
231             if (c == 'x' || c == 'X')\r
232                 start += 2;\r
233         }\r
234 \r
235         if (radix == NO_RADIX) {\r
236             radix = 10;\r
237             if (len - start > 1 && s.charAt(start) == '0') {\r
238                 c = s.charAt(start+1);\r
239                 if (c == 'x' || c == 'X') {\r
240                     radix = 16;\r
241                     start += 2;\r
242                 } else if (c != '.') {\r
243                     radix = 8;\r
244                     start++;\r
245                 }\r
246             }\r
247         }\r
248 \r
249         double d = ScriptRuntime.stringToNumber(s, start, radix);\r
250         return new Double(negative ? -d : d);\r
251     }\r
252 \r
253     /**\r
254      * The global method parseFloat, as per ECMA-262 15.1.2.3.\r
255      *\r
256      * @param cx unused\r
257      * @param thisObj unused\r
258      * @param args the arguments to parseFloat, ignoring args[>=1]\r
259      * @param funObj unused\r
260      */\r
261     private Object js_parseFloat(Context cx, Object[] args) {\r
262         if (args.length < 1)\r
263             return ScriptRuntime.NaNobj;\r
264         String s = ScriptRuntime.toString(args[0]);\r
265         int len = s.length();\r
266         if (len == 0)\r
267             return ScriptRuntime.NaNobj;\r
268 \r
269         int i;\r
270         char c;\r
271         // Scan forward to the first digit or .\r
272         for (i=0; TokenStream.isJSSpace(c = s.charAt(i)) && i+1 < len; i++)\r
273             /* empty */\r
274             ;\r
275 \r
276         int start = i;\r
277 \r
278         if (c == '+' || c == '-')\r
279             c = s.charAt(++i);\r
280 \r
281         if (c == 'I') {\r
282             // check for "Infinity"\r
283             double d;\r
284             if (i+8 <= len && s.substring(i, i+8).equals("Infinity"))\r
285                 d = s.charAt(start) == '-' ? Double.NEGATIVE_INFINITY\r
286                                            : Double.POSITIVE_INFINITY;\r
287             else\r
288                 return ScriptRuntime.NaNobj;\r
289             return new Double(d);\r
290         }\r
291 \r
292         // Find the end of the legal bit\r
293         int decimal = -1;\r
294         int exponent = -1;\r
295         for (; i < len; i++) {\r
296             switch (s.charAt(i)) {\r
297               case '.':\r
298                 if (decimal != -1) // Only allow a single decimal point.\r
299                     break;\r
300                 decimal = i;\r
301                 continue;\r
302 \r
303               case 'e':\r
304               case 'E':\r
305                 if (exponent != -1)\r
306                     break;\r
307                 exponent = i;\r
308                 continue;\r
309 \r
310               case '+':\r
311               case '-':\r
312                  // Only allow '+' or '-' after 'e' or 'E'\r
313                 if (exponent != i-1)\r
314                     break;\r
315                 continue;\r
316 \r
317               case '0': case '1': case '2': case '3': case '4':\r
318               case '5': case '6': case '7': case '8': case '9':\r
319                 continue;\r
320 \r
321               default:\r
322                 break;\r
323             }\r
324             break;\r
325         }\r
326         s = s.substring(start, i);\r
327         try {\r
328             return Double.valueOf(s);\r
329         }\r
330         catch (NumberFormatException ex) {\r
331             return ScriptRuntime.NaNobj;\r
332         }\r
333     }\r
334 \r
335     /**\r
336      * The global method escape, as per ECMA-262 15.1.2.4.\r
337 \r
338      * Includes code for the 'mask' argument supported by the C escape\r
339      * method, which used to be part of the browser imbedding.  Blame\r
340      * for the strange constant names should be directed there.\r
341      */\r
342 \r
343     private Object js_escape(Context cx, Object[] args) {\r
344         final int\r
345             URL_XALPHAS = 1,\r
346             URL_XPALPHAS = 2,\r
347             URL_PATH = 4;\r
348 \r
349         String s = ScriptRuntime.toString(args, 0);\r
350 \r
351         int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;\r
352         if (args.length > 1) { // the 'mask' argument.  Non-ECMA.\r
353             double d = ScriptRuntime.toNumber(args[1]);\r
354             if (d != d || ((mask = (int) d) != d) ||\r
355                 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))\r
356             {\r
357                 String message = Context.getMessage0("msg.bad.esc.mask");\r
358                 cx.reportError(message);\r
359                 // do the ecma thing, in case reportError returns.\r
360                 mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;\r
361             }\r
362         }\r
363 \r
364         StringBuffer R = new StringBuffer();\r
365         for (int k = 0; k < s.length(); k++) {\r
366             int c = s.charAt(k), d;\r
367             if (mask != 0 && ((c >= '0' && c <= '9') ||\r
368                 (c >= 'A' && c <= 'Z') ||\r
369                 (c >= 'a' && c <= 'z') ||\r
370                 c == '@' || c == '*' || c == '_' ||\r
371                 c == '-' || c == '.' ||\r
372                 ((c == '/' || c == '+') && mask > 3)))\r
373                 R.append((char)c);\r
374             else if (c < 256) {\r
375                 if (c == ' ' && mask == URL_XPALPHAS) {\r
376                     R.append('+');\r
377                 } else {\r
378                     R.append('%');\r
379                     R.append(hex_digit_to_char(c >>> 4));\r
380                     R.append(hex_digit_to_char(c & 0xF));\r
381                 }\r
382             } else {\r
383                 R.append('%');\r
384                 R.append('u');\r
385                 R.append(hex_digit_to_char(c >>> 12));\r
386                 R.append(hex_digit_to_char((c & 0xF00) >>> 8));\r
387                 R.append(hex_digit_to_char((c & 0xF0) >>> 4));\r
388                 R.append(hex_digit_to_char(c & 0xF));\r
389             }\r
390         }\r
391         return R.toString();\r
392     }\r
393 \r
394     private static char hex_digit_to_char(int x) {\r
395         return (char)(x <= 9 ? x + '0' : x + ('A' - 10));\r
396     }\r
397 \r
398     /**\r
399      * The global unescape method, as per ECMA-262 15.1.2.5.\r
400      */\r
401 \r
402     private Object js_unescape(Context cx, Object[] args)\r
403     {\r
404         String s = ScriptRuntime.toString(args, 0);\r
405         int firstEscapePos = s.indexOf('%');\r
406         if (firstEscapePos >= 0) {\r
407             int L = s.length();\r
408             char[] buf = s.toCharArray();\r
409             int destination = firstEscapePos;\r
410             for (int k = firstEscapePos; k != L;) {\r
411                 char c = buf[k];\r
412                 ++k;\r
413                 if (c == '%' && k != L) {\r
414                     int end, start;\r
415                     if (buf[k] == 'u') {\r
416                         start = k + 1;\r
417                         end = k + 5;\r
418                     } else {\r
419                         start = k;\r
420                         end = k + 2;\r
421                     }\r
422                     if (end <= L) {\r
423                         int x = 0;\r
424                         for (int i = start; i != end; ++i) {\r
425                             x = (x << 4) | TokenStream.xDigitToInt(buf[i]);\r
426                         }\r
427                         if (x >= 0) {\r
428                             c = (char)x;\r
429                             k = end;\r
430                         }\r
431                     }\r
432                 }\r
433                 buf[destination] = c;\r
434                 ++destination;\r
435             }\r
436             s = new String(buf, 0, destination); \r
437         }\r
438         return s;\r
439     }\r
440 \r
441     /**\r
442      * The global method isNaN, as per ECMA-262 15.1.2.6.\r
443      */\r
444 \r
445     private Object js_isNaN(Context cx, Object[] args) {\r
446         if (args.length < 1)\r
447             return Boolean.TRUE;\r
448         double d = ScriptRuntime.toNumber(args[0]);\r
449         return (d != d) ? Boolean.TRUE : Boolean.FALSE;\r
450     }\r
451 \r
452     private Object js_isFinite(Context cx, Object[] args) {\r
453         if (args.length < 1)\r
454             return Boolean.FALSE;\r
455         double d = ScriptRuntime.toNumber(args[0]);\r
456         return (d != d || d == Double.POSITIVE_INFINITY ||\r
457                 d == Double.NEGATIVE_INFINITY)\r
458                ? Boolean.FALSE\r
459                : Boolean.TRUE;\r
460     }\r
461 \r
462     private Object js_eval(Context cx, Scriptable scope, Object[] args)\r
463         throws JavaScriptException\r
464     {\r
465         String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval");\r
466         throw NativeGlobal.constructError(cx, "EvalError", m, scope);\r
467     }\r
468 \r
469     /**\r
470      * The eval function property of the global object.\r
471      *\r
472      * See ECMA 15.1.2.1\r
473      */\r
474     public static Object evalSpecial(Context cx, Scriptable scope,\r
475                                      Object thisArg, Object[] args,\r
476                                      String filename, int lineNumber)\r
477         throws JavaScriptException\r
478     {\r
479         throw NativeGlobal.constructError(cx, "EvalError", "XWT does not allow eval()", scope);\r
480         /*\r
481         if (args.length < 1)\r
482             return Undefined.instance;\r
483         Object x = args[0];\r
484         if (!(x instanceof String)) {\r
485             String message = Context.getMessage0("msg.eval.nonstring");\r
486             Context.reportWarning(message);\r
487             return x;\r
488         }\r
489         int[] linep = { lineNumber };\r
490         if (filename == null) {\r
491             filename = Context.getSourcePositionFromStack(linep);\r
492             if (filename == null) {\r
493                 filename = "";\r
494                 linep[0] = 1;\r
495             } \r
496         } \r
497         filename += "(eval)";\r
498 \r
499         try {\r
500             StringReader in = new StringReader((String) x);\r
501             Object securityDomain = cx.getSecurityDomainForStackDepth(3);\r
502 \r
503             // Compile the reader with opt level of -1 to force interpreter\r
504             // mode.\r
505             int oldOptLevel = cx.getOptimizationLevel();\r
506             cx.setOptimizationLevel(-1);\r
507             Script script = cx.compileReader(scope, in, filename, linep[0],\r
508                                              securityDomain);\r
509             cx.setOptimizationLevel(oldOptLevel);\r
510 \r
511             // if the compile fails, an error has been reported by the\r
512             // compiler, but we need to stop execution to avoid\r
513             // infinite looping on while(true) { eval('foo bar') } -\r
514             // so we throw an EvaluatorException.\r
515             if (script == null) {\r
516                 String message = Context.getMessage0("msg.syntax");\r
517                 throw new EvaluatorException(message);\r
518             }\r
519 \r
520             InterpretedScript is = (InterpretedScript) script;\r
521             is.itsData.itsFromEvalCode = true;\r
522             Object result = is.call(cx, scope, (Scriptable) thisArg, null);\r
523 \r
524             return result;\r
525         }\r
526         catch (IOException ioe) {\r
527             // should never happen since we just made the Reader from a String\r
528             throw new RuntimeException("unexpected io exception");\r
529         }\r
530         */\r
531     }\r
532 \r
533 \r
534     /**\r
535      * The NativeError functions\r
536      *\r
537      * See ECMA 15.11.6\r
538      */\r
539     public static EcmaError constructError(Context cx,\r
540                                            String error,\r
541                                            String message,\r
542                                            Object scope)\r
543     {\r
544         int[] linep = { 0 };\r
545         String filename = cx.getSourcePositionFromStack(linep);\r
546         return constructError(cx, error, message, scope,\r
547                               filename, linep[0], 0, null);\r
548     }\r
549 \r
550     static EcmaError typeError0(String messageId, Object scope) {\r
551         return constructError(Context.getContext(), "TypeError",\r
552             ScriptRuntime.getMessage0(messageId), scope);\r
553     }\r
554 \r
555     static EcmaError typeError1(String messageId, Object arg1, Object scope) {\r
556         return constructError(Context.getContext(), "TypeError",\r
557             ScriptRuntime.getMessage1(messageId, arg1), scope);\r
558     }\r
559 \r
560     /**\r
561      * The NativeError functions\r
562      *\r
563      * See ECMA 15.11.6\r
564      */\r
565     public static EcmaError constructError(Context cx,\r
566                                            String error,\r
567                                            String message,\r
568                                            Object scope,\r
569                                            String sourceName,\r
570                                            int lineNumber,\r
571                                            int columnNumber,\r
572                                            String lineSource)\r
573     {\r
574         Scriptable scopeObject;\r
575         try {\r
576             scopeObject = (Scriptable) scope;\r
577         }\r
578         catch (ClassCastException x) {\r
579             throw new RuntimeException(x.toString());\r
580         }\r
581 \r
582         Object args[] = { message };\r
583         try {\r
584             Object errorObject = cx.newObject(scopeObject, error, args);\r
585             return new EcmaError((NativeError)errorObject, sourceName,\r
586                                  lineNumber, columnNumber, lineSource);\r
587         }\r
588         catch (PropertyException x) {\r
589             throw new RuntimeException(x.toString());\r
590         }\r
591         catch (JavaScriptException x) {\r
592             throw new RuntimeException(x.toString());\r
593         }\r
594         catch (NotAFunctionException x) {\r
595             throw new RuntimeException(x.toString());\r
596         }\r
597     }\r
598 \r
599     /**\r
600      * The implementation of all the ECMA error constructors (SyntaxError,\r
601      * TypeError, etc.)\r
602      */\r
603     private Object new_CommonError(IdFunction ctorObj, Context cx,\r
604                                    Scriptable scope, Object[] args)\r
605     {\r
606         Scriptable newInstance = new NativeError();\r
607         newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj)));\r
608         newInstance.setParentScope(scope);\r
609         if (args.length > 0)\r
610             newInstance.put("message", newInstance, args[0]);\r
611         return newInstance;\r
612     }\r
613 \r
614     /*\r
615     *   ECMA 3, 15.1.3 URI Handling Function Properties\r
616     *\r
617     *   The following are implementations of the algorithms\r
618     *   given in the ECMA specification for the hidden functions\r
619     *   'Encode' and 'Decode'.\r
620     */\r
621     private static String encode(Context cx, String str, String unescapedSet) {\r
622         int j, k = 0, L;\r
623         char C, C2;\r
624         int V;\r
625         char utf8buf[] = new char[6];\r
626         StringBuffer R;\r
627 \r
628         R = new StringBuffer();\r
629 \r
630         while (k < str.length()) {\r
631             C = str.charAt(k);\r
632             if (unescapedSet.indexOf(C) != -1) {\r
633                 R.append(C);\r
634             } else {\r
635                 if ((C >= 0xDC00) && (C <= 0xDFFF)) {\r
636                     throw cx.reportRuntimeError0("msg.bad.uri");\r
637                 }\r
638                 if ((C < 0xD800) || (C > 0xDBFF))\r
639                     V = C;\r
640                 else {\r
641                     k++;\r
642                     if (k == str.length()) {\r
643                         throw cx.reportRuntimeError0("msg.bad.uri");\r
644                     }\r
645                     C2 = str.charAt(k);\r
646                     if ((C2 < 0xDC00) || (C2 > 0xDFFF)) {\r
647                         throw cx.reportRuntimeError0("msg.bad.uri");\r
648                     }\r
649                     V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000;\r
650                 }\r
651                 L = oneUcs4ToUtf8Char(utf8buf, V);\r
652                 for (j = 0; j < L; j++) {\r
653                     R.append('%');\r
654                     if (utf8buf[j] < 16)\r
655                         R.append('0');\r
656                     R.append(Integer.toHexString(utf8buf[j]));\r
657                 }\r
658             }\r
659             k++;\r
660         }\r
661         return R.toString();\r
662     }\r
663 \r
664     private static boolean isHex(char c) {\r
665         return ((c >= '0' && c <= '9')\r
666                 || (c >= 'a' && c <= 'f')\r
667                 || (c >= 'A' && c <= 'F'));\r
668     }\r
669 \r
670     private static int unHex(char c) {\r
671         if (c >= '0' && c <= '9')\r
672             return c - '0';\r
673         else\r
674             if (c >= 'a' && c <= 'f')\r
675                 return c - 'a' + 10;\r
676             else\r
677                 return c - 'A' +10;\r
678     }\r
679 \r
680     private static String decode(Context cx, String str, String reservedSet) {\r
681         int start, k = 0;\r
682         char C, H;\r
683         int V;\r
684         int B;\r
685         char[] octets = new char[6];\r
686         StringBuffer R;\r
687         int j, n;\r
688 \r
689         R = new StringBuffer();\r
690 \r
691         while (k < str.length()) {\r
692             C = str.charAt(k);\r
693             if (C == '%') {\r
694                 start = k;\r
695                 if ((k + 2) >= str.length())\r
696                     throw cx.reportRuntimeError0("msg.bad.uri");\r
697                 if (!isHex(str.charAt(k + 1)) || !isHex(str.charAt(k + 2)))\r
698                     throw cx.reportRuntimeError0("msg.bad.uri");\r
699                 B = unHex(str.charAt(k + 1)) * 16 + unHex(str.charAt(k + 2));\r
700                 k += 2;\r
701                 if ((B & 0x80) == 0)\r
702                     C = (char)B;\r
703                 else {\r
704                     n = 1;\r
705                     while ((B & (0x80 >>> n)) != 0) n++;\r
706                     if ((n == 1) || (n > 6))\r
707                         throw cx.reportRuntimeError0("msg.bad.uri");\r
708                     octets[0] = (char)B;\r
709                     if ((k + 3 * (n - 1)) >= str.length())\r
710                         throw cx.reportRuntimeError0("msg.bad.uri");\r
711                     for (j = 1; j < n; j++) {\r
712                         k++;\r
713                         if (str.charAt(k) != '%')\r
714                             throw cx.reportRuntimeError0("msg.bad.uri");\r
715                         if (!isHex(str.charAt(k + 1))\r
716                             || !isHex(str.charAt(k + 2)))\r
717                             throw cx.reportRuntimeError0("msg.bad.uri");\r
718                         B = unHex(str.charAt(k + 1)) * 16\r
719                             + unHex(str.charAt(k + 2));\r
720                         if ((B & 0xC0) != 0x80)\r
721                             throw cx.reportRuntimeError0("msg.bad.uri");\r
722                         k += 2;\r
723                         octets[j] = (char)B;\r
724                     }\r
725                     V = utf8ToOneUcs4Char(octets, n);\r
726                     if (V >= 0x10000) {\r
727                         V -= 0x10000;\r
728                         if (V > 0xFFFFF)\r
729                             throw cx.reportRuntimeError0("msg.bad.uri");\r
730                         C = (char)((V & 0x3FF) + 0xDC00);\r
731                         H = (char)((V >>> 10) + 0xD800);\r
732                         R.append(H);\r
733                     }\r
734                     else\r
735                         C = (char)V;\r
736                 }\r
737                 if (reservedSet.indexOf(C) != -1) {\r
738                     for (int x = 0; x < (k - start + 1); x++)\r
739                         R.append(str.charAt(start + x));\r
740                 }\r
741                 else\r
742                     R.append(C);\r
743             }\r
744             else\r
745                 R.append(C);\r
746             k++;\r
747         }\r
748         return R.toString();\r
749     }\r
750 \r
751     private static String uriReservedPlusPound = ";/?:@&=+$,#";\r
752     private static String uriUnescaped =\r
753                                         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";\r
754 \r
755     private String js_decodeURI(Context cx, Object[] args) {\r
756         String str = ScriptRuntime.toString(args, 0);\r
757         return decode(cx, str, uriReservedPlusPound);\r
758     }\r
759 \r
760     private String js_decodeURIComponent(Context cx, Object[] args) {\r
761         String str = ScriptRuntime.toString(args, 0);\r
762         return decode(cx, str, "");\r
763     }\r
764 \r
765     private Object js_encodeURI(Context cx, Object[] args) {\r
766         String str = ScriptRuntime.toString(args, 0);\r
767         return encode(cx, str, uriReservedPlusPound + uriUnescaped);\r
768     }\r
769 \r
770     private String js_encodeURIComponent(Context cx, Object[] args) {\r
771         String str = ScriptRuntime.toString(args, 0);\r
772         return encode(cx, str, uriUnescaped);\r
773     }\r
774 \r
775     /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be\r
776     * at least 6 bytes long.  Return the number of UTF-8 bytes of data written.\r
777     */\r
778     private static int oneUcs4ToUtf8Char(char[] utf8Buffer, int ucs4Char) {\r
779         int utf8Length = 1;\r
780 \r
781         //JS_ASSERT(ucs4Char <= 0x7FFFFFFF);\r
782         if ((ucs4Char & ~0x7F) == 0)\r
783             utf8Buffer[0] = (char)ucs4Char;\r
784         else {\r
785             int i;\r
786             int a = ucs4Char >>> 11;\r
787             utf8Length = 2;\r
788             while (a != 0) {\r
789                 a >>>= 5;\r
790                 utf8Length++;\r
791             }\r
792             i = utf8Length;\r
793             while (--i > 0) {\r
794                 utf8Buffer[i] = (char)((ucs4Char & 0x3F) | 0x80);\r
795                 ucs4Char >>>= 6;\r
796             }\r
797             utf8Buffer[0] = (char)(0x100 - (1 << (8-utf8Length)) + ucs4Char);\r
798         }\r
799         return utf8Length;\r
800     }\r
801 \r
802 \r
803     /* Convert a utf8 character sequence into a UCS-4 character and return that\r
804     * character.  It is assumed that the caller already checked that the sequence is valid.\r
805     */\r
806     private static int utf8ToOneUcs4Char(char[] utf8Buffer, int utf8Length) {\r
807         int ucs4Char;\r
808         int k = 0;\r
809         //JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);\r
810         if (utf8Length == 1) {\r
811             ucs4Char = utf8Buffer[0];\r
812             //            JS_ASSERT(!(ucs4Char & 0x80));\r
813         } else {\r
814             //JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length))));\r
815             ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1);\r
816             while (--utf8Length > 0) {\r
817                 //JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);\r
818                 ucs4Char = ucs4Char<<6 | (utf8Buffer[k++] & 0x3F);\r
819             }\r
820         }\r
821         return ucs4Char;\r
822     }\r
823 \r
824     private static final int\r
825         Id_decodeURI           =  1,\r
826         Id_decodeURIComponent  =  2,\r
827         Id_encodeURI           =  3,\r
828         Id_encodeURIComponent  =  4,\r
829         Id_escape              =  5,\r
830         Id_eval                =  6,\r
831         Id_isFinite            =  7,\r
832         Id_isNaN               =  8,\r
833         Id_parseFloat          =  9,\r
834         Id_parseInt            = 10,\r
835         Id_unescape            = 11,\r
836 \r
837         LAST_SCOPE_FUNCTION_ID = 11,\r
838 \r
839         Id_new_CommonError     = 12;\r
840 \r
841     private boolean scopeSlaveFlag;\r
842 \r
843 }\r