package org.basex.query.func; import static org.basex.query.QueryText.*; import java.io.IOException; import org.basex.io.serial.Serializer; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Arr; import org.basex.query.expr.Expr; import org.basex.query.item.QNm; import org.basex.query.util.Var; import org.basex.query.util.VarStack; import org.basex.util.InputInfo; import org.basex.util.Token; import org.basex.util.TokenBuilder; /** * Function call for user-defined functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public abstract class UserFuncCall extends Arr { /** * A continuation that's thrown to free stack frames. * @author Leo Woerteler */ final class Continuation extends RuntimeException { /** Arguments. */ private final Var[] args; /** * Constructor. * @param arg arguments */ Continuation(final Var[] arg) { args = arg; } /** * Getter for the continuation function. * @return the next function to call */ Expr getFunc() { return func; } /** * Getter for the function arguments. * @return the next function call's arguments */ Var[] getArgs() { return args; } @Override public synchronized Continuation fillInStackTrace() { // ignore this for efficiency reasons return this; } } /** Function name. */ final QNm name; /** Function reference. */ UserFunc func; /** * Function constructor. * @param ii input info * @param nm function name * @param arg arguments */ UserFuncCall(final InputInfo ii, final QNm nm, final Expr... arg) { super(ii, arg); name = nm; } /** * Initializes the function call after all functions have been declared. * @param f function reference */ void init(final UserFunc f) { func = f; } /** * Getter for the called function. * @return user-defined function */ final UserFunc func() { return func; } @Override public Expr comp(final QueryContext ctx) throws QueryException { // compile all arguments super.comp(ctx); // inline if result and arguments are all values. // currently, only functions with values as // return expressions are supported; otherwise, recursive functions // might not be correctly evaluated func.comp(ctx); if(func.expr.isValue() && allAreValues() && !func.uses(Use.NDT)) { // evaluate arguments to catch cast exceptions for(int a = 0; a < expr.length; ++a) func.args[a].bind(expr[a], ctx); ctx.compInfo(OPTINLINE, func.name.string()); return func.value(ctx); } // user-defined functions are not pre-evaluated to avoid various issues // with recursive functions type = func.type(); return this; } /** * Adds the given arguments to the variable stack. * @param ctx query context * @param vs variables to add * @return old stack size */ static VarStack addArgs(final QueryContext ctx, final Var[] vs) { // move variables to stack final VarStack vl = ctx.vars.cache(vs.length); for(final Var v : vs) ctx.vars.add(v); return vl; } /** * Evaluates all function arguments. * @param ctx query context * @return argument values * @throws QueryException query exception */ Var[] args(final QueryContext ctx) throws QueryException { final int al = expr.length; final Var[] args = new Var[al]; // evaluate arguments for(int a = 0; a < al; ++a) args[a] = func.args[a].bind(expr[a].value(ctx), ctx).copy(); return args; } @Override public boolean uses(final Use u) { return u == Use.UPD ? func.updating : super.uses(u); } @Override public void plan(final Serializer ser) throws IOException { ser.openElement(this, NAM, Token.token(toString())); for(final Expr e : expr) e.plan(ser); ser.closeElement(); } @Override public String description() { return FUNC; } @Override public String toString() { return new TokenBuilder(name.string()).add(PAR1).add( toString(SEP)).add(PAR2).toString(); } }