1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
\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
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
13 * The Original Code is Rhino code, released
\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
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
38 package org.mozilla.javascript;
\r
40 import java.io.StringReader;
\r
41 import java.io.IOException;
\r
44 * The JavaScript Script object.
\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
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
57 * @author Norris Boyd
\r
60 public class NativeScript extends NativeFunction implements Script {
\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
67 public NativeScript() {
\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
86 * Returns the name of this JavaScript class, "Script".
\r
88 public String getClassName() {
\r
93 * Initialize script.
\r
95 * Does nothing here, but scripts will override with code
\r
96 * to initialize contained functions, regexp literals, etc.
\r
98 public void initScript(Scriptable scope) {
\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
110 return super.methodArity(methodId);
\r
113 public Object execMethod(int methodId, IdFunction f, Context cx,
\r
114 Scriptable scope, Scriptable thisObj,
\r
116 throws JavaScriptException
\r
118 if (prototypeIdShift != 0) {
\r
119 switch (methodId - prototypeIdShift + 1) {
\r
120 case Id_constructor:
\r
121 return jsConstructor(cx, scope, args);
\r
124 return realThis(thisObj, f, true).
\r
125 jsFunction_toString(cx, args);
\r
128 return realThis(thisObj, f, true).jsFunction_exec();
\r
131 return realThis(thisObj, f, false).
\r
132 jsFunction_compile(ScriptRuntime.toString(args, 0));
\r
136 return super.execMethod(methodId, f, cx, scope, thisObj, args);
\r
139 private NativeScript realThis(Scriptable thisObj, IdFunction f,
\r
142 while (!(thisObj instanceof NativeScript)) {
\r
143 thisObj = nextInstanceCheck(thisObj, f, readOnly);
\r
145 return (NativeScript)thisObj;
\r
149 * The Java method defining the JavaScript Script constructor.
\r
152 private static Object jsConstructor(Context cx, Scriptable scope,
\r
155 String source = args.length == 0
\r
157 : ScriptRuntime.toString(args[0]);
\r
158 return compile(scope, source);
\r
161 public static Script compile(Scriptable scope, String source) {
\r
162 Context cx = Context.getContext();
\r
163 StringReader reader = new StringReader(source);
\r
165 int[] linep = { 0 };
\r
166 String filename = Context.getSourcePositionFromStack(linep);
\r
167 if (filename == null) {
\r
168 filename = "<Script object>";
\r
171 Object securityDomain =
\r
172 cx.getSecurityDomainForStackDepth(5);
\r
173 return cx.compileReader(scope, reader, filename, linep[0],
\r
176 catch (IOException e) {
\r
177 throw new RuntimeException("Unexpected IOException");
\r
181 private Scriptable jsFunction_compile(String source) {
\r
182 script = compile(null, source);
\r
186 private Object jsFunction_exec() throws JavaScriptException {
\r
187 throw Context.reportRuntimeError1
\r
188 ("msg.cant.call.indirect", "exec");
\r
191 private Object jsFunction_toString(Context cx, Object[] args)
\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
200 * Override method in NativeFunction to avoid ever returning "anonymous"
\r
202 public String getFunctionName() {
\r
207 * Execute the script.
\r
209 * Will be overridden by generated scripts; needed to implement Script.
\r
211 public Object exec(Context cx, Scriptable scope)
\r
212 throws JavaScriptException
\r
214 return script == null ? Undefined.instance : script.exec(cx, scope);
\r
217 public Object call(Context cx, Scriptable scope, Scriptable thisObj,
\r
219 throws JavaScriptException
\r
221 return exec(cx, scope);
\r
224 public Scriptable construct(Context cx, Scriptable scope, Object[] args)
\r
225 throws JavaScriptException
\r
227 throw Context.reportRuntimeError0("msg.script.is.not.constructor");
\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
239 return super.getIdName(id);
\r
242 protected int mapNameToId(String s) {
\r
243 if (prototypeIdShift != 0) {
\r
244 int id = toPrototypeId(s);
\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
252 return super.mapNameToId(s);
\r
257 private static int toPrototypeId(String s) {
\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
267 if (X!=null && X!=s && !X.equals(s)) id = 0;
\r
273 private static final int
\r
274 Id_constructor = 1,
\r
278 MAX_PROTOTYPE_ID = 4;
\r
280 // #/string_id_map#
\r
282 private Script script;
\r
284 private int prototypeIdShift;
\r