
package orc.run.porce;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Introspectable;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.RootNode;
import orc.ast.porc.PorcAST;
import orc.run.porce.runtime.PorcEClosure;

@ImportStatic({ SpecializationConfiguration.class })
@Introspectable
public class NewContinuation extends Expression {
	@Children
	protected final Expression[] capturedVariables;
	protected final RootCallTarget callTarget;

	private volatile PorcEClosure closureCache = null;

	@CompilerDirectives.CompilationFinal
	private volatile long closureCacheChangeCount = 0;
	@CompilerDirectives.CompilationFinal
	private volatile long closureCacheUseCount = 0;

	static class StopCachingException extends ControlFlowException {
		private static final long serialVersionUID = 1L;
	}

	@Override
	public void setPorcAST(final PorcAST ast) {
		((PorcERootNode) callTarget.getRootNode()).setPorcAST(ast);
		super.setPorcAST(ast);
	}

	protected NewContinuation(final Expression[] capturedVariables, final RootNode rootNode) {
		this.capturedVariables = capturedVariables;
		this.callTarget = rootNode.getCallTarget();
	}

	boolean isCachable() {
		if (CompilerDirectives.inInterpreter() && closureCacheChangeCount > 0) {
			long usePerChange = closureCacheUseCount / closureCacheChangeCount;
			return usePerChange >= 7 || closureCacheUseCount < 100;
		} else {
			return true;
		}
	}

	@Specialization(guards = { "EnvironmentCaching" }, rewriteOn = StopCachingException.class)
	@ExplodeLoop
	public Object cached(final VirtualFrame frame) {
		CompilerAsserts.compilationConstant(capturedVariables.length);

		if (!isCachable()) {
			CompilerAsserts.neverPartOfCompilation("Cache invalidation should not be in compiled code.");
			throw new StopCachingException();
		}

		PorcEClosure closure = closureCache;

		if (closure != null) {
			// If we have a cached closure then check that the cache is
			// still valid.
			for (int i = 0; i < capturedVariables.length; i++) { // Contains break
				if (closure.environment[i] != capturedVariables[i].execute(frame)) {
					closure = null;
					break;
				}
			}
		}

		if (closure == null) {
			// If we don't have a cached closure build one and set the
			// cache.
			Object[] capturedValues = new Object[capturedVariables.length];
			for (int i = 0; i < capturedVariables.length; i++) {
				capturedValues[i] = capturedVariables[i].execute(frame);
			}

			closure = new PorcEClosure(capturedValues, callTarget, false);
			closureCache = closure;

			if (CompilerDirectives.inInterpreter())
				closureCacheChangeCount++;
		}

		if (CompilerDirectives.inInterpreter()) {
			closureCacheUseCount++;
			long useCount = closureCacheUseCount;

			if (useCount > 5000) {
				closureCacheUseCount = 0;
				closureCacheChangeCount = 0;
			}
		}

		return closure;
	}

	@Specialization(replaces = { "cached" })
	@ExplodeLoop
	public Object universal(final VirtualFrame frame) {
		final Object[] capturedValues = new Object[capturedVariables.length];
		for (int i = 0; i < capturedVariables.length; i++) {
			capturedValues[i] = capturedVariables[i].execute(frame);
		}
		return new PorcEClosure(capturedValues, callTarget, false);
	}

	public static NewContinuation create(final Expression[] capturedVariables, final RootNode rootNode) {
		return NewContinuationNodeGen.create(capturedVariables, rootNode);
	}
}

@@ -512,16 +512,72 @@
 otNode;%0A
+import com.oracle.truffle.api.nodes.SlowPathException;%0A%0A
 import o
@@ -1104,16 +1104,22 @@
 %09static 
+final 
 class St
@@ -1145,27 +1145,24 @@
 extends 
-ControlFlow
+SlowPath
 Exceptio
@@ -1556,16 +1556,24 @@
 );%0A%09%7D%0A%0A%09
+private 
 boolean 
@@ -1976,24 +1976,52 @@
 Frame frame)
+ throws StopCachingException
  %7B%0A%09%09Compile
@@ -2077,16 +2077,21 @@
 ngth);%0A%0A
+%09%09/*%0A
 %09%09if (!i
@@ -2238,24 +2238,29 @@
 ption();%0A%09%09%7D
+%0A%09%09*/
 %0A%0A%09%09PorcEClo
@@ -2569,21 +2569,122 @@
 l;%0A%09%09%09%09%09
-break
+// If we had a cached closure and invalidated it, don't cache again.%0A%09%09%09%09%09throw new StopCachingException()
 ;%0A%09%09%09%09%7D%0A
@@ -3077,16 +3077,22 @@
 osure;%0A%0A
+%09%09%09/*%0A
 %09%09%09if (C
@@ -3159,18 +3159,30 @@
 nt++;%0A%09%09
-%7D%0A
+%09%09*/%0A%09%09%7D%0A%0A%09%09/*
 %0A%09%09if (C
@@ -3380,24 +3380,29 @@
  0;%0A%09%09%09%7D%0A%09%09%7D
+%0A%09%09*/
 %0A%0A%09%09return c

Disable closure caching after any validation