KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > expr > ApplyExp


1 // Copyright (c) 2003, 2004, 2006 Per M.A. Bothner.
2
// This is free software; for terms and warranty disclaimer see ./COPYING.
3

4 package gnu.expr;
5 import gnu.bytecode.*;
6 import gnu.mapping.*;
7 import gnu.text.SourceMessages;
8
9 /** This class is used to represent "combination" or "application".
10  * A function and arguments are evaluated, and then the function applied.
11  * @author Per Bothner
12  */

13
14 public class ApplyExp extends Expression
15 {
16   Expression func;
17   Expression[] args;
18
19   public static final int TAILCALL = NEXT_AVAIL_FLAG;
20   public static final int INLINE_IF_CONSTANT = NEXT_AVAIL_FLAG << 1;
21
22   /** Containing LambdaExp. */
23   LambdaExp context;
24
25   /** The next ApplyExp in ((ReferenceExp)func).binding.firstCall list. */
26   public ApplyExp nextCall;
27
28   public final Expression getFunction() { return func; }
29   public final Expression[] getArgs() { return args; }
30   public final int getArgCount() { return args.length; }
31   public void setFunction(Expression func) { this.func = func; }
32   public void setArgs(Expression[] args) { this.args = args; }
33   public Expression getArg(int i) { return args[i]; }
34   public void setArg(int i, Expression arg) { args[i] = arg; }
35   public final boolean isTailCall() { return getFlag(TAILCALL); }
36   public final void setTailCall(boolean tailCall)
37   { setFlag(tailCall, TAILCALL); }
38
39   /** If getFunction() is constant, return its value; otherwise null. */
40   public final Object JavaDoc getFunctionValue()
41   {
42     return func instanceof QuoteExp ? ((QuoteExp) func).getValue() : null;
43   }
44
45   public ApplyExp (Expression f, Expression[] a) { func = f; args = a; }
46
47   public ApplyExp (Procedure p, Expression[] a) { func = new QuoteExp(p); args = a; }
48
49   public ApplyExp (Method m, Expression[] a)
50   {
51     func = new QuoteExp(new PrimProcedure(m));
52     args = a;
53   }
54
55   protected boolean mustCompile () { return false; }
56
57   public void apply (CallContext ctx) throws Throwable JavaDoc
58   {
59     Object JavaDoc proc = func.eval(ctx);
60     int n = args.length;
61     Object JavaDoc[] vals = new Object JavaDoc[n];
62     for (int i = 0; i < n; i++)
63       vals[i] = args[i].eval(ctx);
64     ((Procedure) proc).checkN(vals, ctx);
65   }
66
67   public static void compileToArray(Expression[] args, Compilation comp)
68   {
69     CodeAttr code = comp.getCode();
70     if (args.length == 0)
71       {
72     code.emitGetStatic(Compilation.noArgsField);
73     return;
74       }
75     code.emitPushInt(args.length);
76     code.emitNewArray(Type.pointer_type);
77     for (int i = 0; i < args.length; ++i)
78       {
79     Expression arg = args[i];
80     if (comp.usingCPStyle()
81         && ! (arg instanceof QuoteExp) && ! (arg instanceof ReferenceExp))
82       {
83         // If the argument involves a CPStyle function call, we will
84
// have to save and restore anything on the JVM stack into
85
// fields in the CallFrame. This is expensive, so defer
86
// pushing the duplicated argument array and the index
87
// until *after* we've calculated the argument. The downside
88
// is that we have to do some extra stack operations.
89
// However, these are cheap (and get compiled away when
90
// compiling to native code).
91
arg.compile (comp, Target.pushObject);
92         code.emitSwap();
93         code.emitDup(1, 1);
94         code.emitSwap();
95         code.emitPushInt(i);
96         code.emitSwap();
97       }
98     else
99       {
100         code.emitDup(Compilation.objArrayType);
101         code.emitPushInt(i);
102         arg.compile (comp, Target.pushObject);
103       }
104     code.emitArrayStore(Type.pointer_type);
105       }
106   }
107
108   public void compile (Compilation comp, Target target)
109   {
110     compile(this, comp, target, true);
111   }
112
113   public static void compile (ApplyExp exp, Compilation comp, Target target)
114   {
115     compile(exp, comp, target, false);
116   }
117
118   static void compile (ApplyExp exp, Compilation comp, Target target,
119                               boolean checkInlineable)
120   {
121     int args_length = exp.args.length;
122     Expression exp_func = exp.func;
123     LambdaExp func_lambda = null;
124     String JavaDoc func_name = null;
125     Declaration owner = null;
126     if (exp_func instanceof LambdaExp)
127       {
128     func_lambda = (LambdaExp) exp_func;
129     func_name = func_lambda.getName();
130     if (func_name == null)
131       func_name = "<lambda>";
132       }
133     else if (exp_func instanceof ReferenceExp)
134       {
135         ReferenceExp func_ref = (ReferenceExp) exp_func;
136         owner = func_ref.contextDecl();
137         Declaration func_decl = func_ref.binding;
138         while (func_decl != null && func_decl.isAlias()
139                && func_decl.value instanceof ReferenceExp)
140           {
141             func_ref = (ReferenceExp) func_decl.value;
142             if (owner != null || func_decl.needsContext() || func_ref.binding == null)
143               break;
144             func_decl = func_ref.binding;
145             owner = func_ref.contextDecl();
146           }
147         if (! func_decl.getFlag(Declaration.IS_UNKNOWN))
148       {
149         Expression value = func_decl.getValue();
150         func_name = func_decl.getName();
151         if (value != null && value instanceof LambdaExp)
152           func_lambda = (LambdaExp) value;
153         if (value != null && value instanceof QuoteExp)
154           {
155         Object JavaDoc quotedValue = ((QuoteExp) value).getValue();
156         if (checkInlineable && quotedValue instanceof Inlineable)
157                   {
158                     ((Inlineable) quotedValue).compile(exp, comp, target);
159                     return;
160           }
161           }
162       }
163       }
164     else if (exp_func instanceof QuoteExp)
165       {
166         Object JavaDoc proc = ((QuoteExp) exp_func).getValue();
167     if (proc instanceof Inlineable)
168       {
169             if (checkInlineable)
170               {
171                 ((Inlineable) proc).compile(exp, comp, target);
172                 return;
173               }
174           }
175       }
176
177     gnu.bytecode.CodeAttr code = comp.getCode();
178     Method method;
179
180     if (func_lambda != null)
181       {
182     if ((func_lambda.max_args >= 0 && args_length > func_lambda.max_args)
183         || args_length < func_lambda.min_args)
184       // This is supposed to get caught by InlineCalls.
185
throw new Error JavaDoc ("internal error - wrong number of parameters for "
186                + func_lambda);
187     int conv = func_lambda.getCallConvention();
188     if (comp.inlineOk(func_lambda)
189         && (conv <= Compilation.CALL_WITH_CONSUMER
190         || (conv == Compilation.CALL_WITH_TAILCALLS
191             && ! exp.isTailCall()))
192         && (method = func_lambda.getMethod(args_length)) != null)
193       {
194         PrimProcedure pproc = new PrimProcedure(method, func_lambda);
195         boolean is_static = method.getStaticFlag();
196         boolean extraArg = false;
197         // ?? Procedure.checkArgCount(this, args.length); // FIXME
198
if (! is_static || func_lambda.declareClosureEnv() != null)
199           {
200         if (is_static)
201           extraArg = true;
202         if (comp.curLambda == func_lambda) // Recursive call.
203
code.emitLoad(func_lambda.closureEnv != null
204                 ? func_lambda.closureEnv
205                 : func_lambda.thisVariable);
206         else if (owner != null)
207                   owner.load(null, 0, comp, Target.pushObject);
208                 else
209                   func_lambda.getOwningLambda().loadHeapFrame(comp);
210           }
211
212         pproc.compile(extraArg ? Type.void_type : null,
213               exp, comp, target);
214         return;
215       }
216       }
217
218     if (comp.usingCPStyle())
219       {
220       {
221         Label l = new Label(code);
222         gnu.bytecode.SwitchState fswitch = comp.fswitch;
223         int pc = fswitch.getMaxValue() + 1;
224         fswitch.addCase(pc, l, code);
225             exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));
226         comp.loadCallContext();
227
228         // Emit: context->pc = pc.
229
comp.loadCallContext();
230         code.emitPushInt(pc);
231         code.emitPutField(Compilation.pcCallContextField);
232         code.emitInvokeVirtual(Compilation.applyCpsMethod);
233
234         // emit[save java stack, if needed]
235
Type[] stackTypes = code.saveStackTypeState(false);
236         java.util.Stack JavaDoc stackFields = new java.util.Stack JavaDoc();
237         if (stackTypes != null)
238           {
239         for (int i = stackTypes.length; --i >= 0; )
240           {
241             Field fld = comp.allocLocalField (stackTypes[i], null);
242             code.emitPushThis();
243             code.emitSwap();
244             code.emitPutField(fld);
245             stackFields.push(fld);
246           }
247           }
248
249         code.emitReturn();
250         l.define(code);
251
252         // emit[restore java stack, if needed]
253
if (stackTypes != null)
254           {
255         for (int i = stackTypes.length; --i >= 0; )
256           {
257             Field fld = (Field) stackFields.pop();
258             code.emitPushThis();
259             code.emitGetField(fld);
260             comp.freeLocalField(fld);
261           }
262           }
263
264         /* FIXME
265         // Load result from stack.value to target.
266         comp.loadCallContext();
267         code.emitGetField(comp.valueCallContextField);
268         target.compileFromStack(comp, Type.pointer_type);
269         */

270       }
271     return;
272       }
273
274     // Check for tail-recursion.
275
boolean tail_recurse
276       = exp.isTailCall()
277       && func_lambda != null && func_lambda == comp.curLambda;
278
279     if (func_lambda != null && func_lambda.getInlineOnly() && !tail_recurse
280     && func_lambda.min_args == args_length)
281       {
282         pushArgs(func_lambda, exp.args, comp);
283     LambdaExp saveLambda = comp.curLambda;
284     comp.curLambda = func_lambda;
285     func_lambda.allocChildClasses(comp);
286     func_lambda.allocParameters(comp);
287     popParams (code, func_lambda, false);
288     func_lambda.enterFunction(comp);
289     func_lambda.body.compileWithPosition(comp, target);
290     func_lambda.compileEnd(comp);
291     func_lambda.compileChildMethods(comp);
292     func_lambda.popScope(code);
293     comp.curLambda = saveLambda;
294     return;
295       }
296
297     if (comp.curLambda.isHandlingTailCalls()
298     && (exp.isTailCall() || target instanceof ConsumerTarget)
299     && ! comp.curLambda.getInlineOnly())
300       {
301     ClassType typeContext = Compilation.typeCallContext;
302     exp_func.compile(comp, new StackTarget(Compilation.typeProcedure));
303     // evaluate args to frame-locals vars; // may recurse!
304
if (args_length <= 4)
305       {
306         for (int i = 0; i < args_length; ++i)
307           exp.args[i].compile(comp, Target.pushObject);
308         comp.loadCallContext();
309         code.emitInvoke(Compilation.typeProcedure
310                 .getDeclaredMethod("check"+args_length,
311                            args_length+1));
312       }
313     else
314       {
315         compileToArray (exp.args, comp);
316         comp.loadCallContext();
317         code.emitInvoke(Compilation.typeProcedure
318                 .getDeclaredMethod("checkN", 2));
319       }
320     if (exp.isTailCall())
321       {
322         code.emitReturn();
323       }
324     else if (((ConsumerTarget) target).isContextTarget())
325       {
326         comp.loadCallContext();
327         code.emitInvoke(typeContext.getDeclaredMethod("runUntilDone", 0));
328       }
329     else
330       {
331         comp.loadCallContext();
332         code.emitLoad(((ConsumerTarget) target).getConsumerVariable());
333         code.emitInvoke(typeContext.getDeclaredMethod("runUntilValue", 1));
334       }
335     return;
336       }
337
338     if (!tail_recurse)
339       exp_func.compile (comp, new StackTarget(Compilation.typeProcedure));
340
341     boolean toArray
342       = (tail_recurse ? func_lambda.min_args != func_lambda.max_args
343          : args_length > 4);
344     if (toArray)
345       {
346     compileToArray(exp.args, comp);
347     method = Compilation.applyNmethod;
348       }
349     else if (tail_recurse)
350       {
351         pushArgs(func_lambda, exp.args, comp);
352         method = null;
353       }
354     else
355       {
356     for (int i = 0; i < args_length; ++i)
357           {
358             exp.args[i].compile (comp, Target.pushObject);
359             if (! code.reachableHere())
360               break;
361           }
362         method = Compilation.applymethods[args_length];
363       }
364     if (! code.reachableHere())
365       {
366         comp.error('e', "unreachable code");
367         return;
368       }
369     if (tail_recurse)
370       {
371     popParams(code, func_lambda, toArray);
372     code.emitTailCall(false, func_lambda.getVarScope());
373     return;
374       }
375     code.emitInvokeVirtual(method);
376     target.compileFromStack(comp, Type.pointer_type);
377   }
378
379   protected Expression walk (ExpWalker walker)
380   {
381     return walker.walkApplyExp(this);
382   }
383
384   protected void walkChildren(ExpWalker walker)
385   {
386     func = walker.walk(func);
387     if (walker.exitValue == null)
388       args = walker.walkExps(args, args.length);
389   }
390
391   public void print (OutPort out)
392   {
393     out.startLogicalBlock("(Apply", ")", 2);
394     if (isTailCall())
395       out.print (" [tailcall]");
396     if (type != null && type != Type.pointer_type)
397       {
398         out.print(" => ");
399         out.print(type);
400       }
401     out.writeSpaceFill();
402     printLineColumn(out);
403     func.print(out);
404     for (int i = 0; i < args.length; ++i)
405       {
406     out.writeSpaceLinear();
407     args[i].print(out);
408       }
409     out.endLogicalBlock(")");
410   }
411
412   /** Only used for inline- and tail-calls. */
413   private static void pushArgs (LambdaExp lexp, Expression[] args, Compilation comp)
414   {
415     Declaration param = lexp.firstDecl();
416     int args_length = args.length;
417     for (int i = 0; i < args_length; ++i)
418       {
419         Expression arg = args[i];
420         if (param.ignorable())
421           arg.compile(comp, Target.Ignore);
422         else
423           arg.compile(comp, param.getType());
424         param = param.nextDecl();
425       }
426   }
427
428   private static void popParams (CodeAttr code, LambdaExp lexp,
429                                  boolean toArray)
430   {
431     Variable vars = lexp.getVarScope().firstVar();
432     Declaration decls = lexp.firstDecl();
433     if (vars != null && vars.getName() == "this")
434       vars = vars.nextVar();
435     if (vars != null && vars.getName() == "$ctx")
436       vars = vars.nextVar();
437     if (vars != null && vars.getName() == "argsArray")
438       {
439     if (toArray)
440       {
441         popParams (code, 1, decls, vars);
442         return;
443       }
444         vars = vars.nextVar();
445       }
446     popParams (code, lexp.min_args, decls, vars);
447   }
448
449   // Recursive helper function.
450
private static void popParams (CodeAttr code, int count,
451                                  Declaration decl, Variable vars)
452   {
453     if (count > 0)
454       {
455     popParams (code, count - 1, decl.nextDecl(),
456                    decl.getVariable() == null ? vars : vars.nextVar());
457         if (! decl.ignorable())
458           code.emitStore(vars);
459       }
460   }
461
462   /** Cache for getType(). */
463   protected Type type;
464
465   public final gnu.bytecode.Type getTypeRaw()
466   {
467     return type;
468   }
469
470   public final void setType (gnu.bytecode.Type type)
471   {
472     this.type = type;
473   }
474
475   public final gnu.bytecode.Type getType()
476   {
477     if (type != null)
478       return type;
479     Expression afunc = func;
480     // In case of cycles.
481
type = Type.pointer_type;
482     if (afunc instanceof ReferenceExp)
483       {
484     Declaration func_decl = ((ReferenceExp) afunc).binding;
485     func_decl = Declaration.followAliases(func_decl);
486     if (func_decl != null && ! func_decl.getFlag(Declaration.IS_UNKNOWN))
487       afunc = func_decl.getValue();
488       }
489     if (afunc instanceof QuoteExp)
490       {
491     Object JavaDoc proc = ((QuoteExp) afunc).getValue();
492     if (proc instanceof Inlineable)
493       type = ((Inlineable) proc).getReturnType(args);
494       }
495     else if (afunc instanceof LambdaExp)
496       {
497     type = ((LambdaExp) afunc).getReturnType();
498       }
499     return type;
500   }
501
502   public final Expression inlineIfConstant(Procedure proc, ExpWalker walker)
503   {
504     return inlineIfConstant(proc, walker.getMessages());
505   }
506
507   /** Inline this ApplyExp if parameters are constant.
508    * @param proc the procedure bound to this.func.
509    * @return the constant result (as a QuoteExp) if inlining was possible;
510    * otherwise this ApplyExp.
511    * If applying proc throws an exception, print a warning on walker.messages.
512    */

513   public final Expression inlineIfConstant(Procedure proc, SourceMessages messages)
514   {
515     int len = args.length;
516     Object JavaDoc[] vals = new Object JavaDoc[len];
517     for (int i = len; --i >= 0; )
518       {
519     Expression arg = args[i];
520     if (arg instanceof ReferenceExp)
521       {
522         Declaration decl = ((ReferenceExp) arg).getBinding();
523         if (decl != null)
524           {
525         arg = decl.getValue();
526         if (arg == QuoteExp.undefined_exp)
527           return this;
528           }
529       }
530     if (! (arg instanceof QuoteExp))
531       return this;
532     vals[i] = ((QuoteExp) arg).getValue();
533       }
534     try
535       {
536     return new QuoteExp(proc.applyN(vals));
537       }
538     catch (Throwable JavaDoc ex)
539       {
540     if (messages != null)
541       messages.error('w', "call to " + proc +
542                 " throws " + ex);
543     return this;
544       }
545   }
546
547   public String JavaDoc toString ()
548   {
549     return "ApplyExp/"+args.length+'['+func+']';
550   }
551 }
552
Popular Tags