Home | History | Annotate | Download | only in Mini
      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  * @version $Id$
     49  */
     50 public class ASTFunDecl extends SimpleNode
     51 implements MiniParserTreeConstants, org.apache.bcel.Constants {
     52   private ASTIdent    name;
     53   private ASTIdent[]  argv;
     54   private ASTExpr     body;
     55   private int         type = T_UNKNOWN;
     56   private int         line, column;
     57   private boolean     is_simple;  // true, if simple expression like `12 + f(a)'
     58   private boolean     is_recursive; // Not used yet, TODO
     59 //  private int         max_depth; // max. expression tree depth
     60   private Environment env;
     61 
     62   // Generated methods
     63   ASTFunDecl(int id) {
     64     super(id);
     65   }
     66 
     67   ASTFunDecl(MiniParser p, int id) {
     68     super(p, id);
     69   }
     70 
     71   public static Node jjtCreate(MiniParser p, int id) {
     72     return new ASTFunDecl(p, id);
     73   }
     74 
     75   ASTFunDecl(ASTIdent name, ASTIdent[] argv, ASTExpr body, int type) {
     76     this(JJTFUNDECL);
     77 
     78     this.name = name;
     79     this.argv = argv;
     80     this.body = body;
     81     this.type = type;
     82   }
     83 
     84   /**
     85    * Overrides SimpleNode.closeNode()
     86    * Cast children to appropiate type.
     87    */
     88   @Override
     89   public void closeNode() {
     90     name = (ASTIdent)children[0];
     91     body = (ASTExpr)children[children.length - 1];
     92 
     93     argv = new ASTIdent[children.length - 2]; // May be 0-size array
     94     for(int i = 1; i < children.length - 1; i++) {
     95         argv[i - 1] = (ASTIdent)children[i];
     96     }
     97 
     98     children=null; // Throw away old reference
     99   }
    100 
    101   /**
    102    * First pass of parse tree.
    103    */
    104   public ASTFunDecl traverse(Environment env) {
    105     this.env = env;
    106 
    107     // Put arguments into hash table aka environment
    108     for(int i=0; i < argv.length; i++) {
    109       EnvEntry entry = env.get(argv[i].getName());
    110 
    111       if(entry != null) {
    112         MiniC.addError(argv[i].getLine(), argv[i].getColumn(),
    113                        "Redeclaration of " + entry + ".");
    114     } else {
    115         env.put(new Variable(argv[i]));
    116     }
    117     }
    118 
    119     /* Update entry of this function, i.e. set argument references.
    120      * The entry is already in there by garantuee, but may be of wrong type,
    121      * i.e. the user defined a function `TRUE', e.g. and `TRUE' is of type `Variable'.
    122      */
    123     try {
    124       Function fun = (Function)env.get(name.getName());
    125       fun.setArgs(argv);
    126     } catch(ClassCastException e) {} // Who cares?
    127 
    128     body = body.traverse(env); // Traverse expression body
    129 
    130     return this;
    131   }
    132 
    133   /** Second pass
    134    * @return type of expression
    135    */
    136   public int eval(int pass) {
    137     int expected = name.getType(); // Maybe other function has already called us
    138     type = body.eval(expected);    // And updated the env
    139 
    140     if((expected != T_UNKNOWN) && (type != expected)) {
    141         MiniC.addError(line, column,
    142                      "Function f ist not of type " + TYPE_NAMES[expected] +
    143                      " as previously assumed, but " + TYPE_NAMES[type]);
    144     }
    145 
    146     name.setType(type);
    147 
    148     is_simple = body.isSimple();
    149 
    150     if(pass == 2 && type == T_UNKNOWN) {
    151         is_recursive = true;
    152     }
    153 
    154     return type;
    155   }
    156 
    157   /**
    158    * Fourth pass, produce Java code.
    159    */
    160   public void code(PrintWriter out) {
    161     String expr;
    162     boolean main=false, ignore=false;
    163 
    164     String fname = name.getName();
    165 
    166     if(fname.equals("main")) {
    167       out.println("  public static void main(String[] _argv) {");
    168       main = true;
    169     }
    170     else if(fname.equals("READ") || fname.equals("WRITE")) { // Do nothing
    171       ignore = true;
    172     }
    173     else {
    174       out.print("  public static final " + "int" + // type_names[type] +
    175                 " " + fname + "(");
    176 
    177       for(int i = 0; i < argv.length; i++) {
    178         out.print("int " + argv[i].getName());
    179 
    180         if(i < argv.length - 1) {
    181         out.print(", ");
    182     }
    183       }
    184 
    185       out.println(")\n    throws IOException\n  {");
    186     }
    187 
    188     if(!ignore) {
    189       StringBuffer buf = new StringBuffer();
    190 
    191       body.code(buf);
    192       out.println(getVarDecls());
    193 
    194       expr = buf.toString();
    195 
    196       if(main) {
    197         out.println("    try {");
    198     }
    199 
    200       out.println(expr);
    201 
    202       if(main) {
    203         out.println("    } catch(Exception e) { System.err.println(e); }\n  }\n");
    204     } else {
    205         out.println("\n    return " + pop() + ";\n  }\n");
    206     }
    207     }
    208 
    209     reset();
    210   }
    211 
    212   /**
    213    * Fifth pass, produce Java byte code.
    214    */
    215   public void byte_code(ClassGen class_gen, ConstantPoolGen cp) {
    216     MethodGen method=null;
    217     boolean main=false, ignore=false;
    218     String class_name = class_gen.getClassName();
    219     String fname      = name.getName();
    220     InstructionList il = new InstructionList();
    221 
    222     Type[] args = { new ArrayType(Type.STRING, 1) }; // default for `main'
    223     String[] arg_names = { "$argv" };
    224 
    225     if(fname.equals("main")) {
    226       method = new MethodGen(ACC_STATIC | ACC_PUBLIC,
    227                              Type.VOID, args, arg_names,
    228                              "main", class_name, il, cp);
    229 
    230       main = true;
    231     } else if(fname.equals("READ") || fname.equals("WRITE")) { // Do nothing
    232       ignore = true;
    233     } else {
    234       int    size  = argv.length;
    235 
    236       arg_names = new String[size];
    237       args      = new Type[size];
    238 
    239       for(int i = 0; i < size; i++) {
    240         args[i] = Type.INT;
    241         arg_names[i] =  argv[i].getName();
    242       }
    243 
    244       method = new MethodGen(ACC_STATIC | ACC_PRIVATE | ACC_FINAL,
    245                              Type.INT, args, arg_names,
    246                              fname, class_name, il, cp);
    247 
    248       LocalVariableGen[] lv = method.getLocalVariables();
    249       for(int i = 0; i < size; i++) {
    250         Variable entry = (Variable)env.get(arg_names[i]);
    251         entry.setLocalVariable(lv[i]);
    252       }
    253 
    254       method.addException("java.io.IOException");
    255     }
    256 
    257     if(!ignore) {
    258       body.byte_code(il, method, cp);
    259 
    260       if(main) {
    261         ObjectType e_type = new ObjectType("java.lang.Exception");
    262         InstructionHandle start = il.getStart(), end, handler, end_handler;
    263         LocalVariableGen exc = method.addLocalVariable("$e",
    264                                                        e_type,
    265                                                        null, null);
    266         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(InstructionHandle[] match) {
    301         BranchInstruction if_icmp = (BranchInstruction)match[0].getInstruction();
    302         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    */
    319   private static void optimizeIFs(InstructionList il) {
    320     InstructionFinder f   = new InstructionFinder(il);
    321     String      pat = "IF_ICMP ICONST_1 GOTO ICONST_0 IFEQ Instruction";
    322 
    323     for(Iterator<InstructionHandle[]> it = f.search(pat, my_constraint); it.hasNext();) {
    324       InstructionHandle[] match = it.next();
    325       // Everything ok, update code
    326       BranchInstruction ifeq    = (BranchInstruction)(match[4].getInstruction());
    327       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(TargetLostException e) {
    334         InstructionHandle[] targets = e.getTargets();
    335 
    336         System.err.println(targets[0]);
    337 
    338         for(int i=0; i < targets.length; i++) {
    339           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
    355   public String toString() {
    356     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 
    370   public boolean    isrecursive()         { return is_recursive; }
    371   public boolean    isSimple()            { return is_simple; }
    372   public ASTIdent   getName()             { return name; }
    373   public int        getNoArgs()           { return argv.length; }
    374   public ASTIdent[] getArgs()             { return argv; }
    375   public int        getType()             { return type; }
    376   public void       setType(int type)     { this.type = type; }
    377   public void       setLine(int line)     { this.line = line; }
    378   public int        getLine()             { return line; }
    379   public void       setColumn(int column) { this.column = column; }
    380   public int        getColumn()           { return column; }
    381   public void       setPosition(int line, int column) {
    382     this.line = line;
    383     this.column = column;
    384   }
    385 
    386   /**
    387    * Overrides SimpleNode.dump()
    388    */
    389   @Override
    390   public void dump(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 
    404   static void reset() { size = max_size = 0; }
    405 
    406   private static String getVarDecls() {
    407     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    */
    423   static void pop(int s) { size -= s; }
    424   static void push(int s) {
    425     size += s;
    426 
    427     if(size > max_size) {
    428         max_size = size;
    429     }
    430   }
    431   static void push() { push(1); }
    432 
    433   /** Used byte code()
    434    */
    435   static void push(StringBuffer buf, String str) {
    436     buf.append("    _s" + size + " = " + str + ";\n");
    437     push(1);
    438   }
    439 
    440   static String pop() {
    441     return "_s" + (--size);
    442   }
    443 }
    444