/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* Generated By:JJTree: Do not edit this line. ASTFunDecl.java */ /* JJT: 0.3pre1 */ package Mini; import java.io.PrintWriter; import java.util.Iterator; import org.apache.bcel.generic.ALOAD; import org.apache.bcel.generic.ASTORE; import org.apache.bcel.generic.ArrayType; import org.apache.bcel.generic.BranchHandle; import org.apache.bcel.generic.BranchInstruction; import org.apache.bcel.generic.ClassGen; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.GETSTATIC; import org.apache.bcel.generic.GOTO; import org.apache.bcel.generic.INVOKEVIRTUAL; import org.apache.bcel.generic.InstructionConstants; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.InstructionTargeter; import org.apache.bcel.generic.LocalVariableGen; import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.TargetLostException; import org.apache.bcel.generic.Type; import org.apache.bcel.util.InstructionFinder; /** * * @version $Id$ */ public class ASTFunDecl extends SimpleNode implements MiniParserTreeConstants, org.apache.bcel.Constants { private ASTIdent name; private ASTIdent[] argv; private ASTExpr body; private int type = T_UNKNOWN; private int line, column; private boolean is_simple; // true, if simple expression like `12 + f(a)' private boolean is_recursive; // Not used yet, TODO // private int max_depth; // max. expression tree depth private Environment env; // Generated methods ASTFunDecl(int id) { super(id); } ASTFunDecl(MiniParser p, int id) { super(p, id); } public static Node jjtCreate(MiniParser p, int id) { return new ASTFunDecl(p, id); } ASTFunDecl(ASTIdent name, ASTIdent[] argv, ASTExpr body, int type) { this(JJTFUNDECL); this.name = name; this.argv = argv; this.body = body; this.type = type; } /** * Overrides SimpleNode.closeNode() * Cast children to appropiate type. */ @Override public void closeNode() { name = (ASTIdent)children[0]; body = (ASTExpr)children[children.length - 1]; argv = new ASTIdent[children.length - 2]; // May be 0-size array for(int i = 1; i < children.length - 1; i++) { argv[i - 1] = (ASTIdent)children[i]; } children=null; // Throw away old reference } /** * First pass of parse tree. */ public ASTFunDecl traverse(Environment env) { this.env = env; // Put arguments into hash table aka environment for(int i=0; i < argv.length; i++) { EnvEntry entry = env.get(argv[i].getName()); if(entry != null) { MiniC.addError(argv[i].getLine(), argv[i].getColumn(), "Redeclaration of " + entry + "."); } else { env.put(new Variable(argv[i])); } } /* Update entry of this function, i.e. set argument references. * The entry is already in there by garantuee, but may be of wrong type, * i.e. the user defined a function `TRUE', e.g. and `TRUE' is of type `Variable'. */ try { Function fun = (Function)env.get(name.getName()); fun.setArgs(argv); } catch(ClassCastException e) {} // Who cares? body = body.traverse(env); // Traverse expression body return this; } /** Second pass * @return type of expression */ public int eval(int pass) { int expected = name.getType(); // Maybe other function has already called us type = body.eval(expected); // And updated the env if((expected != T_UNKNOWN) && (type != expected)) { MiniC.addError(line, column, "Function f ist not of type " + TYPE_NAMES[expected] + " as previously assumed, but " + TYPE_NAMES[type]); } name.setType(type); is_simple = body.isSimple(); if(pass == 2 && type == T_UNKNOWN) { is_recursive = true; } return type; } /** * Fourth pass, produce Java code. */ public void code(PrintWriter out) { String expr; boolean main=false, ignore=false; String fname = name.getName(); if(fname.equals("main")) { out.println(" public static void main(String[] _argv) {"); main = true; } else if(fname.equals("READ") || fname.equals("WRITE")) { // Do nothing ignore = true; } else { out.print(" public static final " + "int" + // type_names[type] + " " + fname + "("); for(int i = 0; i < argv.length; i++) { out.print("int " + argv[i].getName()); if(i < argv.length - 1) { out.print(", "); } } out.println(")\n throws IOException\n {"); } if(!ignore) { StringBuffer buf = new StringBuffer(); body.code(buf); out.println(getVarDecls()); expr = buf.toString(); if(main) { out.println(" try {"); } out.println(expr); if(main) { out.println(" } catch(Exception e) { System.err.println(e); }\n }\n"); } else { out.println("\n return " + pop() + ";\n }\n"); } } reset(); } /** * Fifth pass, produce Java byte code. */ public void byte_code(ClassGen class_gen, ConstantPoolGen cp) { MethodGen method=null; boolean main=false, ignore=false; String class_name = class_gen.getClassName(); String fname = name.getName(); InstructionList il = new InstructionList(); Type[] args = { new ArrayType(Type.STRING, 1) }; // default for `main' String[] arg_names = { "$argv" }; if(fname.equals("main")) { method = new MethodGen(ACC_STATIC | ACC_PUBLIC, Type.VOID, args, arg_names, "main", class_name, il, cp); main = true; } else if(fname.equals("READ") || fname.equals("WRITE")) { // Do nothing ignore = true; } else { int size = argv.length; arg_names = new String[size]; args = new Type[size]; for(int i = 0; i < size; i++) { args[i] = Type.INT; arg_names[i] = argv[i].getName(); } method = new MethodGen(ACC_STATIC | ACC_PRIVATE | ACC_FINAL, Type.INT, args, arg_names, fname, class_name, il, cp); LocalVariableGen[] lv = method.getLocalVariables(); for(int i = 0; i < size; i++) { Variable entry = (Variable)env.get(arg_names[i]); entry.setLocalVariable(lv[i]); } method.addException("java.io.IOException"); } if(!ignore) { body.byte_code(il, method, cp); if(main) { ObjectType e_type = new ObjectType("java.lang.Exception"); InstructionHandle start = il.getStart(), end, handler, end_handler; LocalVariableGen exc = method.addLocalVariable("$e", e_type, null, null); int slot = exc.getIndex(); il.append(InstructionConstants.POP); pop(); // Remove last element on stack end = il.append(InstructionConstants.RETURN); // Use instruction constants, if possible // catch handler = il.append(new ASTORE(slot)); // save exception object il.append(new GETSTATIC(cp.addFieldref("java.lang.System", "err", "Ljava/io/PrintStream;"))); il.append(new ALOAD(slot)); push(2); il.append(new INVOKEVIRTUAL(cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/Object;)V"))); pop(2); end_handler = il.append(InstructionConstants.RETURN); method.addExceptionHandler(start, end, handler, e_type); exc.setStart(handler); exc.setEnd(end_handler); } else { il.append(InstructionConstants.IRETURN); // Reuse object to save memory } method.removeNOPs(); // First optimization pass, provided by MethodGen optimizeIFs(il); // Second optimization pass, application-specific method.setMaxStack(max_size); class_gen.addMethod(method.getMethod()); } il.dispose(); // Dispose instruction handles for better memory utilization reset(); } private static final InstructionFinder.CodeConstraint my_constraint = new InstructionFinder.CodeConstraint() { public boolean checkCode(InstructionHandle[] match) { BranchInstruction if_icmp = (BranchInstruction)match[0].getInstruction(); GOTO goto_ = (GOTO)match[2].getInstruction(); return (if_icmp.getTarget() == match[3]) && (goto_.getTarget() == match[4]); } }; /** * Replaces instruction sequences (typically generated by ASTIfExpr) of the form * * IF_ICMP__, ICONST_1, GOTO, ICONST_0, IFEQ, Instruction * * where the IF_ICMP__ branches to the ICONST_0 (else part) and the GOTO branches * to the IFEQ with the simpler expression * * IF_ICMP__, Instruction * * where the IF_ICMP__ now branches to the target of the previous IFEQ instruction. */ private static void optimizeIFs(InstructionList il) { InstructionFinder f = new InstructionFinder(il); String pat = "IF_ICMP ICONST_1 GOTO ICONST_0 IFEQ Instruction"; for(Iterator it = f.search(pat, my_constraint); it.hasNext();) { InstructionHandle[] match = it.next(); // Everything ok, update code BranchInstruction ifeq = (BranchInstruction)(match[4].getInstruction()); BranchHandle if_icmp = (BranchHandle)match[0]; if_icmp.setTarget(ifeq.getTarget()); try { il.delete(match[1], match[4]); } catch(TargetLostException e) { InstructionHandle[] targets = e.getTargets(); System.err.println(targets[0]); for(int i=0; i < targets.length; i++) { InstructionTargeter[] targeters = targets[i].getTargeters(); for(int j=0; j < targeters.length; j++) { if((targets[i] != match[4]) || (targeters[j] != match[2])) { System.err.println("Ooops: " + e); } } } } } } /** * Overrides SimpleNode.toString() */ @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append(jjtNodeName[id] + " " + name + "("); for(int i = 0; i < argv.length; i++) { buf.append(argv[i].getName()); if(i < argv.length - 1) { buf.append(", "); } } buf.append(")"); return buf.toString(); } public boolean isrecursive() { return is_recursive; } public boolean isSimple() { return is_simple; } public ASTIdent getName() { return name; } public int getNoArgs() { return argv.length; } public ASTIdent[] getArgs() { return argv; } public int getType() { return type; } public void setType(int type) { this.type = type; } public void setLine(int line) { this.line = line; } public int getLine() { return line; } public void setColumn(int column) { this.column = column; } public int getColumn() { return column; } public void setPosition(int line, int column) { this.line = line; this.column = column; } /** * Overrides SimpleNode.dump() */ @Override public void dump(String prefix) { System.out.println(toString(prefix)); for(int i = 0; i < argv.length; i++) { argv[i].dump(prefix + " "); } body.dump(prefix + " "); } /* Used to simpulate stack with local vars and compute maximum stack size. */ static int size, max_size; static void reset() { size = max_size = 0; } private static String getVarDecls() { StringBuffer buf = new StringBuffer(" int "); for(int i=0; i < max_size; i++) { buf.append("_s" + i); if(i < max_size - 1) { buf.append(", "); } } buf.append(";\n"); return buf.toString(); } /** Used by byte_code() */ static void pop(int s) { size -= s; } static void push(int s) { size += s; if(size > max_size) { max_size = size; } } static void push() { push(1); } /** Used byte code() */ static void push(StringBuffer buf, String str) { buf.append(" _s" + size + " = " + str + ";\n"); push(1); } static String pop() { return "_s" + (--size); } }