1 // Copyright (c) 2008, 2011  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.kawa.io.OutPort;
7 import gnu.mapping.*;
8 import java.util.*;
9 import java.lang.annotation.ElementType;
10 
11 public class ClassExp extends LambdaExp
12 {
13     int state;
14     static final int PARTS_PREDECLARED = 1;
15     static final int TYPES_SET = 2;
16     static final int PARTS_DECLARED = 3;
17 
18     private boolean simple;
isSimple()19     public boolean isSimple() { return simple; }
20 
isAbstract()21     public final boolean isAbstract() { return getFlag(IS_ABSTRACT); }
22     public static final int IS_ABSTRACT = LambdaExp.NEXT_AVAIL_FLAG;
23     public static final int INTERFACE_SPECIFIED = 2 * LambdaExp.NEXT_AVAIL_FLAG;
24     public static final int CLASS_SPECIFIED = 4 * LambdaExp.NEXT_AVAIL_FLAG;
25     public static final int HAS_SUBCLASS = 8 * LambdaExp.NEXT_AVAIL_FLAG;
26     /** True if the resulting class(es) are *not* member/inner classes. */
27     public static final int IS_PACKAGE_MEMBER = 16 * LambdaExp.NEXT_AVAIL_FLAG;
28 
29     /** True if there is at least one explicit "<init>" ("*init*"} method. */
30     boolean explicitInit;
31 
32     /** The class of instances of this class.
33      * Same as compiledType unless isMakingClassPair(), in which case super.type
34      * is an interface, and instanceType is a class implementing the interface.
35      * Using an interface plus a class gives us true multiple inheritance. */
36     ClassType instanceType;
37 
38     public String classNameSpecifier;
39 
40     /** True if we should make a pair of an interface and a class. */
isMakingClassPair()41     public boolean isMakingClassPair() {
42         return compiledType != instanceType;
43     }
44 
45     /** The ClassType generated for this class.
46      * Note difference from {@code getClassType}:
47      * The value of a {@code ClassExp} (viewed as an expression) is a
48      * class/type object, so {@code getType} returns the type of a type.
49      */
calculateType()50     protected Type calculateType() {
51         return simple ? Compilation.typeClass : Compilation.typeClassType;
52     }
53 
54     /** The ClassType generated for this class.
55      */
getClassType()56     public ClassType getClassType() { return compiledType; }
57 
setClassType(ClassType type)58     public void setClassType(ClassType type) {
59         this.compiledType = type;
60         this.instanceType = type;
61     }
62 
63     /** List of base classes and implemented interfaces. */
64     public Expression[] supers;
65 
66     /** Index in supers array of class we extend, or -1. */
67     public int superClassIndex = -1;
68 
69     /** An artificial method named {@code "$finit$"} for evaluating
70      * non-static initializations.
71      * All constructors need to call this. */
72     public LambdaExp initMethod;
73 
74     /** An artificial method named {@code "$clinit$"} for evaluating
75      * static initializations. */
76     public LambdaExp clinitMethod;
77 
ClassExp(boolean simple, ClassType type)78     public ClassExp (boolean simple, ClassType type) {
79         this.simple = simple;
80         setClassType(type);
81     }
82 
mustCompile()83     protected boolean mustCompile() { return true; }
84 
compile(Compilation comp, Target target)85     public void compile(Compilation comp, Target target) {
86         if (target instanceof IgnoreTarget)
87             return;
88         compilePushClass(comp, target);
89     }
90 
compilePushClass(Compilation comp, Target target)91     public void compilePushClass(Compilation comp, Target target) {
92         ClassType new_class = compiledType;
93 
94         gnu.bytecode.CodeAttr code = comp.getCode();
95         ClassType typeClass = Type.javalangClassType;
96         ClassType typeType;
97         int nargs;
98         boolean needsLink = getNeedsClosureEnv();
99         comp.loadClassRef(new_class);
100         if (isSimple() && ! needsLink) {
101             typeType = typeClass;
102         } else {
103             if (isMakingClassPair() || needsLink) {
104                 if (new_class == instanceType)
105                     code.emitDup(instanceType);
106                 else
107                     comp.loadClassRef(instanceType);
108                 typeType = ClassType.make("gnu.expr.PairClassType");
109                 nargs = needsLink ? 3 : 2;
110             } else {
111                 typeType = Compilation.typeType;
112                 nargs = 1;
113             }
114             Type[] argsClass = new Type[nargs];
115             if (needsLink) {
116                 getOwningLambda().loadHeapFrame(comp);
117                 argsClass[--nargs] = Type.objectType;
118             }
119             while (--nargs >= 0) argsClass[nargs] = typeClass;
120             Method makeMethod
121                 = typeType.addMethod("make", argsClass,
122                                      typeType, Access.STATIC|Access.PUBLIC);
123             code.emitInvokeStatic(makeMethod);
124         }
125         target.compileFromStack(comp, typeType);
126     }
127 
getCompiledClassType(Compilation comp)128     protected ClassType getCompiledClassType(Compilation comp) {
129         return compiledType;
130     }
131 
132     /** Create a Field in the instanceClass for each declared field.
133      * This allows name SlotGet.lookupMember (used in Translator.rewrite
134      * when looking for a binding for a symbol) to return a match.
135      * Note that if we later (in setTypes) create a pair-type, then the
136      * field will be replaced by a getter/setter pair.  Later yet
137      * (in declareParts): We set the types of the Field or getter/setter pair.
138      * We should create tentative Methods for methods in the same manner,
139      * but that is is a project for another day.
140      */
createFields(Compilation comp)141     public void createFields(Compilation comp) {
142         if (state >= PARTS_PREDECLARED)
143             return;
144         state = PARTS_PREDECLARED;
145         Hashtable<String,Declaration> seenFields
146             = new Hashtable<String,Declaration>();
147         for (Declaration decl = firstDecl();
148              decl != null;  decl = decl.nextDecl()) {
149             // If the declaration derives from a method, don't create field.
150             if (decl.getCanRead()) {
151                 int flags = decl.getAccessFlags(Access.PUBLIC);
152                 if (decl.getFlag(Declaration.STATIC_SPECIFIED))
153                     flags |= Access.STATIC;
154                 String fname = Mangling.mangleField(decl.getName());
155                 decl.setField(instanceType.addField(fname, null, flags));
156                 Declaration old = seenFields.get(fname);
157                 if (old != null)
158                     duplicateDeclarationError(old, decl, comp);
159                 seenFields.put(fname, decl);
160             }
161         }
162     }
163 
setTypes(Compilation comp)164     public void setTypes(Compilation comp) {
165         if (state >= TYPES_SET)
166             return;
167         createFields(comp);
168         state = TYPES_SET;
169         int nsupers = supers == null ? 0 : supers.length;
170         ClassType[] superTypes = new ClassType[nsupers];
171         ClassType superType = null;
172         int j = 0;
173         for (int i = 0;  i < nsupers;  i++) {
174             Type st = Language.getDefaultLanguage().getTypeFor(supers[i]);
175             ClassType t;
176             if (st instanceof ClassType)
177                 t = (ClassType) st;
178             else if (st instanceof ParameterizedType)
179                 t = ((ParameterizedType) st).getRawType();
180             else {
181                 comp.setLine(supers[i]);
182                 comp.error('e', "invalid super type");
183                 continue;
184             }
185             int modifiers;
186             try {
187                 modifiers = t.getModifiers();
188             } catch (RuntimeException ex) {
189                 modifiers = 0;
190                 if (comp != null)
191                     comp.error('e', "unknown super-type "+t.getName());
192             }
193             if ((modifiers & Access.INTERFACE) == 0) {
194                 if (j < i)
195                     comp.error('e', "duplicate superclass for "+this);
196                 superType = t;
197                 superClassIndex = i;
198             } else
199                 superTypes[j++] = t;
200         }
201         if (superType != null && (flags & INTERFACE_SPECIFIED) != 0)
202             comp.error('e', "cannot be interface since has superclass");
203         if (! simple && superType == null && (flags & CLASS_SPECIFIED) == 0
204                 // If the class is module-local and has no sub-classes, we can
205                 // optimize away the need for a pair-class.
206                 && (getFlag(HAS_SUBCLASS)
207                     || (nameDecl != null && nameDecl.isPublic()))) {
208             PairClassType ptype = new PairClassType();//(PairClassType) type;
209             compiledType = ptype;
210             ptype.setInterface(true);
211             ptype.instanceType = instanceType;
212             ClassType[] interfaces = { compiledType };
213             // Can do better.  FIXME.
214             instanceType.setSuper(Type.pointer_type);
215             instanceType.setInterfaces(interfaces);
216         }
217         else if (getFlag(INTERFACE_SPECIFIED))
218             instanceType.setInterface(true);
219         compiledType.setSuper(superType == null ? Type.pointer_type : superType);
220 
221         ClassType[] interfaces;
222         if (j == nsupers)
223             interfaces = superTypes;
224         else {
225             interfaces = new ClassType[j];
226             System.arraycopy(superTypes, 0, interfaces, 0, j);
227         }
228         compiledType.setInterfaces(interfaces);
229 
230         if (compiledType.getName() == null)
231             compiledType.setName(getClassName(comp));
232 
233         // Don't add class twice - i.e. if ModuleExp.USE_DEFINED_CLASS is set.
234         if (compiledType != comp.mainClass)
235             comp.addClass(compiledType);
236         if (isMakingClassPair()) {
237             instanceType.setName(compiledType.getName()+"$class");
238             comp.addClass(instanceType);
239 
240             // Convert fields to getter/setter pairs.
241             // Note we don't know their types yet.
242             Field prev = null;
243             for (Declaration decl = firstDecl();
244                  decl != null;  decl = decl.nextDecl()) {
245                 Field fld = decl.getField();
246                 if (decl.getCanRead()) {
247                     int fflags = decl.getField().getFlags() | Access.ABSTRACT;
248                     String gname = slotToMethodName("get", decl.getName());
249                     decl.getterMethod =
250                         compiledType.addMethod(gname,
251                                                fflags, Type.typeArray0, null);
252                     decl.maybeSourceName(decl.getterMethod, null);
253                     String sname = slotToMethodName("set", decl.getName());
254                     Type[] stypes = { null };
255                     decl.setterMethod =
256                         compiledType.addMethod(sname, fflags, stypes,
257                                                Type.voidType);
258                     instanceType.removeField(fld, prev);
259                     decl.setField(null);
260                 }
261                 prev = fld;
262             }
263         }
264     }
265 
getClassName(Compilation comp)266     public String getClassName(Compilation comp) {
267         String name;
268         if (classNameSpecifier != null)
269             name = classNameSpecifier;
270         else {
271             name = getName();
272             if (name != null) {
273                 int nlen = name.length();
274                 if (nlen > 2
275                     && name.charAt(0) == '<' && name.charAt(nlen-1) == '>')
276                     name = name.substring(1, nlen-1);
277             }
278         }
279         if (name == null) {
280             StringBuffer nbuf = new StringBuffer(100);
281             comp.getModule().classFor(comp);
282             nbuf.append(comp.mainClass.getName());
283             nbuf.append('$');
284             int len = nbuf.length();
285             for (int i = 0;  ; i++) {
286                 nbuf.append(i);
287                 name = nbuf.toString();
288                 if (comp.findNamedClass(name) == null)
289                     break;
290                 nbuf.setLength(len);
291             }
292         }
293         else if (! isSimple() || this instanceof ObjectExp)
294             name = comp.generateClassName(name);
295         else {
296             int start = 0;
297             StringBuilder nbuf = new StringBuilder(100);
298             // Mangle characters in name, if needed - but don't mangle '.'.
299             for (;;) {
300                 int dot = name.indexOf('.', start);
301                 if (dot < 0)
302                     break;
303                 nbuf.append(Mangling
304                             .mangleClassName(name.substring(start, dot)));
305                 start = dot + 1;
306                 if (start < name.length())
307                     nbuf.append('.');
308             }
309             // nbuf contains package prefix (mangled if needed, except for '.')
310             // start is the rest of name (after the package prefix)
311             if (start == 0) { // No '.' in name
312                 setFlag(IS_PACKAGE_MEMBER);
313                 String mainName = comp.mainClass == null ? null
314                     : comp.mainClass.getName();
315                 int dot = mainName == null ? -1 : mainName.lastIndexOf('.');
316                 if (dot > 0)
317                     nbuf.append(mainName.substring(0, dot + 1));
318                 else if (comp.classPrefix != null)
319                     nbuf.append(comp.classPrefix);
320             }
321             else if (start == 1 && start < name.length()) {
322                 // name has a single initial dot. Treat as member class.
323                 nbuf.setLength(0);
324                 nbuf.append(comp.mainClass.getName());
325                 nbuf.append('$');
326             }
327             else
328                 setFlag(IS_PACKAGE_MEMBER);
329             if (start < name.length())
330                 nbuf.append(Mangling
331                             .mangleClassName(name.substring(start)));
332             name = nbuf.toString();
333         }
334         return name;
335     }
336 
declareParts(Compilation comp)337     public void declareParts(Compilation comp) {
338         if (state >= PARTS_DECLARED)
339             return;
340         setTypes(comp);
341         state = PARTS_DECLARED;
342         for (Declaration decl = firstDecl();
343              decl != null;  decl = decl.nextDecl()) {
344             if (decl.getCanRead()) {
345                 if (isMakingClassPair()) {
346                     Type ftype = decl.getType().getImplementationType();
347                     decl.getterMethod.setReturnType(ftype);
348                     decl.setterMethod.getParameterTypes()[0] = ftype;
349                 } else {
350                     decl.setSimple(false);
351                     decl.getField().setType(decl.getType());
352                 }
353             }
354         }
355 
356         for (LambdaExp child = firstChild;  child != null;
357              child = child.nextSibling) {
358             if (child.isAbstract())
359                 setFlag(IS_ABSTRACT);
360             if ("*init*".equals(child.getName())) {
361                 explicitInit = true;
362                 if (child.isAbstract())
363                     comp.error('e', "*init* method cannot be abstract", child);
364                 if (compiledType instanceof PairClassType)
365                     comp.error('e',
366                         "'*init*' methods only supported for simple classes");
367             }
368             // Setting child.outer isn't normally needed.  The exception is
369             // if we're called from object.rewriteClassDef and there is some
370             // funny macro expansion going on, in which case outer
371             // might be a TemplateScope.
372             child.setOuter(this);
373             if ((child != initMethod && child != clinitMethod
374                  && child.nameDecl != null // only if error
375                  && ! child.nameDecl.getFlag(Declaration.STATIC_SPECIFIED))
376                 || ! isMakingClassPair())
377                 child.addMethodFor(compiledType, comp, null);
378             if (isMakingClassPair())
379                 child.addMethodFor(instanceType, comp, compiledType);
380         }
381         if (! explicitInit && ! instanceType.isInterface())
382             Compilation.getConstructor(instanceType, this);
383         int instanceModifiers = instanceType.getModifiers();
384         if (isAbstract()) {
385             instanceModifiers |= Access.ABSTRACT;
386             instanceType.setModifiers(instanceModifiers);
387         }
388         if (nameDecl != null)
389             instanceType.setModifiers((instanceModifiers & ~Access.PUBLIC)
390                                       | nameDecl.getAccessFlags(Access.PUBLIC));
391     }
392 
393     /** Return implementation method matching name and param types.
394      * Used when compiling a pair class and generating a concrete method
395      * implementing an interface method, to find static implementation method
396      * in this or super implementation class we need to call.
397      * @param interfaceType search the implementation classes corresponding
398      *   to this interface type and its super-interfaces.
399      * @param mname method name to look for.
400      * @param paramTypes method types to look for.
401      * @param vec where to place found methods
402      * If a method is found, don't search super-interfaces, as the found method
403      * is more specific and overrides any that might in super-interfaces.
404      */
getImplMethods(ClassType interfaceType, String mname, Type[] paramTypes, ArrayList<Method> vec)405     static void getImplMethods(ClassType interfaceType,
406                                String mname, Type[] paramTypes,
407                                ArrayList<Method> vec) {
408         getImplMethods(interfaceType, mname, paramTypes, vec, null);
409     }
410 
getImplMethods(ClassType interfaceType, String mname, Type[] paramTypes, ArrayList<Method> vec, Type[] itypes)411     private static void getImplMethods(ClassType interfaceType,
412                                        String mname, Type[] paramTypes,
413                                        ArrayList<Method> vec, Type[] itypes) {
414         ClassType implType;
415         if (interfaceType instanceof PairClassType)
416             implType = ((PairClassType) interfaceType).instanceType;
417         else if (! interfaceType.isInterface())
418             return;
419         else {
420             try {
421                 Class reflectClass = interfaceType.getReflectClass();
422                 if (reflectClass == null)
423                     return;
424                 String implTypeName = interfaceType.getName() + "$class";
425                 ClassLoader loader = reflectClass.getClassLoader();
426                 /* #ifdef JAVA2 */
427                 Class implClass = Class.forName(implTypeName, false, loader);
428                 /* #else */
429                 // Class implClass = Class.forName(implTypeName);
430                 /* #endif */
431                 implType = (ClassType) Type.make(implClass);
432             } catch (Exception ex) {
433                 return;
434             }
435         }
436         if (itypes == null) {
437             itypes = new Type[paramTypes.length + 1];
438             System.arraycopy (paramTypes, 0, itypes, 1, paramTypes.length);
439         }
440         itypes[0] = interfaceType;
441         Method implMethod = implType.getDeclaredMethod(mname, itypes);
442         if (implMethod != null) {
443             int count = vec.size();
444             if (count == 0 || ! vec.get(count-1).equals(implMethod))
445                 vec.add(implMethod);
446         } else {
447             ClassType[] superInterfaces = interfaceType.getInterfaces();
448             for (int i = 0;  i < superInterfaces.length;  i++)
449                 getImplMethods(superInterfaces[i], mname, paramTypes, vec,
450                                itypes);
451         }
452     }
453 
454     /** Call comp.usedClass on the first arguments's supertypes. */
usedSuperClasses(ClassType clas, Compilation comp)455     private static void usedSuperClasses(ClassType clas, Compilation comp) {
456         comp.usedClass(clas.getSuperclass());
457         ClassType[] interfaces = clas.getInterfaces();
458         if (interfaces != null) {
459             for (int i = interfaces.length;  --i >= 0; )
460                 comp.usedClass(interfaces[i]);
461         }
462     }
463 
compileMembers(Compilation comp)464     public ClassType compileMembers (Compilation comp) {
465         ClassType saveClass = comp.curClass;
466         Method saveMethod = comp.method;
467         try {
468             ClassType new_class = getCompiledClassType(comp);
469             comp.curClass = new_class;
470 
471             LambdaExp outer = outerLambda();
472             Member enclosing = null;
473             if (outer instanceof ClassExp)
474                 enclosing = outer.compiledType;
475             else if (outer != null && ! (outer instanceof ModuleExp))
476                 enclosing = saveMethod;
477             else if (outer instanceof ModuleExp && ! getFlag(IS_PACKAGE_MEMBER))
478                 enclosing = outer.compiledType;
479             if (enclosing != null) {
480                 new_class.setEnclosingMember(enclosing);
481                 if (enclosing instanceof ClassType)
482                     ((ClassType) enclosing).addMemberClass(new_class);
483             }
484             if (instanceType != new_class) {
485                 instanceType.setEnclosingMember(compiledType);
486                 compiledType.addMemberClass(instanceType);
487             }
488 
489             usedSuperClasses(compiledType, comp);
490             if (compiledType != instanceType)
491                 usedSuperClasses(instanceType, comp);
492 
493             String filename = getFileName();
494             if (filename != null)
495                 new_class.setSourceFile (filename);
496 
497             LambdaExp saveLambda = comp.curLambda;
498             comp.curLambda = this;
499 
500             allocFrame(comp);
501             CodeAttr code;
502 
503             if (nameDecl != null)
504                 nameDecl.compileAnnotations(compiledType, ElementType.TYPE);
505             for (Declaration decl = firstDecl(); decl != null;
506                  decl = decl.nextDecl()) {
507                 decl.compileAnnotations(decl.getField(), ElementType.FIELD);
508             }
509 
510             for (LambdaExp child = firstChild;  child != null;
511                  child = child.nextSibling) {
512                 if (child.isAbstract() || child.isNative())
513                     continue;
514                 if (child == clinitMethod && compiledType == comp.mainClass)
515                     continue;
516                 Method save_method = comp.method;
517                 LambdaExp save_lambda = comp.curLambda;
518                 String saveFilename = comp.getFileName();
519                 int saveLine = comp.getLineNumber();
520                 int saveColumn = comp.getColumnNumber();
521                 comp.setLine(child);
522                 comp.method = child.getMainMethod();
523                 //comp.curClass = comp.method.getDeclaringClass();
524                 Declaration childDecl = child.nameDecl;
525                 if (childDecl != null)
526                     childDecl.compileAnnotations(comp.method, ElementType.METHOD);
527                 comp.curClass = instanceType;
528                 comp.curLambda = child;
529                 comp.method.initCode();
530                 child.allocChildClasses(comp);
531                 child.allocParameters(comp);
532                 if ("*init*".equals(child.getName())) {
533                     code = comp.getCode();
534                     if (staticLinkField != null) {
535                         code.emitPushThis();
536                         Variable var = code.getArg(1);
537                         var.allocateLocal(code);
538                         code.emitLoad(var);
539                         code.emitPutField(staticLinkField);
540                     }
541 
542                     // Extract "first" expression to see if it is special.
543                     Expression bodyFirst = child.getBodyFirstExpression();
544                     // See if bodyFirst is a this(...) or super(...) call.
545                     ClassType calledInit = checkForInitCall(bodyFirst);
546                     ClassType superClass = instanceType.getSuperclass();
547                     if (calledInit == null && superClass != null) {
548                         // Call default super constructor if there isn't
549                         // an explicit call to a super constructor.
550                         invokeDefaultSuperConstructor(superClass, comp, this);
551                         compileCallInitMethods(comp);
552                     }
553                     else if (calledInit != instanceType)
554                         ((ApplyExp) bodyFirst).setFlag(ApplyExp.IS_SUPER_INIT);
555                 }
556                 child.enterFunction(comp);
557                 child.compileBody(comp);
558 
559                 // Check to see if child overrides a superclass method and has
560                 // a covariant return type. If so, generate a bridge method
561                 // matching the superclass method's return type.
562 
563                 Method thisMethod = comp.method;
564                 Type[] ptypes = thisMethod.getParameterTypes();
565                 Type rtype = thisMethod.getReturnType();
566                 ClassType superClass = instanceType.getSuperclass();
567                 Method superMethod = superClass.getMethod(child.getName(),
568                                                           ptypes);
569                 if (superMethod != null &&
570                     superMethod.getReturnType().compare(rtype) == 1) {
571                     generateBridgeMethod(comp, thisMethod, ptypes,
572                                          superMethod.getReturnType());
573                 }
574 
575                 comp.method = save_method;
576                 comp.curClass = new_class;
577                 comp.curLambda = save_lambda;
578                 comp.setLine(saveFilename, saveLine, saveColumn);
579             }
580             if (! explicitInit && ! instanceType.isInterface())
581                 comp.generateConstructor(instanceType, this);
582             else if (initChain != null)
583                 initChain.reportError("unimplemented: explicit constructor cannot initialize ", comp);
584 
585             Method[] methods;
586             int nmethods;
587             if (isAbstract()) {
588                 methods = null;
589                 nmethods = 0;
590             } else {
591                 methods = compiledType.getAbstractMethods();
592                 nmethods = methods.length;
593             }
594             for (int i = 0;  i < nmethods;  i++) {
595                 Method meth = methods[i];
596                 String mname = meth.getName();
597                 Type[] ptypes = meth.getParameterTypes();
598                 Type rtype = meth.getReturnType();
599 
600                 Method mimpl = instanceType.getMethod(mname, ptypes);
601                 if (mimpl != null && ! mimpl.isAbstract())
602                     continue;
603 
604                 char ch;
605                 ArrayList<Method> vec = new ArrayList<Method>();
606                 getImplMethods(compiledType, mname, ptypes, vec);
607                 if (vec.size() == 0
608                     && mname.length() > 3
609                     && mname.charAt(2) == 't'
610                     && mname.charAt(1) == 'e'
611                     && ((ch = mname.charAt(0)) == 'g' || ch == 's')) {
612                     // a "set" or "get" method is treated as a slot accessor.
613                     Type ftype;
614                     if (ch == 's' && rtype.isVoid() && ptypes.length == 1)
615                         ftype = ptypes[0];
616                     else if (ch == 'g' && ptypes.length == 0)
617                         ftype = rtype;
618                     else
619                         continue;
620                     String fname = Character.toLowerCase(mname.charAt(3))
621                         + mname.substring(4);
622                     Field fld = instanceType.getField(fname);
623                     if (fld == null)
624                         fld = instanceType.addField(fname, ftype,
625                                                     Access.PUBLIC);
626                     Method impl = instanceType.addMethod(mname, Access.PUBLIC,
627                                                          ptypes, rtype);
628                     code = impl.startCode();
629                     code.emitPushThis();
630                     if (ch == 'g') {
631                         code.emitGetField(fld);
632                     } else {
633                         code.emitLoad(code.getArg(1));
634                         code.emitPutField(fld);
635                     }
636                     code.emitReturn();
637                 } else {
638                     if (vec.size() != 1) {
639                         Method impl = vec.size() != 0 ? null :
640                             findMethodForBridge(mname, ptypes, rtype);
641 
642                         if (impl != null) {
643                             generateBridgeMethod(comp, impl, ptypes, rtype);
644                         } else {
645                             // FIXME - need better error message!
646                             String msg = vec.size() == 0
647                                 ? "missing implementation for "
648                                 : "ambiguous implementation for ";
649                             comp.error('e', msg+meth);
650                         }
651                     } else {
652                         Method impl
653                             = instanceType.addMethod(mname, Access.PUBLIC,
654                                                      ptypes, rtype);
655                         code = impl.startCode();
656                         for (Variable var = code.getCurrentScope().firstVar();
657                              var != null;  var = var.nextVar())
658                             code.emitLoad(var);
659                         code.emitInvokeStatic(vec.get(0));
660                         code.emitReturn();
661                     }
662                 }
663             }
664 
665             generateApplyMethods(comp);
666             comp.curLambda = saveLambda;
667 
668             return new_class;
669         } finally {
670             comp.curClass = saveClass;
671             comp.method = saveMethod;
672         }
673     }
compileCallInitMethods(Compilation comp)674     void compileCallInitMethods(Compilation comp) {
675         comp.callInitMethods(getCompiledClassType(comp),
676                              new ArrayList<ClassType>(10));
677     }
678 
679     /**
680      * Finds a like-named method suitable for bridging the given
681      * arg/return types (i.e. a method whose arg types and return types
682      * are subclasses of those given here.
683      */
findMethodForBridge(String mname, Type[] ptypes, Type rtype)684     protected Method findMethodForBridge(String mname, Type[] ptypes,
685                                          Type rtype) {
686         Method result = null;
687         for (Method method = compiledType.getDeclaredMethods();
688              method != null; method = method.getNext()) {
689             if (mname.equals(method.getName()) &&
690                 method.getReturnType().isSubtype(rtype) &&
691                 Type.isMoreSpecific(method.getParameterTypes(), ptypes))
692                 result = method;
693         }
694         return result;
695     }
696 
697     /**
698      * Given an existing method and a desired bridge method signature,
699      * generates an appropriate bridge method.
700      */
generateBridgeMethod(Compilation comp, Method src_method, Type[] bridge_arg_types, Type bridge_return_type)701     public final void generateBridgeMethod(Compilation comp,
702                                            Method src_method,
703                                            Type[] bridge_arg_types,
704                                            Type bridge_return_type) {
705         ClassType save_class = comp.curClass;
706         Method save_method = comp.method;
707         try {
708             comp.curClass = getCompiledClassType(comp);
709             comp.method = comp.curClass.addMethod
710                 (src_method.getName(),
711                  Access.PUBLIC|Access.BRIDGE|Access.SYNTHETIC,
712                  bridge_arg_types, bridge_return_type);
713             Type[] src_arg_types = src_method.getParameterTypes();
714 
715             CodeAttr code = comp.method.startCode();
716             code.emitLoad(code.getArg(0));
717             for (int i = 0; i < src_arg_types.length; ++i) {
718                 code.emitLoad(code.getArg(i+1));
719                 code.emitCheckcast(src_arg_types[i]);
720             }
721             code.emitInvokeVirtual(src_method);
722             code.emitReturn();
723         } finally {
724             comp.method = save_method;
725             comp.curClass = save_class;
726         }
727     }
728 
visit(ExpVisitor<R,D> visitor, D d)729     protected <R,D> R visit (ExpVisitor<R,D> visitor, D d) {
730         Compilation comp = visitor.getCompilation();
731         if (comp == null)
732             return visitor.visitClassExp(this, d);
733         ClassType saveClass = comp.curClass;
734         try {
735             comp.curClass = compiledType;
736             return visitor.visitClassExp(this, d);
737         } finally {
738             comp.curClass = saveClass;
739         }
740     }
741 
visitChildren(ExpVisitor<R,D> visitor, D d)742     protected <R,D> void visitChildren (ExpVisitor<R,D> visitor, D d) {
743         LambdaExp save = visitor.currentLambda;
744         visitor.currentLambda = this;
745         if (supers != null)
746             supers = visitor.visitExps(supers, supers.length, d);
747         try {
748             for (LambdaExp child = firstChild;
749                  child != null && visitor.exitValue == null;
750                  child = child.nextSibling) {
751                 if (instanceType != null) {
752                     Declaration firstParam = child.firstDecl();
753                     if (firstParam != null && firstParam.isThisParameter())
754                         firstParam.setType(compiledType);
755                 }
756                 visitor.visitLambdaExp(child, d);
757             }
758         } finally {
759             visitor.currentLambda = save;
760         }
761     }
762 
loadSuperStaticLink(Expression superExp, ClassType superClass, Compilation comp)763     static void loadSuperStaticLink (Expression superExp, ClassType superClass,
764                                      Compilation comp) {
765         CodeAttr code = comp.getCode();
766         // This can be optimized in most cases. FIXME.
767         superExp.compile(comp, Target.pushValue(Compilation.typeClassType));
768         code.emitInvokeStatic(ClassType.make("gnu.expr.PairClassType").getDeclaredMethod("extractStaticLink", 1));
769         code.emitCheckcast(superClass.getOuterLinkType());
770     }
771 
checkDefaultSuperConstructor(ClassType superClass, Compilation comp)772     void checkDefaultSuperConstructor(ClassType superClass, Compilation comp) {
773         if (superClass.getDeclaredMethod("<init>", 0) == null)
774             comp.error('e', ("super class "+superClass.getName()
775                              +" does not have a default constructor"));
776     }
777 
invokeDefaultSuperConstructor(ClassType superClass, Compilation comp, LambdaExp lexp)778     static void invokeDefaultSuperConstructor(ClassType superClass,
779                                               Compilation comp,
780                                               LambdaExp lexp) {
781         CodeAttr code = comp.getCode();
782         Method superConstructor
783             = superClass.getDeclaredMethod("<init>", 0);
784         // InlineCalls catches the missing superConstructor case, using
785         // checkDefaultSuperConstructor.
786         assert superConstructor != null;
787         code.emitPushThis();
788         if (superClass.hasOuterLink() && lexp instanceof ClassExp) {
789             ClassExp clExp = (ClassExp) lexp;
790             Expression superExp = clExp.supers[clExp.superClassIndex];
791             loadSuperStaticLink(superExp, superClass, comp);
792         }
793         code.emitInvokeSpecial(superConstructor);
794     }
795 
print(OutPort out)796     public void print(OutPort out) {
797         out.startLogicalBlock("("+getExpClassName()+"/", ")", 2);
798         Object name = getSymbol();
799         if (name != null) {
800             out.print(name);
801             out.print('/');
802         }
803         out.print(id);
804         out.print("/fl:");  out.print(Integer.toHexString(flags));
805         if (supers.length > 0) {
806             out.writeSpaceFill();
807             out.startLogicalBlock("supers:", "", 2);
808             for (int i = 0;  i < supers.length;  i++) {
809                 supers[i].print(out);
810                 out.writeSpaceFill();
811             }
812             out.endLogicalBlock("");
813         }
814         Special prevMode = null;
815         int key_args = keywords == null ? 0 : keywords.length;
816         //int opt_args = defaultArgs == null ? 0 : defaultArgs.length - key_args;
817         for (Declaration decl = firstDecl();  decl != null;
818              decl = decl.nextDecl()) {
819             out.writeSpaceFill();
820             decl.printInfo(out);
821         }
822         for (LambdaExp child = firstChild;  child != null;
823              child = child.nextSibling) {
824             out.writeBreakLinear();
825             child.print(out);
826         }
827         if (body != null) {
828             out.writeBreakLinear();
829             body.print (out);
830         }
831         out.endLogicalBlock(")");
832     }
833 
compileSetField(Compilation comp)834     public Field compileSetField (Compilation comp) {
835         Field field = allocFieldFor(comp);
836         if (! getNeedsClosureEnv() && field.getStaticFlag()
837             && ! comp.immediate && type != Type.javalangClassType) {
838             new Literal(compiledType, type, comp.litTable)
839                 .assign(field, comp.litTable);
840         } else
841             new ClassInitializer(this, field, comp);
842         return field;
843     }
844 
845     /** Mangle a "slot" name to a get- or set- method name.
846      * @param prefix either "get" or "set" or "add"
847      * @param sname a "slot" (property) name.  This is mangled if needed.
848      */
slotToMethodName(String prefix, String sname)849     public static String slotToMethodName(String prefix, String sname) {
850         if (! Language.isValidJavaName(sname))
851             sname = Mangling.mangleName(sname, false);
852         int slen = sname.length();
853         StringBuffer sbuf = new StringBuffer(slen+3);
854         sbuf.append(prefix);
855         if (slen > 0) {
856             sbuf.append(Character.toTitleCase(sname.charAt(0)));
857             sbuf.append(sname.substring(1));
858         }
859         return sbuf.toString();
860     }
861 
addMethod(LambdaExp lexp, Object mname)862     public Declaration addMethod (LambdaExp lexp, Object mname) {
863         Declaration mdecl = addDeclaration(mname, Compilation.typeProcedure);
864         lexp.setOuter(this);
865         lexp.setClassMethod(true);
866         mdecl.noteValue(lexp);
867         mdecl.setFlag(Declaration.FIELD_OR_METHOD);
868         mdecl.setProcedureDecl(true);
869         lexp.setSymbol(mname);
870         return mdecl;
871     }
872 }
873