/Users/lyon/j4p/src/javassist/compiler/JvstCodeGen.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 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
16 package javassist.compiler;
17
18 import javassist.*;
19 import javassist.bytecode.*;
20 import javassist.compiler.ast.*;
21
22 /* Code generator methods for extensions by Javassist.
23 */
24
25 public class JvstCodeGen extends MemberCodeGen {
26 private String paramArrayName = null;
27 private String paramListName = null;
28 private CtClass[] paramTypeList = null;
29 private int paramVarBase = 0; // variable index for $0 or $1.
30 private boolean useParam0 = false; // true if $0 is used.
31 private String param0Type = null; // JVM name
32 private static final String sigName = "$sig";
33 private static final String dollarTypeName = "$type";
34 private static final String clazzName = "$class";
35 private CtClass dollarType = null;
36 private CtClass returnType = null;
37 private String returnCastName = null;
38 private String returnVarName = null; // null if $_ is not used.
39 private static final String wrapperCastName = "$w";
40 private String proceedName = null;
41 private static final String cflowName = "$cflow";
42 private ProceedHandler procHandler = null; // null if not used.
43
44 public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
45 super(b, cc, cp);
46 }
47
48 /* Index of $1.
49 */
50 private int indexOfParam1() {
51 return paramVarBase + (useParam0 ? 1 : 0);
52 }
53
54 /* Records a ProceedHandler obejct.
55 *
56 * @param name the name of the special method call.
57 * it is usually $proceed.
58 */
59 public void setProceedHandler(ProceedHandler h, String name) {
60 proceedName = name;
61 procHandler = h;
62 }
63
64 /* If the type of the expression compiled last is void,
65 * add ACONST_NULL and change exprType, arrayDim, className.
66 */
67 public void addNullIfVoid() {
68 if (exprType == VOID) {
69 bytecode.addOpcode(ACONST_NULL);
70 exprType = CLASS;
71 arrayDim = 0;
72 className = jvmJavaLangObject;
73 }
74 }
75
76 /* To support $args, $sig, and $type.
77 * $args is an array of parameter list.
78 */
79 public void atMember(Member mem) throws CompileError {
80 String name = mem.get();
81 if (name.equals(paramArrayName)) {
82 compileParameterList(bytecode, paramTypeList, indexOfParam1());
83 exprType = CLASS;
84 arrayDim = 1;
85 className = jvmJavaLangObject;
86 } else if (name.equals(sigName)) {
87 bytecode.addLdc(Descriptor.ofMethod(returnType, paramTypeList));
88 bytecode.addInvokestatic("javassist/runtime/Desc", "getParams",
89 "(Ljava/lang/String;)[Ljava/lang/Class;");
90 exprType = CLASS;
91 arrayDim = 1;
92 className = "java/lang/Class";
93 } else if (name.equals(dollarTypeName)) {
94 if (dollarType == null)
95 throw new CompileError(dollarType + " is not available");
96
97 bytecode.addLdc(Descriptor.of(dollarType));
98 callGetType("getType");
99 } else if (name.equals(clazzName)) {
100 if (param0Type == null)
101 throw new CompileError(clazzName + " is not available");
102
103 bytecode.addLdc(param0Type);
104 callGetType("getClazz");
105 } else
106 super.atMember(mem);
107 }
108
109 private void callGetType(String method) {
110 bytecode.addInvokestatic("javassist/runtime/Desc", method,
111 "(Ljava/lang/String;)Ljava/lang/Class;");
112 exprType = CLASS;
113 arrayDim = 0;
114 className = "java/lang/Class";
115 }
116
117 private void atSigOrType(String sig) throws CompileError {
118 }
119
120 protected void atFieldAssign(Expr expr, int op, ASTree left,
121 ASTree right, boolean doDup) throws CompileError {
122 if (left instanceof Member
123 && ((Member) left).get().equals(paramArrayName)) {
124 if (op != '=')
125 throw new CompileError("bad operator for " + paramArrayName);
126
127 right.accept(this);
128 if (arrayDim != 1 || exprType != CLASS)
129 throw new CompileError("invalid type for " + paramArrayName);
130
131 atAssignParamList(paramTypeList, bytecode);
132 if (!doDup)
133 bytecode.addOpcode(POP);
134 } else
135 super.atFieldAssign(expr, op, left, right, doDup);
136 }
137
138 protected void atAssignParamList(CtClass[] params, Bytecode code)
139 throws CompileError {
140 if (params == null)
141 return;
142
143 int varNo = indexOfParam1();
144 int n = params.length;
145 for (int i = 0; i < n; ++i) {
146 code.addOpcode(DUP);
147 code.addIconst(i);
148 code.addOpcode(AALOAD);
149 compileUnwrapValue(params[i], code);
150 code.addStore(varNo, params[i]);
151 varNo += is2word(exprType, arrayDim) ? 2 : 1;
152 }
153 }
154
155 public void atCastExpr(CastExpr expr) throws CompileError {
156 ASTList classname = expr.getClassName();
157 if (classname != null && expr.getArrayDim() == 0) {
158 ASTree p = classname.head();
159 if (p instanceof Symbol && classname.tail() == null) {
160 String typename = ((Symbol) p).get();
161 if (typename.equals(returnCastName)) {
162 atCastToRtype(expr);
163 return;
164 } else if (typename.equals(wrapperCastName)) {
165 atCastToWrapper(expr);
166 return;
167 }
168 }
169 }
170
171 super.atCastExpr(expr);
172 }
173
174 /**
175 * Inserts a cast operator to the return type.
176 * If the return type is void, this does nothing.
177 */
178 protected void atCastToRtype(CastExpr expr) throws CompileError {
179 expr.getOprand().accept(this);
180 if (exprType == VOID || isRefType(exprType) || arrayDim > 0)
181 compileUnwrapValue(returnType, bytecode);
182 else if (returnType instanceof CtPrimitiveType) {
183 CtPrimitiveType pt = (CtPrimitiveType) returnType;
184 int destType = jvmTypeNameToExprType(pt.getDescriptor());
185 atNumCastExpr(exprType, destType);
186 exprType = destType;
187 arrayDim = 0;
188 className = null;
189 } else
190 throw new CompileError("invalid cast");
191 }
192
193 protected void atCastToWrapper(CastExpr expr) throws CompileError {
194 expr.getOprand().accept(this);
195 if (isRefType(exprType) || arrayDim > 0)
196 return; // Object type. do nothing.
197
198 CtClass clazz = lookupClass(exprType, arrayDim, className);
199 if (clazz instanceof CtPrimitiveType) {
200 CtPrimitiveType pt = (CtPrimitiveType) clazz;
201 String wrapper = pt.getWrapperName();
202 bytecode.addNew(wrapper); // new <wrapper>
203 bytecode.addOpcode(DUP); // dup
204 if (pt.getDataSize() > 1)
205 bytecode.addOpcode(DUP2_X2); // dup2_x2
206 else
207 bytecode.addOpcode(DUP2_X1); // dup2_x1
208
209 bytecode.addOpcode(POP2); // pop2
210 bytecode.addInvokespecial(wrapper, "<init>",
211 "(" + pt.getDescriptor() + ")V");
212 // invokespecial
213 exprType = CLASS;
214 arrayDim = 0;
215 className = jvmJavaLangObject;
216 }
217 }
218
219 /* Delegates to a ProcHandler object if the method call is
220 * $proceed(). It may process $cflow().
221 */
222 protected void atMethodCall(Expr expr) throws CompileError {
223 ASTree method = expr.oprand1();
224 if (method instanceof Member) {
225 String name = ((Member) method).get();
226 if (procHandler != null && name.equals(proceedName)) {
227 procHandler.doit(this, bytecode, (ASTList) expr.oprand2());
228 return;
229 } else if (name.equals(cflowName)) {
230 atCflow((ASTList) expr.oprand2());
231 return;
232 }
233 }
234
235 super.atMethodCall(expr);
236 }
237
238 /* To support $cflow().
239 */
240 protected void atCflow(ASTList cname) throws CompileError {
241 StringBuffer sbuf = new StringBuffer();
242 if (cname == null || cname.tail() != null)
243 throw new CompileError("bad " + cflowName);
244
245 makeCflowName(sbuf, cname.head());
246 String name = sbuf.toString();
247 Object[] names = classPool.lookupCflow(name);
248 if (names == null)
249 throw new CompileError("no such a " + cflowName + ": " + name);
250
251 bytecode.addGetstatic((String) names[0], (String) names[1],
252 "Ljavassist/runtime/Cflow;");
253 bytecode.addInvokevirtual("javassist.runtime.Cflow",
254 "value", "()I");
255 exprType = INT;
256 arrayDim = 0;
257 className = null;
258 }
259
260 /* Syntax:
261 *
262 * <cflow> : $cflow '(' <cflow name> ')'
263 * <cflow name> : <identifier> ('.' <identifier>)*
264 */
265 private static void makeCflowName(StringBuffer sbuf, ASTree name)
266 throws CompileError {
267 if (name instanceof Symbol) {
268 sbuf.append(((Symbol) name).get());
269 return;
270 } else if (name instanceof Expr) {
271 Expr expr = (Expr) name;
272 if (expr.getOperator() == '.') {
273 makeCflowName(sbuf, expr.oprand1());
274 sbuf.append('.');
275 makeCflowName(sbuf, expr.oprand2());
276 return;
277 }
278 }
279
280 throw new CompileError("bad " + cflowName);
281 }
282
283 /* To support $$. ($$) is equivalent to ($1, ..., $n).
284 * It can be used only as a parameter list of method call.
285 */
286 public boolean isParamListName(ASTList args) {
287 if (paramTypeList != null
288 && args != null && args.tail() == null) {
289 ASTree left = args.head();
290 return (left instanceof Member
291 && ((Member) left).get().equals(paramListName));
292 } else
293 return false;
294 }
295
296 /*
297 public int atMethodArgsLength(ASTList args) {
298 if (!isParamListName(args))
299 return super.atMethodArgsLength(args);
300
301 return paramTypeList.length;
302 }
303 */
304
305 public int atMethodArgsLength(ASTList args) {
306 String pname = paramListName;
307 int n = 0;
308 while (args != null) {
309 ASTree a = args.head();
310 if (a instanceof Member && ((Member) a).get().equals(pname)) {
311 if (paramTypeList != null)
312 n += paramTypeList.length;
313 } else
314 ++n;
315
316 args = args.tail();
317 }
318
319 return n;
320 }
321
322 public void atMethodArgs(ASTList args, int[] types, int[] dims,
323 String[] cnames) throws CompileError {
324 CtClass[] params = paramTypeList;
325 String pname = paramListName;
326 int i = 0;
327 while (args != null) {
328 ASTree a = args.head();
329 if (a instanceof Member && ((Member) a).get().equals(pname)) {
330 if (params != null) {
331 int n = params.length;
332 int regno = indexOfParam1();
333 for (int k = 0; k < n; ++k) {
334 CtClass p = params[k];
335 regno += bytecode.addLoad(regno, p);
336 setType(p);
337 types[i] = exprType;
338 dims[i] = arrayDim;
339 cnames[i] = className;
340 ++i;
341 }
342 }
343 } else {
344 a.accept(this);
345 types[i] = exprType;
346 dims[i] = arrayDim;
347 cnames[i] = className;
348 ++i;
349 }
350
351 args = args.tail();
352 }
353 }
354
355 /*
356 public void atMethodArgs(ASTList args, int[] types, int[] dims,
357 String[] cnames) throws CompileError {
358 if (!isParamListName(args)) {
359 super.atMethodArgs(args, types, dims, cnames);
360 return;
361 }
362
363 CtClass[] params = paramTypeList;
364 if (params == null)
365 return;
366
367 int n = params.length;
368 int regno = indexOfParam1();
369 for (int i = 0; i < n; ++i) {
370 CtClass p = params[i];
371 regno += bytecode.addLoad(regno, p);
372 setType(p);
373 types[i] = exprType;
374 dims[i] = arrayDim;
375 cnames[i] = className;
376 }
377 }
378 */
379
380 /*
381 * Makes it valid to write "return <expr>;" for a void method.
382 */
383 protected void atReturnStmnt(Stmnt st) throws CompileError {
384 ASTree result = st.getLeft();
385 if (result != null && returnType == CtClass.voidType) {
386 result.accept(this);
387 if (is2word(exprType, arrayDim))
388 bytecode.addOpcode(POP2);
389 else if (exprType != VOID)
390 bytecode.addOpcode(POP);
391
392 result = null;
393 }
394
395 atReturnStmnt2(result);
396 }
397
398 /**
399 * Makes a cast to the return type ($r) available.
400 * It also enables $_.
401 *
402 * <p>If the return type is void, ($r) does nothing.
403 * The type of $_ is java.lang.Object.
404 *
405 * @param resultName null if $_ is not used.
406 * @return -1 or the variable index assigned to $_.
407 */
408 public int recordReturnType(CtClass type, String castName,
409 String resultName, SymbolTable tbl) throws CompileError {
410 returnType = type;
411 returnCastName = castName;
412 returnVarName = resultName;
413 if (resultName == null)
414 return -1;
415 else {
416 int varNo = getMaxLocals();
417 int locals = varNo + recordVar(type, resultName, varNo, tbl);
418 setMaxLocals(locals);
419 return varNo;
420 }
421 }
422
423 /**
424 * Makes $type available.
425 */
426 public void recordType(CtClass t) {
427 dollarType = t;
428 }
429
430 /**
431 * Makes method parameters $0, $1, ..., $args, and $$ available.
432 * $0 is equivalent to THIS if the method is not static. Otherwise,
433 * if the method is static, then $0 is not available.
434 */
435 public void recordParams(CtClass[] params, boolean isStatic,
436 String prefix, String paramVarName,
437 String paramsName, SymbolTable tbl)
438 throws CompileError {
439 recordParams(params, isStatic, prefix, paramVarName,
440 paramsName, !isStatic, 0, getThisName(), tbl);
441 }
442
443 /**
444 * Makes method parameters $0, $1, ..., $args, and $$ available.
445 * $0 is available only if use0 is true. It might not be equivalent
446 * to THIS.
447 *
448 * @paaram use0 true if $0 is used.
449 * @param paramBase the register number of $0 (use0 is true)
450 * or $1 (otherwise).
451 * @param target the class of $0. If use0 is false, target
452 * can be null.
453 * @param isStatic true if the method in which the compiled bytecode
454 * is embedded is static.
455 */
456 public void recordParams(CtClass[] params, boolean isStatic,
457 String prefix, String paramVarName,
458 String paramsName, boolean use0,
459 int paramBase, String target,
460 SymbolTable tbl)
461 throws CompileError {
462 int varNo;
463
464 paramTypeList = params;
465 paramArrayName = paramVarName;
466 paramListName = paramsName;
467 paramVarBase = paramBase;
468 useParam0 = use0;
469
470 param0Type = jvmToJavaName(target);
471
472 inStaticMethod = isStatic;
473 varNo = paramBase;
474 if (use0) {
475 String varName = prefix + "0";
476 Declarator decl
477 = new Declarator(CLASS, javaToJvmName(target), 0, varNo++,
478 new Symbol(varName));
479 tbl.append(varName, decl);
480 }
481
482 for (int i = 0; i < params.length; ++i)
483 varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl);
484
485 if (getMaxLocals() < varNo)
486 setMaxLocals(varNo);
487 }
488
489 /**
490 * Makes the given variable name available.
491 *
492 * @param type variable type
493 * @param varName variable name
494 */
495 public int recordVariable(CtClass type, String varName, SymbolTable tbl)
496 throws CompileError {
497 if (varName == null)
498 return -1;
499 else {
500 int varNo = getMaxLocals();
501 int locals = varNo + recordVar(type, varName, varNo, tbl);
502 setMaxLocals(locals);
503 return varNo;
504 }
505 }
506
507 private int recordVar(CtClass cc, String varName, int varNo,
508 SymbolTable tbl) throws CompileError {
509 if (cc == CtClass.voidType) {
510 exprType = CLASS;
511 arrayDim = 0;
512 className = jvmJavaLangObject;
513 } else
514 setType(cc);
515
516 Declarator decl
517 = new Declarator(exprType, className, arrayDim,
518 varNo, new Symbol(varName));
519 tbl.append(varName, decl);
520 return is2word(exprType, arrayDim) ? 2 : 1;
521 }
522
523 /* compileParameterList() returns the stack size used
524 * by the produced code.
525 *
526 * This method correctly computes the max_stack value.
527 *
528 * @param regno the index of the local variable in which
529 * the first argument is received.
530 * (0: static method, 1: regular method.)
531 */
532 public static int compileParameterList(Bytecode code,
533 CtClass[] params, int regno) {
534 if (params == null) {
535 code.addIconst(0); // iconst_0
536 code.addAnewarray(javaLangObject); // anewarray Object
537 return 1;
538 } else {
539 CtClass[] args = new CtClass[1];
540 int n = params.length;
541 code.addIconst(n); // iconst_<n>
542 code.addAnewarray(javaLangObject); // anewarray Object
543 for (int i = 0; i < n; ++i) {
544 code.addOpcode(Bytecode.DUP); // dup
545 code.addIconst(i); // iconst_<i>
546 if (params[i].isPrimitive()) {
547 CtPrimitiveType pt = (CtPrimitiveType) params[i];
548 String wrapper = pt.getWrapperName();
549 code.addNew(wrapper); // new <wrapper>
550 code.addOpcode(Bytecode.DUP); // dup
551 int s = code.addLoad(regno, pt); // ?load <regno>
552 regno += s;
553 args[0] = pt;
554 code.addInvokespecial(wrapper, "<init>",
555 Descriptor.ofMethod(CtClass.voidType, args));
556 // invokespecial
557 } else {
558 code.addAload(regno); // aload <regno>
559 ++regno;
560 }
561
562 code.addOpcode(Bytecode.AASTORE); // aastore
563 }
564
565 return 8;
566 }
567 }
568
569 protected void compileUnwrapValue(CtClass type, Bytecode code)
570 throws CompileError {
571 if (type == CtClass.voidType) {
572 addNullIfVoid();
573 return;
574 }
575
576 if (exprType == VOID)
577 throw new CompileError("invalid type for " + returnCastName);
578
579 if (type instanceof CtPrimitiveType) {
580 CtPrimitiveType pt = (CtPrimitiveType) type;
581 // pt is not voidType.
582 String wrapper = pt.getWrapperName();
583 code.addCheckcast(wrapper);
584 code.addInvokevirtual(wrapper, pt.getGetMethodName(),
585 pt.getGetMethodDescriptor());
586 setType(type);
587 } else {
588 code.addCheckcast(type);
589 setType(type);
590 }
591 }
592
593 /* Sets exprType, arrayDim, and className;
594 * If type is void, then this method does nothing.
595 */
596 public void setType(CtClass type) throws CompileError {
597 setType(type, 0);
598 }
599
600 private void setType(CtClass type, int dim) throws CompileError {
601 if (type.isPrimitive()) {
602 CtPrimitiveType pt = (CtPrimitiveType) type;
603 exprType = descToType(pt.getDescriptor());
604 arrayDim = dim;
605 className = null;
606 } else if (type.isArray())
607 try {
608 setType(type.getComponentType(), dim + 1);
609 } catch (NotFoundException e) {
610 throw new CompileError("undefined type: " + type.getName());
611 }
612 else {
613 exprType = CLASS;
614 arrayDim = dim;
615 className = javaToJvmName(type.getName());
616 }
617 }
618
619 /* Performs implicit coercion from exprType to type.
620 */
621 public void doNumCast(CtClass type) throws CompileError {
622 if (arrayDim == 0 && !isRefType(exprType))
623 if (type instanceof CtPrimitiveType) {
624 CtPrimitiveType pt = (CtPrimitiveType) type;
625 atNumCastExpr(exprType, descToType(pt.getDescriptor()));
626 } else
627 throw new CompileError("type mismatch");
628 }
629 }
630