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