2003/05/12 05:10:30
[org.ibex.core.git] / src / org / mozilla / javascript / NativeScript.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  * Roger Lawrence\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 \r
43 /**\r
44  * The JavaScript Script object.\r
45  *\r
46  * Note that the C version of the engine uses XDR as the format used\r
47  * by freeze and thaw. Since this depends on the internal format of\r
48  * structures in the C runtime, we cannot duplicate it.\r
49  *\r
50  * Since we cannot replace 'this' as a result of the compile method,\r
51  * this class has a dual nature. Generated scripts will have a null\r
52  * 'script' field and will override 'exec' and 'call'. Scripts created\r
53  * using the JavaScript constructor will forward requests to the\r
54  * nonnull 'script' field.\r
55  *\r
56  * @since 1.3\r
57  * @author Norris Boyd\r
58  */\r
59 \r
60 public class NativeScript extends NativeFunction implements Script {\r
61 \r
62     public static void init(Context cx, Scriptable scope, boolean sealed) {\r
63         NativeScript obj = new NativeScript();\r
64         obj.scopeInit(cx, scope, sealed);\r
65     }\r
66 \r
67     public NativeScript() {\r
68     }\r
69 \r
70     private void scopeInit(Context cx, Scriptable scope, boolean sealed) {\r
71         // prototypeIdShift != 0 serves as indicator of prototype instance\r
72         // and as id offset to take into account ids present in each instance\r
73         // of the base class NativeFunction. \r
74         // Not to depend on the assumption NativeFunction.maxInstanceId() != 0,\r
75         // 1 is added super.maxInstanceId() to make sure that \r
76         // prototypeIdShift != 0 in the NativeScript prototype.\r
77         // In a similar way the following methods use \r
78         // methodId - prototypeIdShift + 1, not methodId - prototypeIdShift\r
79         // to unshift prototype id to [1 .. MAX_PROTOTYPE_ID] interval\r
80         prototypeIdShift = super.maxInstanceId() + 1;\r
81         addAsPrototype(MAX_PROTOTYPE_ID + prototypeIdShift - 1, \r
82                        cx, scope, sealed);\r
83     }\r
84 \r
85     /**\r
86      * Returns the name of this JavaScript class, "Script".\r
87      */\r
88     public String getClassName() {\r
89         return "Script";\r
90     }\r
91 \r
92     /**\r
93      * Initialize script.\r
94      *\r
95      * Does nothing here, but scripts will override with code\r
96      * to initialize contained functions, regexp literals, etc.\r
97      */\r
98     public void initScript(Scriptable scope) {\r
99     }\r
100 \r
101     public int methodArity(int methodId) {\r
102         if (prototypeIdShift != 0) {\r
103             switch (methodId - prototypeIdShift + 1) {\r
104                 case Id_constructor: return 1;\r
105                 case Id_toString:    return 0;\r
106                 case Id_exec:        return 0;\r
107                 case Id_compile:     return 1;\r
108             }\r
109         }\r
110         return super.methodArity(methodId);\r
111     }\r
112 \r
113     public Object execMethod(int methodId, IdFunction f, Context cx,\r
114                              Scriptable scope, Scriptable thisObj, \r
115                              Object[] args)\r
116         throws JavaScriptException\r
117     {\r
118         if (prototypeIdShift != 0) {\r
119             switch (methodId - prototypeIdShift + 1) {\r
120                 case Id_constructor:\r
121                     return jsConstructor(cx, scope, args);\r
122 \r
123                 case Id_toString:\r
124                     return realThis(thisObj, f, true).\r
125                         jsFunction_toString(cx, args);\r
126 \r
127                 case Id_exec:\r
128                     return realThis(thisObj, f, true).jsFunction_exec();\r
129 \r
130                 case Id_compile:\r
131                     return realThis(thisObj, f, false).\r
132                         jsFunction_compile(ScriptRuntime.toString(args, 0));\r
133             }\r
134         }\r
135 \r
136         return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
137     }\r
138 \r
139     private NativeScript realThis(Scriptable thisObj, IdFunction f, \r
140                                   boolean readOnly)\r
141     {\r
142         while (!(thisObj instanceof NativeScript)) {\r
143             thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
144         }\r
145         return (NativeScript)thisObj;\r
146     }\r
147 \r
148     /**\r
149      * The Java method defining the JavaScript Script constructor.\r
150      *\r
151      */\r
152     private static Object jsConstructor(Context cx, Scriptable scope, \r
153                                         Object[] args)\r
154     {\r
155         String source = args.length == 0\r
156                         ? ""\r
157                         : ScriptRuntime.toString(args[0]);\r
158         return compile(scope, source);\r
159     }\r
160 \r
161     public static Script compile(Scriptable scope, String source) {\r
162         Context cx = Context.getContext();\r
163         StringReader reader = new StringReader(source);\r
164         try {\r
165             int[] linep = { 0 };\r
166             String filename = Context.getSourcePositionFromStack(linep);\r
167             if (filename == null) {\r
168                 filename = "<Script object>";\r
169                 linep[0] = 1;\r
170             }\r
171             Object securityDomain = \r
172                 cx.getSecurityDomainForStackDepth(5);\r
173             return cx.compileReader(scope, reader, filename, linep[0], \r
174                                     securityDomain);\r
175         }\r
176         catch (IOException e) {\r
177             throw new RuntimeException("Unexpected IOException");\r
178         }\r
179     }\r
180 \r
181     private Scriptable jsFunction_compile(String source) {\r
182         script = compile(null, source);\r
183         return this;\r
184     }\r
185 \r
186     private Object jsFunction_exec() throws JavaScriptException {\r
187         throw Context.reportRuntimeError1\r
188             ("msg.cant.call.indirect", "exec");\r
189     }\r
190 \r
191     private Object jsFunction_toString(Context cx, Object[] args)\r
192     {\r
193         Script thisScript = script;\r
194         if (thisScript == null) { thisScript = this; }\r
195         Scriptable scope = getTopLevelScope(this);\r
196         return cx.decompileScript(thisScript, scope, 0);\r
197     }\r
198     \r
199     /*\r
200      * Override method in NativeFunction to avoid ever returning "anonymous"\r
201      */\r
202     public String getFunctionName() {\r
203         return "";\r
204     }\r
205 \r
206     /**\r
207      * Execute the script.\r
208      *\r
209      * Will be overridden by generated scripts; needed to implement Script.\r
210      */\r
211     public Object exec(Context cx, Scriptable scope)\r
212         throws JavaScriptException\r
213     {\r
214         return script == null ? Undefined.instance : script.exec(cx, scope);\r
215     }\r
216 \r
217     public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
218                        Object[] args)\r
219         throws JavaScriptException\r
220     {\r
221         return exec(cx, scope);\r
222     }\r
223 \r
224     public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
225         throws JavaScriptException\r
226     {\r
227         throw Context.reportRuntimeError0("msg.script.is.not.constructor");\r
228     }\r
229 \r
230     protected String getIdName(int id) {\r
231         if (prototypeIdShift != 0) {\r
232             switch (id - prototypeIdShift + 1) {\r
233                 case Id_constructor: return "constructor";\r
234                 case Id_toString:    return "toString";\r
235                 case Id_exec:        return "exec";\r
236                 case Id_compile:     return "compile";\r
237             }\r
238         }\r
239         return super.getIdName(id);\r
240     }\r
241 \r
242     protected int mapNameToId(String s) {\r
243         if (prototypeIdShift != 0) {\r
244             int id = toPrototypeId(s);\r
245             if (id != 0) { \r
246                 // Shift [1, MAX_PROTOTYPE_ID] to\r
247                 // [super.maxInstanceId() + 1,\r
248                 //    super.maxInstanceId() + MAX_PROTOTYPE_ID]\r
249                 return id + prototypeIdShift - 1; \r
250             }\r
251         }\r
252         return super.mapNameToId(s);\r
253     }\r
254 \r
255 // #string_id_map#\r
256 \r
257     private static int toPrototypeId(String s) {\r
258         int id;\r
259 // #generated# Last update: 2001-05-23 13:25:01 GMT+02:00\r
260         L0: { id = 0; String X = null;\r
261             L: switch (s.length()) {\r
262             case 4: X="exec";id=Id_exec; break L;\r
263             case 7: X="compile";id=Id_compile; break L;\r
264             case 8: X="toString";id=Id_toString; break L;\r
265             case 11: X="constructor";id=Id_constructor; break L;\r
266             }\r
267             if (X!=null && X!=s && !X.equals(s)) id = 0;\r
268         }\r
269 // #/generated#\r
270         return id;\r
271     }\r
272 \r
273     private static final int\r
274         Id_constructor    = 1,\r
275         Id_toString       = 2,\r
276         Id_compile        = 3,\r
277         Id_exec           = 4,\r
278         MAX_PROTOTYPE_ID  = 4;\r
279 \r
280 // #/string_id_map#\r
281 \r
282     private Script script;\r
283 \r
284     private int prototypeIdShift;\r
285 }\r
286 \r