package org.mozartoz.truffle.nodes.call;

import org.mozartoz.truffle.Options;
import org.mozartoz.truffle.nodes.OzNode;
import org.mozartoz.truffle.runtime.OzBacktrace;
import org.mozartoz.truffle.runtime.SelfTailCallException;

import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.RepeatingNode;

public class SelfTailCallCatcherNode extends OzNode {

	OzNode body; // For serialization compliance
	@Child LoopNode loopNode;

	public SelfTailCallCatcherNode(OzNode body) {
		this.body = body;
		this.loopNode = Truffle.getRuntime().createLoopNode(new SelfTailCallLoopNode(body));
	}

	public static OzNode create(OzNode body) {
		if (!Options.SELF_TAIL_CALLS_OSR) {
			return new SelfTailCallCatcherNoOSRNode(body);
		}
		return new SelfTailCallCatcherNode(body);
	}

	@Override
	public Object execute(VirtualFrame frame) {
		loopNode.executeLoop(frame);
		return unit;
	}

	private static class SelfTailCallLoopNode extends OzNode implements RepeatingNode {

		@Child OzNode body;

		public SelfTailCallLoopNode(OzNode body) {
			this.body = body;
		}

		@Override
		public boolean executeRepeating(VirtualFrame frame) {
			try {
				body.execute(frame);
				return false;
			} catch (SelfTailCallException e) {
				return true;
			}
		}

		@Override
		public Object execute(VirtualFrame frame) {
			throw new UnsupportedOperationException();
		}

		@Override
		public String toString() {
			return OzBacktrace.formatNode(this);
		}

	}

	public static class SelfTailCallCatcherNoOSRNode extends OzNode {

		@Child OzNode body;

		public SelfTailCallCatcherNoOSRNode(OzNode body) {
			this.body = body;
		}

		@Override
		public Object execute(VirtualFrame frame) {
			while (true) {
				try {
					return body.execute(frame);
				} catch (SelfTailCallException tailCall) {
				}
			}
		}

	}

}

@@ -408,16 +408,70 @@
 ingNode;
+%0Aimport com.oracle.truffle.api.profiles.BranchProfile;
 %0A%0Apublic
@@ -1689,32 +1689,159 @@
 d OzNode body;%0A%0A
+%09%09final BranchProfile returnProfile = BranchProfile.create();%0A%09%09final BranchProfile tailCallProfile = BranchProfile.create();%0A%0A
 %09%09public SelfTai
@@ -1994,22 +1994,30 @@
  %7B%0A%09%09%09%09%09
-return
+Object value =
  body.ex
@@ -2026,24 +2026,71 @@
 ute(frame);%0A
+%09%09%09%09%09returnProfile.enter();%0A%09%09%09%09%09return value;%0A
 %09%09%09%09%7D catch 
@@ -2120,24 +2120,54 @@
 tailCall) %7B%0A
+%09%09%09%09%09tailCallProfile.enter();%0A
 %09%09%09%09%7D%0A%09%09%09%7D%0A%09

Add profiling for SelfTailCallCatcherNoOSRNode