| /* |
| * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.script.javascript; |
| import com.sun.script.util.*; |
| import javax.script.*; |
| import sun.org.mozilla.javascript.internal.*; |
| import java.lang.reflect.Method; |
| import java.io.*; |
| import java.security.*; |
| import java.util.*; |
| |
| |
| /** |
| * Implementation of <code>ScriptEngine</code> using the Mozilla Rhino |
| * interpreter. |
| * |
| * @author Mike Grogan |
| * @author A. Sundararajan |
| * @since 1.6 |
| */ |
| public final class RhinoScriptEngine extends AbstractScriptEngine |
| implements Invocable, Compilable { |
| |
| private static final boolean DEBUG = false; |
| |
| private AccessControlContext accCtxt; |
| |
| /* Scope where standard JavaScript objects and our |
| * extensions to it are stored. Note that these are not |
| * user defined engine level global variables. These are |
| * variables have to be there on all compliant ECMAScript |
| * scopes. We put these standard objects in this top level. |
| */ |
| private RhinoTopLevel topLevel; |
| |
| /* map used to store indexed properties in engine scope |
| * refer to comment on 'indexedProps' in ExternalScriptable.java. |
| */ |
| private Map<Object, Object> indexedProps; |
| |
| private ScriptEngineFactory factory; |
| private InterfaceImplementor implementor; |
| |
| private static final int languageVersion = getLanguageVersion(); |
| private static final int optimizationLevel = getOptimizationLevel(); |
| |
| static { |
| ContextFactory.initGlobal(new ContextFactory() { |
| /** |
| * Create new Context instance to be associated with the current thread. |
| */ |
| @Override |
| protected Context makeContext() { |
| Context cx = super.makeContext(); |
| cx.setLanguageVersion(languageVersion); |
| cx.setOptimizationLevel(optimizationLevel); |
| cx.setClassShutter(RhinoClassShutter.getInstance()); |
| cx.setWrapFactory(RhinoWrapFactory.getInstance()); |
| return cx; |
| } |
| |
| /** |
| * Execute top call to script or function. When the runtime is about to |
| * execute a script or function that will create the first stack frame |
| * with scriptable code, it calls this method to perform the real call. |
| * In this way execution of any script happens inside this function. |
| */ |
| @Override |
| protected Object doTopCall(final Callable callable, |
| final Context cx, final Scriptable scope, |
| final Scriptable thisObj, final Object[] args) { |
| AccessControlContext accCtxt = null; |
| Scriptable global = ScriptableObject.getTopLevelScope(scope); |
| Scriptable globalProto = global.getPrototype(); |
| if (globalProto instanceof RhinoTopLevel) { |
| accCtxt = ((RhinoTopLevel)globalProto).getAccessContext(); |
| } |
| |
| if (accCtxt != null) { |
| return AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| return superDoTopCall(callable, cx, scope, thisObj, args); |
| } |
| }, accCtxt); |
| } else { |
| return superDoTopCall(callable, cx, scope, thisObj, args); |
| } |
| } |
| |
| private Object superDoTopCall(Callable callable, |
| Context cx, Scriptable scope, |
| Scriptable thisObj, Object[] args) { |
| return super.doTopCall(callable, cx, scope, thisObj, args); |
| } |
| }); |
| } |
| |
| private static final String RHINO_JS_VERSION = "rhino.js.version"; |
| private static int getLanguageVersion() { |
| int version; |
| String tmp = java.security.AccessController.doPrivileged( |
| new sun.security.action.GetPropertyAction(RHINO_JS_VERSION)); |
| if (tmp != null) { |
| version = Integer.parseInt((String)tmp); |
| } else { |
| version = Context.VERSION_1_8; |
| } |
| return version; |
| } |
| |
| private static final String RHINO_OPT_LEVEL = "rhino.opt.level"; |
| private static int getOptimizationLevel() { |
| int optLevel = -1; |
| // disable optimizer under security manager, for now. |
| if (System.getSecurityManager() == null) { |
| optLevel = Integer.getInteger(RHINO_OPT_LEVEL, -1); |
| } |
| return optLevel; |
| } |
| |
| /** |
| * Creates a new instance of RhinoScriptEngine |
| */ |
| public RhinoScriptEngine() { |
| if (System.getSecurityManager() != null) { |
| try { |
| AccessController.checkPermission(new AllPermission()); |
| } catch (AccessControlException ace) { |
| accCtxt = AccessController.getContext(); |
| } |
| } |
| |
| Context cx = enterContext(); |
| try { |
| topLevel = new RhinoTopLevel(cx, this); |
| } finally { |
| cx.exit(); |
| } |
| |
| indexedProps = new HashMap<Object, Object>(); |
| |
| //construct object used to implement getInterface |
| implementor = new InterfaceImplementor(this) { |
| protected boolean isImplemented(Object thiz, Class<?> iface) { |
| Context cx = enterContext(); |
| try { |
| if (thiz != null && !(thiz instanceof Scriptable)) { |
| thiz = cx.toObject(thiz, topLevel); |
| } |
| Scriptable engineScope = getRuntimeScope(context); |
| Scriptable localScope = (thiz != null)? (Scriptable) thiz : |
| engineScope; |
| for (Method method : iface.getMethods()) { |
| // ignore methods of java.lang.Object class |
| if (method.getDeclaringClass() == Object.class) { |
| continue; |
| } |
| Object obj = ScriptableObject.getProperty(localScope, method.getName()); |
| if (! (obj instanceof Function)) { |
| return false; |
| } |
| } |
| return true; |
| } finally { |
| cx.exit(); |
| } |
| } |
| |
| protected Object convertResult(Method method, Object res) |
| throws ScriptException { |
| Class desiredType = method.getReturnType(); |
| if (desiredType == Void.TYPE) { |
| return null; |
| } else { |
| return Context.jsToJava(res, desiredType); |
| } |
| } |
| }; |
| } |
| |
| public Object eval(Reader reader, ScriptContext ctxt) |
| throws ScriptException { |
| Object ret; |
| |
| Context cx = enterContext(); |
| try { |
| Scriptable scope = getRuntimeScope(ctxt); |
| String filename = (String) get(ScriptEngine.FILENAME); |
| filename = filename == null ? "<Unknown source>" : filename; |
| |
| ret = cx.evaluateReader(scope, reader, filename , 1, null); |
| } catch (RhinoException re) { |
| if (DEBUG) re.printStackTrace(); |
| int line = (line = re.lineNumber()) == 0 ? -1 : line; |
| String msg; |
| if (re instanceof JavaScriptException) { |
| msg = String.valueOf(((JavaScriptException)re).getValue()); |
| } else { |
| msg = re.toString(); |
| } |
| ScriptException se = new ScriptException(msg, re.sourceName(), line); |
| se.initCause(re); |
| throw se; |
| } catch (IOException ee) { |
| throw new ScriptException(ee); |
| } finally { |
| cx.exit(); |
| } |
| |
| return unwrapReturnValue(ret); |
| } |
| |
| public Object eval(String script, ScriptContext ctxt) throws ScriptException { |
| if (script == null) { |
| throw new NullPointerException("null script"); |
| } |
| return eval(new StringReader(script) , ctxt); |
| } |
| |
| public ScriptEngineFactory getFactory() { |
| if (factory != null) { |
| return factory; |
| } else { |
| return new RhinoScriptEngineFactory(); |
| } |
| } |
| |
| public Bindings createBindings() { |
| return new SimpleBindings(); |
| } |
| |
| //Invocable methods |
| public Object invokeFunction(String name, Object... args) |
| throws ScriptException, NoSuchMethodException { |
| return invoke(null, name, args); |
| } |
| |
| public Object invokeMethod(Object thiz, String name, Object... args) |
| throws ScriptException, NoSuchMethodException { |
| if (thiz == null) { |
| throw new IllegalArgumentException("script object can not be null"); |
| } |
| return invoke(thiz, name, args); |
| } |
| |
| private Object invoke(Object thiz, String name, Object... args) |
| throws ScriptException, NoSuchMethodException { |
| Context cx = enterContext(); |
| try { |
| if (name == null) { |
| throw new NullPointerException("method name is null"); |
| } |
| |
| if (thiz != null && !(thiz instanceof Scriptable)) { |
| thiz = cx.toObject(thiz, topLevel); |
| } |
| |
| Scriptable engineScope = getRuntimeScope(context); |
| Scriptable localScope = (thiz != null)? (Scriptable) thiz : |
| engineScope; |
| Object obj = ScriptableObject.getProperty(localScope, name); |
| if (! (obj instanceof Function)) { |
| throw new NoSuchMethodException("no such method: " + name); |
| } |
| |
| Function func = (Function) obj; |
| Scriptable scope = func.getParentScope(); |
| if (scope == null) { |
| scope = engineScope; |
| } |
| Object result = func.call(cx, scope, localScope, |
| wrapArguments(args)); |
| return unwrapReturnValue(result); |
| } catch (RhinoException re) { |
| if (DEBUG) re.printStackTrace(); |
| int line = (line = re.lineNumber()) == 0 ? -1 : line; |
| ScriptException se = new ScriptException(re.toString(), re.sourceName(), line); |
| se.initCause(re); |
| throw se; |
| } finally { |
| cx.exit(); |
| } |
| } |
| |
| public <T> T getInterface(Class<T> clasz) { |
| try { |
| return implementor.getInterface(null, clasz); |
| } catch (ScriptException e) { |
| return null; |
| } |
| } |
| |
| public <T> T getInterface(Object thiz, Class<T> clasz) { |
| if (thiz == null) { |
| throw new IllegalArgumentException("script object can not be null"); |
| } |
| |
| try { |
| return implementor.getInterface(thiz, clasz); |
| } catch (ScriptException e) { |
| return null; |
| } |
| } |
| |
| private static final String printSource = |
| "function print(str, newline) { \n" + |
| " if (typeof(str) == 'undefined') { \n" + |
| " str = 'undefined'; \n" + |
| " } else if (str == null) { \n" + |
| " str = 'null'; \n" + |
| " } \n" + |
| " var out = context.getWriter(); \n" + |
| " if (!(out instanceof java.io.PrintWriter))\n" + |
| " out = new java.io.PrintWriter(out); \n" + |
| " out.print(String(str)); \n" + |
| " if (newline) out.print('\\n'); \n" + |
| " out.flush(); \n" + |
| "}\n" + |
| "function println(str) { \n" + |
| " print(str, true); \n" + |
| "}"; |
| |
| Scriptable getRuntimeScope(ScriptContext ctxt) { |
| if (ctxt == null) { |
| throw new NullPointerException("null script context"); |
| } |
| |
| // we create a scope for the given ScriptContext |
| Scriptable newScope = new ExternalScriptable(ctxt, indexedProps); |
| |
| // Set the prototype of newScope to be 'topLevel' so that |
| // JavaScript standard objects are visible from the scope. |
| newScope.setPrototype(topLevel); |
| |
| // define "context" variable in the new scope |
| newScope.put("context", newScope, ctxt); |
| |
| // define "print", "println" functions in the new scope |
| Context cx = enterContext(); |
| try { |
| cx.evaluateString(newScope, printSource, "print", 1, null); |
| } finally { |
| cx.exit(); |
| } |
| return newScope; |
| } |
| |
| |
| //Compilable methods |
| public CompiledScript compile(String script) throws ScriptException { |
| return compile(new StringReader(script)); |
| } |
| |
| public CompiledScript compile(java.io.Reader script) throws ScriptException { |
| CompiledScript ret = null; |
| Context cx = enterContext(); |
| |
| try { |
| String fileName = (String) get(ScriptEngine.FILENAME); |
| if (fileName == null) { |
| fileName = "<Unknown Source>"; |
| } |
| |
| Scriptable scope = getRuntimeScope(context); |
| Script scr = cx.compileReader(scope, script, fileName, 1, null); |
| ret = new RhinoCompiledScript(this, scr); |
| } catch (Exception e) { |
| if (DEBUG) e.printStackTrace(); |
| throw new ScriptException(e); |
| } finally { |
| cx.exit(); |
| } |
| return ret; |
| } |
| |
| |
| //package-private helpers |
| |
| static Context enterContext() { |
| // call this always so that initializer of this class runs |
| // and initializes custom wrap factory and class shutter. |
| return Context.enter(); |
| } |
| |
| void setEngineFactory(ScriptEngineFactory fac) { |
| factory = fac; |
| } |
| |
| AccessControlContext getAccessContext() { |
| return accCtxt; |
| } |
| |
| Object[] wrapArguments(Object[] args) { |
| if (args == null) { |
| return Context.emptyArgs; |
| } |
| Object[] res = new Object[args.length]; |
| for (int i = 0; i < res.length; i++) { |
| res[i] = Context.javaToJS(args[i], topLevel); |
| } |
| return res; |
| } |
| |
| Object unwrapReturnValue(Object result) { |
| if (result instanceof Wrapper) { |
| result = ( (Wrapper) result).unwrap(); |
| } |
| |
| return result instanceof Undefined ? null : result; |
| } |
| |
| /* |
| public static void main(String[] args) throws Exception { |
| if (args.length == 0) { |
| System.out.println("No file specified"); |
| return; |
| } |
| |
| InputStreamReader r = new InputStreamReader(new FileInputStream(args[0])); |
| ScriptEngine engine = new RhinoScriptEngine(); |
| |
| engine.put("x", "y"); |
| engine.put(ScriptEngine.FILENAME, args[0]); |
| engine.eval(r); |
| System.out.println(engine.get("x")); |
| } |
| */ |
| } |