/* * 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 javax.inject.Singleton; import org.mozilla.javascript.ContinuationPending; import jj.execution.ExecutionInstance; import jj.script.module.RootScriptEnvironment; import jj.util.Closer; /** * * * @author jason * */ @Singleton public class CurrentScriptEnvironment extends ExecutionInstance<ScriptEnvironment<?>> { private final Provider<RhinoContext> contextProvider; @Inject CurrentScriptEnvironment(final Provider<RhinoContext> contextProvider) { this.contextProvider = contextProvider; } /** * internal method, to allow continuation resumption to also involve environment-dependent * context restoration */ Closer enterScope(final AbstractScriptEnvironment<?> scriptEnvironment, final PendingKey pendingKey) { final Closer environmentCloser = enterScope(scriptEnvironment); final Closer contextCloser = scriptEnvironment.restoreContextForKey(pendingKey); return () -> { environmentCloser.close(); contextCloser.close(); }; } protected AbstractScriptEnvironment<?> innerCurrent() { return (AbstractScriptEnvironment<?>)current(); } public <T extends ScriptEnvironment<?>> T currentAs(Class<T> environmentClass) { return environmentClass.cast(current()); } /** * Prepares a continuation and throws it. Returns the ContinuationPending * so that callers can write * <code>throw context.prepareContinuation(...)</code> * so the compiler stays happy, but this method never returns normally * * the continuation will be configured to restore the state of the script environment * on resumption */ public ContinuationPending preparedContinuation(Continuation continuation) { assert current() != null : "can't perform a continuation unless a script is in context"; ContinuationPending result = prepareContinuation(new ContinuationState(continuation)); PendingKey pendingKey = innerCurrent().createContinuationContext(result); continuation.pendingKey(pendingKey); throw result; } /** * captures a continuation from the rhino interpreter, and stores the information * necessary for resumption */ private ContinuationPending prepareContinuation(ContinuationState continuationState) { try(RhinoContext context = contextProvider.get()) { ContinuationPending continuation = context.captureContinuation(); continuation.setApplicationState(continuationState); return continuation; } catch (Exception e) { throw new AssertionError("could not capture a continuation", e); } } public RootScriptEnvironment<?> currentRootScriptEnvironment() { ScriptEnvironment<?> current = current(); while (current instanceof ChildScriptEnvironment) { current = ((ChildScriptEnvironment<?>)current).parent(); } assert current instanceof RootScriptEnvironment : "declare your root!"; return (RootScriptEnvironment<?>)current; } }