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.*;
019: import javassist.bytecode.*;
020: import javassist.compiler.ast.*;
021:
022: /* Code generator accepting extended Java syntax for Javassist.
023: */
024:
025: public class JvstCodeGen extends MemberCodeGen {
026: String paramArrayName = null;
027: String paramListName = null;
028: CtClass[] paramTypeList = null;
029: private int paramVarBase = 0; // variable index for $0 or $1.
030: private boolean useParam0 = false; // true if $0 is used.
031: private String param0Type = null; // JVM name
032: public static final String sigName = "$sig";
033: public static final String dollarTypeName = "$type";
034: public static final String clazzName = "$class";
035: private CtClass dollarType = null;
036: CtClass returnType = null;
037: String returnCastName = null;
038: private String returnVarName = null; // null if $_ is not used.
039: public static final String wrapperCastName = "$w";
040: String proceedName = null;
041: public static final String cflowName = "$cflow";
042: ProceedHandler procHandler = null; // null if not used.
043:
044: public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
045: super (b, cc, cp);
046: setTypeChecker(new JvstTypeChecker(cc, cp, this ));
047: }
048:
049: /* Index of $1.
050: */
051: private int indexOfParam1() {
052: return paramVarBase + (useParam0 ? 1 : 0);
053: }
054:
055: /* Records a ProceedHandler obejct.
056: *
057: * @param name the name of the special method call.
058: * it is usually $proceed.
059: */
060: public void setProceedHandler(ProceedHandler h, String name) {
061: proceedName = name;
062: procHandler = h;
063: }
064:
065: /* If the type of the expression compiled last is void,
066: * add ACONST_NULL and change exprType, arrayDim, className.
067: */
068: public void addNullIfVoid() {
069: if (exprType == VOID) {
070: bytecode.addOpcode(ACONST_NULL);
071: exprType = CLASS;
072: arrayDim = 0;
073: className = jvmJavaLangObject;
074: }
075: }
076:
077: /* To support $args, $sig, and $type.
078: * $args is an array of parameter list.
079: */
080: public void atMember(Member mem) throws CompileError {
081: String name = mem.get();
082: if (name.equals(paramArrayName)) {
083: compileParameterList(bytecode, paramTypeList,
084: indexOfParam1());
085: exprType = CLASS;
086: arrayDim = 1;
087: className = jvmJavaLangObject;
088: } else if (name.equals(sigName)) {
089: bytecode.addLdc(Descriptor.ofMethod(returnType,
090: paramTypeList));
091: bytecode.addInvokestatic("javassist/runtime/Desc",
092: "getParams",
093: "(Ljava/lang/String;)[Ljava/lang/Class;");
094: exprType = CLASS;
095: arrayDim = 1;
096: className = "java/lang/Class";
097: } else if (name.equals(dollarTypeName)) {
098: if (dollarType == null)
099: throw new CompileError(dollarType + " is not available");
100:
101: bytecode.addLdc(Descriptor.of(dollarType));
102: callGetType("getType");
103: } else if (name.equals(clazzName)) {
104: if (param0Type == null)
105: throw new CompileError(clazzName + " is not available");
106:
107: bytecode.addLdc(param0Type);
108: callGetType("getClazz");
109: } else
110: super .atMember(mem);
111: }
112:
113: private void callGetType(String method) {
114: bytecode.addInvokestatic("javassist/runtime/Desc", method,
115: "(Ljava/lang/String;)Ljava/lang/Class;");
116: exprType = CLASS;
117: arrayDim = 0;
118: className = "java/lang/Class";
119: }
120:
121: protected void atFieldAssign(Expr expr, int op, ASTree left,
122: ASTree right, boolean doDup) throws CompileError {
123: if (left instanceof Member
124: && ((Member) left).get().equals(paramArrayName)) {
125: if (op != '=')
126: throw new CompileError("bad operator for "
127: + paramArrayName);
128:
129: right.accept(this );
130: if (arrayDim != 1 || exprType != CLASS)
131: throw new CompileError("invalid type for "
132: + paramArrayName);
133:
134: atAssignParamList(paramTypeList, bytecode);
135: if (!doDup)
136: bytecode.addOpcode(POP);
137: } else
138: super .atFieldAssign(expr, op, left, right, doDup);
139: }
140:
141: protected void atAssignParamList(CtClass[] params, Bytecode code)
142: throws CompileError {
143: if (params == null)
144: return;
145:
146: int varNo = indexOfParam1();
147: int n = params.length;
148: for (int i = 0; i < n; ++i) {
149: code.addOpcode(DUP);
150: code.addIconst(i);
151: code.addOpcode(AALOAD);
152: compileUnwrapValue(params[i], code);
153: code.addStore(varNo, params[i]);
154: varNo += is2word(exprType, arrayDim) ? 2 : 1;
155: }
156: }
157:
158: public void atCastExpr(CastExpr expr) throws CompileError {
159: ASTList classname = expr.getClassName();
160: if (classname != null && expr.getArrayDim() == 0) {
161: ASTree p = classname.head();
162: if (p instanceof Symbol && classname.tail() == null) {
163: String typename = ((Symbol) p).get();
164: if (typename.equals(returnCastName)) {
165: atCastToRtype(expr);
166: return;
167: } else if (typename.equals(wrapperCastName)) {
168: atCastToWrapper(expr);
169: return;
170: }
171: }
172: }
173:
174: super .atCastExpr(expr);
175: }
176:
177: /**
178: * Inserts a cast operator to the return type.
179: * If the return type is void, this does nothing.
180: */
181: protected void atCastToRtype(CastExpr expr) throws CompileError {
182: expr.getOprand().accept(this );
183: if (exprType == VOID || isRefType(exprType) || arrayDim > 0)
184: compileUnwrapValue(returnType, bytecode);
185: else if (returnType instanceof CtPrimitiveType) {
186: CtPrimitiveType pt = (CtPrimitiveType) returnType;
187: int destType = MemberResolver
188: .descToType(pt.getDescriptor());
189: atNumCastExpr(exprType, destType);
190: exprType = destType;
191: arrayDim = 0;
192: className = null;
193: } else
194: throw new CompileError("invalid cast");
195: }
196:
197: protected void atCastToWrapper(CastExpr expr) throws CompileError {
198: expr.getOprand().accept(this );
199: if (isRefType(exprType) || arrayDim > 0)
200: return; // Object type. do nothing.
201:
202: CtClass clazz = resolver.lookupClass(exprType, arrayDim,
203: className);
204: if (clazz instanceof CtPrimitiveType) {
205: CtPrimitiveType pt = (CtPrimitiveType) clazz;
206: String wrapper = pt.getWrapperName();
207: bytecode.addNew(wrapper); // new <wrapper>
208: bytecode.addOpcode(DUP); // dup
209: if (pt.getDataSize() > 1)
210: bytecode.addOpcode(DUP2_X2); // dup2_x2
211: else
212: bytecode.addOpcode(DUP2_X1); // dup2_x1
213:
214: bytecode.addOpcode(POP2); // pop2
215: bytecode.addInvokespecial(wrapper, "<init>", "("
216: + pt.getDescriptor() + ")V");
217: // invokespecial
218: exprType = CLASS;
219: arrayDim = 0;
220: className = jvmJavaLangObject;
221: }
222: }
223:
224: /* Delegates to a ProcHandler object if the method call is
225: * $proceed(). It may process $cflow().
226: */
227: public void atCallExpr(CallExpr expr) throws CompileError {
228: ASTree method = expr.oprand1();
229: if (method instanceof Member) {
230: String name = ((Member) method).get();
231: if (procHandler != null && name.equals(proceedName)) {
232: procHandler.doit(this , bytecode, (ASTList) expr
233: .oprand2());
234: return;
235: } else if (name.equals(cflowName)) {
236: atCflow((ASTList) expr.oprand2());
237: return;
238: }
239: }
240:
241: super .atCallExpr(expr);
242: }
243:
244: /* To support $cflow().
245: */
246: protected void atCflow(ASTList cname) throws CompileError {
247: StringBuffer sbuf = new StringBuffer();
248: if (cname == null || cname.tail() != null)
249: throw new CompileError("bad " + cflowName);
250:
251: makeCflowName(sbuf, cname.head());
252: String name = sbuf.toString();
253: Object[] names = resolver.getClassPool().lookupCflow(name);
254: if (names == null)
255: throw new CompileError("no such a " + cflowName + ": "
256: + name);
257:
258: bytecode.addGetstatic((String) names[0], (String) names[1],
259: "Ljavassist/runtime/Cflow;");
260: bytecode.addInvokevirtual("javassist.runtime.Cflow", "value",
261: "()I");
262: exprType = INT;
263: arrayDim = 0;
264: className = null;
265: }
266:
267: /* Syntax:
268: *
269: * <cflow> : $cflow '(' <cflow name> ')'
270: * <cflow name> : <identifier> ('.' <identifier>)*
271: */
272: private static void makeCflowName(StringBuffer sbuf, ASTree name)
273: throws CompileError {
274: if (name instanceof Symbol) {
275: sbuf.append(((Symbol) name).get());
276: return;
277: } else if (name instanceof Expr) {
278: Expr expr = (Expr) name;
279: if (expr.getOperator() == '.') {
280: makeCflowName(sbuf, expr.oprand1());
281: sbuf.append('.');
282: makeCflowName(sbuf, expr.oprand2());
283: return;
284: }
285: }
286:
287: throw new CompileError("bad " + cflowName);
288: }
289:
290: /* To support $$. ($$) is equivalent to ($1, ..., $n).
291: * It can be used only as a parameter list of method call.
292: */
293: public boolean isParamListName(ASTList args) {
294: if (paramTypeList != null && args != null
295: && args.tail() == null) {
296: ASTree left = args.head();
297: return (left instanceof Member && ((Member) left).get()
298: .equals(paramListName));
299: } else
300: return false;
301: }
302:
303: /*
304: public int getMethodArgsLength(ASTList args) {
305: if (!isParamListName(args))
306: return super.getMethodArgsLength(args);
307:
308: return paramTypeList.length;
309: }
310: */
311:
312: public int getMethodArgsLength(ASTList args) {
313: String pname = paramListName;
314: int n = 0;
315: while (args != null) {
316: ASTree a = args.head();
317: if (a instanceof Member && ((Member) a).get().equals(pname)) {
318: if (paramTypeList != null)
319: n += paramTypeList.length;
320: } else
321: ++n;
322:
323: args = args.tail();
324: }
325:
326: return n;
327: }
328:
329: public void atMethodArgs(ASTList args, int[] types, int[] dims,
330: String[] cnames) throws CompileError {
331: CtClass[] params = paramTypeList;
332: String pname = paramListName;
333: int i = 0;
334: while (args != null) {
335: ASTree a = args.head();
336: if (a instanceof Member && ((Member) a).get().equals(pname)) {
337: if (params != null) {
338: int n = params.length;
339: int regno = indexOfParam1();
340: for (int k = 0; k < n; ++k) {
341: CtClass p = params[k];
342: regno += bytecode.addLoad(regno, p);
343: setType(p);
344: types[i] = exprType;
345: dims[i] = arrayDim;
346: cnames[i] = className;
347: ++i;
348: }
349: }
350: } else {
351: a.accept(this );
352: types[i] = exprType;
353: dims[i] = arrayDim;
354: cnames[i] = className;
355: ++i;
356: }
357:
358: args = args.tail();
359: }
360: }
361:
362: /*
363: public void atMethodArgs(ASTList args, int[] types, int[] dims,
364: String[] cnames) throws CompileError {
365: if (!isParamListName(args)) {
366: super.atMethodArgs(args, types, dims, cnames);
367: return;
368: }
369:
370: CtClass[] params = paramTypeList;
371: if (params == null)
372: return;
373:
374: int n = params.length;
375: int regno = indexOfParam1();
376: for (int i = 0; i < n; ++i) {
377: CtClass p = params[i];
378: regno += bytecode.addLoad(regno, p);
379: setType(p);
380: types[i] = exprType;
381: dims[i] = arrayDim;
382: cnames[i] = className;
383: }
384: }
385: */
386:
387: /* called by Javac#recordSpecialProceed().
388: */
389: void compileInvokeSpecial(ASTree target, String classname,
390: String methodname, String descriptor, ASTList args)
391: throws CompileError {
392: target.accept(this );
393: int nargs = getMethodArgsLength(args);
394: atMethodArgs(args, new int[nargs], new int[nargs],
395: new String[nargs]);
396: bytecode.addInvokespecial(classname, methodname, descriptor);
397: setReturnType(descriptor, false, false);
398: addNullIfVoid();
399: }
400:
401: /*
402: * Makes it valid to write "return <expr>;" for a void method.
403: */
404: protected void atReturnStmnt(Stmnt st) throws CompileError {
405: ASTree result = st.getLeft();
406: if (result != null && returnType == CtClass.voidType) {
407: compileExpr(result);
408: if (is2word(exprType, arrayDim))
409: bytecode.addOpcode(POP2);
410: else if (exprType != VOID)
411: bytecode.addOpcode(POP);
412:
413: result = null;
414: }
415:
416: atReturnStmnt2(result);
417: }
418:
419: /**
420: * Makes a cast to the return type ($r) available.
421: * It also enables $_.
422: *
423: * <p>If the return type is void, ($r) does nothing.
424: * The type of $_ is java.lang.Object.
425: *
426: * @param resultName null if $_ is not used.
427: * @return -1 or the variable index assigned to $_.
428: */
429: public int recordReturnType(CtClass type, String castName,
430: String resultName, SymbolTable tbl) throws CompileError {
431: returnType = type;
432: returnCastName = castName;
433: returnVarName = resultName;
434: if (resultName == null)
435: return -1;
436: else {
437: int varNo = getMaxLocals();
438: int locals = varNo
439: + recordVar(type, resultName, varNo, tbl);
440: setMaxLocals(locals);
441: return varNo;
442: }
443: }
444:
445: /**
446: * Makes $type available.
447: */
448: public void recordType(CtClass t) {
449: dollarType = t;
450: }
451:
452: /**
453: * Makes method parameters $0, $1, ..., $args, $$, and $class available.
454: * $0 is equivalent to THIS if the method is not static. Otherwise,
455: * if the method is static, then $0 is not available.
456: */
457: public int recordParams(CtClass[] params, boolean isStatic,
458: String prefix, String paramVarName, String paramsName,
459: SymbolTable tbl) throws CompileError {
460: return recordParams(params, isStatic, prefix, paramVarName,
461: paramsName, !isStatic, 0, getThisName(), tbl);
462: }
463:
464: /**
465: * Makes method parameters $0, $1, ..., $args, $$, and $class available.
466: * $0 is available only if use0 is true. It might not be equivalent
467: * to THIS.
468: *
469: * @param params the parameter types (the types of $1, $2, ..)
470: * @param prefix it must be "$" (the first letter of $0, $1, ...)
471: * @param paramVarName it must be "$args"
472: * @param paramsName it must be "$$"
473: * @param use0 true if $0 is used.
474: * @param paramBase the register number of $0 (use0 is true)
475: * or $1 (otherwise).
476: * @param target the class of $0. If use0 is false, target
477: * can be null. The value of "target" is also used
478: * as the name of the type represented by $class.
479: * @param isStatic true if the method in which the compiled bytecode
480: * is embedded is static.
481: */
482: public int recordParams(CtClass[] params, boolean isStatic,
483: String prefix, String paramVarName, String paramsName,
484: boolean use0, int paramBase, String target, SymbolTable tbl)
485: throws CompileError {
486: int varNo;
487:
488: paramTypeList = params;
489: paramArrayName = paramVarName;
490: paramListName = paramsName;
491: paramVarBase = paramBase;
492: useParam0 = use0;
493:
494: if (target != null)
495: param0Type = MemberResolver.jvmToJavaName(target);
496:
497: inStaticMethod = isStatic;
498: varNo = paramBase;
499: if (use0) {
500: String varName = prefix + "0";
501: Declarator decl = new Declarator(CLASS, MemberResolver
502: .javaToJvmName(target), 0, varNo++, new Symbol(
503: varName));
504: tbl.append(varName, decl);
505: }
506:
507: for (int i = 0; i < params.length; ++i)
508: varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl);
509:
510: if (getMaxLocals() < varNo)
511: setMaxLocals(varNo);
512:
513: return varNo;
514: }
515:
516: /**
517: * Makes the given variable name available.
518: *
519: * @param type variable type
520: * @param varName variable name
521: */
522: public int recordVariable(CtClass type, String varName,
523: SymbolTable tbl) throws CompileError {
524: if (varName == null)
525: return -1;
526: else {
527: int varNo = getMaxLocals();
528: int locals = varNo + recordVar(type, varName, varNo, tbl);
529: setMaxLocals(locals);
530: return varNo;
531: }
532: }
533:
534: private int recordVar(CtClass cc, String varName, int varNo,
535: SymbolTable tbl) throws CompileError {
536: if (cc == CtClass.voidType) {
537: exprType = CLASS;
538: arrayDim = 0;
539: className = jvmJavaLangObject;
540: } else
541: setType(cc);
542:
543: Declarator decl = new Declarator(exprType, className, arrayDim,
544: varNo, new Symbol(varName));
545: tbl.append(varName, decl);
546: return is2word(exprType, arrayDim) ? 2 : 1;
547: }
548:
549: /**
550: * Makes the given variable name available.
551: *
552: * @param typeDesc the type descriptor of the variable
553: * @param varName variable name
554: * @param varNo an index into the local variable array
555: */
556: public void recordVariable(String typeDesc, String varName,
557: int varNo, SymbolTable tbl) throws CompileError {
558: char c;
559: int dim = 0;
560: while ((c = typeDesc.charAt(dim)) == '[')
561: ++dim;
562:
563: int type = MemberResolver.descToType(c);
564: String cname = null;
565: if (type == CLASS) {
566: if (dim == 0)
567: cname = typeDesc.substring(1, typeDesc.length() - 1);
568: else
569: cname = typeDesc.substring(dim + 1,
570: typeDesc.length() - 1);
571: }
572:
573: Declarator decl = new Declarator(type, cname, dim, varNo,
574: new Symbol(varName));
575: tbl.append(varName, decl);
576: }
577:
578: /* compileParameterList() returns the stack size used
579: * by the produced code.
580: *
581: * This method correctly computes the max_stack value.
582: *
583: * @param regno the index of the local variable in which
584: * the first argument is received.
585: * (0: static method, 1: regular method.)
586: */
587: public static int compileParameterList(Bytecode code,
588: CtClass[] params, int regno) {
589: if (params == null) {
590: code.addIconst(0); // iconst_0
591: code.addAnewarray(javaLangObject); // anewarray Object
592: return 1;
593: } else {
594: CtClass[] args = new CtClass[1];
595: int n = params.length;
596: code.addIconst(n); // iconst_<n>
597: code.addAnewarray(javaLangObject); // anewarray Object
598: for (int i = 0; i < n; ++i) {
599: code.addOpcode(Bytecode.DUP); // dup
600: code.addIconst(i); // iconst_<i>
601: if (params[i].isPrimitive()) {
602: CtPrimitiveType pt = (CtPrimitiveType) params[i];
603: String wrapper = pt.getWrapperName();
604: code.addNew(wrapper); // new <wrapper>
605: code.addOpcode(Bytecode.DUP); // dup
606: int s = code.addLoad(regno, pt); // ?load <regno>
607: regno += s;
608: args[0] = pt;
609: code.addInvokespecial(wrapper, "<init>", Descriptor
610: .ofMethod(CtClass.voidType, args));
611: // invokespecial
612: } else {
613: code.addAload(regno); // aload <regno>
614: ++regno;
615: }
616:
617: code.addOpcode(Bytecode.AASTORE); // aastore
618: }
619:
620: return 8;
621: }
622: }
623:
624: protected void compileUnwrapValue(CtClass type, Bytecode code)
625: throws CompileError {
626: if (type == CtClass.voidType) {
627: addNullIfVoid();
628: return;
629: }
630:
631: if (exprType == VOID)
632: throw new CompileError("invalid type for " + returnCastName);
633:
634: if (type instanceof CtPrimitiveType) {
635: CtPrimitiveType pt = (CtPrimitiveType) type;
636: // pt is not voidType.
637: String wrapper = pt.getWrapperName();
638: code.addCheckcast(wrapper);
639: code.addInvokevirtual(wrapper, pt.getGetMethodName(), pt
640: .getGetMethodDescriptor());
641: setType(type);
642: } else {
643: code.addCheckcast(type);
644: setType(type);
645: }
646: }
647:
648: /* Sets exprType, arrayDim, and className;
649: * If type is void, then this method does nothing.
650: */
651: public void setType(CtClass type) throws CompileError {
652: setType(type, 0);
653: }
654:
655: private void setType(CtClass type, int dim) throws CompileError {
656: if (type.isPrimitive()) {
657: CtPrimitiveType pt = (CtPrimitiveType) type;
658: exprType = MemberResolver.descToType(pt.getDescriptor());
659: arrayDim = dim;
660: className = null;
661: } else if (type.isArray())
662: try {
663: setType(type.getComponentType(), dim + 1);
664: } catch (NotFoundException e) {
665: throw new CompileError("undefined type: "
666: + type.getName());
667: }
668: else {
669: exprType = CLASS;
670: arrayDim = dim;
671: className = MemberResolver.javaToJvmName(type.getName());
672: }
673: }
674:
675: /* Performs implicit coercion from exprType to type.
676: */
677: public void doNumCast(CtClass type) throws CompileError {
678: if (arrayDim == 0 && !isRefType(exprType))
679: if (type instanceof CtPrimitiveType) {
680: CtPrimitiveType pt = (CtPrimitiveType) type;
681: atNumCastExpr(exprType, MemberResolver.descToType(pt
682: .getDescriptor()));
683: } else
684: throw new CompileError("type mismatch");
685: }
686: }
|