/** * Warlock, the open-source cross-platform game client * * Copyright 2008, Warlock LLC, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ /* * Created on Jan 17, 2005 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package cc.warlock.core.script.javascript; import java.io.Reader; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.Function; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.WrapFactory; import org.mozilla.javascript.WrappedException; import cc.warlock.core.client.IWarlockClientViewer; import cc.warlock.core.script.IScript; import cc.warlock.core.script.IScriptCommands; import cc.warlock.core.script.IScriptEngine; import cc.warlock.core.script.IScriptFileInfo; import cc.warlock.core.script.IScriptInfo; import cc.warlock.core.script.ScriptCommandsFactory; import cc.warlock.core.script.configuration.ScriptConfiguration; import cc.warlock.core.script.javascript.JavascriptScript.StopException; /** * @author Marshall * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class JavascriptEngine implements IScriptEngine { public static final String ENGINE_ID = "cc.warlock.script.javascript.JavascriptEngine"; protected ArrayList<IJavascriptVariableProvider> varProviders = new ArrayList<IJavascriptVariableProvider>(); protected ArrayList<JavascriptScript> runningScripts = new ArrayList<JavascriptScript>(); public Scriptable scope; private class WarlockContextFactory extends ContextFactory { protected Context makeContext() { Context cx = super.makeContext(); // Can't optimize if we want the instruction counter cx.setOptimizationLevel(-1); // Allow the user to break infinite loops or waits that don't use // our APIs (if such things exist). cx.setInstructionObserverThreshold(500); // Expose our API as JS variables rather than Java primitives WrapFactory wf = new WrapFactory(); wf.setJavaPrimitiveWrap(false); cx.setWrapFactory(wf); return cx; } protected void observeInstructionCount(Context cx, int instructionCount) { JavascriptScript script = null; // find our script by context for(JavascriptScript cur : runningScripts) { if(cur.getContext().equals(cx)) { script = cur; break; } } if(script == null) { System.out.println("Couldn't find context."); throw new Error(); } if (!script.isRunning()) throw script.new StopException(); } } public JavascriptEngine() { ContextFactory.initGlobal(new WarlockContextFactory()); } public String getScriptEngineId() { return ENGINE_ID; } public String getScriptEngineName() { return "Standard Javascript Engine (c) 2002-2007 Warlock Team"; } public void addVariableProvider (IJavascriptVariableProvider provider) { varProviders.add(provider); } public void removeVariableProvider (IJavascriptVariableProvider provider) { varProviders.remove(provider); } public boolean supports(IScriptInfo scriptInfo) { if (scriptInfo instanceof IScriptFileInfo) { IScriptFileInfo info = (IScriptFileInfo) scriptInfo; if (info.getExtension() != null) { List<String> extensions = ScriptConfiguration.instance().getEngineExtensions(ENGINE_ID); if (extensions != null && extensions.contains(info.getExtension().toLowerCase())) { return true; } } } return false; } protected static String includeFunction = "function include (src) { script.include(src); }"; protected static String includeFunctionName = "<warlock js:include>"; public IScript startScript(IScriptInfo info, final IWarlockClientViewer viewer, final String[] arguments) { // FIXME need to somehow get dependent IScriptCommands to pass into the following constructor IScriptCommands commands = ScriptCommandsFactory.getFactory().createScriptCommands(viewer, info.getScriptName()); final JavascriptScript script = new JavascriptScript(this, info, viewer, commands); script.start(); runningScripts.add(script); new Thread(new Runnable() { public void run () { script.getCommands().addThread(Thread.currentThread()); Context context = Context.enter(); try { script.setContext(context); scope = context.initStandardObjects(); scope.put("script", scope, script.getCommands()); scope.put("arguments", scope, arguments); Function function = context.compileFunction(scope, includeFunction, includeFunctionName, 0, null); scope.put("include", scope, function); for (IJavascriptVariableProvider provider : varProviders) { provider.loadVariables(script, scope); } ScriptableObject.defineClass(scope, MatchList.class); Reader reader = script.getScriptInfo().openReader(); Object result = context.evaluateReader(scope, reader, script.getName(), 1, null); System.err.println("script result: " + Context.toString(result)); reader.close(); } catch (WrappedException e) { if (!(e.getCause() instanceof Error)) { e.printStackTrace(); viewer.getWarlockClient().getDefaultStream().echo("[JS " + e.details() + " Script: " + script.getName() + " Line: " + e.lineNumber() + "]\n"); } } catch (RhinoException e) { e.printStackTrace(); viewer.getWarlockClient().getDefaultStream().echo("[JS " + e.details() + " Script: " + script.getName() + " Line: " + e.lineNumber() + "]\n"); } catch(StopException e) { // normal exit, do nothing } catch (Exception e) { e.printStackTrace(); viewer.getWarlockClient().getDefaultStream().echo("[unhandled exception in script: " + script.getName() + "]\n"); } finally { script.getCommands().removeThread(Thread.currentThread()); if(script.isRunning()) { script.stop(); } Context.exit(); runningScripts.remove(script); } } }).start(); return script; } public static Object loadString (String content) { Context context = Context.enter(); try { Scriptable scope = context.initStandardObjects(); Object result = context.evaluateString(scope, content, "<cmd>", 1, null); return result; } catch (Exception e) { e.printStackTrace(); return null; } finally { Context.exit(); } } public static void main (String args[]) { loadString("function helloWorld () { return 'Hello World'; } helloWorld(); "); } public Collection<? extends IScript> getRunningScripts() { return runningScripts; } }