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 ASTFunDecl(int id)63 ASTFunDecl(int id) { 64 super(id); 65 } 66 ASTFunDecl(MiniParser p, int id)67 ASTFunDecl(MiniParser p, int id) { 68 super(p, id); 69 } 70 jjtCreate(MiniParser p, int id)71 public static Node jjtCreate(MiniParser p, int id) { 72 return new ASTFunDecl(p, id); 73 } 74 ASTFunDecl(ASTIdent name, ASTIdent[] argv, ASTExpr body, int type)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 closeNode()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 */ traverse(Environment env)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 */ eval(int pass)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 */ code(PrintWriter out)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 */ byte_code(ClassGen class_gen, ConstantPoolGen cp)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 */ optimizeIFs(InstructionList il)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 toString()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 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(int type)376 public void setType(int type) { this.type = type; } setLine(int line)377 public void setLine(int line) { this.line = line; } getLine()378 public int getLine() { return line; } setColumn(int column)379 public void setColumn(int column) { this.column = column; } getColumn()380 public int getColumn() { return column; } setPosition(int line, int 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 dump(String prefix)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 reset()404 static void reset() { size = max_size = 0; } 405 getVarDecls()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 */ pop(int s)423 static void pop(int s) { size -= s; } push(int s)424 static void push(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(StringBuffer buf, String str)435 static void push(StringBuffer buf, 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