1   // Copyright 2011 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.util.ArrayList;
6   import static jminusminus.CLConstants.*;
7   
8   /**
9    * The AST node for a method declaration.
10   */
11  
12  class JMethodDeclaration
13      extends JAST implements JMember {
14  
15      /** Method modifiers. */
16      protected ArrayList<String> mods;
17  
18      /** Method name. */
19      protected String name;
20  
21      /** Return type. */
22      protected Type returnType;
23  
24      /** The formal parameters. */
25      protected ArrayList<JFormalParameter> params;
26  
27      /** Method body. */
28      protected JBlock body;
29  
30      /** Built in analyze(). */
31      protected MethodContext context;
32  
33      /** Computed by preAnalyze(). */
34      protected String descriptor;
35  
36      /** Is method abstract. */
37      protected boolean isAbstract;
38  
39      /** Is method static. */
40      protected boolean isStatic;
41  
42      /** Is method private. */
43      protected boolean isPrivate;
44  
45      /**
46       * Construct an AST node for a method declaration given the
47       * line number, method name, return type, formal parameters,
48       * and the method body.
49       * 
50       * @param line
51       *                line in which the method declaration occurs
52       *                in the source file.
53       * @param mods
54       *                modifiers.
55       * @param name
56       *                method name.
57       * @param returnType
58       *                return type.
59       * @param params
60       *                the formal parameters.
61       * @param body
62       *                method body.
63       */
64  
65      public JMethodDeclaration(int line, ArrayList<String> mods,
66          String name, Type returnType,
67          ArrayList<JFormalParameter> params, JBlock body)
68  
69      {
70          super(line);
71          this.mods = mods;
72          this.name = name;
73          this.returnType = returnType;
74          this.params = params;
75          this.body = body;
76          this.isAbstract = mods.contains("abstract");
77          this.isStatic = mods.contains("static");
78          this.isPrivate = mods.contains("private");
79      }
80  
81      /**
82       * Declare this method in the parent (class) context.
83       * 
84       * @param context
85       *                the parent (class) context.
86       * @param partial
87       *                the code emitter (basically an abstraction
88       *                for producing the partial class).
89       */
90  
91      public void preAnalyze(Context context, CLEmitter partial) {
92          // Resolve types of the formal parameters
93          for (JFormalParameter param : params) {
94              param.setType(param.type().resolve(context));
95          }
96  
97          // Resolve return type
98          returnType = returnType.resolve(context);
99  
100         // Check proper local use of abstract
101         if (isAbstract && body != null) {
102             JAST.compilationUnit.reportSemanticError(line(),
103                 "abstract method cannot have a body");
104         } else if (body == null && !isAbstract) {
105             JAST.compilationUnit.reportSemanticError(line(),
106                 "Method with null body must be abstarct");
107         } else if (isAbstract && isPrivate) {
108             JAST.compilationUnit.reportSemanticError(line(),
109                 "private method cannot be declared abstract");
110         } else if (isAbstract && isStatic) {
111             JAST.compilationUnit.reportSemanticError(line(),
112                 "static method cannot be declared abstract");
113         }
114 
115         // Compute descriptor
116         descriptor = "(";
117         for (JFormalParameter param : params) {
118             descriptor += param.type().toDescriptor();
119         }
120         descriptor += ")" + returnType.toDescriptor();
121 
122         // Generate the method with an empty body (for now)
123         partialCodegen(context, partial);
124     }
125 
126     /**
127      * Analysis for a method declaration involves (1) creating a
128      * new method context (that records the return type; this is
129      * used in the analysis of the method body), (2) bumping up
130      * the offset (for instance methods), (3) declaring the
131      * formal parameters in the method context, and (4) analyzing
132      * the method's body.
133      * 
134      * @param context
135      *                context in which names are resolved.
136      * @return the analyzed (and possibly rewritten) AST subtree.
137      */
138 
139     public JAST analyze(Context context) {
140         MethodContext methodContext = 
141         new MethodContext(context, isStatic, returnType);
142     this.context = methodContext;
143 
144         if (!isStatic) {
145             // Offset 0 is used to address "this".
146             this.context.nextOffset();
147         }
148 
149         // Declare the parameters. We consider a formal parameter 
150         // to be always initialized, via a function call.
151         for (JFormalParameter param : params) {
152             LocalVariableDefn defn = new LocalVariableDefn(param.type(), 
153                 this.context.nextOffset());
154             defn.initialize();
155             this.context.addEntry(param.line(), param.name(), defn);
156         }
157         if (body != null) {
158             body = body.analyze(this.context);
159         if (returnType!=Type.VOID && ! methodContext.methodHasReturn()){
160         JAST.compilationUnit.reportSemanticError(line(),
161             "Non-void method must have a return statement");
162         }
163         }
164     return this;
165     }
166 
167     /**
168      * Add this method declaration to the partial class.
169      * 
170      * @param context
171      *                the parent (class) context.
172      * @param partial
173      *                the code emitter (basically an abstraction
174      *                for producing the partial class).
175      */
176 
177     public void partialCodegen(Context context, CLEmitter partial) {
178         // Generate a method with an empty body; need a return to
179         // make
180         // the class verifier happy.
181         partial.addMethod(mods, name, descriptor, null, false);
182 
183         // Add implicit RETURN
184         if (returnType == Type.VOID) {
185             partial.addNoArgInstruction(RETURN);
186         } else if (returnType == Type.INT
187             || returnType == Type.BOOLEAN || returnType == Type.CHAR) {
188             partial.addNoArgInstruction(ICONST_0);
189             partial.addNoArgInstruction(IRETURN);
190         } else {
191             // A reference type.
192             partial.addNoArgInstruction(ACONST_NULL);
193             partial.addNoArgInstruction(ARETURN);
194         }
195     }
196 
197     /**
198      * Generate code for the method declaration.
199      * 
200      * @param output
201      *                the code emitter (basically an abstraction
202      *                for producing the .class file).
203      */
204 
205     public void codegen(CLEmitter output) {
206         output.addMethod(mods, name, descriptor, null, false);
207         if (body != null) {
208             body.codegen(output);
209         }
210 
211         // Add implicit RETURN
212         if (returnType == Type.VOID) {
213             output.addNoArgInstruction(RETURN);
214         }
215     }
216 
217     /**
218      * @inheritDoc
219      */
220 
221     public void writeToStdOut(PrettyPrinter p) {
222         p.printf("<JMethodDeclaration line=\"%d\" name=\"%s\" "
223             + "returnType=\"%s\">\n", line(), name, returnType
224             .toString());
225         p.indentRight();
226         if (context != null) {
227             context.writeToStdOut(p);
228         }
229         if (mods != null) {
230             p.println("<Modifiers>");
231             p.indentRight();
232             for (String mod : mods) {
233                 p.printf("<Modifier name=\"%s\"/>\n", mod);
234             }
235             p.indentLeft();
236             p.println("</Modifiers>");
237         }
238         if (params != null) {
239             p.println("<FormalParameters>");
240             for (JFormalParameter param : params) {
241                 p.indentRight();
242                 param.writeToStdOut(p);
243                 p.indentLeft();
244             }
245             p.println("</FormalParameters>");
246         }
247         if (body != null) {
248             p.println("<Body>");
249             p.indentRight();
250             body.writeToStdOut(p);
251             p.indentLeft();
252             p.println("</Body>");
253         }
254         p.indentLeft();
255         p.println("</JMethodDeclaration>");
256     }
257 }
258