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