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   * The AST node for a method declaration.
11   */
12  class JMethodDeclaration extends JAST implements JMember {
13      /**
14       * Method modifiers.
15       */
16      protected ArrayList<String> mods;
17  
18      /**
19       * Method name.
20       */
21      protected String name;
22  
23      /**
24       * Return type.
25       */
26      protected Type returnType;
27  
28      /**
29       * The formal parameters.
30       */
31      protected ArrayList<JFormalParameter> params;
32  
33      /**
34       * Exceptions thrown.
35       */
36      protected ArrayList<TypeName> exceptions;
37  
38      /**
39       * Method body.
40       */
41      protected JBlock body;
42  
43      /**
44       * Method context (built in analyze()).
45       */
46      protected MethodContext context;
47  
48      /**
49       * Method descriptor (computed in preAnalyze()).
50       */
51      protected String descriptor;
52  
53      /**
54       * Is this method abstract?
55       */
56      protected boolean isAbstract;
57  
58      /**
59       * Is this method static?
60       */
61      protected boolean isStatic;
62  
63      /**
64       * Is this method private?
65       */
66      protected boolean isPrivate;
67  
68      /**
69       * Constructs an AST node for a method declaration.
70       *
71       * @param line       line in which the method declaration occurs in the source file.
72       * @param mods       modifiers.
73       * @param name       method name.
74       * @param returnType return type.
75       * @param params     the formal parameters.
76       * @param exceptions exceptions thrown.
77       * @param body       method body.
78       */
79      public JMethodDeclaration(int line, ArrayList<String> mods, String name, Type returnType,
80                                ArrayList<JFormalParameter> params,
81                                ArrayList<TypeName> exceptions, JBlock body) {
82          super(line);
83          this.mods = mods;
84          this.name = name;
85          this.returnType = returnType;
86          this.params = params;
87          this.exceptions = exceptions;
88          this.body = body;
89          isAbstract = mods.contains("abstract");
90          isStatic = mods.contains("static");
91          isPrivate = mods.contains("private");
92      }
93  
94      /**
95       * {@inheritDoc}
96       */
97      public void preAnalyze(Context context, CLEmitter partial) {
98          // Resolve types of the formal parameters.
99          for (JFormalParameter param : params) {
100             param.setType(param.type().resolve(context));
101         }
102 
103         // Resolve return type.
104         returnType = returnType.resolve(context);
105 
106         // Check proper local use of abstract
107         if (isAbstract && body != null) {
108             JAST.compilationUnit.reportSemanticError(line(), "abstract method cannot have a body");
109         } else if (body == null && !isAbstract) {
110             JAST.compilationUnit.reportSemanticError(line(),
111                     "Method without body must be abstract");
112         } else if (isAbstract && isPrivate) {
113             JAST.compilationUnit.reportSemanticError(line(), "private method cannot be abstract");
114         } else if (isAbstract && isStatic) {
115             JAST.compilationUnit.reportSemanticError(line(), "static method cannot be abstract");
116         }
117 
118         // Compute descriptor.
119         descriptor = "(";
120         for (JFormalParameter param : params) {
121             descriptor += param.type().toDescriptor();
122         }
123         descriptor += ")" + returnType.toDescriptor();
124 
125         // Generate the method with an empty body (for now).
126         partialCodegen(context, partial);
127     }
128 
129     /**
130      * {@inheritDoc}
131      */
132     public JAST analyze(Context context) {
133         MethodContext methodContext = new MethodContext(context, isStatic, returnType);
134         this.context = methodContext;
135 
136         if (!isStatic) {
137             // Offset 0 is used to address "this".
138             this.context.nextOffset();
139         }
140 
141         // Declare the parameters. We consider a formal parameter to be always initialized, via a
142         // method call.
143         for (JFormalParameter param : params) {
144             LocalVariableDefn defn = new LocalVariableDefn(param.type(), this.context.nextOffset());
145             defn.initialize();
146             this.context.addEntry(param.line(), param.name(), defn);
147         }
148 
149         if (body != null) {
150             body = body.analyze(this.context);
151             if (returnType != Type.VOID && !methodContext.methodHasReturn()) {
152                 JAST.compilationUnit.reportSemanticError(line(),
153                         "Non-void method must have a return statement");
154             }
155         }
156         return this;
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     public void partialCodegen(Context context, CLEmitter partial) {
163         partial.addMethod(mods, name, descriptor, null, false);
164         if (returnType == Type.VOID) {
165             partial.addNoArgInstruction(RETURN);
166         } else if (returnType == Type.INT || returnType == Type.BOOLEAN ||
167                 returnType == Type.CHAR) {
168             partial.addNoArgInstruction(ICONST_0);
169             partial.addNoArgInstruction(IRETURN);
170         } else {
171             partial.addNoArgInstruction(ACONST_NULL);
172             partial.addNoArgInstruction(ARETURN);
173         }
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     public void codegen(CLEmitter output) {
180         output.addMethod(mods, name, descriptor, null, false);
181         if (body != null) {
182             body.codegen(output);
183         }
184         if (returnType == Type.VOID) {
185             output.addNoArgInstruction(RETURN);
186         }
187     }
188 
189     /**
190      * {@inheritDoc}
191      */
192     public void toJSON(JSONElement json) {
193         JSONElement e = new JSONElement();
194         json.addChild("JMethodDeclaration:" + line, e);
195         e.addAttribute("name", name);
196         e.addAttribute("returnType", returnType.toString());
197         if (mods != null) {
198             ArrayList<String> value = new ArrayList<String>();
199             for (String mod : mods) {
200                 value.add(String.format("\"%s\"", mod));
201             }
202             e.addAttribute("modifiers", value);
203         }
204         if (params != null) {
205             ArrayList<String> value = new ArrayList<String>();
206             for (JFormalParameter param : params) {
207                 value.add(String.format("[\"%s\", \"%s\"]", param.name(),
208                         param.type() == null ? "" : param.type().toString()));
209             }
210             e.addAttribute("parameters", value);
211         }
212         if (exceptions != null) {
213             ArrayList<String> value = new ArrayList<String>();
214             for (TypeName exception : exceptions) {
215                 value.add(String.format("\"%s\"", exception.toString()));
216             }
217             e.addAttribute("throws", value);
218         }
219         if (context != null) {
220             context.toJSON(e);
221         }
222         if (body != null) {
223             body.toJSON(e);
224         }
225     }
226 }
227