KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > expr > ClassExp


1 package gnu.expr;
2 import gnu.bytecode.*;
3 import gnu.mapping.*;
4 import java.util.Vector JavaDoc;
5
6 public class ClassExp extends LambdaExp
7 {
8   boolean simple;
9   public boolean isSimple() { return simple; }
10   public void setSimple(boolean value) { simple = value; }
11
12   /** True if there is at least one explicit "<init>" ("*init*"} method. */
13   boolean explicitInit;
14
15   /** The class of instances of this class.
16    * Same as super.type unless isMakingClassPair(), in which case super.type
17    * is an interface, and instanceType is a class implementing the interface.
18    * Using an interface plus a class gives us true multiple inheritance. */

19   ClassType instanceType;
20
21   /** True if we should make a pair of an interface and a class. */
22   public boolean isMakingClassPair()
23   {
24     return ! simple;
25   }
26
27   /** List of base classes and implemented interfaces. */
28   public Expression[] supers;
29
30   /** An artificial method named {@code "$finit$"} for evaluating
31    * non-static initializations.
32    * All constructors need to call this. */

33   public LambdaExp initMethod;
34
35   /** An artificial method named {@code "$clinit$"} for evaluating
36    * static initializations. */

37   public LambdaExp clinitMethod;
38
39   public ClassExp ()
40   {
41     type = null;
42     // Make sure we actually generate a class.
43
setCanRead(true);
44   }
45
46   public ClassExp (boolean simple)
47   {
48     this.simple = simple;
49     if (simple)
50       instanceType = type = new ClassType();
51     else
52       {
53         PairClassType ptype = new PairClassType();
54         type = ptype;
55         instanceType = new ClassType();
56         ptype.setInterface(true);
57         ptype.instanceType = instanceType;
58       }
59     setCanRead(true);
60   }
61
62   protected boolean mustCompile () { return true; }
63
64   public void compile (Compilation comp, Target target)
65   {
66     if (target instanceof IgnoreTarget)
67       return;
68     compile (comp);
69     compilePushClass(comp, target);
70   }
71
72   public void compilePushClass (Compilation comp, Target target)
73   {
74     ClassType new_class = type;
75     // Type.make(Class.forname)
76

77     gnu.bytecode.CodeAttr code = comp.getCode();
78     comp.loadClassRef(new_class);
79     ClassType typeType;
80     int nargs;
81     boolean needsLink = getNeedsClosureEnv();
82     if (isMakingClassPair() || needsLink)
83       {
84         if (new_class == instanceType)
85           code.emitDup(instanceType);
86         else
87           comp.loadClassRef(instanceType);
88     typeType = ClassType.make("gnu.expr.PairClassType");
89     nargs = needsLink ? 3 : 2;
90       }
91     else
92       {
93     typeType = ClassType.make("gnu.bytecode.Type");
94     nargs = 1;
95       }
96     Type[] argsClass = new Type[nargs];
97     if (needsLink)
98       {
99     getOwningLambda().loadHeapFrame(comp);
100     argsClass[--nargs] = Type.pointer_type;
101       }
102     ClassType typeClass = ClassType.make("java.lang.Class");
103     while (--nargs >= 0) argsClass[nargs] = typeClass;
104     Method makeMethod
105       = typeType.addMethod("make", argsClass,
106                typeType, Access.STATIC|Access.PUBLIC);
107     code.emitInvokeStatic(makeMethod);
108
109     target.compileFromStack(comp, typeType);
110   }
111
112   public String JavaDoc getJavaName ()
113   {
114     String JavaDoc name = getName();
115     return name == null ? "object" : Compilation.mangleNameIfNeeded (name);
116   }
117
118   protected ClassType getCompiledClassType(Compilation comp)
119   {
120     return type;
121   }
122
123   public void setClassName (Compilation comp)
124   {
125     if (type.getName() == null)
126       {
127     String JavaDoc name = getName();
128     if (name != null)
129       {
130         int nlen = name.length();
131         if (nlen > 2
132         && name.charAt(0) == '<' && name.charAt(nlen-1) == '>')
133           name = name.substring(1, nlen-1);
134       }
135         if (name == null)
136           {
137         StringBuffer JavaDoc nbuf = new StringBuffer JavaDoc(100);
138             comp.getModule().classFor(comp);
139             nbuf.append(comp.mainClass.getName());
140             nbuf.append('$');
141             int len = nbuf.length();
142             for (int i = 0; ; i++)
143               {
144                 nbuf.append(i);
145                 name = nbuf.toString();
146                 if (comp.findNamedClass(name) == null)
147                   break;
148                 nbuf.setLength(len);
149               }
150           }
151     else if (! isSimple() || this instanceof ObjectExp)
152       name = comp.generateClassName(name);
153     else
154       {
155         int start = 0;
156         StringBuffer JavaDoc nbuf = new StringBuffer JavaDoc(100);
157         for (;;)
158           {
159         int dot = name.indexOf('.', start);
160         if (dot < 0)
161           break;
162         nbuf.append(Compilation
163                 .mangleNameIfNeeded(name.substring(start, dot)));
164         nbuf.append('.');
165         start = dot + 1;
166           }
167         if (start == 0)
168           {
169         String JavaDoc mainName = comp.mainClass == null ? null
170           : comp.mainClass.getName();
171         int dot = mainName == null ? -1 : mainName.lastIndexOf('.');
172         if (dot > 0)
173           nbuf.append(mainName.substring(0, dot + 1));
174         else if (comp.classPrefix != null)
175           nbuf.append(comp.classPrefix);
176           }
177         if (start < name.length())
178           nbuf.append(Compilation
179               .mangleNameIfNeeded(name.substring(start)));
180         name = nbuf.toString();
181       }
182     type.setName(name);
183         comp.addClass(type);
184         if (isMakingClassPair())
185           {
186             instanceType.setName(type.getName()+"$class");
187             comp.addClass(instanceType);
188           }
189       }
190   }
191
192   public void setTypes(Compilation comp)
193   {
194     int len = supers == null ? 0 : supers.length;
195     ClassType[] superTypes = new ClassType[len];
196     ClassType superType = null;
197     int j = 0;
198     for (int i = 0; i < len; i++)
199       {
200         // setTypes may be called at name-resolution time (so we can can
201
// resolve against inherited field and method names). Therefore do
202
// inlining now. Needed (for example) for deprecated PREXIX:<> syntax.
203
supers[i] = new InlineCalls(comp).walk(supers[i]);
204
205     Type st = Language.getDefaultLanguage().getTypeFor(supers[i]);
206     if (! (st instanceof ClassType))
207           {
208             comp.setLine(supers[i]);
209             comp.error('e', "invalid super type");
210             continue;
211           }
212     ClassType t = (ClassType) st;
213     int modifiers;
214     try
215       {
216         modifiers = t.getModifiers();
217       }
218     catch (RuntimeException JavaDoc ex)
219       {
220         modifiers = 0;
221         if (comp != null)
222           comp.error('e', "unknown super-type "+t.getName());
223       }
224     if ((modifiers & Access.INTERFACE) == 0)
225       {
226         if (j < i)
227               comp.error('e', "duplicate superclass for "+this);
228         superType = t;
229       }
230     else
231       superTypes[j++] = t;
232       }
233     if (! isSimple())
234       {
235         if (superType != null)
236           comp.error('e', "non-simple class inherts from non-interface "+superType.getName());
237           
238         ClassType[] interfaces = { type };
239         // Can we better. FIXME.
240
instanceType.setSuper(Type.pointer_type);
241         instanceType.setInterfaces(interfaces);
242       }
243     type.setSuper(superType == null ? Type.pointer_type : superType);
244
245     ClassType[] interfaces;
246     if (j == len)
247       interfaces = superTypes;
248     else
249       {
250     interfaces = new ClassType[j];
251     System.arraycopy(superTypes, 0, interfaces, 0, j);
252       }
253     type.setInterfaces(interfaces);
254   }
255
256   boolean partsDeclared;
257
258   public void declareParts(Compilation comp)
259   {
260     if (partsDeclared)
261       return;
262     partsDeclared = true;
263     for (Declaration decl = firstDecl();
264      decl != null; decl = decl.nextDecl())
265       {
266     // If the declaration derives from a method, don't create field.
267
if (decl.getCanRead())
268       {
269         int flags = decl.getAccessFlags(Access.PUBLIC);
270         if (decl.getFlag(Declaration.STATIC_SPECIFIED))
271           flags |= Access.STATIC;
272         if (isMakingClassPair())
273           {
274         flags |= Access.ABSTRACT;
275         Type ftype = decl.getType().getImplementationType();
276         type.addMethod(slotToMethodName("get", decl.getName()),
277                    flags, Type.typeArray0, ftype);
278         Type[] stypes = { ftype };
279         type.addMethod(slotToMethodName("set",decl.getName()),
280                    flags, stypes, Type.void_type);
281           }
282         else
283           {
284         String JavaDoc fname
285           = Compilation.mangleNameIfNeeded(decl.getName());
286         decl.field
287           = instanceType.addField(fname, decl.getType(), flags);
288         decl.setSimple(false);
289           }
290       }
291       }
292
293     for (LambdaExp child = firstChild; child != null;
294      child = child.nextSibling)
295       {
296         if ("*init*".equals(child.getName()))
297           explicitInit = true;
298     if ((child != initMethod && child != clinitMethod)
299         || ! isMakingClassPair())
300       child.addMethodFor(type, comp, null);
301     if (isMakingClassPair())
302           // FIXME this is wrong if the method is static
303
child.addMethodFor(instanceType, comp, type);
304       }
305     if (! explicitInit)
306       Compilation.getConstructor(instanceType, this);
307   }
308
309   /** Return implementation method matching name and param types.
310    * Used when compiling a pair class and generating a concrete method
311    * implementing an interface method, to find static implementation method
312    * in this or super implementation class we need to call.
313    * @param interfaceType search the implementation classes corresponding
314    * to this interface type and its super-interfaces.
315    * @param mname method name to look for.
316    * @param paramTypes method types to look for.
317    * @param vec where to place found methods
318    * If a method is found, don't search super-interfaces, as the found method
319    * is more specific and overrides any that might in super-interfaces.
320    */

321   static void getImplMethods(ClassType interfaceType,
322                  String JavaDoc mname, Type[] paramTypes, Vector JavaDoc vec)
323   {
324     ClassType implType;
325     if (interfaceType instanceof PairClassType)
326       implType = ((PairClassType) interfaceType).instanceType;
327     else if (! interfaceType.isInterface())
328       return;
329     else
330       {
331     String JavaDoc implTypeName = interfaceType.getName() + "$class";
332     implType = ClassType.make(implTypeName);
333       }
334     Type[] itypes = new Type[paramTypes.length + 1];
335     itypes[0] = interfaceType;
336     System.arraycopy (paramTypes, 0, itypes, 1, paramTypes.length);
337     Method implMethod = implType.getDeclaredMethod(mname, itypes);
338     if (implMethod != null)
339       {
340     int count = vec.size();
341     if (count == 0 || ! vec.elementAt(count-1).equals(implMethod))
342       vec.addElement(implMethod);
343       }
344     else
345       {
346     ClassType[] superInterfaces = interfaceType.getInterfaces();
347     for (int i = 0; i < superInterfaces.length; i++)
348       getImplMethods(superInterfaces[i], mname, paramTypes, vec);
349       }
350   }
351
352   /** Call comp.usedClass on the first arguments's supertypes. */
353   private static void usedSuperClasses(ClassType clas, Compilation comp)
354   {
355     comp.usedClass(clas.getSuperclass());
356     ClassType[] interfaces = clas.getInterfaces();
357     if (interfaces != null)
358       {
359     for (int i = interfaces.length; --i >= 0; )
360       comp.usedClass(interfaces[i]);
361       }
362   }
363
364   public ClassType compile (Compilation comp)
365   {
366     ClassType saveClass = comp.curClass;
367     Method saveMethod = comp.method;
368     try
369       {
370     ClassType new_class = getCompiledClassType(comp);
371     comp.curClass = new_class;
372
373     usedSuperClasses(type, comp);
374     if (type != instanceType)
375       usedSuperClasses(instanceType, comp);
376
377     String JavaDoc filename = getFileName();
378     if (filename != null)
379       new_class.setSourceFile (filename);
380
381     LambdaExp saveLambda = comp.curLambda;
382     comp.curLambda = this;
383
384     allocFrame(comp);
385     if (getNeedsStaticLink())
386       {
387             Variable parentFrame = saveLambda.heapFrame != null
388               ? saveLambda.heapFrame
389               : saveLambda.closureEnv;
390             if (parentFrame != null)
391               closureEnvField = staticLinkField
392                 = instanceType.setOuterLink((ClassType) parentFrame.getType());
393       }
394     CodeAttr code;
395
396     for (LambdaExp child = firstChild; child != null; )
397       {
398         Method save_method = comp.method;
399         LambdaExp save_lambda = comp.curLambda;
400             String JavaDoc saveFilename = comp.getFileName();
401             int saveLine = comp.getLineNumber();
402             int saveColumn = comp.getColumnNumber();
403             comp.setLine(child);
404         comp.method = child.getMainMethod();
405         //comp.curClass = comp.method.getDeclaringClass();
406
Declaration childDecl = child.nameDecl;
407             if (childDecl == null
408                 || ! childDecl.getFlag(Declaration.STATIC_SPECIFIED))
409               child.declareThis(comp.curClass);
410         comp.curClass = instanceType;
411         comp.curLambda = child;
412         comp.method.initCode();
413             child.allocChildClasses(comp);
414         child.allocParameters(comp);
415         child.enterFunction(comp);
416             if ("*init*".equals(child.getName()))
417               {
418                 code = comp.getCode();
419
420                 // Extract "first" expression to see if it is special.
421
Expression bodyFirst = child.body;
422                 while (bodyFirst instanceof BeginExp)
423                   {
424                     BeginExp bbody = (BeginExp) bodyFirst;
425                     if (bbody.length == 0)
426                       bodyFirst = null;
427                     else
428                       bodyFirst = bbody.exps[0];
429                   }
430
431                 // See if bodyFirst is a this(...) or super(...) call.
432
ClassType calledInit = null;
433                 Object JavaDoc value; Expression exp;
434                 if (bodyFirst instanceof ApplyExp
435                     && (exp = ((ApplyExp) bodyFirst).func) instanceof QuoteExp
436                     && (value = ((QuoteExp) exp).getValue()) instanceof PrimProcedure)
437                   {
438                     PrimProcedure pproc = (PrimProcedure) value;
439                     if (pproc.isSpecial()
440                         && ("<init>".equals(pproc.method.getName())))
441                       calledInit = pproc.method.getDeclaringClass();
442                   }
443                 ClassType superClass = instanceType.getSuperclass();
444                 if (calledInit != null)
445                   {
446                     bodyFirst.compileWithPosition(comp, Target.Ignore);
447                     if (calledInit != instanceType && calledInit != superClass)
448                       comp.error('e', "call to <init> for not this or super class");
449                   }
450                 else if (calledInit != superClass)
451                   {
452                     // Call default super constructor if there isn't an explicit
453
// call to a super constructor.
454
Method superConstructor
455                       = superClass.getDeclaredMethod("<init>", 0);
456                     if (superConstructor == null)
457                       comp.error('e', "super class does not have a default constructor");
458                     else
459                       {
460                         code.emitPushThis();
461                         code.emitInvokeSpecial(superConstructor);
462                       }
463                   }
464                 if (calledInit != instanceType)
465                   comp.callInitMethods(getCompiledClassType(comp),
466                                        new Vector JavaDoc(10));
467                 if (calledInit != null)
468                   // Skip bodyFirst since we already compiled it.
469
Expression.compileButFirst(child.body, comp);
470                 else
471                   child.compileBody(comp);
472               }
473             else
474               child.compileBody(comp);
475         child.compileEnd(comp);
476         child.compileChildMethods(comp);
477         comp.method = save_method;
478         comp.curClass = new_class;
479         comp.curLambda = save_lambda;
480             comp.setLine(saveFilename, saveLine, saveColumn);
481         child = child.nextSibling;
482       }
483         if (! explicitInit)
484           comp.generateConstructor(instanceType, this);
485         else if (initChain != null)
486           initChain.reportError("unimplemented: explicit constructor cannot initialize ", comp);
487
488     Method[] methods = type.getMethods(AbstractMethodFilter.instance, 2);
489     for (int i = 0; i < methods.length; i++)
490       {
491         Method meth = methods[i];
492         String JavaDoc mname = meth.getName();
493         Type[] ptypes = meth.getParameterTypes();
494         Type rtype = meth.getReturnType();
495
496         Method mimpl = instanceType.getMethod(mname, ptypes);
497         if (mimpl != null && ! mimpl.isAbstract())
498           continue;
499
500         char ch;
501         if (mname.length() > 3
502         && mname.charAt(2) == 't'
503         && mname.charAt(1) == 'e'
504         && ((ch = mname.charAt(0)) == 'g' || ch == 's'))
505           { // a "set" or "get" method is treated as a slot accessor.
506
Type ftype;
507         if (ch == 's' && rtype.isVoid() && ptypes.length == 1)
508           ftype = ptypes[0];
509         else if (ch == 'g' && ptypes.length == 0)
510           ftype = rtype;
511         else
512           continue;
513         String JavaDoc fname = Character.toLowerCase(mname.charAt(3))
514           + mname.substring(4);
515         Field fld = instanceType.getField(fname);
516         if (fld == null)
517           fld = instanceType.addField(fname, ftype, Access.PUBLIC);
518         Method impl = instanceType.addMethod(mname, Access.PUBLIC,
519                              ptypes, rtype);
520         code = impl.startCode();
521         code.emitPushThis();
522         if (ch == 'g')
523           {
524             code.emitGetField(fld);
525           }
526         else
527           {
528             code.emitLoad(code.getArg(1));
529             code.emitPutField(fld);
530           }
531         code.emitReturn();
532           }
533         else
534           {
535         Vector JavaDoc vec = new Vector JavaDoc();
536         getImplMethods(type, mname, ptypes, vec);
537         if (vec.size() != 1)
538           {
539             // FIXME - need better error message!
540
String JavaDoc msg = vec.size() == 0
541               ? "missing implementation for "
542               : "ambiguous implementation for ";
543             comp.error('e', msg+meth+" mname:"+mname);
544           }
545         else
546           {
547             Method impl = instanceType.addMethod(mname, Access.PUBLIC,
548                              ptypes, rtype);
549             code = impl.startCode();
550             for (Variable var = code.getCurrentScope().firstVar();
551              var != null; var = var.nextVar())
552               code.emitLoad(var);
553             Method imethod = (Method) vec.elementAt(0);
554             code.emitInvokeStatic(imethod);
555             code.emitReturn();
556           }
557           }
558       }
559
560         generateApplyMethods(comp);
561     comp.curLambda = saveLambda;
562
563     return new_class;
564       }
565     finally
566       {
567     comp.curClass = saveClass;
568     comp.method = saveMethod;
569       }
570   }
571
572   protected Expression walk (ExpWalker walker)
573   {
574     Compilation comp = walker.getCompilation();
575     if (comp == null)
576       return walker.walkClassExp(this);
577     ClassType saveClass = comp.curClass;
578     try
579       {
580     comp.curClass = type;
581     return walker.walkClassExp(this);
582       }
583     finally
584       {
585     comp.curClass = saveClass;
586       }
587   }
588
589   protected void walkChildren(ExpWalker walker)
590   {
591     LambdaExp save = walker.currentLambda;
592     walker.currentLambda = this;
593     try
594       {
595     for (LambdaExp child = firstChild;
596          child != null && walker.exitValue == null;
597          child = child.nextSibling)
598           {
599             if (instanceType != null)
600               {
601                 Declaration firstParam = child.firstDecl();
602                 if (firstParam != null && firstParam.isThisParameter())
603                   firstParam.setType(type);
604               }
605             walker.walkLambdaExp(child);
606           }
607       }
608     finally
609       {
610     walker.currentLambda = save;
611       }
612   }
613
614   public void print (OutPort out)
615   {
616     out.startLogicalBlock("("+getExpClassName()+"/", ")", 2);
617     Object JavaDoc name = getSymbol();
618     if (name != null)
619       {
620     out.print(name);
621     out.print('/');
622       }
623     out.print(id);
624     out.print("/fl:"); out.print(Integer.toHexString(flags));
625     out.print(" (");
626     Special prevMode = null;
627     int i = 0;
628     int key_args = keywords == null ? 0 : keywords.length;
629     //int opt_args = defaultArgs == null ? 0 : defaultArgs.length - key_args;
630
for (Declaration decl = firstDecl(); decl != null; decl = decl.nextDecl())
631       {
632     if (i > 0)
633       out.print(' ');
634         decl.printInfo(out);
635     i++;
636       }
637     out.print(") ");
638     for (LambdaExp child = firstChild; child != null;
639      child = child.nextSibling)
640       {
641     out.writeBreakLinear();
642         child.print(out);
643       }
644     if (body != null)
645       {
646         out.writeBreakLinear();
647         body.print (out);
648       }
649     out.endLogicalBlock(")");
650   }
651
652   public Field compileSetField (Compilation comp)
653   {
654     return (new ClassInitializer(this, comp)).field;
655   }
656
657   /** Mangle a "slot" name to a get- or set- method name.
658    * @param prefix either "get" or "set"
659    * @param sname a "slot" (property) name. This is mangled if needed.
660    */

661   public static String JavaDoc slotToMethodName(String JavaDoc prefix, String JavaDoc sname)
662   {
663     if (! Compilation.isValidJavaName(sname))
664       sname = Compilation.mangleName(sname, false);
665     StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(sname.length()+3);
666     sbuf.append(prefix);
667     sbuf.append(Character.toTitleCase(sname.charAt(0)));
668     sbuf.append(sname.substring(1));
669     return sbuf.toString();
670   }
671
672   /** Helper class uses by ClassExp.compile.
673    */

674   private static class AbstractMethodFilter implements gnu.bytecode.Filter
675   {
676     public static final AbstractMethodFilter instance
677       = new AbstractMethodFilter();
678
679     public boolean select(Object JavaDoc value)
680     {
681       gnu.bytecode.Method method = (gnu.bytecode.Method) value;
682       return method.isAbstract();
683     }
684   }
685 }
686
Popular Tags