1 // Copyright (c) 1999, 2000-2005, 2006, 2010, 2014 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 java.util.*; 8 import java.io.*; 9 import kawa.Shell; 10 import java.util.zip.*; 11 import java.util.Stack; 12 import gnu.kawa.functions.Convert; 13 import gnu.kawa.io.*; 14 import gnu.kawa.lispexpr.LangObjType; 15 import gnu.kawa.lispexpr.LangPrimType; 16 import gnu.kawa.reflect.LazyType; 17 import gnu.lists.Pair; 18 import gnu.text.Char; 19 import gnu.text.Lexer; 20 import gnu.text.Options; 21 import gnu.text.SourceLocator; 22 import gnu.text.SourceMessages; 23 import kawa.lang.Translator.FormStack; 24 import kawa.standard.require.DeclSetMapper; 25 26 /** State for a single expression or module. 27 * For each top-level thing (expression or file) we compile or evaluate 28 * we create a new Compilation. 29 */ 30 31 public class Compilation implements SourceLocator 32 { 33 /** True if the form is too complex to evaluate,and we must compile it. 34 * This is because it contains a construct we know how to compile, but not 35 * evaluate, and it it outside a function (which we always compile). 36 * This can be a let scope, or primitive procedure. */ 37 public boolean mustCompile = ModuleExp.alwaysCompile; 38 39 /** Used by LambdaExp.getSelectorValue if need to allocate new selector. */ 40 int maxSelectorValue; 41 42 public ClassType curClass; 43 public ClassType mainClass; 44 /** Generated class that extends ModuleBody. Normally same as mainClass. */ 45 public ClassType moduleClass; 46 47 public LambdaExp curLambda; 48 public ModuleExp mainLambda; 49 public Variable thisDecl; 50 51 /** Contains "$class" if the module is static; otherwise null. */ 52 Variable moduleInstanceVar; 53 54 /** A code, one of the following constants, indicating how far along 55 * we are in the parsing/compilation process. 56 * These codes are even integers for completed stages and odd integers 57 * for begun but not completed stages. */ 58 private int state; 59 /** Returns a code indicating how far along 60 * we are in the parsing/compilation process. */ getState()61 public int getState () { return state; } setState(int state)62 public void setState (int state) { this.state = state; } 63 /** State code for initial pre-parse looking for module name. */ 64 public static final int PROLOG_PARSING = 1; 65 /** We have determined the module name and class, but not finished parsing. */ 66 public static final int PROLOG_PARSED = 2; 67 /** State code indicating the entire module has been parsed. */ 68 public static final int BODY_PARSED = 4; 69 /** State code for lexical bindings having been resolved. */ 70 public static final int RESOLVED = 6; 71 /** State code when initial tree-walking (PushApply) are done. */ 72 public static final int PRE_WALKED = 8; 73 /** State code when various inlining and optimization passes are done. */ 74 public static final int WALKED = 10; 75 /** State code that various compile-only data has been determined. */ 76 public static final int COMPILE_SETUP = 12; 77 /** State code indicating the bytecode has been generated. */ 78 public static final int COMPILED = 14; 79 /** State code indicating that bytecode has been written to its target. */ 80 public static final int CLASS_WRITTEN = 16; 81 public static final int ERROR_SEEN = 100; 82 83 public Lexer lexer; 84 85 private boolean pedantic; isPedantic()86 public boolean isPedantic() { return pedantic; } setPedantic(boolean value)87 public void setPedantic(boolean value) { pedantic = value; } 88 89 /** Used to access the "main" instance. 90 * If we're compiling a static module (FIXME: and not init-run), then {@code moduleInstanceMainField} 91 * is a field in {@code mainClass} named {@code "$instance"} that 92 * points to the single instance of the module. 93 */ 94 Field moduleInstanceMainField; 95 96 /** Stack of quads of (ModuleInfo, ScopeExp, position, formSize). */ 97 public java.util.Stack<Object> pendingImports; 98 pushPendingImport(ModuleInfo info, ScopeExp defs, FormStack forms, DeclSetMapper mapper)99 public void pushPendingImport(ModuleInfo info, ScopeExp defs, 100 FormStack forms, DeclSetMapper mapper) { 101 if (pendingImports == null) 102 pendingImports = new java.util.Stack<Object>(); 103 pendingImports.push(info); 104 pendingImports.push(defs); 105 Expression posExp = new ReferenceExp((Object) null); 106 posExp.setLine(this); 107 pendingImports.push(posExp); 108 pendingImports.push(forms.lastPair()); 109 pendingImports.push(mapper); 110 } 111 112 public Map<String,ModuleInfo> subModuleMap; 113 114 /* Write out implicitly-compiled classes. 115 * Compare javac's -implicit:class flag. Current not set. 116 */ 117 public static boolean writeImplicitClasses = false; 118 119 /** If true, print out expressions after parsing and before optimizations. */ 120 public static boolean debugPrintExpr = false; 121 122 /** If true, print out final expressions after optimizations etc. */ 123 public static boolean debugPrintFinalExpr; 124 125 public static boolean debugPrintANF = false; 126 public static boolean enableANF = false; 127 128 public static Options options = new Options(); 129 public static Options.OptionInfo fullTailCallsVariable = 130 options.add("full-tailcalls", 131 Options.BOOLEAN_OPTION, Boolean.TRUE, 132 "support full tailcalls"); 133 public static Options.OptionInfo mainMethodVariable = 134 options.add("main", 135 Options.BOOLEAN_OPTION, Boolean.FALSE, 136 "generate an application, with a main method"); 137 public static Options.OptionInfo warnUnreachable = 138 options.add("warn-unreachable", 139 Options.BOOLEAN_OPTION, Boolean.TRUE, 140 "warn if this code can never be executed"); 141 public static Options.OptionInfo warnVoidUsed = 142 options.add("warn-void-used", 143 Options.BOOLEAN_OPTION, Boolean.TRUE, 144 "warn if void-valued expression is used"); 145 public static Options.OptionInfo warnUninitialized = 146 options.add("warn-uninitialized", 147 Options.BOOLEAN_OPTION, Boolean.TRUE, 148 "warn if a uninitialized variable may be used"); 149 public static Options.OptionInfo warnUndefinedVariable = 150 options.add("warn-undefined-variable", 151 Options.BOOLEAN_OPTION, Boolean.TRUE, 152 "warn if no compiler-visible binding for a variable"); 153 public static Options.OptionInfo warnUnknownMember = 154 options.add("warn-unknown-member", 155 Options.BOOLEAN_OPTION, Boolean.TRUE, 156 "warn if referencing an unknown method or field"); 157 public static Options.OptionInfo warnInvokeUnknownMethod = 158 options.add("warn-invoke-unknown-method", 159 Options.BOOLEAN_OPTION, warnUnknownMember, 160 "warn if invoke calls an unknown method (subsumed by warn-unknown-member)"); 161 public static Options.OptionInfo warnUnused = 162 options.add("warn-unused", 163 Options.BOOLEAN_OPTION, Boolean.TRUE, 164 "warn if a variable is usused or code never executed"); 165 public static Options.OptionInfo warnAsError = 166 options.add("warn-as-error", Options.BOOLEAN_OPTION, Boolean.FALSE, 167 "Make all warnings into errors"); 168 public static Options.OptionInfo maxErrors = 169 options.add("max-errors", Options.INT_OPTION, null, 170 "maximum number of errors or warnings to print"); 171 172 public Options currentOptions = new Options(options); 173 generateMainMethod()174 public boolean generateMainMethod () 175 { 176 return currentOptions.getBoolean(mainMethodVariable); 177 } 178 warnUnreachable()179 public boolean warnUnreachable () 180 { 181 return currentOptions.getBoolean(warnUnreachable); 182 } warnUndefinedVariable()183 public boolean warnUndefinedVariable () 184 { 185 return currentOptions.getBoolean(warnUndefinedVariable); 186 } warnUnknownMember()187 public boolean warnUnknownMember () 188 { 189 return currentOptions.getBoolean(warnUnknownMember); 190 } warnInvokeUnknownMethod()191 public boolean warnInvokeUnknownMethod () 192 { 193 return currentOptions.getBoolean(warnInvokeUnknownMethod); 194 } warnUnused()195 public boolean warnUnused() 196 { 197 return currentOptions.getBoolean(warnUnused); 198 } warnUninitialized()199 public boolean warnUninitialized() 200 { 201 return currentOptions.getBoolean(warnUninitialized); 202 } warnVoidUsed()203 public boolean warnVoidUsed() 204 { 205 return (!enableANF) && currentOptions.getBoolean(warnVoidUsed); 206 } warnAsError()207 public boolean warnAsError () 208 { 209 return currentOptions.getBoolean(warnAsError); 210 } maxErrors()211 public static int maxErrors() { 212 return options.getInt(maxErrors, 20); 213 } 214 215 /** Get a named boolean option. */ getBooleanOption(String key, boolean defaultValue)216 public final boolean getBooleanOption (String key, boolean defaultValue) 217 { 218 return currentOptions.getBoolean(key, defaultValue); 219 } 220 221 /** Get a named boolean option. */ getBooleanOption(String key)222 public final boolean getBooleanOption (String key) 223 { 224 return currentOptions.getBoolean(key); 225 } 226 227 public static int defaultClassFileVersion = 228 /* #ifdef JAVA10 */ 229 // ClassType.JAVA_10_VERSION 230 /* #else */ 231 /* #ifdef JAVA9 */ 232 // ClassType.JAVA_9_VERSION 233 /* #else */ 234 /* #ifdef JAVA8 */ 235 ClassType.JDK_1_8_VERSION 236 /* #else */ 237 /* #ifdef JAVA7 */ 238 // ClassType.JDK_1_7_VERSION 239 /* #else */ 240 /* #ifdef JAVA6 */ 241 // ClassType.JDK_1_6_VERSION 242 /* #else */ 243 /* #ifdef JAVA5 */ 244 // ClassType.JDK_1_5_VERSION 245 /* #else */ 246 // ClassType.JDK_1_1_VERSION 247 /* #endif */ 248 /* #endif */ 249 /* #endif */ 250 /* #endif */ 251 /* #endif */ 252 /* #endif */ 253 ; 254 255 /** The default calling convention. 256 * One of the following CALL_WITH_xxx values. */ 257 public static int defaultCallConvention; 258 public static final int CALL_WITH_UNSPECIFIED = 0; 259 /** Plain calling convention, using regular Java parameters and returns. */ 260 public static final int CALL_WITH_RETURN = 1; 261 /** Function results are written to the current CallContext's Consumer. */ 262 public static final int CALL_WITH_CONSUMER = 2; 263 /** Like CALL_WITH_CONSUMER, but handle full on-stack-growing tail-calls. */ 264 public static final int CALL_WITH_TAILCALLS = 3; 265 /** Support for full continuations. Not implemented. */ 266 public static final int CALL_WITH_CONTINUATIONS = 4; 267 currentCallConvention()268 public int currentCallConvention() { 269 Object ft = currentOptions.getLocal("full-tailcalls"); 270 if (ft instanceof Boolean) 271 return ((Boolean) ft).booleanValue() 272 ? Compilation.CALL_WITH_TAILCALLS 273 : Compilation.CALL_WITH_RETURN; 274 return defaultCallConvention; 275 } 276 usingCPStyle()277 public boolean usingCPStyle() 278 { return currentCallConvention() == CALL_WITH_CONTINUATIONS; } usingTailCalls()279 public boolean usingTailCalls() 280 { return currentCallConvention() >= CALL_WITH_TAILCALLS; } usingCallContext()281 public boolean usingCallContext() 282 { return currentCallConvention() >= CALL_WITH_CONSUMER; } 283 284 public static final int MODULE_NONSTATIC = -1; 285 public static final int MODULE_STATIC_DEFAULT = 0; 286 public static final int MODULE_STATIC = 1; 287 public static final int MODULE_STATIC_RUN = 2; 288 /** Default for whether a module is static. 289 * If {@code moduleStatic > 0}, 290 * then {@code (module-static #t)} is implied by default. 291 * If {@code moduleStatic == MODULE_STATIC_RUN}, 292 * then {@code <clinit>} calls {@code run}. 293 * If {@code moduleStatic < 0}, 294 * then {@code (module-static #f)} is implied by default. 295 */ 296 public static int moduleStatic = MODULE_STATIC_DEFAULT; 297 298 ClassType[] classes; 299 int numClasses; 300 301 /** When immediate, the ClassLoader we will load the compiled 302 * classes from. */ 303 ArrayClassLoader loader; 304 305 /** True if the compiled result will be immediately loaded. */ 306 public boolean immediate; 307 308 /** Compilation was explicitly requested, rather than being a dependency. */ 309 public boolean explicit; 310 311 /** The current method. */ 312 public Method method; 313 314 Method clinitMethod; 315 getCode()316 public final CodeAttr getCode() { return method.getCode(); } 317 318 int method_counter; 319 320 /* When multiple procedures are compiled into a single method, 321 we use a switch to jump to the correct part of the method. */ 322 SwitchState fswitch; 323 324 Field fswitchIndex; 325 326 // Various standard classes 327 static public ClassType typeObject = Type.objectType; 328 static public ClassType scmBooleanType = ClassType.make("java.lang.Boolean"); 329 static public ClassType typeString = ClassType.make("java.lang.String"); 330 static public ClassType typeCharSequence = ClassType.make("java.lang.CharSequence"); 331 static public ClassType javaStringType = typeString; 332 static public ClassType scmKeywordType = ClassType.make("gnu.expr.Keyword"); 333 static public ClassType scmSequenceType = ClassType.make("gnu.lists.Sequence"); 334 public static final ClassType typeList = ClassType.make("java.util.List"); 335 static public ClassType scmListType = ClassType.make("gnu.lists.LList"); 336 static public ClassType typePair = ClassType.make("gnu.lists.Pair"); 337 public static final ArrayType objArrayType = ArrayType.make(typeObject); 338 static public ClassType typeRunnable = ClassType.make("java.lang.Runnable"); 339 static public ClassType typeRunnableModule = ClassType.make("gnu.expr.RunnableModule"); 340 public static ClassType typeType = ClassType.make("gnu.bytecode.Type"); 341 public static ClassType typeObjectType 342 = ClassType.make("gnu.bytecode.ObjectType"); 343 public static ClassType typeClass = Type.javalangClassType; 344 static public ClassType typeClassType = ClassType.make("gnu.bytecode.ClassType"); 345 static public ClassType typeProcedure 346 = ClassType.make("gnu.mapping.Procedure"); 347 static public ClassType typeLanguage 348 = ClassType.make("gnu.expr.Language"); 349 static public ClassType typeEnvironment 350 = ClassType.make("gnu.mapping.Environment"); 351 static public ClassType typeLocation 352 = ClassType.make("gnu.mapping.Location"); 353 public static final ClassType typeLocationProc 354 = ClassType.make("gnu.mapping.LocationProc"); 355 static public ClassType typeFieldLocation 356 = ClassType.make("gnu.kawa.reflect.FieldLocation"); 357 static public ClassType typeStaticFieldLocation 358 = ClassType.make("gnu.kawa.reflect.StaticFieldLocation"); 359 static public ClassType typeSymbol 360 = ClassType.make("gnu.mapping.Symbol"); 361 static public final Field trueConstant 362 = scmBooleanType.getDeclaredField("TRUE"); 363 static public final Field falseConstant 364 = scmBooleanType.getDeclaredField("FALSE"); 365 public static final Field voidConsumerInstanceField 366 = ClassType.make("gnu.lists.VoidConsumer") 367 .getDeclaredField("instance"); 368 369 static Method makeListMethod; 370 371 public static final Type[] int1Args = { Type.intType }; 372 public static final Type[] string1Arg = { javaStringType }; 373 public static final Type[] sym1Arg = string1Arg; 374 375 static { 376 Type[] makeListArgs = { objArrayType, Type.intType }; 377 makeListMethod = scmListType.addMethod ("makeList", 378 makeListArgs, scmListType, 379 Access.PUBLIC|Access.STATIC); 380 } 381 382 public static Method getCurrentEnvironmentMethod 383 = typeEnvironment.addMethod("getCurrent", Type.typeArray0, 384 typeEnvironment,Access.PUBLIC|Access.STATIC); 385 386 public static Type[] apply0args = Type.typeArray0; 387 public static Type[] apply1args = { typeObject }; 388 public static Type[] apply2args = { typeObject, typeObject }; 389 public static Type[] applyNargs = { objArrayType }; 390 391 static Method checkArgCountMethod; 392 393 public static Method apply0method = typeProcedure.addMethod 394 ("apply0", apply0args, typeObject, Access.PUBLIC|Access.FINAL); 395 396 public static Method apply1method; 397 public static Method apply2method; 398 public static Method apply3method; 399 public static Method apply4method; 400 public static Method applyNmethod; 401 402 static 403 { 404 apply1method = typeProcedure.addMethod ("apply1", apply1args, 405 typeObject, Access.PUBLIC); 406 apply2method = typeProcedure.addMethod ("apply2", apply2args, 407 typeObject, Access.PUBLIC); 408 Type[] apply3args = { typeObject, typeObject, typeObject }; 409 apply3method = typeProcedure.addMethod ("apply3", apply3args, 410 typeObject, Access.PUBLIC); 411 Type[] apply4args = { typeObject , typeObject, typeObject, typeObject}; 412 apply4method = typeProcedure.addMethod ("apply4", apply4args, 413 typeObject, Access.PUBLIC); 414 applyNmethod = typeProcedure.addMethod ("applyN", applyNargs, 415 typeObject, Access.PUBLIC); 416 Type[] args = new Type[2]; 417 args[0] = typeProcedure; 418 args[1] = Type.intType; 419 checkArgCountMethod 420 = typeProcedure.addMethod("checkArgCount", args, Type.voidType, 421 Access.PUBLIC|Access.STATIC); 422 } 423 424 public static Method[] applymethods = { 425 apply0method, apply1method, apply2method, apply3method, 426 apply4method, applyNmethod }; 427 428 public static ClassType typeProcedure0 429 = ClassType.make("gnu.mapping.Procedure0"); 430 public static ClassType typeProcedure1 431 = ClassType.make("gnu.mapping.Procedure1"); 432 public static ClassType typeProcedure2 433 = ClassType.make("gnu.mapping.Procedure2"); 434 public static ClassType typeProcedure3 435 = ClassType.make("gnu.mapping.Procedure3"); 436 public static ClassType typeProcedure4 437 = ClassType.make("gnu.mapping.Procedure4"); 438 public static ClassType typeProcedureN 439 = ClassType.make("gnu.mapping.ProcedureN"); 440 public static ClassType typeModuleBody 441 = ClassType.make("gnu.expr.ModuleBody"); 442 public static ClassType typeApplet = ClassType.make("java.applet.Applet"); 443 public static ClassType typeServlet = ClassType.make("gnu.kawa.servlet.KawaServlet"); 444 445 /* Classes, fields, and methods used wgen usingCPStyle". */ 446 public static ClassType typeCallContext 447 = ClassType.make("gnu.mapping.CallContext"); 448 public static final ClassType typeConsumer 449 = ClassType.make("gnu.lists.Consumer"); 450 public static Method getCallContextInstanceMethod 451 = typeCallContext.getDeclaredMethod("getInstance", 0); 452 public static ClassType typeValues 453 = ClassType.make("gnu.mapping.Values"); 454 public static Field noArgsField 455 = typeValues.getDeclaredField("noArgs"); 456 public static Field pcCallContextField 457 = typeCallContext.getDeclaredField("pc"); 458 public static ClassType typeMethodProc 459 = ClassType.make("gnu.mapping.MethodProc"); 460 public static ClassType typeCompiledProc 461 = ClassType.make("gnu.expr.CompiledProc"); 462 // public static Field numArgsCallFrameField = typeCallFrame.getDeclaredField("numArgs"); 463 public static Field argsCallContextField 464 = typeCallContext.getDeclaredField("values"); 465 public static Field procCallContextField 466 = typeCallContext.getDeclaredField("proc"); 467 private static Type[] applyCpsArgs = { typeCallContext}; 468 public static Method applyCpsMethod 469 = typeProcedure.addMethod("apply", applyCpsArgs, Type.voidType, 470 Access.PUBLIC); 471 472 public static ClassType[] typeProcedureArray = { 473 typeProcedure0, typeProcedure1, typeProcedure2, typeProcedure3, 474 typeProcedure4 }; 475 476 public static final Method getNextArgMethod = typeCallContext.getDeclaredMethod("getNextArg", 0); 477 478 /** Rembembers stuff to do in <clinit> of main class. */ 479 Initializer clinitChain; 480 481 LitTable litTable; 482 483 int langOptions; 484 485 /** True if we should generate an Applet. */ generatingApplet()486 public boolean generatingApplet () 487 { 488 return (langOptions & Language.PARSE_FOR_APPLET) != 0; 489 } 490 491 /** True if we should generate a Servlet. */ generatingServlet()492 public boolean generatingServlet () 493 { 494 return (langOptions & Language.PARSE_FOR_SERVLET) != 0; 495 } 496 sharedModuleDefs()497 public boolean sharedModuleDefs () 498 { 499 return (langOptions & Language.PARSE_CURRENT_NAMES) != 0; 500 } 501 setSharedModuleDefs(boolean shared)502 public void setSharedModuleDefs (boolean shared) 503 { 504 if (shared) 505 langOptions |= Language.PARSE_CURRENT_NAMES; 506 else 507 langOptions &= ~Language.PARSE_CURRENT_NAMES; 508 } 509 getModuleType()510 public final ClassType getModuleType() 511 { 512 return typeModuleBody; 513 } 514 515 /** Emit code to "evaluate" a compile-time constant. 516 * This is the normal external interface. 517 * @param value the value to be compiled 518 */ compileConstant(Object value)519 public void compileConstant (Object value) { 520 gnu.bytecode.CodeAttr code = getCode(); 521 if (value == null) 522 code.emitPushNull(); 523 else if (value instanceof String && ! immediate) 524 code.emitPushString((String) value); 525 else { 526 Literal literal = litTable.findLiteral(value); 527 // if (immediate) maybe we want to use a differnet approach. 528 if (literal.field == null) 529 literal.assign(litTable); 530 code.emitGetStatic(literal.field); 531 } 532 } 533 534 public static boolean inlineOk = true; 535 inlineOk(Expression proc)536 public boolean inlineOk (Expression proc) 537 { 538 if (proc instanceof LambdaExp) 539 { 540 LambdaExp lproc = (LambdaExp) proc; 541 Declaration nameDecl = lproc.nameDecl; 542 // The compiler gets confused if we turn off inlining for nested 543 // procedures - and they can't be rebound anyway. 544 if (nameDecl == null || nameDecl.getSymbol() == null 545 || ! (nameDecl.context instanceof ModuleExp)) 546 return true; 547 if (immediate 548 && nameDecl.isPublic() 549 && ! lproc.getFlag(LambdaExp.OVERLOADABLE_FIELD) 550 && (curLambda == null || lproc.topLevel() != curLambda.topLevel())) 551 return false; 552 } 553 return inlineOk; 554 } 555 inlineOk(Procedure proc)556 public boolean inlineOk(Procedure proc) { 557 if (immediate && proc instanceof CompiledProc) { 558 Class moduleClass = ((CompiledProc) proc).getModuleClass(); 559 if (moduleClass.getClassLoader() instanceof ArrayClassLoader) 560 return false; 561 } 562 return inlineOk; 563 } 564 565 /** Should this inlineable method by inlined? 566 * Usually it's best to not inline a module-level function, since that 567 * makes stack traces less helpful, and increases the risk of 568 * methods getting too big. 569 */ avoidInline(LambdaExp proc)570 static boolean avoidInline(LambdaExp proc) { 571 return proc.getOuter() instanceof ModuleExp && proc.nameDecl != null; 572 } 573 isApplyFunction(Expression exp)574 public boolean isApplyFunction (Expression exp) 575 { 576 return false; 577 } 578 579 /** A simple apply function maps actual arguments to formals directly. 580 * E.g. no distribution of multiple values. 581 */ isSimpleApplyFunction(Expression exp)582 public boolean isSimpleApplyFunction (Expression exp) 583 { 584 return false; 585 } 586 compileConstant(Object value, Target target)587 public void compileConstant (Object value, Target target) 588 { 589 if (target instanceof IgnoreTarget) 590 return; 591 if (value instanceof Values && target instanceof ConsumerTarget) 592 { 593 Object[] values = ((Values) value).getValues(); 594 int len = values.length; 595 for (int i = 0; i < len; i++) 596 { 597 compileConstant(values[i], 598 ((ConsumerTarget) target).getSingleTarget()); 599 } 600 return; 601 } 602 if (target instanceof ConditionalTarget) 603 { 604 ConditionalTarget ctarg = (ConditionalTarget) target; 605 getCode().emitGoto(getLanguage().isTrue(value) ? ctarg.ifTrue 606 : ctarg.ifFalse); 607 return; 608 } 609 if (target instanceof StackTarget) 610 { 611 Type type = ((StackTarget) target).getType(); 612 if (type instanceof LazyType) 613 type = ((LazyType) type).getValueType(); 614 if (type instanceof PrimType) 615 { 616 try 617 { 618 String signature = type.getSignature(); 619 CodeAttr code = getCode(); 620 char sig1 = (signature == null || signature.length() != 1) ? ' ' 621 : signature.charAt(0); 622 if (value instanceof Number) 623 { 624 Number num = (Number) value; 625 switch (sig1) 626 { 627 case 'C': 628 case 'I': 629 code.emitPushInt(num.intValue()); 630 return; 631 case 'S': 632 code.emitPushInt(num.shortValue()); 633 return; 634 case 'B': 635 code.emitPushInt(num.byteValue()); 636 return; 637 case 'J': 638 code.emitPushLong(num.longValue()); 639 return; 640 case 'F': 641 code.emitPushFloat(num.floatValue()); 642 return; 643 case 'D': 644 code.emitPushDouble(num.doubleValue()); 645 return; 646 } 647 } 648 // FIXME we should move this into a new method 649 // PrimType#pushValue(Object value), with LangPrimType override. 650 if (type == LangPrimType.characterType 651 || type == LangPrimType.characterOrEofType) 652 { 653 if (value instanceof Char) 654 { 655 code.emitPushInt(((Char) value).intValue()); 656 return; 657 } 658 if (value instanceof Character) 659 { 660 code.emitPushInt(((Character) value).charValue()); 661 return; 662 } 663 if (value == gnu.lists.Sequence.eofValue 664 && type == LangPrimType.characterOrEofType) 665 { 666 code.emitPushInt(-1); 667 return; 668 } 669 } 670 if (sig1 == 'C') 671 { 672 code.emitPushInt((int) ((PrimType) type).charValue(value)); 673 return; 674 } 675 if (sig1 == 'Z') 676 { 677 boolean val = getLanguage().isTrue(value); 678 code.emitPushInt(val ? 1 : 0); 679 return; 680 } 681 } 682 catch (ClassCastException ex) 683 { 684 // should print an ERROR. 685 } 686 } 687 if (type == Compilation.typeClass && value instanceof ClassType) 688 { 689 loadClassRef((ClassType) value); 690 return; 691 } 692 try 693 { 694 value = type.coerceFromObject(value); 695 } 696 catch (Exception ex) 697 { 698 StringBuffer sbuf = new StringBuffer(); 699 if (value == Values.empty) 700 sbuf.append("cannot convert void to "); 701 else 702 { 703 sbuf.append("cannot convert literal (of type "); 704 if (value == null) 705 sbuf.append("<null>"); 706 else 707 sbuf.append(value.getClass().getName()); 708 sbuf.append(") to "); 709 } 710 sbuf.append(type); 711 error('w', sbuf.toString()); 712 } 713 } 714 compileConstant(value); 715 target.compileFromStack(this, 716 value == null ? target.getType() 717 : Type.make(value.getClass())); 718 } 719 720 /** Push language-specific boxed true or false value. */ emitPushBoolean(boolean value)721 public void emitPushBoolean(boolean value) { 722 CodeAttr code = getCode(); 723 Object valObject = language.booleanObject(value); 724 if (valObject == Boolean.TRUE) 725 code.emitGetStatic(Compilation.trueConstant); 726 else if (valObject == Boolean.FALSE) 727 code.emitGetStatic(Compilation.falseConstant); 728 else compileConstant(valObject); 729 } 730 731 /** Generate code to test if an object is considered true. 732 * Assume the object has been pushed on the JVM stack. 733 * Generate code to push (unboxed) true or false as appropriate. */ emitCoerceToBoolean()734 public void emitCoerceToBoolean() 735 { 736 CodeAttr code = getCode(); 737 Label trueLabel = new Label(code); 738 Label falseLabel = new Label(code); 739 ConditionalTarget ctarget 740 = new ConditionalTarget(trueLabel, falseLabel, getLanguage()); 741 ctarget.compileFromStack(this, Type.objectType); 742 code.emitIfThen(); 743 trueLabel.define(code); 744 code.emitPushInt(1); 745 code.emitElse(); 746 falseLabel.define(code); 747 code.emitPushInt(0); 748 code.emitFi(); 749 } 750 751 /** Hook for language-specific handling in ConditionalTarget. 752 * @param stackType Value to be treated as boolean, already pushed. 753 * @return null if we've handled the conditional transfer; 754 * otherwise type value has been converted to (usually booleanType). 755 */ asBooleanValue(ConditionalTarget target, Type stackType)756 public Type asBooleanValue(ConditionalTarget target, Type stackType) { 757 return stackType; 758 } 759 760 boolean dumpingInitializers; 761 dumpInitializers(Initializer inits)762 private void dumpInitializers(Initializer inits) { 763 dumpingInitializers = true; 764 for (Initializer init = Initializer.reverse(inits); 765 init != null; init = init.next) 766 init.emit(this); 767 dumpingInitializers = false; 768 } 769 770 static final Comparator<ClassType> 771 classTypeComparator = new Comparator<ClassType>() { 772 public int compare(ClassType arg0, ClassType arg1) { 773 return arg0.getName().compareTo(arg1.getName()); 774 } 775 }; 776 777 boolean classesArrayIsSorted; 778 779 /** Search this Compilation for a ClassType with a given name. 780 * @param name the name of the class desired 781 * @return the matching ClassType, or null if none is found */ findNamedClass(String name)782 public ClassType findNamedClass(String name) { 783 784 if (classes == null || numClasses == 0) 785 return null; 786 787 if (name.equals(classes[0].getName())) 788 return classes[0]; 789 790 if (numClasses == 1) 791 return null; 792 793 if (! classesArrayIsSorted) { 794 Arrays.sort(classes, 1, numClasses, classTypeComparator); 795 classesArrayIsSorted = true; 796 } 797 798 ClassType nameType = new ClassType(name); // wrapper for key 799 int index = Arrays.binarySearch 800 (classes, 1, numClasses, nameType, classTypeComparator); 801 return (index > -1) ? classes[index] : null; 802 } 803 804 public static String classPrefixDefault = ""; 805 /** If non-null: a prefix for generateClassName to prepend to names. */ 806 public String classPrefix = classPrefixDefault; 807 808 /** Recusive helper function to reverse order of words in hostname. */ putURLWords(String name, StringBuffer sbuf)809 private static void putURLWords(String name, StringBuffer sbuf) 810 { 811 int dot = name.indexOf('.'); 812 if (dot > 0) 813 { 814 putURLWords(name.substring(dot+1), sbuf); 815 sbuf.append('.'); 816 name = name.substring(0, dot); 817 } 818 sbuf.append(name); 819 } 820 821 /** Map a URI to a package/class name. 822 * Similar to the JAXB mangling, and that in the Java language spec. 823 */ mangleURI(String name)824 public static String mangleURI (String name) 825 { 826 boolean hasSlash = name.indexOf('/') >= 0; 827 int len = name.length(); 828 if (len > 6 && name.startsWith("class:")) 829 return name.substring(6); 830 // Remove "http:" or "urn:". 831 if (len > 5 && name.charAt(4) == ':' 832 && name.substring(0, 4).equalsIgnoreCase("http")) 833 { 834 name = name.substring(5); 835 len -= 5; 836 hasSlash = true; 837 } 838 else if (len > 4 && name.charAt(3) == ':' 839 && name.substring(0, 3).equalsIgnoreCase("uri")) 840 { 841 name = name.substring(4); 842 len -= 4; 843 } 844 int start = 0; 845 StringBuffer sbuf = new StringBuffer(); 846 for (;;) 847 { 848 int slash = name.indexOf('/', start); 849 int end = slash < 0 ? len : slash; 850 boolean first = sbuf.length() == 0; 851 if (first && hasSlash) 852 { 853 // Remove initial "www.". 854 String host = name.substring(start, end); 855 if (end - start > 4 && host.startsWith("www.")) 856 host = host.substring(4); 857 // Reverse order of words in "host" part. 858 putURLWords(host, sbuf); 859 } 860 else if (start != end) 861 { 862 if (! first) 863 sbuf.append('.'); 864 if (end == len) 865 { 866 int dot = name.lastIndexOf('.', len); 867 if (dot > start + 1 && ! first) 868 { 869 // Remove file extension: 870 int extLen = len - dot; 871 if (extLen <= 4 872 || (extLen == 5 && name.endsWith("html"))) 873 { 874 len -= extLen; 875 end = len; 876 name = name.substring(0, len); 877 } 878 } 879 } 880 sbuf.append(name.substring(start, end)); 881 } 882 if (slash < 0) 883 break; 884 start = slash + 1; 885 } 886 return sbuf.toString(); 887 } 888 889 /** Generate an unused class name. 890 * @param hint the requested name (or prefix) 891 * @return a unique class name. 892 */ generateClassName(String hint)893 public String generateClassName (String hint) 894 { 895 hint = Mangling.mangleClassName(hint); 896 if (mainClass != null) 897 hint = mainClass.getName() + '$' + hint; 898 else if (classPrefix != null) 899 hint = classPrefix + hint; 900 if (findNamedClass (hint) == null) 901 return hint; 902 for (int i = 0; ; i++) 903 { 904 String new_hint = hint + i; 905 if (findNamedClass (new_hint) == null) 906 return new_hint; 907 } 908 } 909 Compilation(Language language, SourceMessages messages, NameLookup lexical)910 public Compilation (Language language, SourceMessages messages, 911 NameLookup lexical) 912 { 913 this.language = language; 914 this.messages = messages; 915 this.lexical = lexical; 916 } 917 outputClass(String directory)918 public void outputClass (String directory) throws IOException 919 { 920 char dirSep = File.separatorChar; 921 for (int iClass = 0; iClass < numClasses; iClass++) 922 { 923 ClassType clas = classes[iClass]; 924 String out_name 925 = (directory + clas.getName().replace('.', dirSep) 926 + ".class"); 927 String parent = new File(out_name).getParent(); 928 if (parent != null) 929 new File(parent).mkdirs(); 930 clas.writeToFile(out_name); 931 } 932 getMinfo().cleanupAfterCompilation(); 933 } 934 cleanupAfterCompilation()935 public void cleanupAfterCompilation () 936 { 937 for (int iClass = 0; iClass < numClasses; iClass++) 938 classes[iClass].cleanupAfterCompilation(); 939 classes = null; 940 ModuleInfo minfo = getMinfo(); 941 minfo.className = mainClass.getName(); // In case it hasn't been set yet. 942 minfo.setCompilation(null); 943 // We don't clear minfo.exp itself, since it might be re-required. 944 if (minfo.exp != null) 945 minfo.exp.body = null; 946 mainLambda.body = null; 947 mainLambda = null; 948 if (! immediate) 949 litTable = null; 950 } 951 compileToArchive(ModuleExp mexp, String fname)952 public void compileToArchive (ModuleExp mexp, String fname) 953 throws java.io.IOException 954 { 955 boolean makeJar = false; 956 if (fname.endsWith(".zip")) 957 makeJar = false; 958 else if (fname.endsWith(".jar")) 959 makeJar = true; 960 else 961 { 962 fname = fname + ".zip"; 963 makeJar = false; 964 } 965 966 process(COMPILED); 967 968 File zar_file = new File (fname); 969 if (zar_file.exists ()) 970 zar_file.delete (); 971 ZipOutputStream zout; 972 /* #ifdef JAVA2 */ 973 if (makeJar) 974 zout = new java.util.jar.JarOutputStream(new FileOutputStream(zar_file)); 975 else 976 /* #endif */ 977 zout = new ZipOutputStream(new FileOutputStream(zar_file)); 978 979 byte[][] classBytes = new byte[numClasses][]; 980 CRC32 zcrc = new CRC32(); 981 for (int iClass = 0; iClass < numClasses; iClass++) 982 { 983 ClassType clas = classes[iClass]; 984 classBytes[iClass] = clas.writeToArray (); 985 ZipEntry zent = new ZipEntry(clas.getName ().replace ('.', '/') 986 + ".class"); 987 988 zent.setSize(classBytes[iClass].length); 989 zcrc.reset(); 990 zcrc.update(classBytes[iClass], 0, classBytes[iClass].length); 991 zent.setCrc(zcrc.getValue()); 992 993 zout.putNextEntry (zent); 994 zout.write (classBytes[iClass]); 995 } 996 zout.close (); 997 } 998 999 // FIXME - make this settable, as it does make .class files bigger. 1000 public static boolean emitSourceDebugExtAttr = true; 1001 registerClass(ClassType new_class)1002 private void registerClass (ClassType new_class) 1003 { 1004 if (classes == null) 1005 classes = new ClassType[20]; 1006 else if (numClasses >= classes.length) 1007 { 1008 ClassType[] new_classes = new ClassType[2 * classes.length]; 1009 System.arraycopy (classes, 0, new_classes, 0, numClasses); 1010 classes = new_classes; 1011 } 1012 new_class.addModifiers(new_class.isInterface() ? Access.PUBLIC 1013 : Access.PUBLIC|Access.SUPER); 1014 if (new_class == mainClass && numClasses > 0) 1015 { 1016 // Ensure mainClass is written first when writing an archive. 1017 new_class = classes[0]; 1018 classes[0] = mainClass; 1019 } 1020 classes[numClasses++] = new_class; 1021 classesArrayIsSorted = false; 1022 } 1023 addClass(ClassType new_class)1024 public void addClass (ClassType new_class) 1025 { 1026 String fname = getModule().filename; 1027 if (fname != null) 1028 { 1029 if (emitSourceDebugExtAttr) 1030 new_class.setStratum(getLanguage().getName()); 1031 new_class.setSourceFile(fname); 1032 } 1033 registerClass(new_class); 1034 new_class.setClassfileVersion(defaultClassFileVersion); 1035 } 1036 makeRunnable()1037 public boolean makeRunnable () 1038 { 1039 return ! generatingServlet() && ! generatingApplet() 1040 && ! getModule().staticInitRun() 1041 && ! getModule() 1042 .getFlag(ModuleExp.USE_DEFINED_CLASS|ModuleExp.SUPERTYPE_SPECIFIED); 1043 } 1044 addMainClass(ModuleExp module)1045 public void addMainClass (ModuleExp module) 1046 { 1047 mainClass = module.classFor(this); 1048 ClassType type = mainClass; 1049 ClassType[] interfaces = module.getInterfaces(); 1050 if (interfaces != null) 1051 type.setInterfaces(interfaces); 1052 ClassType sup = module.getSuperType(); 1053 if (sup == null) 1054 { 1055 if (generatingApplet()) 1056 sup = typeApplet; 1057 else if (generatingServlet()) 1058 sup = typeServlet; 1059 else if (module.getFlag(ModuleExp.USE_DEFINED_CLASS)) 1060 sup = Type.objectType; 1061 else 1062 sup = getModuleType(); 1063 } 1064 if (makeRunnable()) 1065 type.addInterface(typeRunnable); 1066 if (! module.staticInitRun()) 1067 type.addInterface(typeRunnableModule); 1068 else 1069 module.setInlineOnly(true); 1070 type.setSuper(sup); 1071 1072 module.compiledType = type; 1073 addClass(type); 1074 } 1075 getConstructor(LambdaExp lexp)1076 public final Method getConstructor (LambdaExp lexp) 1077 { 1078 return getConstructor(lexp.getHeapFrameType(), lexp); 1079 } 1080 getConstructor(ClassType clas, LambdaExp lexp)1081 public static final Method getConstructor (ClassType clas, LambdaExp lexp) 1082 { 1083 Method meth = clas.getDeclaredMethod("<init>", 0); 1084 if (meth != null) 1085 return meth; 1086 Type[] args; 1087 if (lexp instanceof ClassExp && lexp.staticLinkField != null) 1088 { 1089 args = new Type[1]; 1090 args[0] = lexp.staticLinkField.getType(); 1091 } 1092 else 1093 args = apply0args; 1094 return clas.addMethod("<init>", Access.PUBLIC, args, Type.voidType); 1095 } 1096 generateConstructor(LambdaExp lexp)1097 public final void generateConstructor (LambdaExp lexp) 1098 { 1099 generateConstructor (lexp.getHeapFrameType(), lexp); 1100 } 1101 generateConstructor(ClassType clas, LambdaExp lexp)1102 public final void generateConstructor (ClassType clas, LambdaExp lexp) 1103 { 1104 Method save_method = method; 1105 Variable callContextSave = callContextVar; 1106 callContextVar = null; 1107 ClassType save_class = curClass; 1108 curClass = clas; 1109 Method constructor_method = getConstructor(clas, lexp); 1110 clas.constructor = constructor_method; 1111 method = constructor_method; 1112 CodeAttr code = constructor_method.startCode(); 1113 1114 if (lexp instanceof ClassExp && lexp.staticLinkField != null) 1115 { 1116 code.emitPushThis(); 1117 code.emitLoad(code.getCurrentScope().getVariable(1)); 1118 code.emitPutField(lexp.staticLinkField); 1119 } 1120 ClassType superClass = clas.getSuperclass(); 1121 ClassExp.invokeDefaultSuperConstructor(superClass, this, lexp); 1122 1123 if (curClass == mainClass 1124 // Optimization: No point in calling ModuleInfo.register if we aren't 1125 // compiling a named module. 1126 && getMinfo() != null && getMinfo().sourcePath != null 1127 && ! getModule().getFlag(ModuleExp.USE_DEFINED_CLASS)) 1128 { 1129 code.emitPushThis(); 1130 code.emitInvokeStatic(ClassType.make("gnu.expr.ModuleInfo") 1131 .getDeclaredMethod("register", 1)); 1132 } 1133 1134 if (lexp != null && lexp.initChain != null) 1135 { 1136 // Create dummy lambda, for its closureEnv. This may be needed 1137 // if init.value contains a reference that uses our heap frame. 1138 LambdaExp save = curLambda; 1139 curLambda = new LambdaExp(); 1140 curLambda.closureEnv = code.getArg(0); 1141 curLambda.setOuter(save); 1142 Initializer init; 1143 while ((init = lexp.initChain) != null) 1144 { 1145 lexp.initChain = null; 1146 dumpInitializers(init); 1147 } 1148 curLambda = save; 1149 } 1150 1151 if (lexp instanceof ClassExp) 1152 { 1153 ClassExp cexp = (ClassExp) lexp; 1154 callInitMethods(cexp.getCompiledClassType(this), 1155 new ArrayList<ClassType>(10)); 1156 } 1157 1158 code.emitReturn(); 1159 method = save_method; 1160 curClass = save_class; 1161 callContextVar = callContextSave; 1162 } 1163 1164 /** In an <init> for a generated ClassExp, emit $finit$ calls. 1165 * This recursively traverses superclasses, and also calls their $finit$. 1166 * @param clas Class to search for $finit$, and to search supertypes. 1167 * @param seen array of seen classes, to avoid duplicate $finit$ calls. 1168 */ callInitMethods(ClassType clas, ArrayList<ClassType> seen)1169 void callInitMethods (ClassType clas, ArrayList<ClassType> seen) 1170 { 1171 if (clas == null) 1172 return; 1173 1174 String name = clas.getName(); 1175 if ("java.lang.Object".equals(name)) 1176 return; 1177 // Check for duplicates. 1178 for (int i = seen.size(); --i >= 0; ) 1179 if (seen.get(i).getName() == name) 1180 return; 1181 seen.add(clas); 1182 1183 // Recusive call to emit $finit$ of super-types. However, don't do that 1184 // for clas.getSuperclass(), because our <init> will automatically call 1185 // the super-class's <init>, which will call its $finit$. 1186 ClassType[] interfaces = clas.getInterfaces(); 1187 if (interfaces != null) 1188 { 1189 int n = interfaces.length; 1190 for (int i = 0; i < n; i++) 1191 callInitMethods(interfaces[i], seen); 1192 } 1193 1194 int clEnvArgs = 1; 1195 if (clas.isInterface()) 1196 { 1197 if (clas instanceof PairClassType) 1198 clas = ((PairClassType) clas).instanceType; 1199 else 1200 { 1201 try 1202 { 1203 clas = ((ClassType) 1204 Type.make(Class.forName(clas.getName() + "$class"))); 1205 } 1206 catch (Exception ex) 1207 { 1208 return; 1209 } 1210 } 1211 } 1212 else 1213 clEnvArgs = 0; 1214 Method meth = clas.getDeclaredMethod("$finit$", clEnvArgs); 1215 if (meth != null) 1216 { 1217 CodeAttr code = getCode(); 1218 code.emitPushThis(); 1219 code.emitInvoke(meth); 1220 } 1221 } 1222 generateCheckMethod(LambdaExp lexp, LambdaExp parent)1223 public Method generateCheckMethod(LambdaExp lexp, LambdaExp parent) { 1224 Method saveMethod = method; 1225 LambdaExp saveLambda = curLambda; 1226 ClassType saveClass = curClass; 1227 curLambda = lexp; 1228 Method primMethod = lexp.getMainMethod(); 1229 if (! lexp.inlinedInCheckMethod()) 1230 curClass = primMethod.getDeclaringClass(); 1231 Variable callContextSave = callContextVar; 1232 String primName = lexp.getMethodName(this); 1233 String checkName = primName + "$check"; 1234 Type[] checkArgs = { typeProcedure, typeCallContext }; 1235 1236 int mcount = 0; 1237 while (curClass.getDeclaredMethod(checkName, checkArgs) != null) { 1238 checkName = primName + "$" + (++mcount) + "$check"; 1239 } 1240 Method checkMethod = 1241 curClass.addMethod(checkName, Access.PUBLIC|Access.STATIC, 1242 checkArgs, (Type) Type.objectType); 1243 this.method = checkMethod; 1244 CodeAttr code = method.startCode(); 1245 SourceLocator saveLoc1 = messages.swapSourceLocator(lexp); 1246 int line = lexp.getLineNumber(); 1247 if (line > 0) 1248 code.putLineNumber(lexp.getFileName(), line); 1249 Variable ctxVar = code.getArg(method.getStaticFlag()?1:2); 1250 callContextVar = ctxVar; 1251 if (lexp.inlinedInCheckMethod()) { 1252 Variable clEnv = lexp.declareClosureEnv(); 1253 if (clEnv != null) 1254 clEnv.allocateLocal(code); 1255 lexp.allocFrame(this); 1256 lexp.allocChildClasses(this); 1257 lexp.allocParameters(this); 1258 lexp.allocChildMethods(this); 1259 lexp.enterFunction(this); 1260 } else 1261 lexp.scope = code.pushScope(); 1262 Declaration[] keyDecls = generateCheckKeywords(lexp); 1263 ArrayList<Variable> argVariables = new ArrayList<Variable>(); 1264 Declaration param = lexp.firstDecl(); 1265 if (param != null && param.isThisParameter()) 1266 param = param.nextDecl(); 1267 generateCheckArg(param, lexp, 0, code, keyDecls, argVariables, null); 1268 messages.swapSourceLocator(saveLoc1); 1269 this.method = saveMethod; 1270 curLambda = saveLambda; 1271 curClass = saveClass; 1272 callContextVar = callContextSave; 1273 return checkMethod; 1274 } 1275 1276 private static final Comparator<Declaration> keyComparator = 1277 new Comparator<Declaration> () { 1278 public int compare(Declaration d1, Declaration d2) { 1279 String s1 = (String) d1.getSymbol(); 1280 String s2 = (String) d2.getSymbol(); 1281 return s1.compareTo(s2); 1282 } 1283 }; 1284 1285 /** An initial pass through the keyword parameters. 1286 * This is in keyword lexical order. 1287 * For each keyword formal parameter, we check if there is 1288 * a corresponding actual keyword parameter. If so we get its value. 1289 * Default value expressions are only evaluated if "simple" (literals); 1290 * otherwise they are evaluated in the later declaration-order pass. 1291 */ generateCheckKeywords(LambdaExp lexp)1292 private Declaration[] generateCheckKeywords(LambdaExp lexp) { 1293 int nkeys = lexp.keywords == null ? 0 : lexp.keywords.length; 1294 if (nkeys == 0) 1295 return null; 1296 CodeAttr code = getCode(); 1297 Variable ctxVar = callContextVar; 1298 Scope scope = lexp.scope; 1299 // A helper array of fake Declarations. 1300 // We use Declaration as a convenient type for sorting. 1301 Declaration[] keyDecls = new Declaration[nkeys]; 1302 Declaration[] tmpDecls = new Declaration[nkeys]; 1303 int pin = 0; 1304 int kin = 0; 1305 boolean allowOtherKeys = lexp.getFlag(LambdaExp.ALLOW_OTHER_KEYWORDS); 1306 for (Declaration decl = lexp.firstDecl(); 1307 decl != null; decl = decl.nextDecl()) { 1308 if (kin >= nkeys) 1309 break; 1310 if (! decl.getFlag(Declaration.IS_PARAMETER) 1311 || decl.getFlag(Declaration.IS_REST_PARAMETER)) 1312 continue; 1313 if (pin >= lexp.min_args+lexp.opt_args) { 1314 Object key = lexp.keywords[kin]; 1315 if (key instanceof Keyword) 1316 key = ((Keyword) key).getName(); 1317 Declaration keyDecl = new Declaration(key); 1318 keyDecl.base = decl; 1319 keyDecl.evalIndex = kin; 1320 keyDecls[kin++] = keyDecl; 1321 } 1322 pin++; 1323 } 1324 java.util.Arrays.sort(keyDecls, keyComparator); 1325 for (kin = 0; kin < nkeys; kin++) { 1326 Declaration keyDecl = keyDecls[kin]; 1327 Declaration param = keyDecl.base; 1328 Expression dfault = param.getInitValue(); 1329 boolean simple = dfault instanceof QuoteExp 1330 && ! param.getFlag(Declaration.IS_SUPPLIED_PARAMETER); 1331 Variable var; 1332 keyDecl.setSimple(simple); 1333 code.emitLoad(ctxVar); 1334 code.emitPushString((String) keyDecl.getSymbol()); 1335 String mname = allowOtherKeys ? "nextKeywordAllowOthers" : "nextKeyword"; 1336 boolean convertNeeded = param.getType() != Type.objectType; 1337 if (simple) { 1338 // var gets keyword value or dfault if not found 1339 var = param.var != null && ! convertNeeded ? param.var 1340 : scope.addVariable(code, Type.objectType, null); 1341 dfault.compile(this, Target.pushObject); 1342 code.emitInvoke(typeCallContext.getDeclaredMethod(mname, 2)); 1343 } else { 1344 // var gets keyword index or -1 if not found 1345 var = scope.addVariable(code, Type.intType, null); 1346 code.emitInvoke(typeCallContext.getDeclaredMethod(mname, 1)); 1347 } 1348 keyDecl.var = var; 1349 code.emitStore(var); 1350 tmpDecls[keyDecl.evalIndex] = keyDecl; 1351 } 1352 System.arraycopy(tmpDecls, 0, keyDecls, 0, nkeys); 1353 return keyDecls; 1354 } 1355 1356 /** Generate code to process one formal parameter. 1357 * Calls recursively to handle the next parameter. 1358 * If there are no more parameters, calls generateCheckCall to finish, 1359 */ generateCheckArg(Declaration param, LambdaExp lexp, int kin, CodeAttr code, Declaration[] keyDecls, ArrayList<Variable> argVariables, Variable suppliedParameterVar)1360 private void generateCheckArg(Declaration param, LambdaExp lexp, int kin, CodeAttr code, Declaration[] keyDecls, ArrayList<Variable> argVariables, Variable suppliedParameterVar) { 1361 Variable ctxVar = callContextVar; 1362 if (param == null) { 1363 code.emitLoad(ctxVar); 1364 code.emitInvoke(typeCallContext.getDeclaredMethod("checkDone", 0)); 1365 code.emitIfIntNotZero(); 1366 code.emitLoad(ctxVar); 1367 code.emitReturn(); 1368 code.emitFi(); 1369 1370 generateCheckCall(lexp, code, lexp.getMainMethod(), argVariables); 1371 return; 1372 } 1373 boolean recurseNeeded = true; 1374 Type ptype = param.getType(); 1375 1376 // Actual primMethod is already generated. 1377 // Now we're re-using the parameter declaration to set them. 1378 // So they should always be plain variables. 1379 // FIXME: If the parameter is computed by an init expression, 1380 // we have a problem - which we should handle by lambda lifting? 1381 if (! lexp.inlinedInCheckMethod()) 1382 param.setSimple(true); 1383 1384 Variable incoming = null; 1385 boolean convertNeeded = ptype != Type.objectType; 1386 Scope scope = code.pushAutoPoppableScope(); 1387 int knext = kin; 1388 Type itype = param.getType().getImplementationType(); 1389 1390 if (param.getFlag(Declaration.PATTERN_NESTED)) { 1391 Expression init = param.getInitValue(); 1392 init.compile(this, init.getType()); 1393 } else if (param.getFlag(Declaration.IS_REST_PARAMETER)) { 1394 int singleArgs = lexp.min_args; // FIXME 1395 Type lastArgType = ptype.getRawType(); 1396 if (lastArgType instanceof ArrayType) { 1397 varArgsToArray(lexp, singleArgs, null/*counter*/, ptype, ctxVar, param.getFlag(Declaration.KEYWORDS_OK)); 1398 convertNeeded = false; 1399 } else if (ptype == LangObjType.argVectorType 1400 || ptype == LangObjType.argListType) { 1401 code.emitLoad(ctxVar); 1402 String mname = ptype == LangObjType.argListType 1403 ? "getRestArgsList" : "getRestArgsVector"; 1404 code.emitInvokeVirtual(typeCallContext.getDeclaredMethod(mname, 0)); 1405 convertNeeded = false; 1406 } 1407 else if ("gnu.lists.LList".equals 1408 (lastArgType.getName())) { 1409 code.emitLoad(ctxVar); 1410 //code.emitDup(); // 1411 //code.emitPushInt(singleArgs); 1412 //String mname = "peekRestArgsList";// if followed by #!key? 1413 String mname = true /* FIXME*/ ? "getRestArgsList" 1414 : "getRestPlainList"; 1415 code.emitInvokeVirtual(typeCallContext.getDeclaredMethod(mname, 0)); 1416 convertNeeded = false; // FIXME - may need convert if list[T] 1417 } else { 1418 // FIXME 1419 throw new Error("unsupported #!rest conversion in "+lexp+" param:"+param+" pt:"+ptype+" cl:"+curClass); 1420 } 1421 } else if (param.getFlag(Declaration.IS_SUPPLIED_PARAMETER) 1422 && ! param.getFlag(Declaration.IS_PARAMETER)) { 1423 incoming = suppliedParameterVar; 1424 convertNeeded = false; 1425 } else if (kin >=lexp.min_args && kin < lexp.min_args+lexp.opt_args) { 1426 // Optional parameter 1427 code.emitLoad(ctxVar); 1428 code.emitInvoke(typeCallContext.getDeclaredMethod("haveArg", 0)); 1429 if (param.getFlag(Declaration.IS_SUPPLIED_PARAMETER)) { 1430 code.emitDup(); 1431 suppliedParameterVar = 1432 scope.addVariable(code, Type.booleanType, null); 1433 code.emitStore(suppliedParameterVar); 1434 } 1435 if (lexp.primMethods == null || lexp.primMethods.length == 1) { 1436 code.emitIfIntNotZero(); 1437 code.emitLoad(ctxVar); 1438 code.emitInvoke(getNextArgMethod); 1439 code.emitElse(); 1440 Expression defaultArg = param.getInitValue(); 1441 defaultArg.compile(this, Type.objectType); 1442 code.emitFi(); 1443 } else { 1444 code.emitIfIntEqZero(); 1445 code.emitLoad(ctxVar); 1446 code.emitInvoke(typeCallContext.getDeclaredMethod("checkDone", 0)); 1447 code.emitIfIntNotZero(); 1448 code.emitLoad(ctxVar); 1449 code.emitReturn(); 1450 code.emitFi(); 1451 generateCheckCall(lexp, code, 1452 lexp.primMethods[kin-lexp.min_args], argVariables); 1453 code.emitFi(); 1454 code.emitLoad(ctxVar); 1455 code.emitInvoke(getNextArgMethod); 1456 } 1457 knext++; 1458 } else if (kin >= lexp.min_args+lexp.opt_args) { 1459 // keyword parameter 1460 int kindex = kin - (lexp.min_args+lexp.opt_args); 1461 Declaration keyDecl = keyDecls[kindex]; 1462 if (keyDecl.isSimple()) { 1463 if (convertNeeded) 1464 code.emitLoad(keyDecl.var); 1465 else 1466 incoming = keyDecl.var; 1467 } else { 1468 code.emitLoad(ctxVar); 1469 code.emitLoad(keyDecl.var); 1470 if (param.getFlag(Declaration.IS_SUPPLIED_PARAMETER)) { 1471 code.emitDup(); 1472 suppliedParameterVar = 1473 scope.addVariable(code, Type.booleanType, null); 1474 // {suppliedParameterVar} = {keyDecl.var} >= 0, 1475 // where the latter is equal to ({keyDecl.var}>>31)+1. 1476 code.emitPushInt(31); 1477 code.emitShr(); 1478 code.emitStore(suppliedParameterVar); 1479 code.emitInc(suppliedParameterVar, (short) 1); 1480 } 1481 code.emitIfIntGEqZero(); 1482 code.emitLoad(ctxVar); 1483 code.emitLoad(keyDecl.var); 1484 code.emitInvoke(typeCallContext.getDeclaredMethod("getArgAsObject", 1));; 1485 code.emitElse(); 1486 Expression defaultArg = param.getInitValue(); 1487 defaultArg.compile(this, Type.objectType); 1488 code.emitFi(); 1489 } 1490 if (kindex+1 == keyDecls.length 1491 && ! lexp.getFlag(LambdaExp.ALLOW_OTHER_KEYWORDS)) { 1492 code.emitLoad(ctxVar); 1493 code.emitInvoke(typeCallContext.getDeclaredMethod("checkKeywordsDone", 0)); 1494 } 1495 knext++; 1496 } else { 1497 // Required parameter 1498 knext++; 1499 code.emitLoad(ctxVar); 1500 code.emitInvoke(getNextArgMethod); 1501 } 1502 //param.allocateVariable(code, true); 1503 //boolean saveSimple = param.isSimple(); 1504 //param.setSimple(true); 1505 int line = param.getLineNumber(); 1506 if (line > 0) 1507 code.putLineNumber(param.getFileName(), line); 1508 // The uncoerced parameter value is either in incoming (if non-null), 1509 // or on the stack (if incoming is null). 1510 if (incoming != null && ! convertNeeded) 1511 param.var = incoming; 1512 else { 1513 param.var = scope.addVariable(code, itype, null/*vname*/); 1514 } 1515 if (param.parameterForMethod() 1516 && (lexp.inlinedInCheckMethod() 1517 || ! param.getFlag(Declaration.IS_SUPPLIED_PARAMETER) 1518 || param.getFlag(Declaration.IS_PARAMETER))) { 1519 argVariables.add(param.var); 1520 } 1521 if (! convertNeeded) { 1522 if (! param.isSimple() || incoming != param.var) { 1523 if (lexp.inlinedInCheckMethod()) { 1524 if (incoming != null) 1525 code.emitLoad(incoming); 1526 param.compileStore(this); 1527 } else if (incoming == null) 1528 code.emitStore(param.var); 1529 } 1530 } 1531 else if (ptype instanceof TypeValue || 1532 ptype instanceof PrimType /*FIXME only if number */) { 1533 // what if incoming!=null && convertNeeded 1534 if (incoming != null) 1535 throw new InternalError(); 1536 if (param.isGuard()) { 1537 code.emitIfIntNotZero(); 1538 } 1539 else { 1540 StackTarget.forceLazyIfNeeded(this, Type.objectType, ptype); 1541 incoming = code.addLocal(Type.pointer_type); 1542 code.emitStore(incoming); 1543 if (ptype instanceof TypeValue) { 1544 ((TypeValue) ptype).emitTestIf(incoming, param, this); 1545 } 1546 else { 1547 code.emitLoad(incoming); 1548 LangPrimType.emitTestIfNumber(incoming, param, 1549 ptype, this); 1550 } 1551 } 1552 generateCheckArg(param.nextDecl(), lexp, knext, code, keyDecls, argVariables, suppliedParameterVar); 1553 recurseNeeded = false; 1554 code.emitElse(); 1555 if (line > 0) 1556 code.putLineNumber(param.getFileName(), line); 1557 code.emitLoad(ctxVar); 1558 code.emitDup(); 1559 int errCode = param.isGuard() ? MethodProc.NO_MATCH_GUARD_FALSE 1560 : MethodProc.NO_MATCH_BAD_TYPE|kin; 1561 code.emitPushInt(errCode); 1562 code.emitInvoke(typeCallContext.getDeclaredMethod("matchError", 1)); 1563 code.emitReturn(); 1564 code.emitFi(); 1565 } else { 1566 if (incoming != null) 1567 code.emitLoad(incoming); 1568 StackTarget.forceLazyIfNeeded(this, Type.objectType, ptype); 1569 // FIXME 1570 if (ptype instanceof ObjectType && 1571 ptype != Type.objectType 1572 && ptype != Type.toStringType) { // FIXME 1573 code.emitDup(); 1574 ptype.getRawType().emitIsInstance(code); 1575 code.emitIfIntEqZero(); 1576 code.emitLoad(ctxVar); 1577 code.emitPushInt(MethodProc.NO_MATCH_BAD_TYPE|kin); 1578 code.emitInvoke(typeCallContext.getDeclaredMethod("matchError", 1)); 1579 code.emitLoad(ctxVar); 1580 code.emitReturn(); 1581 //code.emitElse(); 1582 code.emitFi(); 1583 } 1584 ptype.emitCoerceFromObject(code); 1585 if (lexp.inlinedInCheckMethod()) 1586 param.compileStore(this); 1587 else 1588 code.emitStore(param.var); 1589 } 1590 if (recurseNeeded) 1591 generateCheckArg(param.nextDecl(), lexp, knext, code, keyDecls, argVariables, suppliedParameterVar); 1592 } 1593 generateCheckCall(LambdaExp lexp, CodeAttr code, Method primMethod, ArrayList<Variable> argVariables)1594 private void generateCheckCall(LambdaExp lexp, CodeAttr code, Method primMethod, ArrayList<Variable> argVariables) { 1595 boolean usingCallContext = lexp.usingCallContext(); 1596 Variable ctxVar = callContextVar; 1597 if (lexp.inlinedInCheckMethod()) { 1598 lexp.compileBody(this); 1599 } else { 1600 if (lexp.getNeedsClosureEnv() || ! primMethod.getStaticFlag()) { 1601 ClassType mtype = 1602 ! primMethod.getStaticFlag() 1603 ? primMethod.getDeclaringClass() 1604 : (ClassType) lexp.closureEnv.getType(); 1605 loadModuleRef(mtype); 1606 } 1607 for (Variable var : argVariables) 1608 code.emitLoad(var); 1609 for (Declaration param = lexp.firstDecl(); 1610 param != null; param = param.nextDecl()) { 1611 param.var = null; 1612 } 1613 if (primMethod == lexp.getMainMethod()) 1614 code.popScope(); 1615 if (usingCallContext) 1616 code.emitLoad(ctxVar); // get $ctx 1617 code.emitInvoke(primMethod); 1618 } 1619 if (usingCallContext) { 1620 code.emitPushNull(); 1621 } else { 1622 Target.pushObject.compileFromStack(this, 1623 lexp.getReturnType()); 1624 } 1625 code.emitReturn(); 1626 } 1627 generateCheckMethods(LambdaExp parent)1628 public void generateCheckMethods(LambdaExp parent) { 1629 int numApplyMethods 1630 = parent.applyMethods == null ? 0 : parent.applyMethods.size(); 1631 if (numApplyMethods == 0) 1632 return; 1633 for (int j = 0; j < numApplyMethods; ++j) { 1634 LambdaExp source = parent.applyMethods.get(j); 1635 generateCheckMethod(source, parent); 1636 } 1637 } 1638 loadModuleRef(ClassType clas)1639 void loadModuleRef(ClassType clas) { 1640 CodeAttr code = getCode(); 1641 code.emitLoad(code.getArg(0)); 1642 code.emitCheckcast(Compilation.typeCompiledProc); 1643 code.emitInvoke(Compilation.typeCompiledProc 1644 .getDeclaredMethod("getModule", 0)); 1645 code.emitCheckcast(clas); 1646 } 1647 1648 /** Copy incoming arguments to varargs/#!rest array. 1649 */ varArgsToArray(LambdaExp source, int singleArgs, Variable sizeVar, Type lastArgType, Variable ctxVar, boolean keywordsOk)1650 private void varArgsToArray (LambdaExp source, int singleArgs, 1651 Variable sizeVar, Type lastArgType, 1652 Variable ctxVar, boolean keywordsOk) 1653 { 1654 CodeAttr code = getCode(); 1655 Type elType = ((ArrayType) lastArgType).getComponentType(); 1656 boolean mustConvert = ! "java.lang.Object".equals(elType.getName()); 1657 if (! mustConvert) 1658 { 1659 code.emitLoad(ctxVar); 1660 //code.emitPushInt(singleArgs); 1661 String mname = keywordsOk ? "getRestArgsArray" : "getRestPlainArray"; 1662 code.emitInvokeVirtual(typeCallContext.getDeclaredMethod(mname, 0)); 1663 } 1664 else 1665 { 1666 code.pushScope(); 1667 if (sizeVar == null) 1668 { 1669 sizeVar = code.addLocal(Type.intType); 1670 code.emitLoad(ctxVar); 1671 code.emitInvoke(typeCallContext.getDeclaredMethod("getArgCount", 0)); 1672 if (singleArgs != 0) 1673 { 1674 code.emitPushInt(singleArgs); 1675 code.emitSub(Type.intType); 1676 } 1677 code.emitStore(sizeVar); 1678 } 1679 code.emitLoad(sizeVar); 1680 code.emitNewArray(elType.getImplementationType()); 1681 Variable index = code.addLocal(Type.intType); 1682 code.emitPushInt(0); 1683 code.emitStore(index); 1684 Label testLabel = new Label(code); 1685 Label loopTopLabel = new Label(code); 1686 loopTopLabel.setTypes(code); 1687 code.emitGoto(testLabel); 1688 loopTopLabel.define(code); 1689 1690 code.emitDup(1); // new array 1691 code.emitLoad(index); 1692 code.emitLoad(ctxVar); 1693 code.emitInvokeVirtual(typeCallContext.getDeclaredMethod("getNextArg", 0)); 1694 if (mustConvert) 1695 { 1696 CheckedTarget.emitCheckedCoerce 1697 (this, source, source.getName(), 1698 0, elType, null); 1699 } 1700 code.emitArrayStore(elType); 1701 code.emitInc(index, (short) 1); 1702 testLabel.define(code); 1703 code.emitLoad(index); 1704 code.emitLoad(sizeVar); 1705 code.emitGotoIfLt(loopTopLabel); 1706 code.popScope(); 1707 } 1708 } 1709 startClassInit()1710 private Method startClassInit () 1711 { 1712 method = curClass.addMethod ("<clinit>", apply0args, Type.voidType, 1713 Access.PUBLIC|Access.STATIC); 1714 1715 CodeAttr code = method.startCode(); 1716 1717 if (generateMainMethod() || generatingApplet() || generatingServlet()) 1718 { 1719 ClassType languageType 1720 = (ClassType) Type.make(getLanguage().getClass()); 1721 Method registerMethod 1722 = languageType.getDeclaredMethod("registerEnvironment", 0); 1723 if (registerMethod != null) 1724 code.emitInvokeStatic(registerMethod); 1725 } 1726 return method; 1727 } 1728 1729 /** Parse/visit/compile this module as needed and requested. 1730 * This method does not process any dependent modules (expect indirectly, 1731 * such as may be done by a require form). 1732 * @param wantedState the desired value of getState(). 1733 */ process(int wantedState)1734 public void process (int wantedState) 1735 { 1736 Compilation saveCompilation = Compilation.setSaveCurrent(this); 1737 try 1738 { 1739 ModuleExp mexp = getModule(); 1740 if (wantedState >= BODY_PARSED && getState() < BODY_PARSED-1) 1741 { 1742 setState(BODY_PARSED-1); 1743 language.parse(this, 0); 1744 mexp.classFor(this); 1745 if (lexer != null) 1746 lexer.close(); 1747 lexer = null; 1748 setState(BODY_PARSED); 1749 if (pendingImports != null) 1750 return; 1751 } 1752 if (wantedState >= RESOLVED && getState() < RESOLVED) 1753 { 1754 language.resolve(this); 1755 // Doing addMainClass is a bit flakey in the case that 1756 // ModuleExp.alwaysCompile is false. We don't want to 1757 // call addMainClass *unless* we're compiling, but when 1758 // dealing with eval, mutually recursive modules, etc 1759 // it doesn't quite work. 1760 addMainClass(mexp); 1761 if (generateMainMethod() && mexp.staticInitRun()) { 1762 error('e', "a static init-run module cannot have a 'main' method"); 1763 } 1764 setState(RESOLVED); 1765 } 1766 1767 // Avoid writing class needlessly. 1768 if (! explicit && ! immediate 1769 && (langOptions & Language.PARSE_FOR_LINT) == 0 1770 && getMinfo().checkCurrent(ModuleManager.getInstance(), System.currentTimeMillis())) 1771 { 1772 getMinfo().cleanupAfterCompilation(); 1773 setState(CLASS_WRITTEN); 1774 } 1775 1776 if (wantedState >= PRE_WALKED && getState() < PRE_WALKED) 1777 { 1778 if (debugPrintExpr) { 1779 OutPort dout = OutPort.errDefault(); 1780 dout.println("[Module:" + mexp.getName()); 1781 mexp.print(dout); 1782 dout.println(']'); 1783 dout.flush(); 1784 } 1785 PushApply.pushApply(mexp, this); 1786 setState(PRE_WALKED); 1787 } 1788 1789 if (wantedState >= WALKED && getState() < WALKED) 1790 { 1791 InlineCalls.inlineCalls(mexp, this); 1792 if (enableANF) 1793 ANormalize.aNormalize(mexp, this); 1794 if (debugPrintANF) { 1795 options.set("warn-void-used", Boolean.FALSE); 1796 OutPort dout = OutPort.errDefault(); 1797 dout.println ("[Normalized module: "+mexp.getName() 1798 + " to " + mainClass.getName() + ":"); 1799 mexp.print(dout); 1800 dout.println(']'); 1801 dout.flush(); 1802 } 1803 ChainLambdas.chainLambdas(mexp, this); 1804 FindTailCalls.findTailCalls(mexp, this); 1805 FindCapturedVars.findCapturedVars(mexp, this); 1806 setState(WALKED); 1807 } 1808 1809 if (wantedState >= COMPILE_SETUP && getState() < COMPILE_SETUP) 1810 { 1811 litTable = new LitTable(this); 1812 mexp.setCanRead(true); 1813 mexp.allocFields(this); 1814 mexp.allocChildMethods(this); 1815 setState(COMPILE_SETUP); 1816 } 1817 if (wantedState >= COMPILED && messages.seenErrors()) 1818 setState(ERROR_SEEN); 1819 if (wantedState >= COMPILED && getState() < COMPILED) 1820 { 1821 if (mexp.subModulesOnly()) 1822 { 1823 setState(wantedState < CLASS_WRITTEN ? COMPILED : CLASS_WRITTEN); 1824 } 1825 else 1826 { 1827 if (immediate) 1828 { 1829 ClassLoader parentLoader = ObjectType.getContextClassLoader(); 1830 loader = new ArrayClassLoader(parentLoader); 1831 } 1832 generateBytecode(); 1833 setState(messages.seenErrors() ? ERROR_SEEN : COMPILED); 1834 } 1835 } 1836 if (wantedState >= CLASS_WRITTEN && getState() < CLASS_WRITTEN 1837 && ! mexp.subModulesOnly()) 1838 { 1839 outputClass(ModuleManager.getInstance().getCompilationDirectory()); 1840 setState(CLASS_WRITTEN); 1841 } 1842 } 1843 catch (gnu.text.SyntaxException ex) 1844 { 1845 setState(ERROR_SEEN); 1846 if (ex.getMessages() != getMessages()) 1847 throw new RuntimeException ("confussing syntax error: "+ex); 1848 // otherwise ignore it - it's already been recorded in messages. 1849 } 1850 catch (java.io.IOException ex) 1851 { 1852 ex.printStackTrace(); 1853 error('f', "caught "+ex); 1854 setState(ERROR_SEEN); 1855 } 1856 finally 1857 { 1858 Compilation.restoreCurrent(saveCompilation); 1859 } 1860 } 1861 1862 /** The guts of compiling a module to one or more classes. 1863 * Assumes walkModule has been done. 1864 */ generateBytecode()1865 void generateBytecode() 1866 { 1867 ModuleExp module = getModule(); 1868 if (debugPrintFinalExpr) { 1869 OutPort dout = OutPort.errDefault(); 1870 dout.println ("[Compiling final "+module.getName() 1871 + " to " + mainClass.getName() + ":"); 1872 module.print(dout); 1873 dout.println(']'); 1874 dout.flush(); 1875 } 1876 1877 ClassType neededSuper = getModuleType(); 1878 moduleClass = mainClass; 1879 1880 curClass = module.compiledType; 1881 LambdaExp saveLambda = curLambda; 1882 curLambda = module; 1883 1884 CodeAttr code; 1885 Variable heapFrame = module.heapFrame; 1886 boolean staticModule = module.isStatic(); 1887 1888 if (curClass.getSuperclass() != typeModuleBody 1889 && ! module.staticInitRun()) { 1890 Field runDoneField = curClass.addField("$runDone$", 1891 Type.booleanType, 1892 Access.PROTECTED); 1893 Method runDoneMethod = 1894 curClass.addMethod ("checkRunDone", 1895 new Type[] { Type.booleanType }, 1896 Type.booleanType, Access.PUBLIC); 1897 method = runDoneMethod; 1898 code = method.startCode(); 1899 code.emitLoad(code.getArg(0)); 1900 code.emitGetField(runDoneField); 1901 code.emitLoad(code.getArg(0)); 1902 code.emitLoad(code.getArg(1)); 1903 code.emitPutField(runDoneField); 1904 code.emitReturn(); 1905 } 1906 1907 Method apply_method; 1908 if (module.staticInitRun()) { 1909 apply_method = startClassInit(); 1910 } else { 1911 Type[] arg_types = { typeCallContext }; 1912 ClassType sup = module.getSuperType(); 1913 Method srun = sup.getMethod("run", arg_types); 1914 String mname = "run"; 1915 if (srun != null && ! srun.isAbstract()) { 1916 Method srunx = sup.getMethod("$run$", arg_types); 1917 if (srunx != null && srunx.isAbstract()) 1918 mname = "$run$"; 1919 } 1920 apply_method = curClass.addMethod (mname, arg_types, Type.voidType, 1921 Access.PUBLIC+Access.FINAL); 1922 apply_method.initCode(); 1923 } 1924 method = apply_method; 1925 // For each parameter, assign it to its proper slot. 1926 // If a parameter !isSimple(), we cannot assign it to a local slot, 1927 // so instead create an artificial Variable for the incoming argument. 1928 // Below, we assign the value to the slot. 1929 code = getCode(); 1930 // if (usingCPStyle()) code.addParamLocals(); 1931 1932 thisDecl = method.getStaticFlag() ? null : module.declareThis(module.compiledType); 1933 module.closureEnv = module.thisVariable; 1934 module.heapFrame = module.isStatic() ? null : module.thisVariable; 1935 module.allocChildClasses(this); 1936 1937 callContextVar = new Variable ("$ctx", typeCallContext); 1938 module.getVarScope().addVariableAfter(thisDecl, callContextVar); 1939 callContextVar.setParameter(true); 1940 1941 if (! module.staticInitRun()) { 1942 module.allocParameters(this); 1943 module.enterFunction(this); 1944 if (usingCPStyle()) { 1945 loadCallContext(); 1946 code.emitGetField(pcCallContextField); 1947 fswitch = code.startSwitch(); 1948 fswitch.addCase(0, code); 1949 } 1950 module.compileBody(this); 1951 } 1952 1953 Method save_method = method; 1954 Variable callContextSave = callContextVar; 1955 callContextVar = null; 1956 1957 Method initMethod; 1958 if (module.staticInitRun()) 1959 initMethod = apply_method; 1960 else 1961 initMethod = startClassInit(); 1962 clinitMethod = initMethod; 1963 method = clinitMethod; 1964 code = getCode(); 1965 Label beforeBody = null; 1966 Label afterBody = null; 1967 Label afterInits = null; 1968 if (module.staticInitRun()) { 1969 beforeBody = new Label(code); 1970 afterBody = new Label(code); 1971 afterInits = new Label(code); 1972 code.fixupChain(beforeBody, afterBody); 1973 module.compileAsInlined(this, Target.Ignore); 1974 code.fixupChain(afterBody, afterInits); 1975 } 1976 1977 Label startLiterals = new Label(code); 1978 Label afterLiterals = new Label(code); 1979 code.fixupChain(afterLiterals, startLiterals); 1980 1981 if (staticModule && ! module.staticInitRun()) { 1982 if (! module.getFlag(ModuleExp.USE_DEFINED_CLASS)) 1983 generateConstructor(module); 1984 else if (moduleClass.constructor == null) 1985 moduleClass.constructor = getConstructor(module); 1986 1987 code.emitNew(moduleClass); 1988 code.emitDup(moduleClass); 1989 code.emitInvokeSpecial(moduleClass.constructor); 1990 // The $instance field needs to be public so 1991 // ModuleContext.findInstance can find it. 1992 // It needs to be non-final in case moduleClass!=mainClass. 1993 // (The latter should probably be fixed by moving this code 1994 // to moduleClass's <clinit>.) 1995 moduleInstanceMainField 1996 = moduleClass.addField("$instance", moduleClass, 1997 Access.STATIC|Access.PUBLIC); 1998 code.emitPutStatic(moduleInstanceMainField); 1999 } 2000 Initializer init; 2001 while ((init = clinitChain) != null) { 2002 clinitChain = null; 2003 dumpInitializers(init); 2004 } 2005 2006 if (! module.staticInitRun()) 2007 code.emitReturn(); 2008 2009 method = save_method; 2010 callContextVar = callContextSave; 2011 2012 curLambda = saveLambda; 2013 2014 module.heapFrame = heapFrame; // Restore heapFrame. 2015 if (usingCPStyle()) { 2016 code = getCode(); 2017 fswitch.finish(code); 2018 } 2019 2020 if (startLiterals != null || callContextVar != null) { 2021 method = initMethod; 2022 code = getCode(); 2023 2024 Label endLiterals = new Label(code); 2025 code.fixupChain(startLiterals, endLiterals); 2026 2027 if (callContextVarForInit != null) { 2028 code.emitInvokeStatic(getCallContextInstanceMethod); 2029 code.emitStore(callContextVarForInit); 2030 } 2031 2032 try { 2033 if (immediate) { 2034 code.emitPushInt(registerForImmediateLiterals(this)); 2035 code.emitInvokeStatic(ClassType.make("gnu.expr.Compilation") 2036 .getDeclaredMethod("setupLiterals", 1)); 2037 } 2038 else 2039 litTable.emit(); 2040 } 2041 catch (Exception ex) { 2042 error('e', "Literals: Internal error:" + ex); 2043 } 2044 code.fixupChain(endLiterals, afterLiterals); 2045 } 2046 if (module.staticInitRun()) { 2047 code.fixupChain(afterInits, beforeBody); 2048 code.emitReturn(); 2049 } 2050 2051 if (generateMainMethod() && curClass == mainClass) { 2052 Type[] args = { new ArrayType(javaStringType) }; 2053 method = curClass.addMethod("main", Access.PUBLIC|Access.STATIC, 2054 args, Type.voidType); 2055 2056 code = method.startCode(); 2057 2058 if (Shell.defaultFormatName != null) { 2059 code.emitPushString(Shell.defaultFormatName); 2060 code.emitInvokeStatic(ClassType.make("kawa.Shell") 2061 .getDeclaredMethod("setDefaultFormat", 1)); 2062 } 2063 code.emitLoad(code.getArg(0)); 2064 code.emitInvokeStatic(ClassType.make("gnu.expr.ApplicationMainSupport") 2065 .getDeclaredMethod("processArgs", 1)); 2066 if (moduleInstanceMainField != null) 2067 code.emitGetStatic(moduleInstanceMainField); 2068 else { 2069 code.emitNew(curClass); 2070 code.emitDup(curClass); 2071 code.emitInvokeSpecial(curClass.constructor); 2072 } 2073 Method runAsMainMethod = null; 2074 ClassType superClass = curClass.getSuperclass(); 2075 if (superClass != typeModuleBody) 2076 runAsMainMethod = superClass.getDeclaredMethod("runAsMain", 0); 2077 if (runAsMainMethod == null) 2078 runAsMainMethod = typeModuleBody.getDeclaredMethod("runAsMain", 1); 2079 code.emitInvoke(runAsMainMethod); 2080 code.emitReturn(); 2081 } 2082 2083 String uri; 2084 if (getMinfo() != null && (uri = getMinfo().getNamespaceUri()) != null) { 2085 // Need to generate a ModuleSet for this class, so XQuery can find 2086 // this module and other modules in the same namespace. 2087 ModuleManager manager = ModuleManager.getInstance(); 2088 String mainPrefix = mainClass.getName(); 2089 int dot = mainPrefix.lastIndexOf('.'); 2090 if (dot < 0) 2091 mainPrefix = ""; 2092 else { 2093 String mainPackage = mainPrefix.substring(0, dot); 2094 try { 2095 manager.loadPackageInfo(mainPackage); 2096 } catch (ClassNotFoundException ex) { 2097 // Do nothing. 2098 } 2099 catch (Exception ex) { 2100 error('e', "error loading map for "+mainPackage+" - "+ex); 2101 } 2102 mainPrefix = mainPrefix.substring(0, dot+1); 2103 } 2104 ClassType mapClass = new ClassType(mainPrefix + ModuleSet.MODULES_MAP); 2105 ClassType typeModuleSet = ClassType.make("gnu.expr.ModuleSet"); 2106 mapClass.setSuper(typeModuleSet); 2107 registerClass(mapClass); 2108 2109 method = mapClass.addMethod("<init>", Access.PUBLIC, 2110 apply0args, Type.voidType); 2111 Method superConstructor 2112 = typeModuleSet.addMethod("<init>", Access.PUBLIC, 2113 apply0args, Type.voidType); 2114 code = method.startCode(); 2115 code.emitPushThis(); 2116 code.emitInvokeSpecial(superConstructor); 2117 code.emitReturn(); 2118 2119 ClassType typeModuleManager = ClassType.make("gnu.expr.ModuleManager"); 2120 Type[] margs = { typeModuleManager }; 2121 method = mapClass.addMethod("register", margs, Type.voidType, 2122 Access.PUBLIC); 2123 code = method.startCode(); 2124 Method reg = typeModuleManager.getDeclaredMethod("register", 3); 2125 2126 for (int i = manager.numModules; --i >= 0; ) { 2127 ModuleInfo mi = manager.modules[i]; 2128 String miClassName = mi.getClassName(); 2129 if (miClassName == null 2130 || ! miClassName.startsWith(mainPrefix)) 2131 continue; 2132 String moduleSource = mi.sourcePath; 2133 String moduleUri = mi.getNamespaceUri(); 2134 code.emitLoad(code.getArg(1)); 2135 compileConstant(miClassName); 2136 if (! Path.valueOf(moduleSource).isAbsolute()) 2137 try { 2138 // If the source path was relative, emit it as relative. 2139 // But make it relative to the compilation directory, 2140 // to allow sources to be moved along with binaries. 2141 String path = Path.toURL(manager.getCompilationDirectory()) 2142 + mainPrefix.replace('.', '/'); 2143 int plen = path.length(); 2144 if (plen > 0 && path.charAt(plen-1) != '/') 2145 path = path + '/'; 2146 String sourcePath = 2147 Path.toURL(mi.getSourceAbsPathname()).toString(); 2148 moduleSource = Path.relativize(sourcePath, path); 2149 } catch (Exception ex) { 2150 throw new WrappedException("exception while fixing up '" 2151 +moduleSource+'\'', 2152 ex); 2153 } 2154 compileConstant(moduleSource); 2155 compileConstant(moduleUri); 2156 code.emitInvokeVirtual(reg); 2157 } 2158 code.emitReturn(); 2159 } 2160 } 2161 2162 int localFieldIndex; allocLocalField(Type type, String name)2163 public Field allocLocalField (Type type, String name) 2164 { 2165 if (name == null) 2166 name = "tmp_"+(++localFieldIndex); 2167 Field field = curClass.addField(name, type, 0); 2168 return field; 2169 } 2170 2171 /** If non-null, contains the value of the current CallContext. */ 2172 Variable callContextVar; 2173 Variable callContextVarForInit; 2174 2175 /** Generate code to push the current CallContext on the JVM stack. */ loadCallContext()2176 public final void loadCallContext() 2177 { 2178 CodeAttr code = getCode(); 2179 if (callContextVar != null && ! callContextVar.dead()) 2180 code.emitLoad(callContextVar); 2181 // We're cautious about re-using a previously extracted CallContext, 2182 // because it's tricky to manage the variables safely. 2183 // A possible solution is to inject a Variable into the current scope, 2184 // and making sure each separate straight-line block has its own scope. 2185 // (If the current scope is in the same "basic block" as an outer scope, 2186 // we can use that instead.) FIXME 2187 else if (method == clinitMethod) 2188 { 2189 // The variable is initialized just after literals. 2190 callContextVar = new Variable("$ctx", typeCallContext); 2191 // To make sure it doesn't clash with variables that have already 2192 // allocated and freed for previous initializer. 2193 callContextVar.reserveLocal(code.getMaxLocals(), code); 2194 code.emitLoad(callContextVar); 2195 callContextVarForInit = callContextVar; 2196 } 2197 else 2198 { 2199 code.emitInvokeStatic(getCallContextInstanceMethod); 2200 code.emitDup(); 2201 callContextVar = new Variable("$ctx", typeCallContext); 2202 Scope scope = code.pushAutoPoppableScope(); 2203 scope.addVariable(code, callContextVar); 2204 code.emitStore(callContextVar); 2205 } 2206 } 2207 freeLocalField(Field field)2208 public void freeLocalField (Field field) 2209 { 2210 // FIXME 2211 } 2212 2213 /** This may not make sense, except for Lisp-like languages. 2214 * For those, 'input' an s-expression from the reader. */ parse(Object input)2215 public Expression parse (Object input) 2216 { 2217 throw new Error("unimeplemented parse"); 2218 } 2219 2220 protected Language language; getLanguage()2221 public Language getLanguage() { return language; } 2222 currentLambda()2223 public LambdaExp currentLambda () { return current_scope.currentLambda (); } 2224 getModule()2225 public final ModuleExp getModule() { return mainLambda; } setModule(ModuleExp mexp)2226 public void setModule(ModuleExp mexp) { mainLambda = mexp; } 2227 isStatic()2228 public boolean isStatic() { return mainLambda.isStatic(); } 2229 isInteractive()2230 public boolean isInteractive() { 2231 return mainLambda != null && mainLambda.getFlag(ModuleExp.INTERACTIVE); 2232 } 2233 2234 /** The same as getModule, until we allow nested modules. */ currentModule()2235 public ModuleExp currentModule() { return current_scope.currentModule(); } 2236 2237 /** Note that we have seen a construct that must be compiled, not evaluated. 2238 * If we are not inside a lambda (which is always compiled), but 2239 * only inside the outer-most ModuleExp, note that it must be compiled. 2240 */ mustCompileHere()2241 public void mustCompileHere () 2242 { 2243 if (! mustCompile && ! ModuleExp.compilerAvailable()) 2244 error('e', "this expression must be compiled, but compiler is unavailable"); 2245 mustCompile = true; 2246 } 2247 currentScope()2248 public ScopeExp currentScope() { return current_scope; } 2249 2250 /** Set <code>currentScope()</code>. 2251 * Also update the <code>nesting</code> object. 2252 */ setCurrentScope(ScopeExp scope)2253 public void setCurrentScope (ScopeExp scope) 2254 { 2255 int scope_nesting = ScopeExp.nesting(scope); 2256 int current_nesting = ScopeExp.nesting(current_scope); 2257 while (current_nesting > scope_nesting) 2258 { 2259 pop(current_scope); 2260 current_nesting--; 2261 } 2262 ScopeExp sc = scope; 2263 while (scope_nesting > current_nesting) 2264 { 2265 sc = sc.getOuter(); 2266 scope_nesting--; 2267 } 2268 while (sc != current_scope) 2269 { 2270 pop(current_scope); 2271 sc = sc.getOuter(); 2272 } 2273 pushChain(scope, sc); 2274 } 2275 setPushCurrentScope(ScopeExp scope)2276 public ScopeExp setPushCurrentScope (ScopeExp scope) { 2277 ScopeExp old = currentScope(); 2278 lexical.pushSaveTopLevelRedefs(); 2279 setCurrentScope(scope); 2280 return old; 2281 } 2282 setPopCurrentScope(ScopeExp old)2283 public void setPopCurrentScope (ScopeExp old) { 2284 setCurrentScope(old); 2285 lexical.popSaveTopLevelRedefs(); 2286 } 2287 pushChain(ScopeExp scope, ScopeExp limit)2288 void pushChain (ScopeExp scope, ScopeExp limit) 2289 { 2290 if (scope != limit) 2291 { 2292 pushChain(scope.getOuter(), limit); 2293 pushScope(scope); 2294 lexical.push(scope); 2295 } 2296 } 2297 pushNewModule(Lexer lexer)2298 public ModuleExp pushNewModule (Lexer lexer) 2299 { 2300 this.lexer = lexer; 2301 String filename = lexer == null ? null : lexer.getName(); 2302 ModuleExp module = new ModuleExp(); 2303 if (filename != null) 2304 module.setFile(filename); 2305 if (generatingApplet() || generatingServlet()) 2306 module.setFlag(ModuleExp.SUPERTYPE_SPECIFIED); 2307 mainLambda = module; 2308 if (immediate) 2309 { 2310 module.setFlag(ModuleExp.IMMEDIATE); 2311 ModuleInfo minfo = new ModuleInfo(); 2312 minfo.setCompilation(this); 2313 } 2314 push(module); 2315 return module; 2316 } 2317 push(ScopeExp scope)2318 public void push (ScopeExp scope) 2319 { 2320 pushScope(scope); 2321 lexical.push(scope); 2322 } 2323 pushScope(ScopeExp scope)2324 public final void pushScope (ScopeExp scope) 2325 { 2326 if (! mustCompile 2327 && (scope.mustCompile() 2328 || (ModuleExp.compilerAvailable() 2329 // We set mustCompile if we see a LambdaExp - not because 2330 // we must but because it is usually desirable. 2331 && scope instanceof LambdaExp 2332 && ! (scope instanceof ModuleExp)))) 2333 mustCompileHere(); 2334 scope.setOuter(current_scope); 2335 current_scope = scope; 2336 } 2337 pop(ScopeExp scope)2338 public void pop (ScopeExp scope) 2339 { 2340 lexical.pop(scope); 2341 current_scope = scope.getOuter(); 2342 } 2343 pop()2344 public final void pop () 2345 { 2346 pop(current_scope); 2347 } 2348 push(Declaration decl)2349 public void push (Declaration decl) 2350 { 2351 lexical.push(decl); 2352 } 2353 lookup(Object name, int namespace)2354 public Declaration lookup(Object name, int namespace) 2355 { 2356 return lexical.lookup(name, namespace); 2357 } 2358 2359 /** Called for classes referenced in bytecode. 2360 * Since this only does something when immediate, we only care about 2361 * classes referenced in the bytecode when immediate. 2362 * It is used to ensure that we can inherit from classes defined when in 2363 * immediate mode (in Scheme using define-class or similar). 2364 */ usedClass(Type type)2365 public void usedClass(Type type) { 2366 while (type instanceof ArrayType) 2367 type = ((ArrayType) type).getComponentType(); 2368 if (immediate && type instanceof ClassType) { 2369 ClassType cl = (ClassType) type; 2370 for (;;) { 2371 loader.addClass(cl); 2372 ClassType enc = cl.getDeclaringClass(); 2373 if (enc == null) 2374 break; 2375 cl = enc; 2376 } 2377 } 2378 } 2379 2380 /** Set module name - which sets name of generated class. */ setModuleName(String name)2381 public void setModuleName(String name) { 2382 getModule().setName(name); 2383 } 2384 2385 /** Generate and set unique module name suitable for an interactive session. */ setInteractiveName()2386 public void setInteractiveName() { 2387 setModuleName(ModuleManager.getInstance().getNewInteractiveName()); 2388 } 2389 2390 /** Generate and set unique module name suitable for a call to eval. */ setEvalName()2391 public void setEvalName() { 2392 setModuleName(ModuleManager.getInstance().getNewEvalName()); 2393 } 2394 getMessages()2395 public SourceMessages getMessages() { return messages; } setMessages(SourceMessages messages)2396 public void setMessages (SourceMessages messages) 2397 { this.messages = messages; } 2398 error(char severity, String message, SourceLocator location)2399 public void error(char severity, String message, SourceLocator location) 2400 { 2401 if (location.getFileName() == null || location.getLineNumber() <= 0) 2402 location = this; 2403 if (severity == 'w' && warnAsError()) 2404 severity = 'e'; 2405 messages.error(severity, location, message); 2406 } 2407 error(char severity, String message)2408 public void error(char severity, String message) 2409 { 2410 if (severity == 'w' && warnAsError()) 2411 severity = 'e'; 2412 2413 messages.error(severity, this, message); 2414 } 2415 error(char severity, Declaration decl, String msg1, String msg2)2416 public void error(char severity, Declaration decl, String msg1, String msg2) 2417 { 2418 error(severity, msg1 + decl.getName() + msg2, null, decl); 2419 } 2420 error(char severity, String message, String code, SourceLocator decl)2421 public void error(char severity, String message, 2422 String code, SourceLocator decl) 2423 { 2424 if (severity == 'w' && warnAsError()) 2425 severity = 'e'; 2426 2427 String filename = getFileName(); 2428 int line = getLineNumber(); 2429 int column = getColumnNumber(); 2430 int decl_line = decl.getLineNumber(); 2431 if (decl_line > 0) 2432 { 2433 filename = decl.getFileName(); 2434 line = decl_line; 2435 column = decl.getColumnNumber(); 2436 } 2437 messages.error(severity, filename, line, column, message, code); 2438 } 2439 2440 /** 2441 * Handle syntax errors (at rewrite time). 2442 * @param message an error message to print out 2443 * @return an ErrorExp 2444 */ syntaxError(String message)2445 public ErrorExp syntaxError (String message) 2446 { 2447 error('e', message); 2448 return new ErrorExp (message); 2449 } 2450 getLineNumber()2451 public final int getLineNumber() { return messages.getLineNumber(); } getColumnNumber()2452 public final int getColumnNumber() { return messages.getColumnNumber(); } getStartLine()2453 public final int getStartLine() { return messages.getStartLine(); } getStartColumn()2454 public final int getStartColumn() { return messages.getStartColumn(); } getEndLine()2455 public final int getEndLine() { return messages.getEndLine(); } getEndColumn()2456 public final int getEndColumn() { return messages.getEndColumn(); } getFileName()2457 public final String getFileName() { return messages.getFileName(); } getPublicId()2458 public String getPublicId() { return messages.getPublicId(); } getSystemId()2459 public String getSystemId() { return messages.getSystemId(); } isStableSourceLocation()2460 public boolean isStableSourceLocation() { return false; } 2461 setFile(String filename)2462 public void setFile(String filename) { messages.setFile(filename); } setLine(int line)2463 public void setLine(int line) { messages.setLine(line); } setColumn(int column)2464 public void setColumn(int column) { messages.setColumn(column); } setLine(Expression position)2465 public final void setLine(Expression position) 2466 { messages.setLocation(position); } 2467 setLine(Object location)2468 public void setLine (Object location) 2469 { 2470 if (location instanceof SourceLocator) 2471 messages.setLocation((SourceLocator) location); 2472 } setLocation(SourceLocator position)2473 public final void setLocation (SourceLocator position) 2474 { messages.setLocation(position); } 2475 setLine(String filename, int line, int column)2476 public void setLine(String filename, int line, int column) 2477 { 2478 messages.setLine(filename, line, column); 2479 } 2480 2481 /** Get source filename as an absolute Path, or null. 2482 * Return null if there is no actual file, such as a {@code <string>}. 2483 * Note the {@link ModuleInfo#getSourceAbsPath} is similar, 2484 * but this version is not canonicalized. 2485 */ getSourceAbsPath()2486 public Path getSourceAbsPath() { 2487 String currentFileName = getFileName(); 2488 if (currentFileName != null) { 2489 ModuleInfo info = getMinfo(); 2490 // info.getSourceAbsPath() is null if the source port is 2491 // a string or a console, in which we should return null. 2492 if (info != null && info.getSourceAbsPath() != null) { 2493 return Path.valueOf(currentFileName).getAbsolute(); 2494 } 2495 } 2496 return null; 2497 } 2498 2499 /** A help vector for building expressions. */ 2500 public Stack<Expression> exprStack; 2501 letStart()2502 public void letStart () 2503 { 2504 pushScope(new LetExp()); 2505 } 2506 letVariable(Object name, Type type, Expression init)2507 public Declaration letVariable (Object name, Type type, Expression init) 2508 { 2509 Declaration decl = new Declaration(name, type); 2510 letVariable(decl, init); 2511 return decl; 2512 } 2513 letVariable(Declaration decl, Expression init)2514 public void letVariable (Declaration decl, Expression init) 2515 { 2516 LetExp let = (LetExp) current_scope; 2517 let.add(decl); 2518 decl.setInitValue(init); 2519 } 2520 letEnter()2521 public void letEnter () 2522 { 2523 LetExp let = (LetExp) current_scope; 2524 // Set a flag which letDone uses to check if letEnter has been called. 2525 let.setFlag(Expression.VALIDATED); 2526 for (Declaration decl = let.firstDecl(); 2527 decl != null; decl = decl.nextDecl()) 2528 { 2529 Expression init = decl.getInitValue(); 2530 if (init != QuoteExp.undefined_exp) 2531 decl.noteValueFromLet(let); 2532 } 2533 lexical.push(let); 2534 } 2535 letDone(Expression body)2536 public LetExp letDone (Expression body) 2537 { 2538 LetExp let = (LetExp) current_scope; 2539 // Check if letEnter has been called. 2540 if (! let.getFlag(Expression.VALIDATED)) 2541 letEnter(); 2542 let.setFlag(false, Expression.VALIDATED); 2543 let.body = body; 2544 pop(let); 2545 return let; 2546 } 2547 checkLoop()2548 private void checkLoop() { 2549 if (((LambdaExp) current_scope).getName() != "%do%loop") 2550 throw new Error("internal error - bad loop state"); 2551 } 2552 2553 /** Start a new loop. 2554 * This provides the functionality of Scheme 'named let'. 2555 */ loopStart()2556 public LambdaExp loopStart() { 2557 if (exprStack == null) 2558 exprStack = new Stack<Expression>(); 2559 LambdaExp loopLambda = new LambdaExp(); 2560 LetExp let = new LetExp(); 2561 String fname = "%do%loop"; 2562 Declaration fdecl = let.addDeclaration(fname); 2563 fdecl.setInitValue(loopLambda); 2564 fdecl.noteValueFromLet(let); 2565 loopLambda.setName(fname); 2566 let.setOuter(current_scope); 2567 loopLambda.setOuter(let); 2568 current_scope = loopLambda; 2569 return loopLambda; 2570 } 2571 2572 /** Add a new loop variable, with initializer. */ loopVariable(Object name, Type type, Expression init)2573 public Declaration loopVariable(Object name, Type type, Expression init) { 2574 checkLoop(); 2575 LambdaExp loopLambda = (LambdaExp) current_scope; 2576 Declaration decl = loopLambda.addDeclaration(name, type); 2577 exprStack.push(init); 2578 loopLambda.min_args++; 2579 return decl; 2580 } 2581 2582 /** Done handling loop variables, and pushes them into the lexical scope. 2583 * Ready to parse the loop condition. 2584 */ loopEnter()2585 public void loopEnter() { 2586 checkLoop(); 2587 LambdaExp loopLambda = (LambdaExp) current_scope; 2588 int ninits = loopLambda.min_args; 2589 loopLambda.max_args = ninits; 2590 Expression[] inits = new Expression[ninits]; 2591 for (int i = ninits; --i >= 0; ) 2592 inits[i] = (Expression) exprStack.pop(); 2593 LetExp let = (LetExp) loopLambda.getOuter(); 2594 Declaration fdecl = let.firstDecl(); // The decls for loopLambda. 2595 let.setBody(new ApplyExp(new ReferenceExp(fdecl), inits)); 2596 lexical.push(loopLambda); 2597 } 2598 2599 @Deprecated loopCond(Expression cond)2600 public void loopCond(Expression cond) { 2601 checkLoop(); 2602 exprStack.push(cond); 2603 } 2604 2605 @Deprecated loopBody(Expression body)2606 public void loopBody(Expression body) { 2607 LambdaExp loopLambda = (LambdaExp) current_scope; 2608 loopLambda.body = body; 2609 } 2610 2611 /** Recurse to next iteration of specified loop. */ loopRepeat(LambdaExp loop, Expression... exps)2612 public Expression loopRepeat(LambdaExp loop, Expression... exps) { 2613 ScopeExp let = loop.getOuter(); 2614 Declaration fdecl = let.firstDecl(); // The decls for loopLambda. 2615 return new ApplyExp(new ReferenceExp(fdecl), exps); 2616 } 2617 2618 /** Finish building a loop and return resulting expression. */ loopDone(Expression body)2619 public Expression loopDone(Expression body) { 2620 LambdaExp loopLambda = (LambdaExp) current_scope; 2621 ScopeExp let = loopLambda.getOuter(); 2622 loopLambda.body = body; 2623 lexical.pop(loopLambda); 2624 current_scope = let.getOuter(); 2625 return let; 2626 } 2627 2628 /** Combine loopRepeat and loopDone. 2629 * Assume loopCond and loopBody have been called. 2630 */ loopRepeatDone(Expression... exps)2631 public Expression loopRepeatDone(Expression... exps) { 2632 LambdaExp loopLambda = (LambdaExp) current_scope; 2633 ScopeExp let = loopLambda.getOuter(); 2634 Expression cond = (Expression) exprStack.pop(); 2635 Expression recurse = loopRepeat(loopLambda, exps); 2636 loopLambda.body = new IfExp(cond, 2637 new BeginExp(loopLambda.body, recurse), 2638 QuoteExp.voidExp); 2639 lexical.pop(loopLambda); 2640 current_scope = let.getOuter(); 2641 return let; 2642 } 2643 2644 /** A language-dependent "apply" function for generic application. 2645 */ applyFunction(Expression func)2646 public Expression applyFunction(Expression func) { 2647 return null; 2648 } 2649 makeQuoteExp(Object value)2650 public QuoteExp makeQuoteExp (Object value) 2651 { 2652 return QuoteExp.getInstance(value, this); 2653 } 2654 2655 /** 2656 * Convenience method to make an Expression that coerces a value. 2657 * @param value to be coerced 2658 * @param type to coerce value to 2659 * @return expression that coerces value to type 2660 */ makeCoercion(Expression value, Expression type)2661 public static ApplyExp makeCoercion(Expression value, Expression type) 2662 { 2663 Expression[] exps = new Expression[2]; 2664 exps[0] = type; 2665 exps[1] = value; 2666 QuoteExp c = new QuoteExp(Convert.cast); 2667 return new ApplyExp(c, exps); 2668 } 2669 2670 /** 2671 * Convenience method to make an Expression that coerces a value. 2672 * @param value to be coerced 2673 * @param type to coerce value to 2674 * @return expression that coerces value to type 2675 */ makeCoercion(Expression value, Type type)2676 public static ApplyExp makeCoercion(Expression value, Type type) { 2677 return makeCoercion(value, new QuoteExp(type)); 2678 } 2679 2680 /** If non-null, a helper method generated by getForNameHelper. */ 2681 Method forNameHelper; 2682 2683 /** Generate code to load a named Class without initializing it. 2684 */ loadClassRef(ObjectType clas)2685 public void loadClassRef (ObjectType clas) 2686 { 2687 CodeAttr code = getCode(); 2688 // Try an optimization 2689 if (curClass.getClassfileVersion() >= ClassType.JDK_1_5_VERSION) 2690 code.emitPushClass(clas); 2691 else if (clas == mainClass && mainLambda.isStatic() 2692 // moduleInstanceMainField may not have been set yet. 2693 && moduleInstanceMainField != null) 2694 { 2695 code.emitGetStatic(moduleInstanceMainField); 2696 code.emitInvokeVirtual(Type.objectType.getDeclaredMethod("getClass", 0)); 2697 } 2698 else 2699 { 2700 String name = clas instanceof ClassType ? clas.getName() 2701 : clas.getInternalName().replace('/', '.'); 2702 code.emitPushString(name); 2703 code.emitInvokeStatic(getForNameHelper()); 2704 } 2705 } 2706 2707 /** Generate a method to find a named Class without initializing it. 2708 * Generate a static helper method "class$" like javac generates for 2709 * 'CLASS.class', but does not initialize CLASS. Also, we don't bother 2710 * catching exceptions, since the JVM doesn't require us to. I.e. generates: 2711 * public static class $(String name) 2712 * { return Class.forName(name, false, 2713 * Class.forName(THISCLASSNAME).getClassLoader()); } 2714 * Note that we want the result to use the same ClassLoader as the caller, 2715 * which is why we generate a static helper method. 2716 */ getForNameHelper()2717 public Method getForNameHelper () 2718 { 2719 if (forNameHelper == null) 2720 { 2721 /* #ifdef JAVA2 */ 2722 Method save_method = method; 2723 method = curClass.addMethod("class$", Access.PUBLIC|Access.STATIC, 2724 string1Arg, typeClass); 2725 forNameHelper = method; 2726 CodeAttr code = method.startCode(); 2727 code.emitLoad(code.getArg(0)); 2728 code.emitPushInt(0); 2729 code.emitPushString(mainClass.getName()); 2730 code.emitInvokeStatic(typeClass.getDeclaredMethod("forName", 1)); 2731 code.emitInvokeVirtual(typeClass.getDeclaredMethod("getClassLoader", 0)); 2732 code.emitInvokeStatic(typeClass.getDeclaredMethod("forName", 3)); 2733 code.emitReturn(); 2734 method = save_method; 2735 /* #else */ 2736 // forNameHelper = typeClass.getDeclaredMethod("forName", 1); 2737 /* #endif */ 2738 } 2739 return forNameHelper; 2740 } 2741 getGlobalEnvironment()2742 public Environment getGlobalEnvironment() { return Environment.getCurrent(); } 2743 resolve(Object name, boolean function)2744 public Object resolve(Object name, boolean function) 2745 { 2746 Environment env = getGlobalEnvironment(); 2747 Symbol symbol; 2748 if (name instanceof String) 2749 symbol = env.defaultNamespace().lookup((String) name); 2750 else 2751 symbol = (Symbol) name; 2752 if (symbol == null) 2753 return null; 2754 if (function && getLanguage().hasSeparateFunctionNamespace()) 2755 return env.getFunction(symbol, null); 2756 return env.get(symbol, null); 2757 } 2758 2759 /** A key we can pass from the compiler to identity a Compilation. */ 2760 private int keyUninitialized; 2761 /** Chain of immediate Compilation whose setupLiterals hasn't been called. */ 2762 private static Compilation chainUninitialized; 2763 /** Next in chain headed by chainUninitialized. */ 2764 private Compilation nextUninitialized; 2765 2766 /** Call-back from compiled code to initialize literals in immediate mode. 2767 * In non-immediate mode (i.e. generating class files) the compiler emits 2768 * code to "re-construct" literal values. However, in immediate mode 2769 * that would be wasteful, plus we would get values that are similar (equals) 2770 * to but not necessarily identical (eq) to the compile-time literal. 2771 * So we need to pass the literal values to the compiled code, by using 2772 * reflection to initialize various static fields. This method does that. 2773 * It is called at the start of the generated static initializer, which 2774 * helps makes things more consistent between immediate and non-immediate 2775 * mode. 2776 */ setupLiterals(int key)2777 public static void setupLiterals (int key) 2778 { 2779 Compilation comp = Compilation.findForImmediateLiterals(key); 2780 try 2781 { 2782 Class clas = comp.loader.loadClass(comp.mainClass.getName()); 2783 2784 /* Pass literal values to the compiled code. */ 2785 for (Literal init = comp.litTable.literalsChain; init != null; 2786 init = init.next) 2787 { 2788 /* DEBUGGING: 2789 OutPort out = OutPort.errDefault(); 2790 out.print("init["+init.index+"]="); 2791 out.print(init.value); 2792 out.println(); 2793 */ 2794 clas.getDeclaredField(init.field.getName()) 2795 .set(null, init.value); 2796 } 2797 comp.litTable = null; 2798 } 2799 catch (Throwable ex) 2800 { 2801 WrappedException.rethrow(ex); 2802 } 2803 } 2804 2805 public static synchronized int registerForImmediateLiterals(Compilation comp)2806 registerForImmediateLiterals (Compilation comp) 2807 { 2808 int i = 0; 2809 for (Compilation c = chainUninitialized; c != null; c = c.nextUninitialized) 2810 { 2811 if (i <= c.keyUninitialized) 2812 i = c.keyUninitialized + 1; 2813 } 2814 comp.keyUninitialized = i; 2815 comp.nextUninitialized = chainUninitialized; 2816 chainUninitialized = comp; 2817 return i; 2818 } 2819 findForImmediateLiterals(int key)2820 public static synchronized Compilation findForImmediateLiterals (int key) 2821 { 2822 Compilation prev = null; 2823 for (Compilation comp = chainUninitialized; ; ) 2824 { 2825 Compilation next = comp.nextUninitialized; 2826 if (comp.keyUninitialized == key) 2827 { 2828 if (prev == null) 2829 chainUninitialized = next; 2830 else 2831 prev.nextUninitialized = next; 2832 comp.nextUninitialized = null; 2833 return comp; 2834 } 2835 prev = comp; 2836 comp = next; 2837 } 2838 } 2839 2840 /** Current lexical scope - map name to Declaration. */ 2841 public NameLookup lexical; 2842 2843 protected ScopeExp current_scope; 2844 2845 protected SourceMessages messages; 2846 2847 private static final ThreadLocal<Compilation> current = 2848 new InheritableThreadLocal<Compilation>(); 2849 getCurrent()2850 public static Compilation getCurrent () 2851 { 2852 return current.get(); 2853 } 2854 setCurrent(Compilation comp)2855 public static void setCurrent (Compilation comp) 2856 { 2857 current.set(comp); 2858 } 2859 setSaveCurrent(Compilation comp)2860 public static Compilation setSaveCurrent (Compilation comp) 2861 { 2862 Compilation save = current.get(); 2863 current.set(comp); 2864 return save; 2865 } 2866 restoreCurrent(Compilation saved)2867 public static void restoreCurrent (Compilation saved) 2868 { 2869 current.set(saved); 2870 } 2871 toString()2872 public String toString () 2873 { 2874 return "<compilation "+mainLambda+">"; 2875 } 2876 getMinfo()2877 public ModuleInfo getMinfo() { 2878 return mainLambda.info; 2879 } 2880 } 2881