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