/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.script.js.engine; import java.io.Reader; import java.io.StringReader; import javax.script.AbstractScriptEngine; import javax.script.Bindings; import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptException; import javax.script.SimpleBindings; import org.mozilla.javascript.Context; import org.mozilla.javascript.EcmaError; import org.mozilla.javascript.Function; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.tools.shell.Global; public class CommonJSEngine extends AbstractScriptEngine implements Invocable { private CommonJSEngineFactory factory; public CommonJSEngine() { this(new CommonJSEngineFactory(null)); } public CommonJSEngine(CommonJSEngineFactory factory) { this.factory = factory; } @Override public Bindings createBindings() { return new SimpleBindings(); } @Override public Object eval(String script, ScriptContext context) throws ScriptException { if (script == null) { throw new NullPointerException("Null script"); } return eval(new StringReader(script) , context); } @Override public Object eval(Reader reader, ScriptContext context) throws ScriptException { String filename = (String) get(ScriptEngine.FILENAME); if (filename == null) { filename = "<Unknown Source>"; } Object result; EngineScope scope = new EngineScope(context); Global global = getGlobal(); scope.setParentScope(global); scope.setPrototype(global); Context cx = enterContext(); try { scope.put("exports", scope, cx.newObject(global)); result = cx.evaluateReader(scope, reader, filename, 1, null); } catch (EcmaError e) { throw new ScriptException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber()); } catch (Exception e) { throw new ScriptException(e); } finally { Context.exit(); } return result; } @Override public ScriptEngineFactory getFactory() { return factory; } private Global getGlobal() { return factory.getGlobal(); } @Override public <T> T getInterface(Class<T> cls) { throw new RuntimeException("getInterface not implemented"); } @Override public <T> T getInterface(Object thisObj, Class<T> cls) { throw new RuntimeException("getInterface not implemented"); } @Override public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException { return invokeMethod(null, name, args); } @Override public Object invokeMethod(Object thisObj, String name, Object... args) throws ScriptException, NoSuchMethodException { if (name == null) { throw new NullPointerException("Method name is null"); } if (thisObj == null) { thisObj = getGlobal(); } else { if (!(thisObj instanceof Scriptable)) { thisObj = Context.toObject(thisObj, getGlobal()); } } Object methodObj = ScriptableObject.getProperty((Scriptable) thisObj, name); if (!(methodObj instanceof Function)) { throw new NoSuchMethodException("No such method: " + name); } Function method = (Function) methodObj; Scriptable scope = method.getParentScope(); if (scope == null) { scope = getGlobal(); } Context cx = enterContext(); Object result; try { result = method.call(cx, scope, (Scriptable) thisObj, args); } finally { Context.exit(); } return result; } /** * Associate a context with the current thread. This calls Context.enter() * and sets the language version to 1.8. * @return a Context associated with the thread */ public static Context enterContext() { Context cx = Context.enter(); cx.setLanguageVersion(Context.VERSION_1_8); return cx; } }