| /* |
| * 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 |
| * |
| * https://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.Const; |
| 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.InstructionConst; |
| 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; |
| |
| /** |
| */ |
| public class ASTFunDecl extends SimpleNode implements MiniParserTreeConstants { |
| private static final InstructionFinder.CodeConstraint my_constraint = match -> { |
| final BranchInstruction if_icmp = (BranchInstruction) match[0].getInstruction(); |
| final GOTO goto_ = (GOTO) match[2].getInstruction(); |
| return if_icmp.getTarget() == match[3] && goto_.getTarget() == match[4]; |
| }; |
| /* |
| * Used to simpulate stack with local vars and compute maximum stack size. |
| */ |
| static int size, max_size; |
| |
| private static String getVarDecls() { |
| final StringBuilder buf = new StringBuilder(" 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(); |
| } |
| |
| public static Node jjtCreate(final MiniParser p, final int id) { |
| return new ASTFunDecl(p, id); |
| } |
| |
| /** |
| * 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(final InstructionList il) { |
| final InstructionFinder f = new InstructionFinder(il); |
| final String pat = "IF_ICMP ICONST_1 GOTO ICONST_0 IFEQ Instruction"; |
| |
| for (final Iterator<InstructionHandle[]> it = f.search(pat, my_constraint); it.hasNext();) { |
| final InstructionHandle[] match = it.next(); |
| // Everything ok, update code |
| final BranchInstruction ifeq = (BranchInstruction) match[4].getInstruction(); |
| final BranchHandle if_icmp = (BranchHandle) match[0]; |
| |
| if_icmp.setTarget(ifeq.getTarget()); |
| |
| try { |
| il.delete(match[1], match[4]); |
| } catch (final TargetLostException e) { |
| final InstructionHandle[] targets = e.getTargets(); |
| |
| System.err.println(targets[0]); |
| |
| for (final InstructionHandle target : targets) { |
| final InstructionTargeter[] targeters = target.getTargeters(); |
| |
| for (final InstructionTargeter targeter : targeters) { |
| // TODO Is this really identity equality and not object equality? |
| if (target != match[4] || targeter != match[2]) { |
| System.err.println("Unexpected: " + e); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| static String pop() { |
| return "_s" + --size; |
| } |
| |
| /** |
| * Used by byte_code() |
| */ |
| static void pop(final int s) { |
| size -= s; |
| } |
| |
| static void push() { |
| push(1); |
| } |
| |
| static void push(final int s) { |
| size += s; |
| |
| if (size > max_size) { |
| max_size = size; |
| } |
| } |
| |
| /** |
| * Used byte code() |
| */ |
| static void push(final StringBuffer buf, final String str) { |
| buf.append(" _s" + size + " = " + str + ";\n"); |
| push(1); |
| } |
| |
| static void reset() { |
| size = max_size = 0; |
| } |
| |
| private ASTIdent name; |
| |
| private ASTIdent[] argv; |
| |
| private ASTExpr body; |
| |
| private int type = Const.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; |
| |
| ASTFunDecl(final ASTIdent name, final ASTIdent[] argv, final ASTExpr body, final int type) { |
| this(JJTFUNDECL); |
| |
| this.name = name; |
| this.argv = argv; |
| this.body = body; |
| this.type = type; |
| } |
| |
| // Generated methods |
| ASTFunDecl(final int id) { |
| super(id); |
| } |
| |
| ASTFunDecl(final MiniParser p, final int id) { |
| super(p, id); |
| } |
| |
| /** |
| * Fifth pass, produce Java byte code. |
| */ |
| public void byte_code(final ClassGen classGen, final ConstantPoolGen cp) { |
| MethodGen method = null; |
| boolean main = false, ignore = false; |
| final String className = classGen.getClassName(); |
| final String fname = name.getName(); |
| final 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(Const.ACC_STATIC | Const.ACC_PUBLIC, Type.VOID, args, arg_names, "main", className, il, cp); |
| |
| main = true; |
| } else if (fname.equals("READ") || fname.equals("WRITE")) { // Do nothing |
| ignore = true; |
| } else { |
| final 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(Const.ACC_STATIC | Const.ACC_PRIVATE | Const.ACC_FINAL, Type.INT, args, arg_names, fname, className, il, cp); |
| |
| final LocalVariableGen[] lv = method.getLocalVariables(); |
| for (int i = 0; i < size; i++) { |
| final 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) { |
| final ObjectType eType = new ObjectType("java.lang.Exception"); |
| final InstructionHandle start = il.getStart(); |
| InstructionHandle end, handler, end_handler; |
| final LocalVariableGen exc = method.addLocalVariable("$e", eType, null, null); |
| final int slot = exc.getIndex(); |
| |
| il.append(InstructionConst.POP); |
| pop(); // Remove last element on stack |
| end = il.append(InstructionConst.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(InstructionConst.RETURN); |
| method.addExceptionHandler(start, end, handler, eType); |
| exc.setStart(handler); |
| exc.setEnd(end_handler); |
| } else { |
| il.append(InstructionConst.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); |
| classGen.addMethod(method.getMethod()); |
| } |
| |
| il.dispose(); // Dispose instruction handles for better memory utilization |
| |
| reset(); |
| } |
| |
| /** |
| * Overrides SimpleNode.closeNode() Cast children to appropriate 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 |
| } |
| |
| /** |
| * Fourth pass, produce Java code. |
| */ |
| public void code(final PrintWriter out) { |
| String expr; |
| boolean main = false, ignore = false; |
| |
| final 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) { |
| final 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(); |
| } |
| |
| /** |
| * Overrides SimpleNode.dump() |
| */ |
| @Override |
| public void dump(final String prefix) { |
| System.out.println(toString(prefix)); |
| |
| for (final ASTIdent element : argv) { |
| element.dump(prefix + " "); |
| } |
| |
| body.dump(prefix + " "); |
| } |
| |
| /** |
| * Second pass. |
| * |
| * @return type of expression. |
| */ |
| public int eval(final int pass) { |
| final int expected = name.getType(); // Maybe other function has already called us |
| type = body.eval(expected); // And updated the env |
| |
| if (expected != Const.T_UNKNOWN && type != expected) { |
| MiniC.addError(line, column, |
| "Function f ist not of type " + Const.getTypeName(expected) + " as previously assumed, but " + Const.getTypeName(type)); |
| } |
| |
| name.setType(type); |
| |
| is_simple = body.isSimple(); |
| |
| if (pass == 2 && type == Const.T_UNKNOWN) { |
| is_recursive = true; |
| } |
| |
| return type; |
| } |
| |
| public ASTIdent[] getArgs() { |
| return argv; |
| } |
| |
| public int getColumn() { |
| return column; |
| } |
| |
| public int getLine() { |
| return line; |
| } |
| |
| public ASTIdent getName() { |
| return name; |
| } |
| |
| public int getNoArgs() { |
| return argv.length; |
| } |
| |
| public int getType() { |
| return type; |
| } |
| |
| public boolean isrecursive() { |
| return is_recursive; |
| } |
| |
| public boolean isSimple() { |
| return is_simple; |
| } |
| |
| public void setColumn(final int column) { |
| this.column = column; |
| } |
| |
| public void setLine(final int line) { |
| this.line = line; |
| } |
| |
| public void setPosition(final int line, final int column) { |
| this.line = line; |
| this.column = column; |
| } |
| |
| public void setType(final int type) { |
| this.type = type; |
| } |
| |
| /** |
| * Overrides SimpleNode.toString() |
| */ |
| @Override |
| public String toString() { |
| final StringBuilder buf = new StringBuilder(); |
| 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(); |
| } |
| |
| /** |
| * First pass of parse tree. |
| */ |
| public ASTFunDecl traverse(final Environment env) { |
| this.env = env; |
| |
| // Put arguments into hash table aka environment |
| for (final ASTIdent element : argv) { |
| final EnvEntry entry = env.get(element.getName()); |
| |
| if (entry != null) { |
| MiniC.addError(element.getLine(), element.getColumn(), "Redeclaration of " + entry + "."); |
| } else { |
| env.put(new Variable(element)); |
| } |
| } |
| |
| /* |
| * Update entry of this function, that is, sets argument references. The entry is already in there by garantee, but may be |
| * of the wrong type: The user-defined a function 'TRUE', for example and 'TRUE' is of type 'Variable'. |
| */ |
| try { |
| final Function fun = (Function) env.get(name.getName()); |
| fun.setArgs(argv); |
| } catch (final ClassCastException e) { |
| } // Who cares? |
| |
| body = body.traverse(env); // Traverse expression body |
| |
| return this; |
| } |
| } |