/* * Copyright 2012 Jason Miller * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jj.script; import javax.inject.Inject; import javax.inject.Provider; import jj.event.Publisher; import jj.util.Closer; import org.mozilla.javascript.Callable; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.ContinuationPending; import org.mozilla.javascript.Function; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.json.JsonParser; /** * <p> * Thin wrapper around the rhino context mechanism to take advantage of * try-with-resources, and to facilitate mocking and ensure all script * errors are logged * * <p> * Inject this as a {@link Provider}, then use it like: * <pre class="brush:java"> * try (RhinoContext context = contextProvider.get()) { * context.whatever(); * // and so on * } * </pre> * @author jason * */ public class RhinoContext implements Closer { private final Context context; private boolean closed = false; private final Publisher publisher; private final ContextFactory contextFactory = new ContextFactory() { @Override public boolean hasFeature(Context cx, int featureIndex) { return featureIndex == Context.FEATURE_LOCATION_INFORMATION_IN_ERROR || featureIndex == Context.FEATURE_STRICT_MODE || //featureIndex == Context.FEATURE_WARNING_AS_ERROR || super.hasFeature(cx, featureIndex); } }; @Inject RhinoContext(final Publisher publisher) { this.publisher = publisher; this.context = contextFactory.enterContext(); context.setLanguageVersion(Context.VERSION_1_8); context.setOptimizationLevel(-1); } public RhinoContext withoutContinuations() { assertNotClosed(); context.setOptimizationLevel(1); return this; } private void assertNotClosed() { assert !closed : "no performing operations on a context that has been closed!"; } @Override public void close() { closed = true; Context.exit(); } public ContinuationPending captureContinuation() { assertNotClosed(); return context.captureContinuation(); } public ScriptableObject initStandardObjects() { assertNotClosed(); return context.initStandardObjects(); } public ScriptableObject initStandardObjects(boolean sealed) { assertNotClosed(); return context.initStandardObjects(null, sealed); } public ScriptableObject newObject(Scriptable scope) { assertNotClosed(); return (ScriptableObject)context.newObject(scope); } public Scriptable newArray(Scriptable scope, int length) { assertNotClosed(); return context.newArray(scope, length); } public ScriptableObject newChainedScope(Scriptable prototype) { assertNotClosed(); ScriptableObject local = newObject(prototype); local.setPrototype(prototype); local.setParentScope(null); return local; } public JsonParser newJsonParser(Scriptable scope) { assertNotClosed(); return new JsonParser(context, scope); } private void publishRhinoException(String description, RhinoException re) { publisher.publish(new ScriptError(description, re)); } public Function compileFunction(final Scriptable scope, final String source, final String sourceName) { assertNotClosed(); try { return context.compileFunction(scope, source, sourceName, 1, null); } catch (RhinoException re) { publishRhinoException("script error compiling a function\n{}", re); throw re; } } public Script compileString(final String source, final String sourceName) { assertNotClosed(); try { return context.compileString(source, sourceName, 1, null); } catch (RhinoException re) { publishRhinoException("script error compiling a function\n{}", re); throw re; } } public Object callFunction(Function function, Scriptable scope, Scriptable thisObj, Object...args) { assertNotClosed(); try { return function.call(context, scope, thisObj, args); } catch (RhinoException re) { publishRhinoException("script error executing a function\n{}\n{}", re); throw re; } } public Object evaluateString(Scriptable scope, String source, String sourceName) { assertNotClosed(); try { return context.evaluateString(scope, source, sourceName, 1, null); } catch (RhinoException re) { publishRhinoException("script error evaluating a string\n{}\n{}", re); throw re; } } public Object executeScript(Script script, Scriptable scope) { assertNotClosed(); try { return script.exec(context, scope); } catch (RhinoException re) { publishRhinoException("script error during execution\n{}\n{}", re); throw re; } } public Object executeScriptWithContinuations(Script script, Scriptable scope) { assertNotClosed(); try { return context.executeScriptWithContinuations(script, scope); } catch (RhinoException re) { publishRhinoException("script error during execution\n{}\n{}", re); throw re; } } public Object callFunctionWithContinuations(Callable function, Scriptable scope, Object[] args) { assertNotClosed(); try { return context.callFunctionWithContinuations(function, scope, args); } catch (RhinoException re) { publishRhinoException("script error during function execution execution\n{}\n{}", re); throw re; } } public Object resumeContinuation(Object continuation, Scriptable scope, Object result) { assertNotClosed(); try { return context.resumeContinuation(continuation, scope, result); } catch (RhinoException re) { publishRhinoException("script error resuming a continuation\n{}\n{}", re); throw re; } } }