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