/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.internal;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.FunctionEnvironmentRecord;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Type;
/**
* Object representing a tail-call invocation.
*/
public abstract class TailCallInvocation {
private TailCallInvocation() {
}
protected abstract Object apply(ExecutionContext callerContext) throws Throwable;
/**
* Converts this tail-call invocation into a construct tail-call invocation.
*
* @param object
* the this-argument object
* @return the tail call trampoline object
*/
// Called from generated code
public abstract TailCallInvocation toConstructTailCall(ScriptObject object);
/**
* Converts this tail-call invocation into a construct tail-call invocation.
*
* @param envRec
* the function environment record
* @return the tail call trampoline object
*/
// Called from generated code
public abstract TailCallInvocation toConstructTailCall(FunctionEnvironmentRecord envRec);
public static TailCallInvocation newTailCallInvocation(Callable function, Object thisValue,
Object[] argumentsList) {
return new CallTailCallInvocation(function, thisValue, argumentsList);
}
private static final class CallTailCallInvocation extends TailCallInvocation {
private final Callable function;
private final Object thisValue;
private final Object[] argumentsList;
CallTailCallInvocation(Callable function, Object thisValue, Object[] argumentsList) {
this.function = function;
this.thisValue = thisValue;
this.argumentsList = argumentsList;
}
@Override
protected Object apply(ExecutionContext callerContext) throws Throwable {
return function.tailCall(callerContext, thisValue, argumentsList);
}
@Override
public TailCallInvocation toConstructTailCall(ScriptObject object) {
return new ConstructBaseTailCallInvocation(this, object);
}
@Override
public TailCallInvocation toConstructTailCall(FunctionEnvironmentRecord envRec) {
return new ConstructDerivedTailCallInvocation(this, envRec);
}
}
private static final class ConstructBaseTailCallInvocation extends TailCallInvocation {
private final TailCallInvocation invocation;
private final ScriptObject object;
ConstructBaseTailCallInvocation(TailCallInvocation invocation, ScriptObject object) {
this.invocation = invocation;
this.object = object;
}
@Override
protected Object apply(ExecutionContext callerContext) throws Throwable {
Object result = invocation.apply(callerContext);
if (result instanceof TailCallInvocation) {
return ((TailCallInvocation) result).toConstructTailCall(object);
}
if (Type.isObject(result)) {
return result;
}
return object;
}
@Override
public TailCallInvocation toConstructTailCall(ScriptObject object) {
return this;
}
@Override
public TailCallInvocation toConstructTailCall(FunctionEnvironmentRecord envRec) {
return this;
}
}
private static final class ConstructDerivedTailCallInvocation extends TailCallInvocation {
private final TailCallInvocation invocation;
private final FunctionEnvironmentRecord envRec;
ConstructDerivedTailCallInvocation(TailCallInvocation invocation,
FunctionEnvironmentRecord envRec) {
this.invocation = invocation;
this.envRec = envRec;
}
@Override
protected Object apply(ExecutionContext callerContext) throws Throwable {
Object result = invocation.apply(callerContext);
if (result instanceof TailCallInvocation) {
return ((TailCallInvocation) result).toConstructTailCall(envRec);
}
if (Type.isObject(result)) {
return result;
}
if (!Type.isUndefined(result)) {
throw Errors.newTypeError(callerContext, Messages.Key.NotObjectTypeFromConstructor);
}
return envRec.getThisBinding(callerContext);
}
@Override
public TailCallInvocation toConstructTailCall(ScriptObject object) {
return this;
}
@Override
public TailCallInvocation toConstructTailCall(FunctionEnvironmentRecord envRec) {
return this;
}
}
private static final MethodHandle tailCallTrampolineMH = MethodLookup.findStatic(
MethodHandles.lookup(), "tailCallTrampoline",
MethodType.methodType(Object.class, Object.class, ExecutionContext.class));
private static final MethodHandle tailConstructTrampolineMH = MethodLookup.findStatic(
MethodHandles.lookup(), "tailConstructTrampoline",
MethodType.methodType(ScriptObject.class, Object.class, ExecutionContext.class));
/**
* (Object, ExecutionContext) {@literal ->} Object.
*
* @return the tail call method handler
*/
public static MethodHandle getTailCallHandler() {
return tailCallTrampolineMH;
}
/**
* (Object, ExecutionContext) {@literal ->} ScriptObject.
*
* @return the tail call method handler
*/
public static MethodHandle getTailConstructHandler() {
return tailConstructTrampolineMH;
}
@SuppressWarnings("unused")
private static Object tailCallTrampoline(Object result, ExecutionContext callerContext)
throws Throwable {
// tail-call with trampoline
while (result instanceof TailCallInvocation) {
result = ((TailCallInvocation) result).apply(callerContext);
}
return result;
}
@SuppressWarnings("unused")
private static ScriptObject tailConstructTrampoline(Object result,
ExecutionContext callerContext) throws Throwable {
// tail-call with trampoline
while (result instanceof TailCallInvocation) {
result = ((TailCallInvocation) result).apply(callerContext);
}
return (ScriptObject) result;
}
}