1   // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.util.ArrayList;
6   
7   import static jminusminus.CLConstants.*;
8   
9   /**
10   * A representation of a class declaration.
11   */
12  class JClassDeclaration extends JAST implements JTypeDecl {
13      // Class modifiers.
14      private ArrayList<String> mods;
15  
16      // Class name.
17      private String name;
18  
19      // This class type.
20      private Type thisType;
21  
22      // Super class type.
23      private Type superType;
24  
25      // Implemented interfaces.
26      private ArrayList<TypeName> superInterfaces;
27  
28      // Class block.
29      private ArrayList<JMember> classBlock;
30  
31      // Context for this class.
32      private ClassContext context;
33  
34      // Whether this class has an explicit constructor.
35      private boolean hasExplicitConstructor;
36  
37      // Instance fields of this class.
38      private ArrayList<JFieldDeclaration> instanceFieldInitializations;
39  
40      // Static (class) fields of this class.
41      private ArrayList<JFieldDeclaration> staticFieldInitializations;
42  
43      /**
44       * Constructs an AST node for a class declaration.
45       *
46       * @param line            line in which the class declaration occurs in the source file.
47       * @param mods            class modifiers.
48       * @param name            class name.
49       * @param superType       super class type.
50       * @param superInterfaces implemented interfaces.
51       * @param classBlock      class block.
52       */
53      public JClassDeclaration(int line, ArrayList<String> mods, String name, Type superType,
54                               ArrayList<TypeName> superInterfaces, ArrayList<JMember> classBlock) {
55          super(line);
56          this.mods = mods;
57          this.name = name;
58          this.superType = superType;
59          this.superInterfaces = superInterfaces;
60          this.classBlock = classBlock;
61          hasExplicitConstructor = false;
62          instanceFieldInitializations = new ArrayList<JFieldDeclaration>();
63          staticFieldInitializations = new ArrayList<JFieldDeclaration>();
64      }
65  
66      /**
67       * Returns the initializations for instance fields (expressed as assignment statements).
68       *
69       * @return the initializations for instance fields (expressed as assignment statements).
70       */
71      public ArrayList<JFieldDeclaration> instanceFieldInitializations() {
72          return instanceFieldInitializations;
73      }
74  
75      /**
76       * {@inheritDoc}
77       */
78      public void declareThisType(Context context) {
79          String qualifiedName = JAST.compilationUnit.packageName() == "" ?
80                  name : JAST.compilationUnit.packageName() + "/" + name;
81          CLEmitter partial = new CLEmitter(false);
82          partial.addClass(mods, qualifiedName, Type.OBJECT.jvmName(), null, false);
83          thisType = Type.typeFor(partial.toClass());
84          context.addType(line, thisType);
85      }
86  
87      /**
88       * {@inheritDoc}
89       */
90      public void preAnalyze(Context context) {
91          // Construct a class context.
92          this.context = new ClassContext(this, context);
93  
94          // Resolve superclass.
95          superType = superType.resolve(this.context);
96  
97          // Creating a partial class in memory can result in a java.lang.VerifyError if the
98          // semantics below are violated, so we can't defer these checks to analyze().
99          thisType.checkAccess(line, superType);
100         if (superType.isFinal()) {
101             JAST.compilationUnit.reportSemanticError(line, "Cannot extend a final type: %s",
102                     superType.toString());
103         }
104 
105         // Create the (partial) class.
106         CLEmitter partial = new CLEmitter(false);
107 
108         // Add the class header to the partial class
109         String qualifiedName = JAST.compilationUnit.packageName() == "" ?
110                 name : JAST.compilationUnit.packageName() + "/" + name;
111         partial.addClass(mods, qualifiedName, superType.jvmName(), null, false);
112 
113         // Pre-analyze the members and add them to the partial class.
114         for (JMember member : classBlock) {
115             member.preAnalyze(this.context, partial);
116             hasExplicitConstructor =
117                     hasExplicitConstructor || member instanceof JConstructorDeclaration;
118         }
119 
120         // Add the implicit empty constructor?
121         if (!hasExplicitConstructor) {
122             codegenPartialImplicitConstructor(partial);
123         }
124 
125         // Get the ClassRep for the (partial) class and make it the representation for this type.
126         Type id = this.context.lookupType(name);
127         if (id != null && !JAST.compilationUnit.errorHasOccurred()) {
128             id.setClassRep(partial.toClass());
129         }
130     }
131 
132     /**
133      * {@inheritDoc}
134      */
135     public String name() {
136         return name;
137     }
138 
139     /**
140      * {@inheritDoc}
141      */
142     public Type thisType() {
143         return thisType;
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
149     public Type superType() {
150         return superType;
151     }
152 
153     /**
154      * {@inheritDoc}
155      */
156     public ArrayList<TypeName> superInterfaces() {
157         return superInterfaces;
158     }
159 
160     /**
161      * {@inheritDoc}
162      */
163     public JAST analyze(Context context) {
164         // Analyze all members.
165         for (JMember member : classBlock) {
166             ((JAST) member).analyze(this.context);
167         }
168 
169         // Separate declared fields for purposes of initialization.
170         for (JMember member : classBlock) {
171             if (member instanceof JFieldDeclaration) {
172                 JFieldDeclaration fieldDecl = (JFieldDeclaration) member;
173                 if (fieldDecl.mods().contains("static")) {
174                     staticFieldInitializations.add(fieldDecl);
175                 } else {
176                     instanceFieldInitializations.add(fieldDecl);
177                 }
178             }
179         }
180 
181         // Finally, ensure that a non-abstract class has no abstract methods.
182         if (!thisType.isAbstract() && thisType.abstractMethods().size() > 0) {
183             String methods = "";
184             for (Method method : thisType.abstractMethods()) {
185                 methods += "\n" + method;
186             }
187             JAST.compilationUnit.reportSemanticError(line,
188                     "Class must be abstract since it defines abstract methods: %s", methods);
189         }
190         return this;
191     }
192 
193     /**
194      * {@inheritDoc}
195      */
196     public void codegen(CLEmitter output) {
197         // The class header.
198         String qualifiedName = JAST.compilationUnit.packageName() == "" ?
199                 name : JAST.compilationUnit.packageName() + "/" + name;
200         output.addClass(mods, qualifiedName, superType.jvmName(), null, false);
201 
202         // The implicit empty constructor?
203         if (!hasExplicitConstructor) {
204             codegenImplicitConstructor(output);
205         }
206 
207         // The members.
208         for (JMember member : classBlock) {
209             ((JAST) member).codegen(output);
210         }
211 
212         // Generate a class initialization method?
213         if (staticFieldInitializations.size() > 0) {
214             codegenClassInit(output);
215         }
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     public void toJSON(JSONElement json) {
222         JSONElement e = new JSONElement();
223         json.addChild("JClassDeclaration:" + line, e);
224         if (mods != null) {
225             ArrayList<String> value = new ArrayList<String>();
226             for (String mod : mods) {
227                 value.add(String.format("\"%s\"", mod));
228             }
229             e.addAttribute("modifiers", value);
230         }
231         e.addAttribute("name", name);
232         e.addAttribute("super", superType == null ? "" : superType.toString());
233         if (superInterfaces != null) {
234             ArrayList<String> value = new ArrayList<String>();
235             for (TypeName impl : superInterfaces) {
236                 value.add(String.format("\"%s\"", impl.toString()));
237             }
238             e.addAttribute("implements", value);
239         }
240         if (context != null) {
241             context.toJSON(e);
242         }
243         if (classBlock != null) {
244             for (JMember member : classBlock) {
245                 ((JAST) member).toJSON(e);
246             }
247         }
248     }
249 
250     // Generates code for an implicit empty constructor (necessary only if there is not already
251     // an explicit one).
252     private void codegenPartialImplicitConstructor(CLEmitter partial) {
253         ArrayList<String> mods = new ArrayList<String>();
254         mods.add("public");
255         partial.addMethod(mods, "<init>", "()V", null, false);
256         partial.addNoArgInstruction(ALOAD_0);
257         partial.addMemberAccessInstruction(INVOKESPECIAL, superType.jvmName(), "<init>", "()V");
258         partial.addNoArgInstruction(RETURN);
259     }
260 
261     // Generates code for an implicit empty constructor (necessary only if there is not already
262     // an explicit one).
263     private void codegenImplicitConstructor(CLEmitter output) {
264         ArrayList<String> mods = new ArrayList<String>();
265         mods.add("public");
266         output.addMethod(mods, "<init>", "()V", null, false);
267         output.addNoArgInstruction(ALOAD_0);
268         output.addMemberAccessInstruction(INVOKESPECIAL, superType.jvmName(), "<init>", "()V");
269 
270         // If there are instance field initializations, generate code for them.
271         for (JFieldDeclaration instanceField : instanceFieldInitializations) {
272             instanceField.codegenInitializations(output);
273         }
274 
275         output.addNoArgInstruction(RETURN);
276     }
277 
278     // Generates code for class initialization (in j-- this means static field initializations.
279     private void codegenClassInit(CLEmitter output) {
280         ArrayList<String> mods = new ArrayList<String>();
281         mods.add("public");
282         mods.add("static");
283         output.addMethod(mods, "<clinit>", "()V", null, false);
284 
285         // If there are static field initializations, generate code for them.
286         for (JFieldDeclaration staticField : staticFieldInitializations) {
287             staticField.codegenInitializations(output);
288         }
289 
290         output.addNoArgInstruction(RETURN);
291     }
292 }
293