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