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