1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16package javassist.compiler; 17 18import javassist.CtClass; 19import javassist.CtPrimitiveType; 20import javassist.CtMember; 21import javassist.CtField; 22import javassist.CtBehavior; 23import javassist.CtMethod; 24import javassist.CtConstructor; 25import javassist.CannotCompileException; 26import javassist.Modifier; 27import javassist.bytecode.Bytecode; 28import javassist.bytecode.CodeAttribute; 29import javassist.bytecode.LocalVariableAttribute; 30import javassist.bytecode.BadBytecode; 31import javassist.bytecode.Opcode; 32import javassist.NotFoundException; 33 34import javassist.compiler.ast.*; 35 36public class Javac { 37 JvstCodeGen gen; 38 SymbolTable stable; 39 private Bytecode bytecode; 40 41 public static final String param0Name = "$0"; 42 public static final String resultVarName = "$_"; 43 public static final String proceedName = "$proceed"; 44 45 /** 46 * Constructs a compiler. 47 * 48 * @param thisClass the class that a compiled method/field 49 * belongs to. 50 */ 51 public Javac(CtClass thisClass) { 52 this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0), 53 thisClass); 54 } 55 56 /** 57 * Constructs a compiler. 58 * The produced bytecode is stored in the <code>Bytecode</code> object 59 * specified by <code>b</code>. 60 * 61 * @param thisClass the class that a compiled method/field 62 * belongs to. 63 */ 64 public Javac(Bytecode b, CtClass thisClass) { 65 gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool()); 66 stable = new SymbolTable(); 67 bytecode = b; 68 } 69 70 /** 71 * Returns the produced bytecode. 72 */ 73 public Bytecode getBytecode() { return bytecode; } 74 75 /** 76 * Compiles a method, constructor, or field declaration 77 * to a class. 78 * A field declaration can declare only one field. 79 * 80 * <p>In a method or constructor body, $0, $1, ... and $_ 81 * are not available. 82 * 83 * @return a <code>CtMethod</code>, <code>CtConstructor</code>, 84 * or <code>CtField</code> object. 85 * @see #recordProceed(String,String) 86 */ 87 public CtMember compile(String src) throws CompileError { 88 Parser p = new Parser(new Lex(src)); 89 ASTList mem = p.parseMember1(stable); 90 try { 91 if (mem instanceof FieldDecl) 92 return compileField((FieldDecl)mem); 93 else { 94 CtBehavior cb = compileMethod(p, (MethodDecl)mem); 95 CtClass decl = cb.getDeclaringClass(); 96 cb.getMethodInfo2() 97 .rebuildStackMapIf6(decl.getClassPool(), 98 decl.getClassFile2()); 99 return cb; 100 } 101 } 102 catch (BadBytecode bb) { 103 throw new CompileError(bb.getMessage()); 104 } 105 catch (CannotCompileException e) { 106 throw new CompileError(e.getMessage()); 107 } 108 } 109 110 public static class CtFieldWithInit extends CtField { 111 private ASTree init; 112 113 CtFieldWithInit(CtClass type, String name, CtClass declaring) 114 throws CannotCompileException 115 { 116 super(type, name, declaring); 117 init = null; 118 } 119 120 protected void setInit(ASTree i) { init = i; } 121 122 protected ASTree getInitAST() { 123 return init; 124 } 125 } 126 127 private CtField compileField(FieldDecl fd) 128 throws CompileError, CannotCompileException 129 { 130 CtFieldWithInit f; 131 Declarator d = fd.getDeclarator(); 132 f = new CtFieldWithInit(gen.resolver.lookupClass(d), 133 d.getVariable().get(), gen.getThisClass()); 134 f.setModifiers(MemberResolver.getModifiers(fd.getModifiers())); 135 if (fd.getInit() != null) 136 f.setInit(fd.getInit()); 137 138 return f; 139 } 140 141 private CtBehavior compileMethod(Parser p, MethodDecl md) 142 throws CompileError 143 { 144 int mod = MemberResolver.getModifiers(md.getModifiers()); 145 CtClass[] plist = gen.makeParamList(md); 146 CtClass[] tlist = gen.makeThrowsList(md); 147 recordParams(plist, Modifier.isStatic(mod)); 148 md = p.parseMethod2(stable, md); 149 try { 150 if (md.isConstructor()) { 151 CtConstructor cons = new CtConstructor(plist, 152 gen.getThisClass()); 153 cons.setModifiers(mod); 154 md.accept(gen); 155 cons.getMethodInfo().setCodeAttribute( 156 bytecode.toCodeAttribute()); 157 cons.setExceptionTypes(tlist); 158 return cons; 159 } 160 else { 161 Declarator r = md.getReturn(); 162 CtClass rtype = gen.resolver.lookupClass(r); 163 recordReturnType(rtype, false); 164 CtMethod method = new CtMethod(rtype, r.getVariable().get(), 165 plist, gen.getThisClass()); 166 method.setModifiers(mod); 167 gen.setThisMethod(method); 168 md.accept(gen); 169 if (md.getBody() != null) 170 method.getMethodInfo().setCodeAttribute( 171 bytecode.toCodeAttribute()); 172 else 173 method.setModifiers(mod | Modifier.ABSTRACT); 174 175 method.setExceptionTypes(tlist); 176 return method; 177 } 178 } 179 catch (NotFoundException e) { 180 throw new CompileError(e.toString()); 181 } 182 } 183 184 /** 185 * Compiles a method (or constructor) body. 186 * 187 * @src a single statement or a block. 188 * If null, this method produces a body returning zero or null. 189 */ 190 public Bytecode compileBody(CtBehavior method, String src) 191 throws CompileError 192 { 193 try { 194 int mod = method.getModifiers(); 195 recordParams(method.getParameterTypes(), Modifier.isStatic(mod)); 196 197 CtClass rtype; 198 if (method instanceof CtMethod) { 199 gen.setThisMethod((CtMethod)method); 200 rtype = ((CtMethod)method).getReturnType(); 201 } 202 else 203 rtype = CtClass.voidType; 204 205 recordReturnType(rtype, false); 206 boolean isVoid = rtype == CtClass.voidType; 207 208 if (src == null) 209 makeDefaultBody(bytecode, rtype); 210 else { 211 Parser p = new Parser(new Lex(src)); 212 SymbolTable stb = new SymbolTable(stable); 213 Stmnt s = p.parseStatement(stb); 214 if (p.hasMore()) 215 throw new CompileError( 216 "the method/constructor body must be surrounded by {}"); 217 218 boolean callSuper = false; 219 if (method instanceof CtConstructor) 220 callSuper = !((CtConstructor)method).isClassInitializer(); 221 222 gen.atMethodBody(s, callSuper, isVoid); 223 } 224 225 return bytecode; 226 } 227 catch (NotFoundException e) { 228 throw new CompileError(e.toString()); 229 } 230 } 231 232 private static void makeDefaultBody(Bytecode b, CtClass type) { 233 int op; 234 int value; 235 if (type instanceof CtPrimitiveType) { 236 CtPrimitiveType pt = (CtPrimitiveType)type; 237 op = pt.getReturnOp(); 238 if (op == Opcode.DRETURN) 239 value = Opcode.DCONST_0; 240 else if (op == Opcode.FRETURN) 241 value = Opcode.FCONST_0; 242 else if (op == Opcode.LRETURN) 243 value = Opcode.LCONST_0; 244 else if (op == Opcode.RETURN) 245 value = Opcode.NOP; 246 else 247 value = Opcode.ICONST_0; 248 } 249 else { 250 op = Opcode.ARETURN; 251 value = Opcode.ACONST_NULL; 252 } 253 254 if (value != Opcode.NOP) 255 b.addOpcode(value); 256 257 b.addOpcode(op); 258 } 259 260 /** 261 * Records local variables available at the specified program counter. 262 * If the LocalVariableAttribute is not available, this method does not 263 * record any local variable. It only returns false. 264 * 265 * @param pc program counter (>= 0) 266 * @return false if the CodeAttribute does not include a 267 * LocalVariableAttribute. 268 */ 269 public boolean recordLocalVariables(CodeAttribute ca, int pc) 270 throws CompileError 271 { 272 LocalVariableAttribute va 273 = (LocalVariableAttribute) 274 ca.getAttribute(LocalVariableAttribute.tag); 275 if (va == null) 276 return false; 277 278 int n = va.tableLength(); 279 for (int i = 0; i < n; ++i) { 280 int start = va.startPc(i); 281 int len = va.codeLength(i); 282 if (start <= pc && pc < start + len) 283 gen.recordVariable(va.descriptor(i), va.variableName(i), 284 va.index(i), stable); 285 } 286 287 return true; 288 } 289 290 /** 291 * Records parameter names if the LocalVariableAttribute is available. 292 * It returns false unless the LocalVariableAttribute is available. 293 * 294 * @param numOfLocalVars the number of local variables used 295 * for storing the parameters. 296 * @return false if the CodeAttribute does not include a 297 * LocalVariableAttribute. 298 */ 299 public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars) 300 throws CompileError 301 { 302 LocalVariableAttribute va 303 = (LocalVariableAttribute) 304 ca.getAttribute(LocalVariableAttribute.tag); 305 if (va == null) 306 return false; 307 308 int n = va.tableLength(); 309 for (int i = 0; i < n; ++i) { 310 int index = va.index(i); 311 if (index < numOfLocalVars) 312 gen.recordVariable(va.descriptor(i), va.variableName(i), 313 index, stable); 314 } 315 316 return true; 317 } 318 319 320 /** 321 * Makes variables $0 (this), $1, $2, ..., and $args represent method 322 * parameters. $args represents an array of all the parameters. 323 * It also makes $$ available as a parameter list of method call. 324 * 325 * <p>This must be called before calling <code>compileStmnt()</code> and 326 * <code>compileExpr()</code>. The correct value of 327 * <code>isStatic</code> must be recorded before compilation. 328 * <code>maxLocals</code> is updated to include $0,... 329 */ 330 public int recordParams(CtClass[] params, boolean isStatic) 331 throws CompileError 332 { 333 return gen.recordParams(params, isStatic, "$", "$args", "$$", stable); 334 } 335 336 /** 337 * Makes variables $0, $1, $2, ..., and $args represent method 338 * parameters. $args represents an array of all the parameters. 339 * It also makes $$ available as a parameter list of method call. 340 * $0 can represent a local variable other than THIS (variable 0). 341 * $class is also made available. 342 * 343 * <p>This must be called before calling <code>compileStmnt()</code> and 344 * <code>compileExpr()</code>. The correct value of 345 * <code>isStatic</code> must be recorded before compilation. 346 * <code>maxLocals</code> is updated to include $0,... 347 * 348 * @paaram use0 true if $0 is used. 349 * @param varNo the register number of $0 (use0 is true) 350 * or $1 (otherwise). 351 * @param target the type of $0 (it can be null if use0 is false). 352 * It is used as the name of the type represented 353 * by $class. 354 * @param isStatic true if the method in which the compiled bytecode 355 * is embedded is static. 356 */ 357 public int recordParams(String target, CtClass[] params, 358 boolean use0, int varNo, boolean isStatic) 359 throws CompileError 360 { 361 return gen.recordParams(params, isStatic, "$", "$args", "$$", 362 use0, varNo, target, stable); 363 } 364 365 /** 366 * Sets <code>maxLocals</code> to <code>max</code>. 367 * This method tells the compiler the local variables that have been 368 * allocated for the rest of the code. When the compiler needs 369 * new local variables, the local variables at the index <code>max</code>, 370 * <code>max + 1</code>, ... are assigned. 371 * 372 * <p>This method is indirectly called by <code>recordParams</code>. 373 */ 374 public void setMaxLocals(int max) { 375 gen.setMaxLocals(max); 376 } 377 378 /** 379 * Prepares to use cast $r, $w, $_, and $type. 380 * $type is made to represent the specified return type. 381 * It also enables to write a return statement with a return value 382 * for void method. 383 * 384 * <p>If the return type is void, ($r) does nothing. 385 * The type of $_ is java.lang.Object. 386 * 387 * @param type the return type. 388 * @param useResultVar true if $_ is used. 389 * @return -1 or the variable index assigned to $_. 390 * @see #recordType(CtClass) 391 */ 392 public int recordReturnType(CtClass type, boolean useResultVar) 393 throws CompileError 394 { 395 gen.recordType(type); 396 return gen.recordReturnType(type, "$r", 397 (useResultVar ? resultVarName : null), stable); 398 } 399 400 /** 401 * Prepares to use $type. Note that recordReturnType() overwrites 402 * the value of $type. 403 * 404 * @param t the type represented by $type. 405 */ 406 public void recordType(CtClass t) { 407 gen.recordType(t); 408 } 409 410 /** 411 * Makes the given variable available. 412 * 413 * @param type variable type 414 * @param name variable name 415 */ 416 public int recordVariable(CtClass type, String name) 417 throws CompileError 418 { 419 return gen.recordVariable(type, name, stable); 420 } 421 422 /** 423 * Prepares to use $proceed(). 424 * If the return type of $proceed() is void, null is pushed on the 425 * stack. 426 * 427 * @param target an expression specifying the target object. 428 * if null, "this" is the target. 429 * @param method the method name. 430 */ 431 public void recordProceed(String target, String method) 432 throws CompileError 433 { 434 Parser p = new Parser(new Lex(target)); 435 final ASTree texpr = p.parseExpression(stable); 436 final String m = method; 437 438 ProceedHandler h = new ProceedHandler() { 439 public void doit(JvstCodeGen gen, Bytecode b, ASTList args) 440 throws CompileError 441 { 442 ASTree expr = new Member(m); 443 if (texpr != null) 444 expr = Expr.make('.', texpr, expr); 445 446 expr = CallExpr.makeCall(expr, args); 447 gen.compileExpr(expr); 448 gen.addNullIfVoid(); 449 } 450 451 public void setReturnType(JvstTypeChecker check, ASTList args) 452 throws CompileError 453 { 454 ASTree expr = new Member(m); 455 if (texpr != null) 456 expr = Expr.make('.', texpr, expr); 457 458 expr = CallExpr.makeCall(expr, args); 459 expr.accept(check); 460 check.addNullIfVoid(); 461 } 462 }; 463 464 gen.setProceedHandler(h, proceedName); 465 } 466 467 /** 468 * Prepares to use $proceed() representing a static method. 469 * If the return type of $proceed() is void, null is pushed on the 470 * stack. 471 * 472 * @param targetClass the fully-qualified dot-separated name 473 * of the class declaring the method. 474 * @param method the method name. 475 */ 476 public void recordStaticProceed(String targetClass, String method) 477 throws CompileError 478 { 479 final String c = targetClass; 480 final String m = method; 481 482 ProceedHandler h = new ProceedHandler() { 483 public void doit(JvstCodeGen gen, Bytecode b, ASTList args) 484 throws CompileError 485 { 486 Expr expr = Expr.make(TokenId.MEMBER, 487 new Symbol(c), new Member(m)); 488 expr = CallExpr.makeCall(expr, args); 489 gen.compileExpr(expr); 490 gen.addNullIfVoid(); 491 } 492 493 public void setReturnType(JvstTypeChecker check, ASTList args) 494 throws CompileError 495 { 496 Expr expr = Expr.make(TokenId.MEMBER, 497 new Symbol(c), new Member(m)); 498 expr = CallExpr.makeCall(expr, args); 499 expr.accept(check); 500 check.addNullIfVoid(); 501 } 502 }; 503 504 gen.setProceedHandler(h, proceedName); 505 } 506 507 /** 508 * Prepares to use $proceed() representing a private/super's method. 509 * If the return type of $proceed() is void, null is pushed on the 510 * stack. This method is for methods invoked by INVOKESPECIAL. 511 * 512 * @param target an expression specifying the target object. 513 * if null, "this" is the target. 514 * @param classname the class name declaring the method. 515 * @param methodname the method name. 516 * @param descriptor the method descriptor. 517 */ 518 public void recordSpecialProceed(String target, String classname, 519 String methodname, String descriptor) 520 throws CompileError 521 { 522 Parser p = new Parser(new Lex(target)); 523 final ASTree texpr = p.parseExpression(stable); 524 final String cname = classname; 525 final String method = methodname; 526 final String desc = descriptor; 527 528 ProceedHandler h = new ProceedHandler() { 529 public void doit(JvstCodeGen gen, Bytecode b, ASTList args) 530 throws CompileError 531 { 532 gen.compileInvokeSpecial(texpr, cname, method, desc, args); 533 } 534 535 public void setReturnType(JvstTypeChecker c, ASTList args) 536 throws CompileError 537 { 538 c.compileInvokeSpecial(texpr, cname, method, desc, args); 539 } 540 541 }; 542 543 gen.setProceedHandler(h, proceedName); 544 } 545 546 /** 547 * Prepares to use $proceed(). 548 */ 549 public void recordProceed(ProceedHandler h) { 550 gen.setProceedHandler(h, proceedName); 551 } 552 553 /** 554 * Compiles a statement (or a block). 555 * <code>recordParams()</code> must be called before invoking 556 * this method. 557 * 558 * <p>Local variables that are not declared 559 * in the compiled source text might not be accessible within that 560 * source text. Fields and method parameters ($0, $1, ..) are available. 561 */ 562 public void compileStmnt(String src) throws CompileError { 563 Parser p = new Parser(new Lex(src)); 564 SymbolTable stb = new SymbolTable(stable); 565 while (p.hasMore()) { 566 Stmnt s = p.parseStatement(stb); 567 if (s != null) 568 s.accept(gen); 569 } 570 } 571 572 /** 573 * Compiles an exression. <code>recordParams()</code> must be 574 * called before invoking this method. 575 * 576 * <p>Local variables are not accessible 577 * within the compiled source text. Fields and method parameters 578 * ($0, $1, ..) are available if <code>recordParams()</code> 579 * have been invoked. 580 */ 581 public void compileExpr(String src) throws CompileError { 582 ASTree e = parseExpr(src, stable); 583 compileExpr(e); 584 } 585 586 /** 587 * Parsers an expression. 588 */ 589 public static ASTree parseExpr(String src, SymbolTable st) 590 throws CompileError 591 { 592 Parser p = new Parser(new Lex(src)); 593 return p.parseExpression(st); 594 } 595 596 /** 597 * Compiles an exression. <code>recordParams()</code> must be 598 * called before invoking this method. 599 * 600 * <p>Local variables are not accessible 601 * within the compiled source text. Fields and method parameters 602 * ($0, $1, ..) are available if <code>recordParams()</code> 603 * have been invoked. 604 */ 605 public void compileExpr(ASTree e) throws CompileError { 606 if (e != null) 607 gen.compileExpr(e); 608 } 609} 610