//
// PorcEClosure.java -- Java class PorcEClosure
// Project PorcE
//
// Copyright (c) 2018 The University of Texas at Austin. All rights reserved.
//
// Use and redistribution of this file is governed by the license terms in
// the LICENSE file found in the project's top-level directory and also found at
// URL: http://orc.csres.utexas.edu/license.shtml .
//

package orc.run.porce.runtime;

import orc.run.porce.PorcERootNode;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.RootCallTarget;

/**
 * A class representing PorcE closures including both Orc methods (def, site) and internal Porc continuations.
 *
 * @author amp
 */
final public class PorcEClosure {
    public final Object[] environment;
    public final RootCallTarget body;

    public final boolean isRoutine;

    // TODO: PERFORMANCE: Using a frame instead of an array for captured values may perform better. Though that will mainly be true when we start using native values.
	public PorcEClosure(final Object[] environment, final RootCallTarget body, final boolean isRoutine) {
		CompilerDirectives.interpreterOnly(() -> {
			if (body == null) {
				throw new IllegalArgumentException("body == null");
			}
			if (environment == null) {
				throw new IllegalArgumentException("environment == null");
			}
		});

		this.environment = environment;
		this.body = body;
		this.isRoutine = isRoutine;
	}
	
	public long getTimePerCall() {
		if (body.getRootNode() instanceof PorcERootNode) {
			PorcERootNode root = (PorcERootNode)body.getRootNode();
			return root.getTimePerCall();
		} else {
			return Long.MAX_VALUE;
		}
	}

    public Object callFromRuntimeArgArray(final Object[] values) {
        values[0] = environment;
        return body.call(values);
    }

    public Object callFromRuntime() {
        return body.call((Object) environment);
    }

    /*
    public Object callFromRuntime(final Object p1) {
        return body.call(environment, p1);
    }

    public Object callFromRuntime(final Object p1, final Object p2) {
        return body.call(environment, p1, p2);
    }

    public Object callFromRuntime(final Object p1, final Object p2, final Object p3) {
        return body.call(environment, p1, p2, p3);
    }

    public Object callFromRuntimeVarArgs(final Object[] args) {
        final Object[] values = new Object[args.length + 1];
        values[0] = environment;
        System.arraycopy(args, 0, values, 1, args.length);
        return body.call(values);
    }
    */
    
	@Override
	public String toString() {
		return body.getRootNode().getName();
	}

}

@@ -2505,87 +2505,1041 @@
  */%0A
+%0A
     
-%0A%09@Override%0A%09public String toString() %7B%0A%09%09return body.getRootNode().getName
+private static final ThreadLocal%3CBoolean%3E toStringRecursionCheck = new ThreadLocal%3CBoolean%3E() %7B%0A        @Override%0A        @SuppressWarnings(%22boxing%22)%0A        protected Boolean initialValue() %7B%0A            return false;%0A        %7D%0A    %7D;%0A%0A%09@Override%0A    @SuppressWarnings(%22boxing%22)%0A%09public String toString() %7B%0A      // FIXME: The recursion check will be SLOW. But it may not matter. However it may not even be good to have this as a default.%0A      StringBuilder sb = new StringBuilder();%0A      sb.append(body.getRootNode().getName());%0A      sb.append(':');%0A      sb.append(body.getRootNode().getClass().getSimpleName());%0A      if (!toStringRecursionCheck.get()) %7B%0A        toStringRecursionCheck.set(true);%0A        sb.append('%5B');%0A        boolean notFirst = false;%0A        for (Object v : environment) %7B%0A          if (notFirst) %7B%0A            sb.append(%22, %22);%0A          %7D%0A          sb.append(v);%0A          notFirst = true;%0A        %7D%0A        sb.append('%5D');%0A        toStringRecursionCheck.set(false);%0A      %7D%0A      return sb.toString
 ();%0A

Improve toString in PorcEClosure