001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.compiler;
017:
018: import javassist.CtClass;
019: import javassist.CtPrimitiveType;
020: import javassist.CtMember;
021: import javassist.CtField;
022: import javassist.CtBehavior;
023: import javassist.CtMethod;
024: import javassist.CtConstructor;
025: import javassist.CannotCompileException;
026: import javassist.Modifier;
027: import javassist.bytecode.Bytecode;
028: import javassist.bytecode.CodeAttribute;
029: import javassist.bytecode.LocalVariableAttribute;
030: import javassist.bytecode.Opcode;
031: import javassist.NotFoundException;
032:
033: import javassist.compiler.ast.*;
034:
035: public class Javac {
036: JvstCodeGen gen;
037: SymbolTable stable;
038: private Bytecode bytecode;
039:
040: public static final String param0Name = "$0";
041: public static final String resultVarName = "$_";
042: public static final String proceedName = "$proceed";
043:
044: /**
045: * Constructs a compiler.
046: *
047: * @param thisClass the class that a compiled method/field
048: * belongs to.
049: */
050: public Javac(CtClass this Class) {
051: this (new Bytecode(this Class.getClassFile2().getConstPool(), 0,
052: 0), this Class);
053: }
054:
055: /**
056: * Constructs a compiler.
057: * The produced bytecode is stored in the <code>Bytecode</code> object
058: * specified by <code>b</code>.
059: *
060: * @param thisClass the class that a compiled method/field
061: * belongs to.
062: */
063: public Javac(Bytecode b, CtClass this Class) {
064: gen = new JvstCodeGen(b, this Class, this Class.getClassPool());
065: stable = new SymbolTable();
066: bytecode = b;
067: }
068:
069: /**
070: * Returns the produced bytecode.
071: */
072: public Bytecode getBytecode() {
073: return bytecode;
074: }
075:
076: /**
077: * Compiles a method, constructor, or field declaration
078: * to a class.
079: * A field declaration can declare only one field.
080: *
081: * <p>In a method or constructor body, $0, $1, ... and $_
082: * are not available.
083: *
084: * @return a <code>CtMethod</code>, <code>CtConstructor</code>,
085: * or <code>CtField</code> object.
086: * @see #recordProceed(String,String)
087: */
088: public CtMember compile(String src) throws CompileError {
089: Parser p = new Parser(new Lex(src));
090: ASTList mem = p.parseMember1(stable);
091: try {
092: if (mem instanceof FieldDecl)
093: return compileField((FieldDecl) mem);
094: else
095: return compileMethod(p, (MethodDecl) mem);
096: } catch (CannotCompileException e) {
097: throw new CompileError(e.getMessage());
098: }
099: }
100:
101: public static class CtFieldWithInit extends CtField {
102: private ASTree init;
103:
104: CtFieldWithInit(CtClass type, String name, CtClass declaring)
105: throws CannotCompileException {
106: super (type, name, declaring);
107: init = null;
108: }
109:
110: protected void setInit(ASTree i) {
111: init = i;
112: }
113:
114: protected ASTree getInitAST() {
115: return init;
116: }
117: }
118:
119: private CtField compileField(FieldDecl fd) throws CompileError,
120: CannotCompileException {
121: CtFieldWithInit f;
122: Declarator d = fd.getDeclarator();
123: f = new CtFieldWithInit(gen.resolver.lookupClass(d), d
124: .getVariable().get(), gen.getThisClass());
125: f.setModifiers(MemberResolver.getModifiers(fd.getModifiers()));
126: if (fd.getInit() != null)
127: f.setInit(fd.getInit());
128:
129: return f;
130: }
131:
132: private CtMember compileMethod(Parser p, MethodDecl md)
133: throws CompileError {
134: int mod = MemberResolver.getModifiers(md.getModifiers());
135: CtClass[] plist = gen.makeParamList(md);
136: CtClass[] tlist = gen.makeThrowsList(md);
137: recordParams(plist, Modifier.isStatic(mod));
138: md = p.parseMethod2(stable, md);
139: try {
140: if (md.isConstructor()) {
141: CtConstructor cons = new CtConstructor(plist, gen
142: .getThisClass());
143: cons.setModifiers(mod);
144: md.accept(gen);
145: cons.getMethodInfo().setCodeAttribute(
146: bytecode.toCodeAttribute());
147: cons.setExceptionTypes(tlist);
148: return cons;
149: } else {
150: Declarator r = md.getReturn();
151: CtClass rtype = gen.resolver.lookupClass(r);
152: recordReturnType(rtype, false);
153: CtMethod method = new CtMethod(rtype, r.getVariable()
154: .get(), plist, gen.getThisClass());
155: method.setModifiers(mod);
156: gen.setThisMethod(method);
157: md.accept(gen);
158: if (md.getBody() != null)
159: method.getMethodInfo().setCodeAttribute(
160: bytecode.toCodeAttribute());
161: else
162: method.setModifiers(mod | Modifier.ABSTRACT);
163:
164: method.setExceptionTypes(tlist);
165: return method;
166: }
167: } catch (NotFoundException e) {
168: throw new CompileError(e.toString());
169: }
170: }
171:
172: /**
173: * Compiles a method (or constructor) body.
174: *
175: * @src a single statement or a block.
176: * If null, this method produces a body returning zero or null.
177: */
178: public Bytecode compileBody(CtBehavior method, String src)
179: throws CompileError {
180: try {
181: int mod = method.getModifiers();
182: recordParams(method.getParameterTypes(), Modifier
183: .isStatic(mod));
184:
185: CtClass rtype;
186: if (method instanceof CtMethod) {
187: gen.setThisMethod((CtMethod) method);
188: rtype = ((CtMethod) method).getReturnType();
189: } else
190: rtype = CtClass.voidType;
191:
192: recordReturnType(rtype, false);
193: boolean isVoid = rtype == CtClass.voidType;
194:
195: if (src == null)
196: makeDefaultBody(bytecode, rtype);
197: else {
198: Parser p = new Parser(new Lex(src));
199: SymbolTable stb = new SymbolTable(stable);
200: Stmnt s = p.parseStatement(stb);
201: if (p.hasMore())
202: throw new CompileError(
203: "the method/constructor body must be surrounded by {}");
204:
205: boolean callSuper = false;
206: if (method instanceof CtConstructor)
207: callSuper = !((CtConstructor) method)
208: .isClassInitializer();
209:
210: gen.atMethodBody(s, callSuper, isVoid);
211: }
212:
213: return bytecode;
214: } catch (NotFoundException e) {
215: throw new CompileError(e.toString());
216: }
217: }
218:
219: private static void makeDefaultBody(Bytecode b, CtClass type) {
220: int op;
221: int value;
222: if (type instanceof CtPrimitiveType) {
223: CtPrimitiveType pt = (CtPrimitiveType) type;
224: op = pt.getReturnOp();
225: if (op == Opcode.DRETURN)
226: value = Opcode.DCONST_0;
227: else if (op == Opcode.FRETURN)
228: value = Opcode.FCONST_0;
229: else if (op == Opcode.LRETURN)
230: value = Opcode.LCONST_0;
231: else if (op == Opcode.RETURN)
232: value = Opcode.NOP;
233: else
234: value = Opcode.ICONST_0;
235: } else {
236: op = Opcode.ARETURN;
237: value = Opcode.ACONST_NULL;
238: }
239:
240: if (value != Opcode.NOP)
241: b.addOpcode(value);
242:
243: b.addOpcode(op);
244: }
245:
246: /**
247: * Records local variables available at the specified program counter.
248: * If the LocalVariableAttribute is not available, this method does not
249: * record any local variable. It only returns false.
250: *
251: * @param pc program counter (>= 0)
252: * @return false if the CodeAttribute does not include a
253: * LocalVariableAttribute.
254: */
255: public boolean recordLocalVariables(CodeAttribute ca, int pc)
256: throws CompileError {
257: LocalVariableAttribute va = (LocalVariableAttribute) ca
258: .getAttribute(LocalVariableAttribute.tag);
259: if (va == null)
260: return false;
261:
262: int n = va.tableLength();
263: for (int i = 0; i < n; ++i) {
264: int start = va.startPc(i);
265: int len = va.codeLength(i);
266: if (start <= pc && pc < start + len)
267: gen.recordVariable(va.descriptor(i),
268: va.variableName(i), va.index(i), stable);
269: }
270:
271: return true;
272: }
273:
274: /**
275: * Records parameter names if the LocalVariableAttribute is available.
276: * It returns false unless the LocalVariableAttribute is available.
277: *
278: * @param numOfLocalVars the number of local variables used
279: * for storing the parameters.
280: * @return false if the CodeAttribute does not include a
281: * LocalVariableAttribute.
282: */
283: public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars)
284: throws CompileError {
285: LocalVariableAttribute va = (LocalVariableAttribute) ca
286: .getAttribute(LocalVariableAttribute.tag);
287: if (va == null)
288: return false;
289:
290: int n = va.tableLength();
291: for (int i = 0; i < n; ++i) {
292: int index = va.index(i);
293: if (index < numOfLocalVars)
294: gen.recordVariable(va.descriptor(i),
295: va.variableName(i), index, stable);
296: }
297:
298: return true;
299: }
300:
301: /**
302: * Makes variables $0 (this), $1, $2, ..., and $args represent method
303: * parameters. $args represents an array of all the parameters.
304: * It also makes $$ available as a parameter list of method call.
305: *
306: * <p>This must be called before calling <code>compileStmnt()</code> and
307: * <code>compileExpr()</code>. The correct value of
308: * <code>isStatic</code> must be recorded before compilation.
309: * <code>maxLocals</code> is updated to include $0,...
310: */
311: public int recordParams(CtClass[] params, boolean isStatic)
312: throws CompileError {
313: return gen.recordParams(params, isStatic, "$", "$args", "$$",
314: stable);
315: }
316:
317: /**
318: * Makes variables $0, $1, $2, ..., and $args represent method
319: * parameters. $args represents an array of all the parameters.
320: * It also makes $$ available as a parameter list of method call.
321: * $0 can represent a local variable other than THIS (variable 0).
322: * $class is also made available.
323: *
324: * <p>This must be called before calling <code>compileStmnt()</code> and
325: * <code>compileExpr()</code>. The correct value of
326: * <code>isStatic</code> must be recorded before compilation.
327: * <code>maxLocals</code> is updated to include $0,...
328: *
329: * @paaram use0 true if $0 is used.
330: * @param varNo the register number of $0 (use0 is true)
331: * or $1 (otherwise).
332: * @param target the type of $0 (it can be null if use0 is false).
333: * It is used as the name of the type represented
334: * by $class.
335: * @param isStatic true if the method in which the compiled bytecode
336: * is embedded is static.
337: */
338: public int recordParams(String target, CtClass[] params,
339: boolean use0, int varNo, boolean isStatic)
340: throws CompileError {
341: return gen.recordParams(params, isStatic, "$", "$args", "$$",
342: use0, varNo, target, stable);
343: }
344:
345: /**
346: * Sets <code>maxLocals</code> to <code>max</code>.
347: * This method tells the compiler the local variables that have been
348: * allocated for the rest of the code. When the compiler needs
349: * new local variables, the local variables at the index <code>max</code>,
350: * <code>max + 1</code>, ... are assigned.
351: *
352: * <p>This method is indirectly called by <code>recordParams</code>.
353: */
354: public void setMaxLocals(int max) {
355: gen.setMaxLocals(max);
356: }
357:
358: /**
359: * Prepares to use cast $r, $w, $_, and $type.
360: * $type is made to represent the specified return type.
361: * It also enables to write a return statement with a return value
362: * for void method.
363: *
364: * <p>If the return type is void, ($r) does nothing.
365: * The type of $_ is java.lang.Object.
366: *
367: * @param type the return type.
368: * @param useResultVar true if $_ is used.
369: * @return -1 or the variable index assigned to $_.
370: * @see #recordType(CtClass)
371: */
372: public int recordReturnType(CtClass type, boolean useResultVar)
373: throws CompileError {
374: gen.recordType(type);
375: return gen.recordReturnType(type, "$r",
376: (useResultVar ? resultVarName : null), stable);
377: }
378:
379: /**
380: * Prepares to use $type. Note that recordReturnType() overwrites
381: * the value of $type.
382: *
383: * @param t the type represented by $type.
384: */
385: public void recordType(CtClass t) {
386: gen.recordType(t);
387: }
388:
389: /**
390: * Makes the given variable available.
391: *
392: * @param type variable type
393: * @param name variable name
394: */
395: public int recordVariable(CtClass type, String name)
396: throws CompileError {
397: return gen.recordVariable(type, name, stable);
398: }
399:
400: /**
401: * Prepares to use $proceed().
402: * If the return type of $proceed() is void, null is pushed on the
403: * stack.
404: *
405: * @param target an expression specifying the target object.
406: * if null, "this" is the target.
407: * @param method the method name.
408: */
409: public void recordProceed(String target, String method)
410: throws CompileError {
411: Parser p = new Parser(new Lex(target));
412: final ASTree texpr = p.parseExpression(stable);
413: final String m = method;
414:
415: ProceedHandler h = new ProceedHandler() {
416: public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
417: throws CompileError {
418: ASTree expr = new Member(m);
419: if (texpr != null)
420: expr = Expr.make('.', texpr, expr);
421:
422: expr = CallExpr.makeCall(expr, args);
423: gen.compileExpr(expr);
424: gen.addNullIfVoid();
425: }
426:
427: public void setReturnType(JvstTypeChecker check,
428: ASTList args) throws CompileError {
429: ASTree expr = new Member(m);
430: if (texpr != null)
431: expr = Expr.make('.', texpr, expr);
432:
433: expr = CallExpr.makeCall(expr, args);
434: expr.accept(check);
435: check.addNullIfVoid();
436: }
437: };
438:
439: gen.setProceedHandler(h, proceedName);
440: }
441:
442: /**
443: * Prepares to use $proceed() representing a static method.
444: * If the return type of $proceed() is void, null is pushed on the
445: * stack.
446: *
447: * @param targetClass the fully-qualified dot-separated name
448: * of the class declaring the method.
449: * @param method the method name.
450: */
451: public void recordStaticProceed(String targetClass, String method)
452: throws CompileError {
453: final String c = targetClass;
454: final String m = method;
455:
456: ProceedHandler h = new ProceedHandler() {
457: public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
458: throws CompileError {
459: Expr expr = Expr.make(TokenId.MEMBER, new Symbol(c),
460: new Member(m));
461: expr = CallExpr.makeCall(expr, args);
462: gen.compileExpr(expr);
463: gen.addNullIfVoid();
464: }
465:
466: public void setReturnType(JvstTypeChecker check,
467: ASTList args) throws CompileError {
468: Expr expr = Expr.make(TokenId.MEMBER, new Symbol(c),
469: new Member(m));
470: expr = CallExpr.makeCall(expr, args);
471: expr.accept(check);
472: check.addNullIfVoid();
473: }
474: };
475:
476: gen.setProceedHandler(h, proceedName);
477: }
478:
479: /**
480: * Prepares to use $proceed() representing a private/super's method.
481: * If the return type of $proceed() is void, null is pushed on the
482: * stack. This method is for methods invoked by INVOKESPECIAL.
483: *
484: * @param target an expression specifying the target object.
485: * if null, "this" is the target.
486: * @param classname the class name declaring the method.
487: * @param methodname the method name.
488: * @param descriptor the method descriptor.
489: */
490: public void recordSpecialProceed(String target, String classname,
491: String methodname, String descriptor) throws CompileError {
492: Parser p = new Parser(new Lex(target));
493: final ASTree texpr = p.parseExpression(stable);
494: final String cname = classname;
495: final String method = methodname;
496: final String desc = descriptor;
497:
498: ProceedHandler h = new ProceedHandler() {
499: public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
500: throws CompileError {
501: gen.compileInvokeSpecial(texpr, cname, method, desc,
502: args);
503: }
504:
505: public void setReturnType(JvstTypeChecker c, ASTList args)
506: throws CompileError {
507: c
508: .compileInvokeSpecial(texpr, cname, method,
509: desc, args);
510: }
511:
512: };
513:
514: gen.setProceedHandler(h, proceedName);
515: }
516:
517: /**
518: * Prepares to use $proceed().
519: */
520: public void recordProceed(ProceedHandler h) {
521: gen.setProceedHandler(h, proceedName);
522: }
523:
524: /**
525: * Compiles a statement (or a block).
526: * <code>recordParams()</code> must be called before invoking
527: * this method.
528: *
529: * <p>Local variables that are not declared
530: * in the compiled source text might not be accessible within that
531: * source text. Fields and method parameters ($0, $1, ..) are available.
532: */
533: public void compileStmnt(String src) throws CompileError {
534: Parser p = new Parser(new Lex(src));
535: SymbolTable stb = new SymbolTable(stable);
536: while (p.hasMore()) {
537: Stmnt s = p.parseStatement(stb);
538: if (s != null)
539: s.accept(gen);
540: }
541: }
542:
543: /**
544: * Compiles an exression. <code>recordParams()</code> must be
545: * called before invoking this method.
546: *
547: * <p>Local variables are not accessible
548: * within the compiled source text. Fields and method parameters
549: * ($0, $1, ..) are available if <code>recordParams()</code>
550: * have been invoked.
551: */
552: public void compileExpr(String src) throws CompileError {
553: ASTree e = parseExpr(src, stable);
554: compileExpr(e);
555: }
556:
557: /**
558: * Parsers an expression.
559: */
560: public static ASTree parseExpr(String src, SymbolTable st)
561: throws CompileError {
562: Parser p = new Parser(new Lex(src));
563: return p.parseExpression(st);
564: }
565:
566: /**
567: * Compiles an exression. <code>recordParams()</code> must be
568: * called before invoking this method.
569: *
570: * <p>Local variables are not accessible
571: * within the compiled source text. Fields and method parameters
572: * ($0, $1, ..) are available if <code>recordParams()</code>
573: * have been invoked.
574: */
575: public void compileExpr(ASTree e) throws CompileError {
576: if (e != null)
577: gen.compileExpr(e);
578: }
579: }
|