package com.sap.runlet.interpreter.objects; import java.lang.reflect.InvocationTargetException; import com.sap.ap.metamodel.utils.StringFormatter; import com.sap.runlet.abstractinterpreter.objects.RunletObject; import com.sap.runlet.interpreter.RunletInterpreter; import com.sap.runlet.interpreter.statements.ReturnInterpreter; import data.classes.AssociationEnd; import data.classes.ClassTypeDefinition; import data.classes.FunctionSignatureTypeDefinition; import data.classes.SignatureImplementation; import data.classes.TypeDefinition; /** * Represents an (possibly multi-)object that is a callable function. Its {@link #getType() type} * therefore is a {@link FunctionSignatureTypeDefinition}. The object holds a reference to the * function implementation. * <p> * * Calling multiple functions in a single expression (in case this represents a multi-valued object) * means that the results are multi-valued and perhaps nested where the nesting structure * corresponds with the structure of this multi-object, and the innermost parts of the nesting are * the actual results returned by each function call. * <p> * * For example, assume there are two functions both of type <tt>Number->Number</tt>: * * <pre> * f1 = { Number i -> Number | i+1 } * f2 = { Number i -> Number | i*i } * </pre> * * Then we can join them into a multi-object of type <tt>Number->Number[*]</tt> like this: * * <pre> * multipleFs = [f1, f2] * </pre> * * This expression can still be "invoked", e.g., like this: * * <pre> * results = multipleFs(5) * </pre> * * yielding <tt>[6, 25]</tt>. Let's look at another example where the functions are of type * <tt>Number->Number[*]</tt>, defined like this: * * <pre> * f1 = { Number i -> Number[*] | [i+1, i+2] } * f2 = { Number i -> Number[*] | [i*i, i*i*i] } * </pre> * * Then we can join them into a multi-object of type <tt>Number->Number[*])[*]</tt> like this: * * <pre> * multipleFs = [f1, f2] * </pre> * * This expression can still be "invoked", e.g., like this: * * <pre> * results = multipleFs(5) * </pre> * * yielding <tt>[[6, 7], [25, 125]]</tt>. * * @author Axel Uhl (D043530) */ public class FunctionObject extends RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> { private SignatureImplementation implementation; /** * ensures that the <tt>type</tt> is really a {@link FunctionSignatureTypeDefinition} */ public FunctionObject(TypeDefinition type, SignatureImplementation implementation) { super(type); this.implementation = implementation; } public SignatureImplementation getImplementation() { return implementation; } /** * Assumes the arguments have already been put on a new stack frame which has already been * pushed to the interpreter's call stack. Computes the result of executing the function * implementation. If the implementation returns a non-<tt>null</tt> object that is not a * {@link ReturnInterpreter.ReturnResult}, it was an "inofficial" last computed value during * executing the implementation which will not be returned; <tt>null</tt> will be returned * instead. If the implementation returns a {@link ReturnInterpreter.ReturnResult}, the object * wrapped within represents the real return value of the implementation. The inner object which * represents the actual return value will be unwrapped and returned from this method. * <p> * * The stack frame is left on the stack. It's the caller's obligation to remove the stack again that * previously was added by the caller. This should give "symmetric" behavior.<p> * * The result undergoes implicit object-parameterized type conversion. See * {@link RunletInterpreter#convert(ValueObject, data.classes.ClassTypeDefinition)}. */ public RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> evaluate(RunletInterpreter interpreter) throws SecurityException, IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> result; RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> unconvertedResult = interpreter.evaluateSignatureImplementation(getImplementation()); if (unconvertedResult instanceof ReturnInterpreter.ReturnResult) { // the explicit return value for the signature; unwrap and return unconvertedResult = ((ReturnInterpreter.ReturnResult) unconvertedResult).getResult(); result = interpreter.convert(unconvertedResult, implementation.getImplementedSignature().getOutput()); } else { result = null; } return result; } @Override public String toString() { return "function "+StringFormatter.toString(getImplementation().getImplementedSignature()); } }