001: // Copyright (c) 2003, 2004, 2006 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.bytecode.*;
007: import gnu.mapping.*;
008: import gnu.text.SourceMessages;
009:
010: /** This class is used to represent "combination" or "application".
011: * A function and arguments are evaluated, and then the function applied.
012: * @author Per Bothner
013: */
014:
015: public class ApplyExp extends Expression {
016: Expression func;
017: Expression[] args;
018:
019: public static final int TAILCALL = NEXT_AVAIL_FLAG;
020: public static final int INLINE_IF_CONSTANT = NEXT_AVAIL_FLAG << 1;
021:
022: /** Containing LambdaExp. */
023: LambdaExp context;
024:
025: /** The next ApplyExp in ((ReferenceExp)func).binding.firstCall list. */
026: public ApplyExp nextCall;
027:
028: public final Expression getFunction() {
029: return func;
030: }
031:
032: public final Expression[] getArgs() {
033: return args;
034: }
035:
036: public final int getArgCount() {
037: return args.length;
038: }
039:
040: public void setFunction(Expression func) {
041: this .func = func;
042: }
043:
044: public void setArgs(Expression[] args) {
045: this .args = args;
046: }
047:
048: public Expression getArg(int i) {
049: return args[i];
050: }
051:
052: public void setArg(int i, Expression arg) {
053: args[i] = arg;
054: }
055:
056: public final boolean isTailCall() {
057: return getFlag(TAILCALL);
058: }
059:
060: public final void setTailCall(boolean tailCall) {
061: setFlag(tailCall, TAILCALL);
062: }
063:
064: /** If getFunction() is constant, return its value; otherwise null. */
065: public final Object getFunctionValue() {
066: return func instanceof QuoteExp ? ((QuoteExp) func).getValue()
067: : null;
068: }
069:
070: public ApplyExp(Expression f, Expression[] a) {
071: func = f;
072: args = a;
073: }
074:
075: public ApplyExp(Procedure p, Expression[] a) {
076: func = new QuoteExp(p);
077: args = a;
078: }
079:
080: public ApplyExp(Method m, Expression[] a) {
081: func = new QuoteExp(new PrimProcedure(m));
082: args = a;
083: }
084:
085: protected boolean mustCompile() {
086: return false;
087: }
088:
089: public void apply(CallContext ctx) throws Throwable {
090: Object proc = func.eval(ctx);
091: int n = args.length;
092: Object[] vals = new Object[n];
093: for (int i = 0; i < n; i++)
094: vals[i] = args[i].eval(ctx);
095: ((Procedure) proc).checkN(vals, ctx);
096: }
097:
098: public static void compileToArray(Expression[] args,
099: Compilation comp) {
100: CodeAttr code = comp.getCode();
101: if (args.length == 0) {
102: code.emitGetStatic(Compilation.noArgsField);
103: return;
104: }
105: code.emitPushInt(args.length);
106: code.emitNewArray(Type.pointer_type);
107: for (int i = 0; i < args.length; ++i) {
108: Expression arg = args[i];
109: if (comp.usingCPStyle() && !(arg instanceof QuoteExp)
110: && !(arg instanceof ReferenceExp)) {
111: // If the argument involves a CPStyle function call, we will
112: // have to save and restore anything on the JVM stack into
113: // fields in the CallFrame. This is expensive, so defer
114: // pushing the duplicated argument array and the index
115: // until *after* we've calculated the argument. The downside
116: // is that we have to do some extra stack operations.
117: // However, these are cheap (and get compiled away when
118: // compiling to native code).
119: arg.compile(comp, Target.pushObject);
120: code.emitSwap();
121: code.emitDup(1, 1);
122: code.emitSwap();
123: code.emitPushInt(i);
124: code.emitSwap();
125: } else {
126: code.emitDup(Compilation.objArrayType);
127: code.emitPushInt(i);
128: arg.compile(comp, Target.pushObject);
129: }
130: code.emitArrayStore(Type.pointer_type);
131: }
132: }
133:
134: public void compile(Compilation comp, Target target) {
135: compile(this , comp, target, true);
136: }
137:
138: public static void compile(ApplyExp exp, Compilation comp,
139: Target target) {
140: compile(exp, comp, target, false);
141: }
142:
143: static void compile(ApplyExp exp, Compilation comp, Target target,
144: boolean checkInlineable) {
145: int args_length = exp.args.length;
146: Expression exp_func = exp.func;
147: LambdaExp func_lambda = null;
148: String func_name = null;
149: Declaration owner = null;
150: if (exp_func instanceof LambdaExp) {
151: func_lambda = (LambdaExp) exp_func;
152: func_name = func_lambda.getName();
153: if (func_name == null)
154: func_name = "<lambda>";
155: } else if (exp_func instanceof ReferenceExp) {
156: ReferenceExp func_ref = (ReferenceExp) exp_func;
157: owner = func_ref.contextDecl();
158: Declaration func_decl = func_ref.binding;
159: while (func_decl != null && func_decl.isAlias()
160: && func_decl.value instanceof ReferenceExp) {
161: func_ref = (ReferenceExp) func_decl.value;
162: if (owner != null || func_decl.needsContext()
163: || func_ref.binding == null)
164: break;
165: func_decl = func_ref.binding;
166: owner = func_ref.contextDecl();
167: }
168: if (!func_decl.getFlag(Declaration.IS_UNKNOWN)) {
169: Expression value = func_decl.getValue();
170: func_name = func_decl.getName();
171: if (value != null && value instanceof LambdaExp)
172: func_lambda = (LambdaExp) value;
173: if (value != null && value instanceof QuoteExp) {
174: Object quotedValue = ((QuoteExp) value).getValue();
175: if (checkInlineable
176: && quotedValue instanceof Inlineable) {
177: ((Inlineable) quotedValue).compile(exp, comp,
178: target);
179: return;
180: }
181: }
182: }
183: } else if (exp_func instanceof QuoteExp) {
184: Object proc = ((QuoteExp) exp_func).getValue();
185: if (proc instanceof Inlineable) {
186: if (checkInlineable) {
187: ((Inlineable) proc).compile(exp, comp, target);
188: return;
189: }
190: }
191: }
192:
193: gnu.bytecode.CodeAttr code = comp.getCode();
194: Method method;
195:
196: if (func_lambda != null) {
197: if ((func_lambda.max_args >= 0 && args_length > func_lambda.max_args)
198: || args_length < func_lambda.min_args)
199: // This is supposed to get caught by InlineCalls.
200: throw new Error(
201: "internal error - wrong number of parameters for "
202: + func_lambda);
203: int conv = func_lambda.getCallConvention();
204: if (comp.inlineOk(func_lambda)
205: && (conv <= Compilation.CALL_WITH_CONSUMER || (conv == Compilation.CALL_WITH_TAILCALLS && !exp
206: .isTailCall()))
207: && (method = func_lambda.getMethod(args_length)) != null) {
208: PrimProcedure pproc = new PrimProcedure(method,
209: func_lambda);
210: boolean is_static = method.getStaticFlag();
211: boolean extraArg = false;
212: // ?? Procedure.checkArgCount(this, args.length); // FIXME
213: if (!is_static
214: || func_lambda.declareClosureEnv() != null) {
215: if (is_static)
216: extraArg = true;
217: if (comp.curLambda == func_lambda) // Recursive call.
218: code
219: .emitLoad(func_lambda.closureEnv != null ? func_lambda.closureEnv
220: : func_lambda.this Variable);
221: else if (owner != null)
222: owner.load(null, 0, comp, Target.pushObject);
223: else
224: func_lambda.getOwningLambda().loadHeapFrame(
225: comp);
226: }
227:
228: pproc.compile(extraArg ? Type.void_type : null, exp,
229: comp, target);
230: return;
231: }
232: }
233:
234: if (comp.usingCPStyle()) {
235: {
236: Label l = new Label(code);
237: gnu.bytecode.SwitchState fswitch = comp.fswitch;
238: int pc = fswitch.getMaxValue() + 1;
239: fswitch.addCase(pc, l, code);
240: exp_func.compile(comp, new StackTarget(
241: Compilation.typeProcedure));
242: comp.loadCallContext();
243:
244: // Emit: context->pc = pc.
245: comp.loadCallContext();
246: code.emitPushInt(pc);
247: code.emitPutField(Compilation.pcCallContextField);
248: code.emitInvokeVirtual(Compilation.applyCpsMethod);
249:
250: // emit[save java stack, if needed]
251: Type[] stackTypes = code.saveStackTypeState(false);
252: java.util.Stack stackFields = new java.util.Stack();
253: if (stackTypes != null) {
254: for (int i = stackTypes.length; --i >= 0;) {
255: Field fld = comp.allocLocalField(stackTypes[i],
256: null);
257: code.emitPushThis();
258: code.emitSwap();
259: code.emitPutField(fld);
260: stackFields.push(fld);
261: }
262: }
263:
264: code.emitReturn();
265: l.define(code);
266:
267: // emit[restore java stack, if needed]
268: if (stackTypes != null) {
269: for (int i = stackTypes.length; --i >= 0;) {
270: Field fld = (Field) stackFields.pop();
271: code.emitPushThis();
272: code.emitGetField(fld);
273: comp.freeLocalField(fld);
274: }
275: }
276:
277: /* FIXME
278: // Load result from stack.value to target.
279: comp.loadCallContext();
280: code.emitGetField(comp.valueCallContextField);
281: target.compileFromStack(comp, Type.pointer_type);
282: */
283: }
284: return;
285: }
286:
287: // Check for tail-recursion.
288: boolean tail_recurse = exp.isTailCall() && func_lambda != null
289: && func_lambda == comp.curLambda;
290:
291: if (func_lambda != null && func_lambda.getInlineOnly()
292: && !tail_recurse && func_lambda.min_args == args_length) {
293: pushArgs(func_lambda, exp.args, comp);
294: LambdaExp saveLambda = comp.curLambda;
295: comp.curLambda = func_lambda;
296: func_lambda.allocChildClasses(comp);
297: func_lambda.allocParameters(comp);
298: popParams(code, func_lambda, false);
299: func_lambda.enterFunction(comp);
300: func_lambda.body.compileWithPosition(comp, target);
301: func_lambda.compileEnd(comp);
302: func_lambda.compileChildMethods(comp);
303: func_lambda.popScope(code);
304: comp.curLambda = saveLambda;
305: return;
306: }
307:
308: if (comp.curLambda.isHandlingTailCalls()
309: && (exp.isTailCall() || target instanceof ConsumerTarget)
310: && !comp.curLambda.getInlineOnly()) {
311: ClassType typeContext = Compilation.typeCallContext;
312: exp_func.compile(comp, new StackTarget(
313: Compilation.typeProcedure));
314: // evaluate args to frame-locals vars; // may recurse!
315: if (args_length <= 4) {
316: for (int i = 0; i < args_length; ++i)
317: exp.args[i].compile(comp, Target.pushObject);
318: comp.loadCallContext();
319: code.emitInvoke(Compilation.typeProcedure
320: .getDeclaredMethod("check" + args_length,
321: args_length + 1));
322: } else {
323: compileToArray(exp.args, comp);
324: comp.loadCallContext();
325: code.emitInvoke(Compilation.typeProcedure
326: .getDeclaredMethod("checkN", 2));
327: }
328: if (exp.isTailCall()) {
329: code.emitReturn();
330: } else if (((ConsumerTarget) target).isContextTarget()) {
331: comp.loadCallContext();
332: code.emitInvoke(typeContext.getDeclaredMethod(
333: "runUntilDone", 0));
334: } else {
335: comp.loadCallContext();
336: code.emitLoad(((ConsumerTarget) target)
337: .getConsumerVariable());
338: code.emitInvoke(typeContext.getDeclaredMethod(
339: "runUntilValue", 1));
340: }
341: return;
342: }
343:
344: if (!tail_recurse)
345: exp_func.compile(comp, new StackTarget(
346: Compilation.typeProcedure));
347:
348: boolean toArray = (tail_recurse ? func_lambda.min_args != func_lambda.max_args
349: : args_length > 4);
350: if (toArray) {
351: compileToArray(exp.args, comp);
352: method = Compilation.applyNmethod;
353: } else if (tail_recurse) {
354: pushArgs(func_lambda, exp.args, comp);
355: method = null;
356: } else {
357: for (int i = 0; i < args_length; ++i) {
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: comp.error('e', "unreachable code");
366: return;
367: }
368: if (tail_recurse) {
369: popParams(code, func_lambda, toArray);
370: code.emitTailCall(false, func_lambda.getVarScope());
371: return;
372: }
373: code.emitInvokeVirtual(method);
374: target.compileFromStack(comp, Type.pointer_type);
375: }
376:
377: protected Expression walk(ExpWalker walker) {
378: return walker.walkApplyExp(this );
379: }
380:
381: protected void walkChildren(ExpWalker walker) {
382: func = walker.walk(func);
383: if (walker.exitValue == null)
384: args = walker.walkExps(args, args.length);
385: }
386:
387: public void print(OutPort out) {
388: out.startLogicalBlock("(Apply", ")", 2);
389: if (isTailCall())
390: out.print(" [tailcall]");
391: if (type != null && type != Type.pointer_type) {
392: out.print(" => ");
393: out.print(type);
394: }
395: out.writeSpaceFill();
396: printLineColumn(out);
397: func.print(out);
398: for (int i = 0; i < args.length; ++i) {
399: out.writeSpaceLinear();
400: args[i].print(out);
401: }
402: out.endLogicalBlock(")");
403: }
404:
405: /** Only used for inline- and tail-calls. */
406: private static void pushArgs(LambdaExp lexp, Expression[] args,
407: Compilation comp) {
408: Declaration param = lexp.firstDecl();
409: int args_length = args.length;
410: for (int i = 0; i < args_length; ++i) {
411: Expression arg = args[i];
412: if (param.ignorable())
413: arg.compile(comp, Target.Ignore);
414: else
415: arg.compile(comp, param.getType());
416: param = param.nextDecl();
417: }
418: }
419:
420: private static void popParams(CodeAttr code, LambdaExp lexp,
421: boolean toArray) {
422: Variable vars = lexp.getVarScope().firstVar();
423: Declaration decls = lexp.firstDecl();
424: if (vars != null && vars.getName() == "this")
425: vars = vars.nextVar();
426: if (vars != null && vars.getName() == "$ctx")
427: vars = vars.nextVar();
428: if (vars != null && vars.getName() == "argsArray") {
429: if (toArray) {
430: popParams(code, 1, decls, vars);
431: return;
432: }
433: vars = vars.nextVar();
434: }
435: popParams(code, lexp.min_args, decls, vars);
436: }
437:
438: // Recursive helper function.
439: private static void popParams(CodeAttr code, int count,
440: Declaration decl, Variable vars) {
441: if (count > 0) {
442: popParams(code, count - 1, decl.nextDecl(), decl
443: .getVariable() == null ? vars : vars.nextVar());
444: if (!decl.ignorable())
445: code.emitStore(vars);
446: }
447: }
448:
449: /** Cache for getType(). */
450: protected Type type;
451:
452: public final gnu.bytecode.Type getTypeRaw() {
453: return type;
454: }
455:
456: public final void setType(gnu.bytecode.Type type) {
457: this .type = type;
458: }
459:
460: public final gnu.bytecode.Type getType() {
461: if (type != null)
462: return type;
463: Expression afunc = func;
464: // In case of cycles.
465: type = Type.pointer_type;
466: if (afunc instanceof ReferenceExp) {
467: Declaration func_decl = ((ReferenceExp) afunc).binding;
468: func_decl = Declaration.followAliases(func_decl);
469: if (func_decl != null
470: && !func_decl.getFlag(Declaration.IS_UNKNOWN))
471: afunc = func_decl.getValue();
472: }
473: if (afunc instanceof QuoteExp) {
474: Object proc = ((QuoteExp) afunc).getValue();
475: if (proc instanceof Inlineable)
476: type = ((Inlineable) proc).getReturnType(args);
477: } else if (afunc instanceof LambdaExp) {
478: type = ((LambdaExp) afunc).getReturnType();
479: }
480: return type;
481: }
482:
483: public final Expression inlineIfConstant(Procedure proc,
484: ExpWalker walker) {
485: return inlineIfConstant(proc, walker.getMessages());
486: }
487:
488: /** Inline this ApplyExp if parameters are constant.
489: * @param proc the procedure bound to this.func.
490: * @return the constant result (as a QuoteExp) if inlining was possible;
491: * otherwise this ApplyExp.
492: * If applying proc throws an exception, print a warning on walker.messages.
493: */
494: public final Expression inlineIfConstant(Procedure proc,
495: SourceMessages messages) {
496: int len = args.length;
497: Object[] vals = new Object[len];
498: for (int i = len; --i >= 0;) {
499: Expression arg = args[i];
500: if (arg instanceof ReferenceExp) {
501: Declaration decl = ((ReferenceExp) arg).getBinding();
502: if (decl != null) {
503: arg = decl.getValue();
504: if (arg == QuoteExp.undefined_exp)
505: return this ;
506: }
507: }
508: if (!(arg instanceof QuoteExp))
509: return this ;
510: vals[i] = ((QuoteExp) arg).getValue();
511: }
512: try {
513: return new QuoteExp(proc.applyN(vals));
514: } catch (Throwable ex) {
515: if (messages != null)
516: messages
517: .error('w', "call to " + proc + " throws " + ex);
518: return this ;
519: }
520: }
521:
522: public String toString() {
523: return "ApplyExp/" + args.length + '[' + func + ']';
524: }
525: }
|