1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 /* Generated By:JJTree: Do not edit this line. ASTFunDecl.java */
19 /* JJT: 0.3pre1 */
20 
21 package Mini;
22 import java.io.PrintWriter;
23 import java.util.Iterator;
24 
25 import org.apache.bcel.generic.ALOAD;
26 import org.apache.bcel.generic.ASTORE;
27 import org.apache.bcel.generic.ArrayType;
28 import org.apache.bcel.generic.BranchHandle;
29 import org.apache.bcel.generic.BranchInstruction;
30 import org.apache.bcel.generic.ClassGen;
31 import org.apache.bcel.generic.ConstantPoolGen;
32 import org.apache.bcel.generic.GETSTATIC;
33 import org.apache.bcel.generic.GOTO;
34 import org.apache.bcel.generic.INVOKEVIRTUAL;
35 import org.apache.bcel.generic.InstructionConstants;
36 import org.apache.bcel.generic.InstructionHandle;
37 import org.apache.bcel.generic.InstructionList;
38 import org.apache.bcel.generic.InstructionTargeter;
39 import org.apache.bcel.generic.LocalVariableGen;
40 import org.apache.bcel.generic.MethodGen;
41 import org.apache.bcel.generic.ObjectType;
42 import org.apache.bcel.generic.TargetLostException;
43 import org.apache.bcel.generic.Type;
44 import org.apache.bcel.util.InstructionFinder;
45 
46 /**
47  *
48  */
49 public class ASTFunDecl extends SimpleNode
50 implements MiniParserTreeConstants, org.apache.bcel.Constants {
51   private ASTIdent    name;
52   private ASTIdent[]  argv;
53   private ASTExpr     body;
54   private int         type = T_UNKNOWN;
55   private int         line, column;
56   private boolean     is_simple;  // true, if simple expression like `12 + f(a)'
57   private boolean     is_recursive; // Not used yet, TODO
58 //  private int         max_depth; // max. expression tree depth
59   private Environment env;
60 
61   // Generated methods
ASTFunDecl(final int id)62   ASTFunDecl(final int id) {
63     super(id);
64   }
65 
ASTFunDecl(final MiniParser p, final int id)66   ASTFunDecl(final MiniParser p, final int id) {
67     super(p, id);
68   }
69 
jjtCreate(final MiniParser p, final int id)70   public static Node jjtCreate(final MiniParser p, final int id) {
71     return new ASTFunDecl(p, id);
72   }
73 
ASTFunDecl(final ASTIdent name, final ASTIdent[] argv, final ASTExpr body, final int type)74   ASTFunDecl(final ASTIdent name, final ASTIdent[] argv, final ASTExpr body, final int type) {
75     this(JJTFUNDECL);
76 
77     this.name = name;
78     this.argv = argv;
79     this.body = body;
80     this.type = type;
81   }
82 
83   /**
84    * Overrides SimpleNode.closeNode()
85    * Cast children to appropiate type.
86    */
87   @Override
closeNode()88   public void closeNode() {
89     name = (ASTIdent)children[0];
90     body = (ASTExpr)children[children.length - 1];
91 
92     argv = new ASTIdent[children.length - 2]; // May be 0-size array
93     for(int i = 1; i < children.length - 1; i++) {
94         argv[i - 1] = (ASTIdent)children[i];
95     }
96 
97     children=null; // Throw away old reference
98   }
99 
100   /**
101    * First pass of parse tree.
102    */
traverse(final Environment env)103   public ASTFunDecl traverse(final Environment env) {
104     this.env = env;
105 
106     // Put arguments into hash table aka environment
107     for(int i=0; i < argv.length; i++) {
108       final EnvEntry entry = env.get(argv[i].getName());
109 
110       if(entry != null) {
111         MiniC.addError(argv[i].getLine(), argv[i].getColumn(),
112                        "Redeclaration of " + entry + ".");
113     } else {
114         env.put(new Variable(argv[i]));
115     }
116     }
117 
118     /* Update entry of this function, i.e. set argument references.
119      * The entry is already in there by garantuee, but may be of wrong type,
120      * i.e. the user defined a function `TRUE', e.g. and `TRUE' is of type `Variable'.
121      */
122     try {
123       final Function fun = (Function)env.get(name.getName());
124       fun.setArgs(argv);
125     } catch(final ClassCastException e) {} // Who cares?
126 
127     body = body.traverse(env); // Traverse expression body
128 
129     return this;
130   }
131 
132   /** Second pass
133    * @return type of expression
134    */
eval(final int pass)135   public int eval(final int pass) {
136     final int expected = name.getType(); // Maybe other function has already called us
137     type = body.eval(expected);    // And updated the env
138 
139     if((expected != T_UNKNOWN) && (type != expected)) {
140         MiniC.addError(line, column,
141                      "Function f ist not of type " + TYPE_NAMES[expected] +
142                      " as previously assumed, but " + TYPE_NAMES[type]);
143     }
144 
145     name.setType(type);
146 
147     is_simple = body.isSimple();
148 
149     if(pass == 2 && type == T_UNKNOWN) {
150         is_recursive = true;
151     }
152 
153     return type;
154   }
155 
156   /**
157    * Fourth pass, produce Java code.
158    */
code(final PrintWriter out)159   public void code(final PrintWriter out) {
160     String expr;
161     boolean main=false, ignore=false;
162 
163     final String fname = name.getName();
164 
165     if(fname.equals("main")) {
166       out.println("  public static void main(String[] _argv) {");
167       main = true;
168     }
169     else if(fname.equals("READ") || fname.equals("WRITE")) { // Do nothing
170       ignore = true;
171     }
172     else {
173       out.print("  public static final " + "int" + // type_names[type] +
174                 " " + fname + "(");
175 
176       for(int i = 0; i < argv.length; i++) {
177         out.print("int " + argv[i].getName());
178 
179         if(i < argv.length - 1) {
180         out.print(", ");
181     }
182       }
183 
184       out.println(")\n    throws IOException\n  {");
185     }
186 
187     if(!ignore) {
188       final StringBuffer buf = new StringBuffer();
189 
190       body.code(buf);
191       out.println(getVarDecls());
192 
193       expr = buf.toString();
194 
195       if(main) {
196         out.println("    try {");
197     }
198 
199       out.println(expr);
200 
201       if(main) {
202         out.println("    } catch(Exception e) { System.err.println(e); }\n  }\n");
203     } else {
204         out.println("\n    return " + pop() + ";\n  }\n");
205     }
206     }
207 
208     reset();
209   }
210 
211   /**
212    * Fifth pass, produce Java byte code.
213    */
byte_code(final ClassGen class_gen, final ConstantPoolGen cp)214   public void byte_code(final ClassGen class_gen, final ConstantPoolGen cp) {
215     MethodGen method=null;
216     boolean main=false, ignore=false;
217     final String class_name = class_gen.getClassName();
218     final String fname      = name.getName();
219     final InstructionList il = new InstructionList();
220 
221     Type[] args = { new ArrayType(Type.STRING, 1) }; // default for `main'
222     String[] arg_names = { "$argv" };
223 
224     if(fname.equals("main")) {
225       method = new MethodGen(ACC_STATIC | ACC_PUBLIC,
226                              Type.VOID, args, arg_names,
227                              "main", class_name, il, cp);
228 
229       main = true;
230     } else if(fname.equals("READ") || fname.equals("WRITE")) { // Do nothing
231       ignore = true;
232     } else {
233       final int    size  = argv.length;
234 
235       arg_names = new String[size];
236       args      = new Type[size];
237 
238       for(int i = 0; i < size; i++) {
239         args[i] = Type.INT;
240         arg_names[i] =  argv[i].getName();
241       }
242 
243       method = new MethodGen(ACC_STATIC | ACC_PRIVATE | ACC_FINAL,
244                              Type.INT, args, arg_names,
245                              fname, class_name, il, cp);
246 
247       final LocalVariableGen[] lv = method.getLocalVariables();
248       for(int i = 0; i < size; i++) {
249         final Variable entry = (Variable)env.get(arg_names[i]);
250         entry.setLocalVariable(lv[i]);
251       }
252 
253       method.addException("java.io.IOException");
254     }
255 
256     if(!ignore) {
257       body.byte_code(il, method, cp);
258 
259       if(main) {
260         final ObjectType e_type = new ObjectType("java.lang.Exception");
261         final InstructionHandle start = il.getStart();
262         InstructionHandle end, handler, end_handler;
263         final LocalVariableGen exc = method.addLocalVariable("$e",
264                                                        e_type,
265                                                        null, null);
266         final int slot = exc.getIndex();
267 
268         il.append(InstructionConstants.POP); pop(); // Remove last element on stack
269         end = il.append(InstructionConstants.RETURN); // Use instruction constants, if possible
270 
271         // catch
272         handler = il.append(new ASTORE(slot)); // save exception object
273         il.append(new GETSTATIC(cp.addFieldref("java.lang.System", "err",
274                                                "Ljava/io/PrintStream;")));
275         il.append(new ALOAD(slot)); push(2);
276         il.append(new INVOKEVIRTUAL(cp.addMethodref("java.io.PrintStream",
277                                                 "println",
278                                                 "(Ljava/lang/Object;)V")));
279         pop(2);
280         end_handler = il.append(InstructionConstants.RETURN);
281         method.addExceptionHandler(start, end, handler, e_type);
282         exc.setStart(handler); exc.setEnd(end_handler);
283       } else {
284         il.append(InstructionConstants.IRETURN); // Reuse object to save memory
285     }
286 
287       method.removeNOPs(); // First optimization pass, provided by MethodGen
288       optimizeIFs(il);     // Second optimization pass, application-specific
289       method.setMaxStack(max_size);
290       class_gen.addMethod(method.getMethod());
291     }
292 
293     il.dispose(); // Dispose instruction handles for better memory utilization
294 
295     reset();
296   }
297 
298   private static final InstructionFinder.CodeConstraint my_constraint =
299     new InstructionFinder.CodeConstraint() {
300       public boolean checkCode(final InstructionHandle[] match) {
301         final BranchInstruction if_icmp = (BranchInstruction)match[0].getInstruction();
302         final GOTO              goto_   = (GOTO)match[2].getInstruction();
303         return (if_icmp.getTarget() == match[3]) && (goto_.getTarget() == match[4]);
304       }
305     };
306 
307   /**
308    * Replaces instruction sequences (typically generated by ASTIfExpr) of the form
309    *
310    * IF_ICMP__, ICONST_1, GOTO, ICONST_0, IFEQ, Instruction
311    *
312    * where the IF_ICMP__ branches to the ICONST_0 (else part) and the GOTO branches
313    * to the IFEQ with the simpler expression
314    *
315    * IF_ICMP__, Instruction
316    *
317    * where the IF_ICMP__ now branches to the target of the previous IFEQ instruction.
318    */
optimizeIFs(final InstructionList il)319   private static void optimizeIFs(final InstructionList il) {
320     final InstructionFinder f   = new InstructionFinder(il);
321     final String      pat = "IF_ICMP ICONST_1 GOTO ICONST_0 IFEQ Instruction";
322 
323     for(final Iterator<InstructionHandle[]> it = f.search(pat, my_constraint); it.hasNext();) {
324       final InstructionHandle[] match = it.next();
325       // Everything ok, update code
326       final BranchInstruction ifeq    = (BranchInstruction)(match[4].getInstruction());
327       final BranchHandle      if_icmp = (BranchHandle)match[0];
328 
329       if_icmp.setTarget(ifeq.getTarget());
330 
331       try {
332         il.delete(match[1], match[4]);
333       } catch(final TargetLostException e) {
334         final InstructionHandle[] targets = e.getTargets();
335 
336         System.err.println(targets[0]);
337 
338         for(int i=0; i < targets.length; i++) {
339           final InstructionTargeter[] targeters = targets[i].getTargeters();
340 
341           for(int j=0; j < targeters.length; j++) {
342         if((targets[i] != match[4]) || (targeters[j] != match[2])) {
343             System.err.println("Ooops: " + e);
344         }
345     }
346         }
347       }
348     }
349   }
350 
351   /**
352    * Overrides SimpleNode.toString()
353    */
354   @Override
toString()355   public String toString() {
356     final StringBuffer buf = new StringBuffer();
357     buf.append(jjtNodeName[id] + " " + name + "(");
358 
359     for(int i = 0; i < argv.length; i++) {
360       buf.append(argv[i].getName());
361       if(i < argv.length - 1) {
362         buf.append(", ");
363     }
364     }
365 
366     buf.append(")");
367     return buf.toString();
368   }
369 
isrecursive()370   public boolean    isrecursive()         { return is_recursive; }
isSimple()371   public boolean    isSimple()            { return is_simple; }
getName()372   public ASTIdent   getName()             { return name; }
getNoArgs()373   public int        getNoArgs()           { return argv.length; }
getArgs()374   public ASTIdent[] getArgs()             { return argv; }
getType()375   public int        getType()             { return type; }
setType(final int type)376   public void       setType(final int type)     { this.type = type; }
setLine(final int line)377   public void       setLine(final int line)     { this.line = line; }
getLine()378   public int        getLine()             { return line; }
setColumn(final int column)379   public void       setColumn(final int column) { this.column = column; }
getColumn()380   public int        getColumn()           { return column; }
setPosition(final int line, final int column)381   public void       setPosition(final int line, final int column) {
382     this.line = line;
383     this.column = column;
384   }
385 
386   /**
387    * Overrides SimpleNode.dump()
388    */
389   @Override
dump(final String prefix)390   public void dump(final String prefix) {
391     System.out.println(toString(prefix));
392 
393     for(int i = 0; i < argv.length; i++) {
394         argv[i].dump(prefix + " ");
395     }
396 
397     body.dump(prefix + " ");
398   }
399 
400   /* Used to simpulate stack with local vars and compute maximum stack size.
401    */
402   static int size, max_size;
403 
reset()404   static void reset() { size = max_size = 0; }
405 
getVarDecls()406   private static String getVarDecls() {
407     final StringBuffer buf = new StringBuffer("    int ");
408 
409     for(int i=0; i < max_size; i++) {
410       buf.append("_s" + i);
411 
412       if(i < max_size - 1) {
413         buf.append(", ");
414     }
415     }
416 
417     buf.append(";\n");
418     return buf.toString();
419   }
420 
421   /** Used by byte_code()
422    */
pop(final int s)423   static void pop(final int s) { size -= s; }
push(final int s)424   static void push(final int s) {
425     size += s;
426 
427     if(size > max_size) {
428         max_size = size;
429     }
430   }
push()431   static void push() { push(1); }
432 
433   /** Used byte code()
434    */
push(final StringBuffer buf, final String str)435   static void push(final StringBuffer buf, final String str) {
436     buf.append("    _s" + size + " = " + str + ";\n");
437     push(1);
438   }
439 
pop()440   static String pop() {
441     return "_s" + (--size);
442   }
443 }
444