1 // Copyright (c) 2003, 2004, 2006, 2014  Per M.A. Bothner.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.expr;
5 
6 import gnu.bytecode.*;
7 import gnu.mapping.*;
8 import gnu.kawa.functions.MakeList;
9 import gnu.kawa.functions.MakeSplice;
10 import gnu.kawa.lispexpr.LangObjType;
11 import gnu.kawa.reflect.CompileArrays;
12 import gnu.kawa.io.OutPort;
13 import gnu.text.SourceMessages;
14 import gnu.text.SyntaxException;
15 import java.util.LinkedList;
16 /* #ifdef use:java.lang.invoke */
17 import java.lang.invoke.*;
18 /* #endif */
19 
20 /** This class is used to represent "combination" or "application".
21  * A function and arguments are evaluated, and then the function applied.
22  * @author	Per Bothner
23  */
24 
25 public class ApplyExp extends Expression
26 {
27   Expression func;
28   Expression[] args;
29 
30   public static final int TAILCALL = NEXT_AVAIL_FLAG;
31   public static final int INLINE_IF_CONSTANT = NEXT_AVAIL_FLAG << 1;
32   public static final int MAY_CONTAIN_BACK_JUMP = NEXT_AVAIL_FLAG << 2;
33   public static final int IS_SUPER_INIT = NEXT_AVAIL_FLAG << 3;
34 
35   /** Containing LambdaExp. */
36   LambdaExp context;
37 
38   /** The next ApplyExp in ((ReferenceExp)func).binding.firstCall list. */
39   public ApplyExp nextCall;
40 
41     /** Index of argument for first keyword argument.
42      * If zero, no keyword arguments.  If non-zero, then
43      * {@code firstKeywordArgIndex-1} is the index in the {@code args}
44      * array of the first keyword.
45      */
46     public int firstKeywordArgIndex;
47     public int numKeywordArgs;
48 
49     /** Index of first argument that is a MakeSplice.
50      * The value is -1 is no argument is a splice.
51      */
52     public int firstSpliceArg = -1;
53 
getFunction()54   public final Expression getFunction() { return func; }
getArgs()55   public final Expression[] getArgs() { return args; }
getArgCount()56   public final int getArgCount() { return args.length; }
setFunction(Expression func)57     public void setFunction(Expression func) { this.func = func; }
setFunction(Procedure proc)58   public void setFunction(Procedure proc) { this.func = new QuoteExp(proc); }
setArgs(Expression[] args)59   public void setArgs(Expression[] args) { this.args = args; }
getArg(int i)60   public Expression getArg(int i) { return args[i]; }
setArg(int i, Expression arg)61   public void setArg(int i, Expression arg) { args[i] = arg; }
isTailCall()62   public final boolean isTailCall() { return getFlag(TAILCALL); }
setTailCall(boolean tailCall)63   public final void setTailCall(boolean tailCall)
64   { setFlag(tailCall, TAILCALL); }
65 
setFuncArgs(Expression func, Expression[] args)66   public ApplyExp setFuncArgs (Expression func, Expression[] args)
67   {
68     setFunction(func);
69     setArgs(args);
70     setFlag(false, Expression.VALIDATED);
71     return this;
72   }
73 
setFuncArgs(Procedure proc, Expression[] args)74   public ApplyExp setFuncArgs (Procedure proc, Expression[] args)
75   {
76     return setFuncArgs(new QuoteExp(proc), args);
77   }
78 
79   /** If getFunction() is constant, return its value; otherwise null. */
getFunctionValue()80   public final Object getFunctionValue()
81   {
82     return func instanceof QuoteExp ? ((QuoteExp) func).getValue() : null;
83   }
84 
85     /** Copy over splice and keyword start indexes.
86      * @param src orginal ApplyExp (may be the same as this)
87      * @param delta amount to adjust indexes by
88      */
adjustSplice(ApplyExp src, int delta)89     public void adjustSplice(ApplyExp src, int delta) {
90         if (src.firstSpliceArg >= 0)
91             this.firstSpliceArg = src.firstSpliceArg + delta;
92         if (src.firstKeywordArgIndex > 0)
93             this.firstKeywordArgIndex = src.firstKeywordArgIndex + delta;
94         this.numKeywordArgs = src.numKeywordArgs;
95     }
96 
spliceCount()97     public int spliceCount() {
98         int count = 0;
99         if (firstSpliceArg >= 0) {
100             Expression[] args = this.args;
101             int nargs = args.length;
102             for (int i = firstSpliceArg; i < nargs; i++) {
103                 if (MakeSplice.argIfSplice(args[i]) != null)
104                     count++;
105             }
106         }
107         return count;
108     }
109 
isSimple()110     public boolean isSimple() {
111         return firstSpliceArg < 0 && firstKeywordArgIndex == 0;
112     }
113 
isSimple(int min)114     public boolean isSimple(int min) {
115         return isSimple() && getArgCount() >= min;
116     }
117 
isSimple(int min, int max)118     public boolean isSimple(int min, int max) {
119         int ac = getArgCount();
120         return isSimple() && ac >= min && ac <= max;
121     }
122 
hasSpliceAllowingKeywords()123     public boolean hasSpliceAllowingKeywords() {
124         if (firstSpliceArg < 0)
125             return false;
126         int n = args.length;
127         for (int i = 0; i < n; i++) {
128             Expression arg = args[i];
129             if (arg instanceof ApplyExp
130                 && (((ApplyExp) arg).getFunction()
131                     == MakeSplice.quoteKeywordsAllowedInstance))
132                 return true;
133         }
134         return false;
135     }
136 
isAppendValues()137     public boolean isAppendValues() {
138         return func instanceof QuoteExp
139             && (((QuoteExp) func).getValue()
140                 == gnu.kawa.functions.AppendValues.appendValues);
141     }
142 
ApplyExp(Expression f, Expression... a)143   public ApplyExp (Expression f, Expression... a) { func = f; args = a; }
144 
ApplyExp(Procedure p, Expression... a)145   public ApplyExp (Procedure p, Expression... a) { this(new QuoteExp(p), a); }
146 
ApplyExp(Method m, Expression... a)147   public ApplyExp (Method m, Expression... a)
148   {
149     this(new QuoteExp(new PrimProcedure(m)), a);
150   }
151 
mustCompile()152   protected boolean mustCompile () { return false; }
153 
154   @Override
apply(CallContext ctx)155   public void apply (CallContext ctx) throws Throwable
156   {
157     Procedure proc = (Procedure) func.eval(ctx);
158     int n = args.length;
159     Object[] vals = new Object[n];
160     for (int i = 0; i < n; i++)
161       vals[i] = args[i].eval(ctx);
162     ctx.setupApplyAll(proc, vals);
163   }
164 
compile(Compilation comp, Target target)165   public void compile (Compilation comp, Target target)
166   {
167     compile(this, comp, target, true);
168     if (getFlag(IS_SUPER_INIT)) {
169         ((ClassExp) comp.currentScope().currentLambda().getOuter())
170             .compileCallInitMethods(comp);
171     }
172   }
173 
compile(ApplyExp exp, Compilation comp, Target target)174   public static void compile (ApplyExp exp, Compilation comp, Target target)
175   {
176     compile(exp, comp, target, false);
177   }
178 
compile(ApplyExp exp, Compilation comp, Target target, boolean checkInlineable)179     static void compile (ApplyExp exp, Compilation comp, Target target,
180                          boolean checkInlineable) {
181         int args_length = exp.args.length;
182         Expression exp_func = exp.func;
183         LambdaExp func_lambda = null;
184         String func_name = null;
185         Declaration owner = null;
186         Object quotedValue = null;
187         if (exp_func instanceof LambdaExp) {
188             func_lambda = (LambdaExp) exp_func;
189             func_name = func_lambda.getName();
190             if (func_name == null)
191                 func_name = "<lambda>";
192         } else if (exp_func instanceof ReferenceExp) {
193             ReferenceExp func_ref = (ReferenceExp) exp_func;
194             owner = func_ref.contextDecl();
195             Declaration func_decl = func_ref.binding;
196             Expression dval;
197             while (func_decl != null && func_decl.isAlias()
198                    && (dval = func_decl.getValueRaw()) instanceof ReferenceExp) {
199                 func_ref = (ReferenceExp) dval;
200             if (owner != null || func_decl.needsContext()
201                 || func_ref.binding == null)
202                 break;
203             func_decl = func_ref.binding;
204             owner = func_ref.contextDecl();
205             }
206             if (! func_decl.getFlag(Declaration.IS_UNKNOWN)) {
207                 Expression value = func_decl.getValue();
208                 func_name = func_decl.getName();
209                 if (value != null && value instanceof LambdaExp)
210                     func_lambda = (LambdaExp) value;
211                 if (value != null && value instanceof QuoteExp)
212                     quotedValue = ((QuoteExp) value).getValue();
213             }
214         } else if (exp_func instanceof QuoteExp) {
215             quotedValue = ((QuoteExp) exp_func).getValue();
216         }
217         if (checkInlineable && quotedValue instanceof Procedure) {
218             Procedure proc = (Procedure) quotedValue;
219             if (target instanceof IgnoreTarget && proc.isSideEffectFree()) {
220                 for (int i = 0; i < args_length;  i++)
221                     exp.args[i].compile(comp, target);
222                 return;
223             }
224             try {
225                 if (inlineCompile(proc, exp, comp, target))
226                     return;
227             } catch (Error ex) {
228                 throw ex;
229             } catch (Throwable ex) {
230                 SourceMessages msg = comp.getMessages();
231                 ex.printStackTrace();
232                 msg.error('f',
233                           "caught exception in inline-compiler for "
234                           +quotedValue+" - "+ex, ex);
235                 throw new SyntaxException(msg);
236             }
237         }
238 
239         CodeAttr code = comp.getCode();
240         Method method;
241 
242     // Check for tail-recursion.
243     boolean tail_recurse
244       = exp.isTailCall()
245       && func_lambda != null && func_lambda == comp.curLambda
246       // No keyword or optional arguments.
247       && func_lambda.opt_args == 0 && func_lambda.keywords == null;
248 
249     int spliceCount = exp.spliceCount();
250     int nonSpliceCount = args_length - spliceCount;
251     int nonSpliceNonKeyCount = nonSpliceCount - 2 * exp.numKeywordArgs;
252 
253     if (func_lambda != null)
254       {
255 	if ((func_lambda.max_args >= 0
256              && nonSpliceNonKeyCount > func_lambda.max_args)
257 	    || (nonSpliceNonKeyCount < func_lambda.min_args)
258             && spliceCount == 0)
259 	  // This is supposed to get caught by InlineCalls.
260 	  throw new Error ("internal error - wrong number of parameters for "
261 			   + func_lambda);
262 	int conv = func_lambda.getCallConvention();
263         if (func_lambda.primMethods==null && ! func_lambda.isClassGenerated()
264             && ! func_lambda.inlinedInCallerOrCheckMethodOnly())
265             func_lambda.allocMethod(func_lambda.outerLambda(), comp);
266         // Mostly duplicates logic with LambdaExp.validateApply.
267         // See comment there.
268 	if (comp.inlineOk(func_lambda)
269             && ! tail_recurse
270 	    && (conv <= Compilation.CALL_WITH_CONSUMER
271 		|| (conv == Compilation.CALL_WITH_TAILCALLS
272 		    && ! exp.isTailCall()))
273 	    && (method = func_lambda.getMethod(nonSpliceCount, spliceCount)) != null
274             && ! func_lambda.getFlag(LambdaExp.HAS_NONTRIVIAL_PATTERN)
275             && (exp.firstSpliceArg < 0
276                 || (PrimProcedure.takesVarArgs(method)
277                     && func_lambda.min_args <= exp.firstSpliceArg)))
278 	  {
279 	    PrimProcedure pproc = new PrimProcedure(method, func_lambda);
280 	    boolean is_static = method.getStaticFlag();
281 	    boolean extraArg = false;
282 	    // ?? Procedure.checkArgCount(this, args.length); // FIXME
283 	    if (! is_static || func_lambda.declareClosureEnv() != null)
284 	      {
285 		if (is_static)
286 		  extraArg = true;
287 		if (comp.curLambda == func_lambda)  // Recursive call.
288 		  code.emitLoad(func_lambda.closureEnv != null
289 				? func_lambda.closureEnv
290 				: func_lambda.thisVariable);
291 		else if (owner != null)
292                   owner.load(null, 0, comp, Target.pushObject);
293                 else
294                   func_lambda.getOwningLambda().loadHeapFrame(comp);
295 	      }
296 
297 	    pproc.compile(extraArg ? Type.voidType : null,
298 			  exp, comp, target);
299 	    return;
300 	  }
301       }
302 
303     /*
304     if (comp.usingCPStyle())
305       {
306 	  {
307 	    Label l = new Label(code);
308 	    gnu.bytecode.SwitchState fswitch = comp.fswitch;
309 	    int pc = fswitch.getMaxValue() + 1;
310 	    fswitch.addCase(pc, code, l);
311             exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));
312 	    comp.loadCallContext();
313 
314 	    // Emit: context->pc = pc.
315 	    comp.loadCallContext();
316 	    code.emitPushInt(pc);
317 	    code.emitPutField(Compilation.pcCallContextField);
318 	    code.emitInvokeVirtual(Compilation.applyCpsMethod);
319 
320 	    // emit[save java stack, if needed]
321 	    Type[] stackTypes = code.saveStackTypeState(false);
322 	    java.util.Stack stackFields = new java.util.Stack();
323 	    if (stackTypes != null)
324 	      {
325 		for (int i = stackTypes.length;  --i >= 0; )
326 		  {
327 		    Field fld = comp.allocLocalField (stackTypes[i], null);
328 		    code.emitPushThis();
329 		    code.emitSwap();
330 		    code.emitPutField(fld);
331 		    stackFields.push(fld);
332 		  }
333 	      }
334 
335 	    code.emitReturn();
336 	    l.define(code);
337 
338 	    // emit[restore java stack, if needed]
339 	    if (stackTypes != null)
340 	      {
341 		for (int i = stackTypes.length;  --i >= 0; )
342 		  {
343 		    Field fld = (Field) stackFields.pop();
344 		    code.emitPushThis();
345 		    code.emitGetField(fld);
346 		    comp.freeLocalField(fld);
347 		  }
348 	      }
349 
350 	    // FIXME
351 	    // Load result from stack.value to target.
352 	    comp.loadCallContext();
353 	    code.emitGetField(comp.valueCallContextField);
354 	    target.compileFromStack(comp, Type.pointer_type);
355 	  }
356 	return;
357       }
358     */
359     if (func_lambda != null && func_lambda.getInlineOnly() && !tail_recurse
360 	&& func_lambda.min_args == nonSpliceCount)
361       {
362         pushArgs(func_lambda, exp.args, exp.args.length, null, comp);
363         if (func_lambda.getFlag(LambdaExp.METHODS_COMPILED)
364             || (exp.isTailCall()
365                 && func_lambda.nameDecl != null
366                 && ! func_lambda.nestedIn(comp.curLambda)))
367           {
368               if (func_lambda.startForInlining == null) {
369                   func_lambda.startForInlining = new Label(code);
370                   if (comp.curLambda.pendingInlines == null)
371                       comp.curLambda.pendingInlines = new LinkedList<Object>();
372                   comp.curLambda.pendingInlines.add(func_lambda);
373                   comp.curLambda.pendingInlines.add(target);
374               }
375             code.emitTailCall(false, func_lambda.startForInlining);
376             return;
377           }
378         func_lambda.compileAsInlined(comp, target);
379 	return;
380       }
381 
382     if ((comp.curLambda.isHandlingTailCalls()
383          && ! comp.curLambda.inlinedInCallerOrCheckMethodOnly()
384          && (exp.isTailCall() || target instanceof ConsumerTarget))
385         || exp.firstSpliceArg >= 0 || exp.numKeywordArgs > 0
386         || (! tail_recurse && args_length > 4))
387       {
388         comp.loadCallContext();
389 	exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));
390 
391         compileArgsToContext(exp, null, comp);
392 
393         finishTrampoline(exp.isTailCall(), target, comp);
394  	return;
395       }
396     if (!tail_recurse)
397       exp_func.compile (comp, new StackTarget(Compilation.typeProcedure));
398 
399     boolean toArray = tail_recurse && func_lambda.max_args < 0;
400     int[] incValues = null; // Increments if we can use iinc.
401     if (tail_recurse)
402       {
403         int fixed = func_lambda.min_args;
404         incValues = new int[fixed];
405         Declaration rest = pushArgs(func_lambda, exp.args, fixed, incValues, comp);
406         if (toArray)
407             PrimProcedure.compileRestArg(rest.getType(), exp,
408                                          0, fixed, comp);
409         method = null;
410       }
411     else
412       {
413 	for (int i = 0; i < args_length; ++i)
414           {
415             exp.args[i].compileWithPosition(comp, Target.pushObject);
416             if (! code.reachableHere())
417               break;
418           }
419         method = Compilation.applymethods[args_length];
420       }
421     if (! code.reachableHere())
422       {
423         if (comp.warnUnreachable())
424           comp.error('w', "unreachable code");
425         return;
426       }
427     if (tail_recurse)
428       {
429         Label startLabel = func_lambda.startForInlining;
430         boolean mustStore = startLabel == null;
431         if (incValues != null && ! mustStore)
432           {
433             for (int i = incValues.length;  --i >= 0; )
434               if (incValues[i] != SetExp.BAD_SHORT)
435                 {
436                   mustStore = true;
437                   break;
438                 }
439           }
440         if (mustStore)
441           {
442             popParams(code, func_lambda, incValues, toArray);
443             startLabel = func_lambda.getVarScope().getStartLabel();
444           }
445         code.emitTailCall(false, startLabel);
446 	return;
447       }
448     code.emitInvokeVirtual(method);
449     target.compileFromStack(comp, Type.pointer_type);
450   }
451 
452     public static void compileArgsToContext(ApplyExp exp, Method setupMethod,
453                                             Compilation comp) {
454         CodeAttr code = comp.getCode();
455         int args_length = exp.args.length;
456 
457         // evaluate args to frame-locals vars;  // may recurse!
458 
459         Scope scope = code.pushScope();
460         Variable[] vars = new Variable[args_length];
461         int initial = setupMethod != null ? 0 : args_length <= 4 ? args_length : 4;
462         if (exp.numKeywordArgs > 0 && initial >= exp.firstKeywordArgIndex)
463             initial = exp.firstKeywordArgIndex-1;
464         if (exp.firstSpliceArg >= 0 && initial > exp.firstSpliceArg)
465             initial = exp.firstSpliceArg;
466         if (setupMethod == null)
467             setupMethod = Compilation.typeCallContext
468                 .getDeclaredMethod("setupApply", initial+1);
469         if (initial != args_length) {
470             for (int i = 0; i < args_length; ++i) {
471                 Expression arg = exp.args[i];
472                 Expression sarg = MakeSplice.argIfSplice(arg);
473                 if (sarg != null)
474                     arg = sarg;
475                 if (arg instanceof QuoteExp)
476                     continue;
477                 arg.compileWithPosition(comp, Target.pushObject);
478                 Variable var = scope.addVariable(code, Type.objectType, null);
479                 code.emitStore(var);
480                 vars[i] = var;
481             }
482         }
483         for (int i = 0; i < initial; ++i) {
484             Expression arg = exp.args[i];
485             if (vars[i] != null)
486                 code.emitLoad(vars[i]);
487             else
488                 arg.compileWithPosition(comp, Target.pushObject);
489         }
490         code.emitInvoke(setupMethod);
491         int firstKeyword = exp.firstKeywordArgIndex - 1;
492         int numKeywords = exp.numKeywordArgs;
493         boolean useSetKeys = numKeywords > 0
494             && ! exp.hasSpliceAllowingKeywords();
495         ClassType typeArgListImpl = Compilation.typeCallContext.getSuperclass();
496         for (int i = initial;  i < args_length;  i++) {
497             Expression arg = exp.args[i];
498             char mode = '\0'; // ''\0', 'K', '@' or ':'
499             String key = null;
500             if (i >= firstKeyword
501                 && i < firstKeyword + 2 * numKeywords) {
502                 // a keyword or keyword arg
503                 if (((i - firstKeyword) & 1) == 0)
504                     continue ; // keyword - handled next iteration
505                 Object keyarg = exp.args[i-1].valueIfConstant();
506                 key = ((Keyword) keyarg).getName();
507                 mode = 'K';
508             } else {
509                 Expression sarg = MakeSplice.argIfSplice(arg);
510                 if (sarg != null) {
511                     if (((ApplyExp) arg).getFunction()
512                         == MakeSplice.quoteKeywordsAllowedInstance)
513                         mode = ':';
514                     else
515                         mode = '@';
516                     arg = sarg;
517                 }
518             }
519             comp.loadCallContext();
520             if (key != null && ! useSetKeys)
521                 code.emitPushString(key);
522             if (vars[i] != null)
523                 code.emitLoad(vars[i]);
524             else
525                 arg.compileWithPosition(comp, Target.pushObject);
526             if (mode == '@' || mode == ':') {
527                 String mname = mode == '@' ? "addSequence" : "addArgList";
528                 code.emitInvoke(new Method(typeArgListImpl
529                                            .getDeclaredMethod(mname, 1),
530                                            Compilation.typeCallContext));
531             } else if (mode == 'K' && ! useSetKeys) {
532                 code.emitInvoke(new Method(typeArgListImpl
533                                 .getDeclaredMethod("addKey", 2),
534                                            Compilation.typeCallContext));
535             } else {
536               code.emitInvoke(Compilation.typeCallContext
537                                 .getDeclaredMethod("addArg", 1));
538             }
539             if (mode == 'K' && useSetKeys
540                 // is this the last keyword argument?
541                 && i == firstKeyword + 2 * numKeywords - 1) {
542                 String[] keywords = new String[numKeywords];
543                 for (int j = numKeywords; --j >= 0; )
544                     keywords[j] = ((Keyword) exp.args[firstKeyword + 2 * j]
545                                    .valueIfConstant()).getName();
546                 short[] sorted
547                     = CallContext.getSortedKeywords(keywords, keywords.length);
548                 comp.loadCallContext();
549                 code.emitPushInt(numKeywords);
550                 comp.compileConstant(keywords, Target.pushObject);
551                 comp.compileConstant(sorted, Target.pushObject);
552                 code.emitInvoke(typeArgListImpl
553                                 .getDeclaredMethod("setKeys", 3));
554             }
555         }
556         code.popScope();
557     }
558 
finishTrampoline(boolean isTailCall, Target target, Compilation comp)559     static void finishTrampoline(boolean isTailCall, Target target, Compilation comp) {
560         CodeAttr code = comp.getCode();
561         ClassType typeContext = Compilation.typeCallContext;
562         if (isTailCall && comp.curLambda.isHandlingTailCalls()
563             && ! comp.curLambda.inlinedInCheckMethod()) {
564             code.emitReturn();
565         } else if (! (target instanceof ConsumerTarget
566                       || target instanceof IgnoreTarget)) {
567             comp.loadCallContext();
568             code.emitInvoke(typeContext.getDeclaredMethod("runUntilValue", 0));
569             target.compileFromStack(comp, Type.pointer_type);
570         } else if (target instanceof IgnoreTarget
571                    || ((ConsumerTarget) target).isContextTarget()) {
572             comp.loadCallContext();
573             code.emitInvoke(typeContext.getDeclaredMethod("runUntilDone", 0));
574         } else {
575             comp.loadCallContext();
576             code.emitLoad(((ConsumerTarget) target).getConsumerVariable());
577             code.emitInvoke(typeContext.getDeclaredMethod("runUntilValue", 1));
578         }
579     }
580 
deepCopy(gnu.kawa.util.IdentityHashTable mapper)581   public Expression deepCopy (gnu.kawa.util.IdentityHashTable mapper)
582   {
583     Expression f = deepCopy(func, mapper);
584     Expression[] a = deepCopy(args, mapper);
585     if ((f == null && func != null) || (a == null && args != null))
586       return null;
587     ApplyExp copy = new ApplyExp(f, a);
588     copy.flags = getFlags();
589     return copy;
590   }
591 
visit(ExpVisitor<R,D> visitor, D d)592   protected <R,D> R visit (ExpVisitor<R,D> visitor, D d)
593   {
594     return visitor.visitApplyExp(this, d);
595   }
596 
visitArgs(InlineCalls visitor)597     public void visitArgs(InlineCalls visitor) {
598         visitArgs(visitor, null);
599     }
visitArgs(InlineCalls visitor, LambdaExp lexp)600     public void visitArgs(InlineCalls visitor, LambdaExp lexp) {
601         int nargs = args.length;
602         Type dtype = isAppendValues() ? null
603             : InlineCalls.ValueNeededType.instance;
604         // For simplicity, for now.
605         Declaration param = lexp == null
606             || firstKeywordArgIndex != 0
607             || lexp.keywords != null
608             ? null
609             : lexp.firstDecl();
610         for (int i = 0;  i < nargs && visitor.getExitValue() == null;  i++) {
611             while (param != null
612                    && (param.isThisParameter()
613                        || param.getFlag(Declaration.PATTERN_NESTED)
614                        || (param.getFlag(Declaration.IS_SUPPLIED_PARAMETER)
615                            && ! param.getFlag(Declaration.IS_PARAMETER))))
616                 param = param.nextDecl();
617             Type ptype = dtype;
618             if (param != null && i < lexp.min_args+lexp.opt_args
619                 && (firstSpliceArg < 0 || i > firstSpliceArg))
620                 ptype = param.getType();
621             else if (param != null
622                      && param.getFlag(Declaration.IS_REST_PARAMETER)) {
623                 Type vtype = param.getType();
624                 if (vtype instanceof ArrayType)
625                     ptype = ((ArrayType) vtype).getComponentType();
626             }
627             args[i] = visitor.visitAndUpdate(args[i], ptype);
628             if (param != null)
629                 param.noteValueFromApply(this, i);
630             if (param != null && ! param.getFlag(Declaration.IS_REST_PARAMETER))
631                 param = param.nextDecl();
632         }
633     }
634 
visitChildren(ExpVisitor<R,D> visitor, D d)635   protected <R,D> void visitChildren(ExpVisitor<R,D> visitor, D d)
636   {
637     func = visitor.visitAndUpdate(func, d);
638     if (visitor.exitValue == null)
639       args = visitor.visitExps(args, args.length, d);
640   }
641 
print(OutPort out)642   public void print (OutPort out)
643   {
644     out.startLogicalBlock("(Apply", ")", 2);
645     if (isTailCall())
646       out.print (" [tailcall]");
647     if (type != null && type != Type.pointer_type)
648       {
649         out.print(" => ");
650         out.print(type);
651       }
652     out.writeSpaceFill();
653     printLineColumn(out);
654     func.print(out);
655     if (args != null && args.length > 0) {
656         out.writeShowHideButton(true);
657         out.startHiderSection(true);
658         int firstKeyword = firstKeywordArgIndex-1;
659         for (int i = 0; i < args.length; ++i)
660         {
661             out.writeSpaceLinear();
662             Expression arg = args[i];
663             if (i >= firstKeyword && ((i - firstKeyword) & 1) == 0
664                 && i < firstKeyword + 2 * numKeywordArgs
665                 && arg.valueIfConstant() instanceof Keyword) {
666                 out.print(arg.valueIfConstant().toString());
667             } else
668                 arg.print(out);
669         }
670         out.endHiderSection();
671     }
672     out.endLogicalBlock(")");
673   }
674 
675   /** Only used for inline- and tail-calls. */
pushArgs(LambdaExp lexp, Expression[] args, int args_length, int[] incValues, Compilation comp)676   private static Declaration pushArgs (LambdaExp lexp,
677                                 Expression[] args, int args_length,
678                                 int[] incValues, Compilation comp)
679   {
680     Declaration param = lexp.firstDecl();
681     for (int i = 0; i < args_length; ++i)
682       {
683         Expression arg = args[i];
684         if (param.ignorable())
685           arg.compile(comp, Target.Ignore);
686         else if (incValues != null
687             && (incValues[i] = SetExp.canUseInc(arg, param)) != SetExp.BAD_SHORT)
688           ;
689         else
690           arg.compileWithPosition(comp,
691                                   StackTarget.getInstance(param.getType()));
692         param = param.nextDecl();
693       }
694     return param;
695   }
696 
popParams(CodeAttr code, LambdaExp lexp, int[] incValues, boolean toArray)697   static void popParams (CodeAttr code, LambdaExp lexp,
698                                  int[] incValues, boolean toArray)
699   {
700     Variable vars = lexp.getVarScope().firstVar();
701     Declaration decls = lexp.firstDecl();
702     if (vars != null && vars.getName() == "this")
703       vars = vars.nextVar();
704     if (vars != null && vars.getName() == "$ctx")
705       vars = vars.nextVar();
706     if (vars != null && vars.getName() == LambdaExp.CLOSURE_ENV_NAME)
707       vars = vars.nextVar();
708     popParams (code, 0, lexp.min_args, toArray, incValues, decls, vars);
709   }
710 
711   // Recursive helper function.
popParams(CodeAttr code, int paramNo, int count, boolean toArray, int[] incValues, Declaration decl, Variable vars)712   private static void popParams (CodeAttr code, int paramNo,
713                                  int count, boolean toArray,
714                                  int[] incValues,
715                                  Declaration decl, Variable vars)
716   {
717     if (count > 0)
718       {
719         count--;
720 	popParams (code, paramNo+1, count, toArray, incValues, decl.nextDecl(),
721                    decl.getVariable() == null ? vars : vars.nextVar());
722         if (! decl.ignorable())
723           {
724             if (incValues != null && incValues[paramNo] != SetExp.BAD_SHORT)
725               code.emitInc(vars, (short) incValues[paramNo]);
726             else
727               code.emitStore(vars);
728           }
729       }
730     else if (toArray)
731       code.emitStore(vars); // rest array
732   }
733 
side_effects()734   public boolean side_effects ()
735   {
736     Object value = derefFunc(func).valueIfConstant();
737     if (value instanceof Procedure
738         && ((Procedure) value).isSideEffectFree())
739       {
740         Expression[] a = args;
741         int alen = a.length;
742         for (int i = 0;  i < alen;  i++)
743           {
744             if (a[i].side_effects())
745               return true;
746           }
747         return false;
748       }
749     return true;
750   }
751 
derefFunc(Expression afunc)752   static Expression derefFunc(Expression afunc)
753   {
754     if (afunc instanceof ReferenceExp)
755       {
756 	Declaration func_decl = ((ReferenceExp) afunc).binding;
757 	func_decl = Declaration.followAliases(func_decl);
758 	if (func_decl != null && ! func_decl.getFlag(Declaration.IS_UNKNOWN))
759 	  afunc = func_decl.getValue();
760       }
761     return afunc;
762   }
763 
calculateType()764   protected gnu.bytecode.Type calculateType()
765   {
766     Expression afunc = derefFunc(func);
767     if (afunc instanceof QuoteExp)
768       {
769         Object value = ((QuoteExp) afunc).getValue();
770         // This logic is deprecated - instead set type during InlineCalls.
771         if (value instanceof Procedure)
772           type = ((Procedure) value).getReturnType(args);
773       }
774     else if (afunc instanceof LambdaExp)
775       {
776 	type = ((LambdaExp) afunc).getReturnType();
777       }
778     return type;
779   }
780 
isInlineable(Procedure proc)781     public static boolean isInlineable(Procedure proc) {
782         return
783             proc instanceof Inlineable
784             || Procedure.compilerKey.get(proc) != null
785             || proc.getProperty(Procedure.compilerXKey, null) != null;
786     }
787 
inlineCompile(Procedure proc, ApplyExp exp, Compilation comp, Target target)788     static boolean inlineCompile(Procedure proc, ApplyExp exp,
789                                  Compilation comp, Target target)
790             throws Throwable {
791         if (proc instanceof PrimProcedure)
792             return ((PrimProcedure) proc).compile(exp, comp, target);
793         Object propval = proc.getProperty(Procedure.compilerXKey, null);
794         if (propval instanceof CharSequence) {
795             Object method = InlineCalls.resolveInliner(proc,
796                                                        propval.toString(),
797                                                        compilerMethodType);
798             if (method != null)
799                 propval = method;
800         }
801         /* #ifdef use:java.lang.invoke */
802         if (propval instanceof MethodHandle) {
803             /*
804             System.err.println("inlineCompile "+exp);
805             for (int i = 0; i < exp.getArgCount(); i++)
806                 System.err.println("- #"+i+": "+exp.getArg(i));
807             System.err.println("propval: "+java.lang.invoke.MethodHandles.lookup().revealDirect((MethodHandle)propval));
808             */
809             return (boolean) ((MethodHandle) propval).invokeExact(exp, comp, target, proc);
810         }
811         /* #else */
812         // if (propval instanceof java.lang.reflect.Method)
813         //    return (Boolean) ((java.lang.reflect.Method) propval)
814         //        .invoke(null, new Object[] { exp, comp, target, proc });
815         /* #endif */
816         if (propval != null) {
817             comp.error('e', "compiler property string for "+proc
818                        +" is not of the form CLASS:METHOD");
819             return false;
820         }
821         if (! exp.isSimple())
822             return false;
823         Inlineable compiler;
824         if (proc instanceof Inlineable)
825             compiler = (Inlineable) proc;
826         else if ((propval = Procedure.compilerKey.get(proc)) != null)
827             compiler = (Inlineable) Procedure.compilerKey.get(proc);
828         else
829             compiler = null;
830         if (compiler == null)
831             return false;
832         compiler.compile(exp, comp, target);
833         return true;
834     }
835 
inlineIfConstant(Procedure proc, InlineCalls visitor)836   public final Expression inlineIfConstant(Procedure proc, InlineCalls visitor)
837   {
838     return inlineIfConstant(proc, visitor.getMessages());
839   }
840 
841   /** Inline this ApplyExp if parameters are constant.
842    * @param proc the procedure bound to this.func.
843    * @return the constant result (as a QuoteExp) if inlining was possible;
844    *   otherwise this ApplyExp.
845    * If applying proc throws an exception, print a warning on walker.messages.
846    */
inlineIfConstant(Procedure proc, SourceMessages messages)847   public final Expression inlineIfConstant(Procedure proc, SourceMessages messages)
848   {
849     int len = args.length;
850     Object[] vals = new Object[len];
851     for (int i = len;  --i >= 0; )
852       {
853 	Expression arg = args[i];
854 	if (arg instanceof ReferenceExp)
855 	  {
856 	    Declaration decl = ((ReferenceExp) arg).getBinding();
857 	    if (decl != null)
858 	      {
859 		arg = decl.getValue();
860 		if (arg == QuoteExp.undefined_exp)
861 		  return this;
862 	      }
863 	  }
864 	if (! (arg instanceof QuoteExp))
865 	  return this;
866 	vals[i] = ((QuoteExp) arg).getValue();
867       }
868     try
869       {
870 	return new QuoteExp(proc.applyN(vals), type).setLine(this);
871       }
872     catch (Error ex)
873       {
874         throw ex;
875       }
876     catch (Throwable ex)
877       {
878 	if (messages != null)
879 	  messages.error('w', "call to " + proc +
880 				" throws " + ex);
881 	return this;
882       }
883   }
884 
toString()885   public String toString ()
886   {
887     if (this==LambdaExp.unknownContinuation)
888       return "ApplyExp[unknownContinuation]";
889     return "ApplyExp/"+(args == null?0:args.length)+'['+func+']';
890   }
891 
892     /* #ifdef use:java.lang.invoke */
893     static final MethodType compilerMethodType =
894         MethodType.methodType(boolean.class,
895                               gnu.expr.ApplyExp.class,
896                               gnu.expr.Compilation.class,
897                               gnu.expr.Target.class,
898                               gnu.mapping.Procedure.class);
899     /* #else */
900     // private static final Class[] compilerMethodType =
901     //     new Class[] { gnu.expr.ApplyExp.class,
902     //                   gnu.expr.Compilation.class,
903     //                   gnu.expr.Target.class,
904     //                   gnu.mapping.Procedure.class };
905     /* #endif */
906 }
907