| /* |
| * 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<InstructionHandle[]> 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); |
| } |
| } |