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