blob: d6a5798f57908a7895c47450cc1d4483dea7cef6 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.script.javascript;
27import com.sun.script.util.*;
28import javax.script.*;
29import sun.org.mozilla.javascript.internal.*;
30import java.lang.reflect.Method;
31import java.io.*;
32import java.util.*;
33
34
35/**
36 * Implementation of <code>ScriptEngine</code> using the Mozilla Rhino
37 * interpreter.
38 *
39 * @author Mike Grogan
40 * @author A. Sundararajan
41 * @since 1.6
42 */
43public final class RhinoScriptEngine extends AbstractScriptEngine
44 implements Invocable, Compilable {
45
46 private static final boolean DEBUG = false;
47
48 /* Scope where standard JavaScript objects and our
49 * extensions to it are stored. Note that these are not
50 * user defined engine level global variables. These are
51 * variables have to be there on all compliant ECMAScript
52 * scopes. We put these standard objects in this top level.
53 */
54 private RhinoTopLevel topLevel;
55
56 /* map used to store indexed properties in engine scope
57 * refer to comment on 'indexedProps' in ExternalScriptable.java.
58 */
59 private Map<Object, Object> indexedProps;
60
61 private ScriptEngineFactory factory;
62 private InterfaceImplementor implementor;
63
64 static {
65 ContextFactory.initGlobal(new ContextFactory() {
66 protected Context makeContext() {
67 Context cx = super.makeContext();
68 cx.setClassShutter(RhinoClassShutter.getInstance());
69 cx.setWrapFactory(RhinoWrapFactory.getInstance());
70 return cx;
71 }
72
73 public boolean hasFeature(Context cx, int feature) {
74 // we do not support E4X (ECMAScript for XML)!
75 if (feature == Context.FEATURE_E4X) {
76 return false;
77 } else {
78 return super.hasFeature(cx, feature);
79 }
80 }
81 });
82 }
83
84
85 /**
86 * Creates a new instance of RhinoScriptEngine
87 */
88 public RhinoScriptEngine() {
89
90 Context cx = enterContext();
91 try {
92 topLevel = new RhinoTopLevel(cx, this);
93 } finally {
94 cx.exit();
95 }
96
97 indexedProps = new HashMap<Object, Object>();
98
99 //construct object used to implement getInterface
100 implementor = new InterfaceImplementor(this) {
101 protected Object convertResult(Method method, Object res)
102 throws ScriptException {
103 Class desiredType = method.getReturnType();
104 if (desiredType == Void.TYPE) {
105 return null;
106 } else {
107 return Context.jsToJava(res, desiredType);
108 }
109 }
110 };
111 }
112
113 public Object eval(Reader reader, ScriptContext ctxt)
114 throws ScriptException {
115 Object ret;
116
117 Context cx = enterContext();
118 try {
119 Scriptable scope = getRuntimeScope(ctxt);
120 String filename = (String) get(ScriptEngine.FILENAME);
121 filename = filename == null ? "<Unknown source>" : filename;
122
123 ret = cx.evaluateReader(scope, reader, filename , 1, null);
124 } catch (RhinoException re) {
125 if (DEBUG) re.printStackTrace();
126 int line = (line = re.lineNumber()) == 0 ? -1 : line;
127 String msg;
128 if (re instanceof JavaScriptException) {
129 msg = String.valueOf(((JavaScriptException)re).getValue());
130 } else {
131 msg = re.toString();
132 }
133 ScriptException se = new ScriptException(msg, re.sourceName(), line);
134 se.initCause(re);
135 throw se;
136 } catch (IOException ee) {
137 throw new ScriptException(ee);
138 } finally {
139 cx.exit();
140 }
141
142 return unwrapReturnValue(ret);
143 }
144
145 public Object eval(String script, ScriptContext ctxt) throws ScriptException {
146 if (script == null) {
147 throw new NullPointerException("null script");
148 }
149 return eval(new StringReader(script) , ctxt);
150 }
151
152 public ScriptEngineFactory getFactory() {
153 if (factory != null) {
154 return factory;
155 } else {
156 return new RhinoScriptEngineFactory();
157 }
158 }
159
160 public Bindings createBindings() {
161 return new SimpleBindings();
162 }
163
164 //Invocable methods
165 public Object invokeFunction(String name, Object... args)
166 throws ScriptException, NoSuchMethodException {
167 return invoke(null, name, args);
168 }
169
170 public Object invokeMethod(Object thiz, String name, Object... args)
171 throws ScriptException, NoSuchMethodException {
172 if (thiz == null) {
173 throw new IllegalArgumentException("script object can not be null");
174 }
175 return invoke(thiz, name, args);
176 }
177
178 private Object invoke(Object thiz, String name, Object... args)
179 throws ScriptException, NoSuchMethodException {
180 Context cx = enterContext();
181 try {
182 if (name == null) {
183 throw new NullPointerException("method name is null");
184 }
185
186 if (thiz != null && !(thiz instanceof Scriptable)) {
187 thiz = cx.toObject(thiz, topLevel);
188 }
189
190 Scriptable engineScope = getRuntimeScope(context);
191 Scriptable localScope = (thiz != null)? (Scriptable) thiz :
192 engineScope;
193 Object obj = ScriptableObject.getProperty(localScope, name);
194 if (! (obj instanceof Function)) {
195 throw new NoSuchMethodException("no such method: " + name);
196 }
197
198 Function func = (Function) obj;
199 Scriptable scope = func.getParentScope();
200 if (scope == null) {
201 scope = engineScope;
202 }
203 Object result = func.call(cx, scope, localScope,
204 wrapArguments(args));
205 return unwrapReturnValue(result);
206 } catch (RhinoException re) {
207 if (DEBUG) re.printStackTrace();
208 int line = (line = re.lineNumber()) == 0 ? -1 : line;
209 throw new ScriptException(re.toString(), re.sourceName(), line);
210 } finally {
211 cx.exit();
212 }
213 }
214
215 public <T> T getInterface(Class<T> clasz) {
216 try {
217 return implementor.getInterface(null, clasz);
218 } catch (ScriptException e) {
219 return null;
220 }
221 }
222
223 public <T> T getInterface(Object thiz, Class<T> clasz) {
224 if (thiz == null) {
225 throw new IllegalArgumentException("script object can not be null");
226 }
227
228 try {
229 return implementor.getInterface(thiz, clasz);
230 } catch (ScriptException e) {
231 return null;
232 }
233 }
234
235 private static final String printSource =
236 "function print(str, newline) { \n" +
237 " if (typeof(str) == 'undefined') { \n" +
238 " str = 'undefined'; \n" +
239 " } else if (str == null) { \n" +
240 " str = 'null'; \n" +
241 " } \n" +
242 " var out = context.getWriter(); \n" +
243 " out.print(String(str)); \n" +
244 " if (newline) out.print('\\n'); \n" +
245 " out.flush(); \n" +
246 "}\n" +
247 "function println(str) { \n" +
248 " print(str, true); \n" +
249 "}";
250
251 Scriptable getRuntimeScope(ScriptContext ctxt) {
252 if (ctxt == null) {
253 throw new NullPointerException("null script context");
254 }
255
256 // we create a scope for the given ScriptContext
257 Scriptable newScope = new ExternalScriptable(ctxt, indexedProps);
258
259 // Set the prototype of newScope to be 'topLevel' so that
260 // JavaScript standard objects are visible from the scope.
261 newScope.setPrototype(topLevel);
262
263 // define "context" variable in the new scope
264 newScope.put("context", newScope, ctxt);
265
266 // define "print", "println" functions in the new scope
267 Context cx = enterContext();
268 try {
269 cx.evaluateString(newScope, printSource, "print", 1, null);
270 } finally {
271 cx.exit();
272 }
273 return newScope;
274 }
275
276
277 //Compilable methods
278 public CompiledScript compile(String script) throws ScriptException {
279 return compile(new StringReader(script));
280 }
281
282 public CompiledScript compile(java.io.Reader script) throws ScriptException {
283 CompiledScript ret = null;
284 Context cx = enterContext();
285
286 try {
287 String fileName = (String) get(ScriptEngine.FILENAME);
288 if (fileName == null) {
289 fileName = "<Unknown Source>";
290 }
291
292 Scriptable scope = getRuntimeScope(context);
293 Script scr = cx.compileReader(scope, script, fileName, 1, null);
294 ret = new RhinoCompiledScript(this, scr);
295 } catch (Exception e) {
296 if (DEBUG) e.printStackTrace();
297 throw new ScriptException(e);
298 } finally {
299 cx.exit();
300 }
301 return ret;
302 }
303
304
305 //package-private helpers
306
307 static Context enterContext() {
308 // call this always so that initializer of this class runs
309 // and initializes custom wrap factory and class shutter.
310 return Context.enter();
311 }
312
313 void setEngineFactory(ScriptEngineFactory fac) {
314 factory = fac;
315 }
316
317 Object[] wrapArguments(Object[] args) {
318 if (args == null) {
319 return Context.emptyArgs;
320 }
321 Object[] res = new Object[args.length];
322 for (int i = 0; i < res.length; i++) {
323 res[i] = Context.javaToJS(args[i], topLevel);
324 }
325 return res;
326 }
327
328 Object unwrapReturnValue(Object result) {
329 if (result instanceof Wrapper) {
330 result = ( (Wrapper) result).unwrap();
331 }
332
333 return result instanceof Undefined ? null : result;
334 }
335
336 public static void main(String[] args) throws Exception {
337 if (args.length == 0) {
338 System.out.println("No file specified");
339 return;
340 }
341
342 InputStreamReader r = new InputStreamReader(new FileInputStream(args[0]));
343 ScriptEngine engine = new RhinoScriptEngine();
344
345 engine.put("x", "y");
346 engine.put(ScriptEngine.FILENAME, args[0]);
347 engine.eval(r);
348 System.out.println(engine.get("x"));
349 }
350}