You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MemberCodeGen.java 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999-2006 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. package javassist.compiler;
  16. import javassist.*;
  17. import javassist.bytecode.*;
  18. import javassist.compiler.ast.*;
  19. import java.util.ArrayList;
  20. /* Code generator methods depending on javassist.* classes.
  21. */
  22. public class MemberCodeGen extends CodeGen {
  23. protected MemberResolver resolver;
  24. protected CtClass thisClass;
  25. protected MethodInfo thisMethod;
  26. protected boolean resultStatic;
  27. public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
  28. super(b);
  29. resolver = new MemberResolver(cp);
  30. thisClass = cc;
  31. thisMethod = null;
  32. }
  33. /**
  34. * Records the currently compiled method.
  35. */
  36. public void setThisMethod(CtMethod m) {
  37. thisMethod = m.getMethodInfo2();
  38. if (typeChecker != null)
  39. typeChecker.setThisMethod(thisMethod);
  40. }
  41. public CtClass getThisClass() { return thisClass; }
  42. /**
  43. * Returns the JVM-internal representation of this class name.
  44. */
  45. protected String getThisName() {
  46. return MemberResolver.javaToJvmName(thisClass.getName());
  47. }
  48. /**
  49. * Returns the JVM-internal representation of this super class name.
  50. */
  51. protected String getSuperName() throws CompileError {
  52. return MemberResolver.javaToJvmName(
  53. MemberResolver.getSuperclass(thisClass).getName());
  54. }
  55. protected void insertDefaultSuperCall() throws CompileError {
  56. bytecode.addAload(0);
  57. bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
  58. "<init>", "()V");
  59. }
  60. static class JsrHook extends ReturnHook {
  61. ArrayList jsrList;
  62. int var;
  63. JsrHook(CodeGen gen) {
  64. super(gen);
  65. jsrList = new ArrayList();
  66. var = gen.getMaxLocals();
  67. gen.incMaxLocals(1);
  68. }
  69. private void jsrJmp(Bytecode b) {
  70. b.addOpcode(JSR);
  71. jsrList.add(new Integer(b.currentPc()));
  72. b.addIndex(0);
  73. }
  74. protected void doit(Bytecode b, int opcode) {
  75. switch (opcode) {
  76. case Opcode.RETURN :
  77. jsrJmp(b);
  78. break;
  79. case ARETURN :
  80. b.addAstore(var);
  81. jsrJmp(b);
  82. b.addAload(var);
  83. break;
  84. case IRETURN :
  85. b.addIstore(var);
  86. jsrJmp(b);
  87. b.addIload(var);
  88. break;
  89. case LRETURN :
  90. b.addLstore(var);
  91. jsrJmp(b);
  92. b.addLload(var);
  93. break;
  94. case DRETURN :
  95. b.addDstore(var);
  96. jsrJmp(b);
  97. b.addDload(var);
  98. break;
  99. case FRETURN :
  100. b.addFstore(var);
  101. jsrJmp(b);
  102. b.addFload(var);
  103. break;
  104. default :
  105. throw new RuntimeException("fatal");
  106. }
  107. }
  108. }
  109. protected void atTryStmnt(Stmnt st) throws CompileError {
  110. Bytecode bc = bytecode;
  111. Stmnt body = (Stmnt)st.getLeft();
  112. if (body == null)
  113. return;
  114. ASTList catchList = (ASTList)st.getRight().getLeft();
  115. Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
  116. ArrayList gotoList = new ArrayList();
  117. JsrHook jsrHook = null;
  118. if (finallyBlock != null)
  119. jsrHook = new JsrHook(this);
  120. int start = bc.currentPc();
  121. body.accept(this);
  122. int end = bc.currentPc();
  123. if (start == end)
  124. throw new CompileError("empty try block");
  125. boolean tryNotReturn = !hasReturned;
  126. if (tryNotReturn) {
  127. bc.addOpcode(Opcode.GOTO);
  128. gotoList.add(new Integer(bc.currentPc()));
  129. bc.addIndex(0); // correct later
  130. }
  131. int var = getMaxLocals();
  132. incMaxLocals(1);
  133. while (catchList != null) {
  134. // catch clause
  135. Pair p = (Pair)catchList.head();
  136. catchList = catchList.tail();
  137. Declarator decl = (Declarator)p.getLeft();
  138. Stmnt block = (Stmnt)p.getRight();
  139. decl.setLocalVar(var);
  140. CtClass type = resolver.lookupClassByJvmName(decl.getClassName());
  141. decl.setClassName(MemberResolver.javaToJvmName(type.getName()));
  142. bc.addExceptionHandler(start, end, bc.currentPc(), type);
  143. bc.growStack(1);
  144. bc.addAstore(var);
  145. hasReturned = false;
  146. if (block != null)
  147. block.accept(this);
  148. if (!hasReturned) {
  149. bc.addOpcode(Opcode.GOTO);
  150. gotoList.add(new Integer(bc.currentPc()));
  151. bc.addIndex(0); // correct later
  152. tryNotReturn = true;
  153. }
  154. }
  155. int pcFinally = -1;
  156. if (finallyBlock != null) {
  157. jsrHook.remove(this);
  158. // catch (any) clause
  159. int pcAnyCatch = bc.currentPc();
  160. bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0);
  161. bc.growStack(1);
  162. bc.addAstore(var);
  163. bc.addOpcode(JSR);
  164. int pcJsrIndex = bc.currentPc();
  165. bc.addIndex(0); // correct later
  166. bc.addAload(var);
  167. bc.addOpcode(ATHROW);
  168. // finally clause
  169. pcFinally = bc.currentPc();
  170. bc.write16bit(pcJsrIndex, pcFinally - pcJsrIndex + 1);
  171. int retAddr = getMaxLocals();
  172. incMaxLocals(1);
  173. bc.growStack(1); // return address
  174. bc.addAstore(retAddr);
  175. hasReturned = false;
  176. finallyBlock.accept(this);
  177. if (!hasReturned) {
  178. bc.addOpcode(RET);
  179. bc.add(retAddr);
  180. }
  181. }
  182. int pcEnd = bc.currentPc();
  183. patchGoto(gotoList, pcEnd);
  184. if (finallyBlock != null) {
  185. patchGoto(jsrHook.jsrList, pcFinally);
  186. if (tryNotReturn) {
  187. bc.addOpcode(JSR);
  188. bc.addIndex(pcFinally - pcEnd);
  189. }
  190. }
  191. hasReturned = !tryNotReturn;
  192. }
  193. public void atNewExpr(NewExpr expr) throws CompileError {
  194. if (expr.isArray())
  195. atNewArrayExpr(expr);
  196. else {
  197. CtClass clazz = resolver.lookupClassByName(expr.getClassName());
  198. String cname = clazz.getName();
  199. ASTList args = expr.getArguments();
  200. bytecode.addNew(cname);
  201. bytecode.addOpcode(DUP);
  202. atMethodCallCore(clazz, MethodInfo.nameInit, args,
  203. false, true, -1, null);
  204. exprType = CLASS;
  205. arrayDim = 0;
  206. className = MemberResolver.javaToJvmName(cname);
  207. }
  208. }
  209. public void atNewArrayExpr(NewExpr expr) throws CompileError {
  210. int type = expr.getArrayType();
  211. ASTList size = expr.getArraySize();
  212. ASTList classname = expr.getClassName();
  213. ArrayInit init = expr.getInitializer();
  214. if (size.length() > 1) {
  215. if (init != null)
  216. throw new CompileError(
  217. "sorry, multi-dimensional array initializer " +
  218. "for new is not supported");
  219. atMultiNewArray(type, classname, size);
  220. return;
  221. }
  222. ASTree sizeExpr = size.head();
  223. atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init);
  224. }
  225. private void atNewArrayExpr2(int type, ASTree sizeExpr,
  226. String jvmClassname, ArrayInit init) throws CompileError {
  227. if (init == null)
  228. if (sizeExpr == null)
  229. throw new CompileError("no array size");
  230. else
  231. sizeExpr.accept(this);
  232. else
  233. if (sizeExpr == null) {
  234. int s = init.length();
  235. bytecode.addIconst(s);
  236. }
  237. else
  238. throw new CompileError("unnecessary array size specified for new");
  239. String elementClass;
  240. if (type == CLASS) {
  241. elementClass = resolveClassName(jvmClassname);
  242. bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass));
  243. }
  244. else {
  245. elementClass = null;
  246. int atype = 0;
  247. switch (type) {
  248. case BOOLEAN :
  249. atype = T_BOOLEAN;
  250. break;
  251. case CHAR :
  252. atype = T_CHAR;
  253. break;
  254. case FLOAT :
  255. atype = T_FLOAT;
  256. break;
  257. case DOUBLE :
  258. atype = T_DOUBLE;
  259. break;
  260. case BYTE :
  261. atype = T_BYTE;
  262. break;
  263. case SHORT :
  264. atype = T_SHORT;
  265. break;
  266. case INT :
  267. atype = T_INT;
  268. break;
  269. case LONG :
  270. atype = T_LONG;
  271. break;
  272. default :
  273. badNewExpr();
  274. break;
  275. }
  276. bytecode.addOpcode(NEWARRAY);
  277. bytecode.add(atype);
  278. }
  279. if (init != null) {
  280. int s = init.length();
  281. ASTList list = init;
  282. for (int i = 0; i < s; i++) {
  283. bytecode.addOpcode(DUP);
  284. bytecode.addIconst(i);
  285. list.head().accept(this);
  286. if (!isRefType(type))
  287. atNumCastExpr(exprType, type);
  288. bytecode.addOpcode(getArrayWriteOp(type, 0));
  289. list = list.tail();
  290. }
  291. }
  292. exprType = type;
  293. arrayDim = 1;
  294. className = elementClass;
  295. }
  296. private static void badNewExpr() throws CompileError {
  297. throw new CompileError("bad new expression");
  298. }
  299. protected void atArrayVariableAssign(ArrayInit init, int varType,
  300. int varArray, String varClass) throws CompileError {
  301. atNewArrayExpr2(varType, null, varClass, init);
  302. }
  303. public void atArrayInit(ArrayInit init) throws CompileError {
  304. throw new CompileError("array initializer is not supported");
  305. }
  306. protected void atMultiNewArray(int type, ASTList classname, ASTList size)
  307. throws CompileError
  308. {
  309. int count, dim;
  310. dim = size.length();
  311. for (count = 0; size != null; size = size.tail()) {
  312. ASTree s = size.head();
  313. if (s == null)
  314. break; // int[][][] a = new int[3][4][];
  315. ++count;
  316. s.accept(this);
  317. if (exprType != INT)
  318. throw new CompileError("bad type for array size");
  319. }
  320. String desc;
  321. exprType = type;
  322. arrayDim = dim;
  323. if (type == CLASS) {
  324. className = resolveClassName(classname);
  325. desc = toJvmArrayName(className, dim);
  326. }
  327. else
  328. desc = toJvmTypeName(type, dim);
  329. bytecode.addMultiNewarray(desc, count);
  330. }
  331. public void atCallExpr(CallExpr expr) throws CompileError {
  332. String mname = null;
  333. CtClass targetClass = null;
  334. ASTree method = expr.oprand1();
  335. ASTList args = (ASTList)expr.oprand2();
  336. boolean isStatic = false;
  337. boolean isSpecial = false;
  338. int aload0pos = -1;
  339. MemberResolver.Method cached = expr.getMethod();
  340. if (method instanceof Member) {
  341. mname = ((Member)method).get();
  342. targetClass = thisClass;
  343. if (inStaticMethod || (cached != null && cached.isStatic()))
  344. isStatic = true; // should be static
  345. else {
  346. aload0pos = bytecode.currentPc();
  347. bytecode.addAload(0); // this
  348. }
  349. }
  350. else if (method instanceof Keyword) { // constructor
  351. isSpecial = true;
  352. mname = MethodInfo.nameInit; // <init>
  353. targetClass = thisClass;
  354. if (inStaticMethod)
  355. throw new CompileError("a constructor cannot be static");
  356. else
  357. bytecode.addAload(0); // this
  358. if (((Keyword)method).get() == SUPER)
  359. targetClass = MemberResolver.getSuperclass(targetClass);
  360. }
  361. else if (method instanceof Expr) {
  362. Expr e = (Expr)method;
  363. mname = ((Symbol)e.oprand2()).get();
  364. int op = e.getOperator();
  365. if (op == MEMBER) { // static method
  366. targetClass
  367. = resolver.lookupClass(((Symbol)e.oprand1()).get(), false);
  368. isStatic = true;
  369. }
  370. else if (op == '.') {
  371. ASTree target = e.oprand1();
  372. if (target instanceof Keyword)
  373. if (((Keyword)target).get() == SUPER)
  374. isSpecial = true;
  375. try {
  376. target.accept(this);
  377. }
  378. catch (NoFieldException nfe) {
  379. if (nfe.getExpr() != target)
  380. throw nfe;
  381. // it should be a static method.
  382. exprType = CLASS;
  383. arrayDim = 0;
  384. className = nfe.getField(); // JVM-internal
  385. resolver.recordPackage(className);
  386. isStatic = true;
  387. }
  388. if (arrayDim > 0)
  389. targetClass = resolver.lookupClass(javaLangObject, true);
  390. else if (exprType == CLASS /* && arrayDim == 0 */)
  391. targetClass = resolver.lookupClassByJvmName(className);
  392. else
  393. badMethod();
  394. }
  395. else
  396. badMethod();
  397. }
  398. else
  399. fatal();
  400. atMethodCallCore(targetClass, mname, args, isStatic, isSpecial,
  401. aload0pos, cached);
  402. }
  403. private static void badMethod() throws CompileError {
  404. throw new CompileError("bad method");
  405. }
  406. /*
  407. * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew
  408. *
  409. * @param targetClass the class at which method lookup starts.
  410. * @param found not null if the method look has been already done.
  411. */
  412. public void atMethodCallCore(CtClass targetClass, String mname,
  413. ASTList args, boolean isStatic, boolean isSpecial,
  414. int aload0pos, MemberResolver.Method found)
  415. throws CompileError
  416. {
  417. int nargs = getMethodArgsLength(args);
  418. int[] types = new int[nargs];
  419. int[] dims = new int[nargs];
  420. String[] cnames = new String[nargs];
  421. if (!isStatic && found != null && found.isStatic()) {
  422. bytecode.addOpcode(POP);
  423. isStatic = true;
  424. }
  425. int stack = bytecode.getStackDepth();
  426. // generate code for evaluating arguments.
  427. atMethodArgs(args, types, dims, cnames);
  428. // used by invokeinterface
  429. int count = bytecode.getStackDepth() - stack + 1;
  430. if (found == null)
  431. found = resolver.lookupMethod(targetClass, thisClass, thisMethod,
  432. mname, types, dims, cnames);
  433. if (found == null) {
  434. String msg;
  435. if (mname.equals(MethodInfo.nameInit))
  436. msg = "constructor not found";
  437. else
  438. msg = "Method " + mname + " not found in "
  439. + targetClass.getName();
  440. throw new CompileError(msg);
  441. }
  442. atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
  443. aload0pos, count, found);
  444. }
  445. private void atMethodCallCore2(CtClass targetClass, String mname,
  446. boolean isStatic, boolean isSpecial,
  447. int aload0pos, int count,
  448. MemberResolver.Method found)
  449. throws CompileError
  450. {
  451. CtClass declClass = found.declaring;
  452. MethodInfo minfo = found.info;
  453. String desc = minfo.getDescriptor();
  454. int acc = minfo.getAccessFlags();
  455. if (mname.equals(MethodInfo.nameInit)) {
  456. isSpecial = true;
  457. if (declClass != targetClass)
  458. throw new CompileError("no such a constructor");
  459. if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
  460. desc = getAccessibleConstructor(desc, declClass, minfo);
  461. bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
  462. }
  463. }
  464. else if (AccessFlag.isPrivate(acc))
  465. if (declClass == thisClass)
  466. isSpecial = true;
  467. else {
  468. isSpecial = false;
  469. isStatic = true;
  470. String origDesc = desc;
  471. if ((acc & AccessFlag.STATIC) == 0)
  472. desc = Descriptor.insertParameter(declClass.getName(),
  473. origDesc);
  474. acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
  475. mname = getAccessiblePrivate(mname, origDesc, desc,
  476. minfo, declClass);
  477. }
  478. boolean popTarget = false;
  479. if ((acc & AccessFlag.STATIC) != 0) {
  480. if (!isStatic) {
  481. /* this method is static but the target object is
  482. on stack. It must be popped out. If aload0pos >= 0,
  483. then the target object was pushed by aload_0. It is
  484. overwritten by NOP.
  485. */
  486. isStatic = true;
  487. if (aload0pos >= 0)
  488. bytecode.write(aload0pos, NOP);
  489. else
  490. popTarget = true;
  491. }
  492. bytecode.addInvokestatic(declClass, mname, desc);
  493. }
  494. else if (isSpecial) // if (isSpecial && notStatic(acc))
  495. bytecode.addInvokespecial(declClass, mname, desc);
  496. else if (declClass.isInterface())
  497. bytecode.addInvokeinterface(declClass, mname, desc, count);
  498. else
  499. if (isStatic)
  500. throw new CompileError(mname + " is not static");
  501. else
  502. bytecode.addInvokevirtual(declClass, mname, desc);
  503. setReturnType(desc, isStatic, popTarget);
  504. }
  505. /*
  506. * Finds (or adds if necessary) a hidden accessor if the method
  507. * is in an enclosing class.
  508. *
  509. * @param desc the descriptor of the method.
  510. * @param declClass the class declaring the method.
  511. */
  512. protected String getAccessiblePrivate(String methodName, String desc,
  513. String newDesc, MethodInfo minfo,
  514. CtClass declClass)
  515. throws CompileError
  516. {
  517. if (isEnclosing(declClass, thisClass)) {
  518. AccessorMaker maker = declClass.getAccessorMaker();
  519. if (maker != null)
  520. return maker.getMethodAccessor(methodName, desc, newDesc,
  521. minfo);
  522. }
  523. throw new CompileError("Method " + methodName
  524. + " is private");
  525. }
  526. /*
  527. * Finds (or adds if necessary) a hidden constructor if the given
  528. * constructor is in an enclosing class.
  529. *
  530. * @param desc the descriptor of the constructor.
  531. * @param declClass the class declaring the constructor.
  532. * @param minfo the method info of the constructor.
  533. * @return the descriptor of the hidden constructor.
  534. */
  535. protected String getAccessibleConstructor(String desc, CtClass declClass,
  536. MethodInfo minfo)
  537. throws CompileError
  538. {
  539. if (isEnclosing(declClass, thisClass)) {
  540. AccessorMaker maker = declClass.getAccessorMaker();
  541. if (maker != null)
  542. return maker.getConstructor(declClass, desc, minfo);
  543. }
  544. throw new CompileError("the called constructor is private in "
  545. + declClass.getName());
  546. }
  547. private boolean isEnclosing(CtClass outer, CtClass inner) {
  548. try {
  549. while (inner != null) {
  550. inner = inner.getDeclaringClass();
  551. if (inner == outer)
  552. return true;
  553. }
  554. }
  555. catch (NotFoundException e) {}
  556. return false;
  557. }
  558. public int getMethodArgsLength(ASTList args) {
  559. return ASTList.length(args);
  560. }
  561. public void atMethodArgs(ASTList args, int[] types, int[] dims,
  562. String[] cnames) throws CompileError {
  563. int i = 0;
  564. while (args != null) {
  565. ASTree a = args.head();
  566. a.accept(this);
  567. types[i] = exprType;
  568. dims[i] = arrayDim;
  569. cnames[i] = className;
  570. ++i;
  571. args = args.tail();
  572. }
  573. }
  574. void setReturnType(String desc, boolean isStatic, boolean popTarget)
  575. throws CompileError
  576. {
  577. int i = desc.indexOf(')');
  578. if (i < 0)
  579. badMethod();
  580. char c = desc.charAt(++i);
  581. int dim = 0;
  582. while (c == '[') {
  583. ++dim;
  584. c = desc.charAt(++i);
  585. }
  586. arrayDim = dim;
  587. if (c == 'L') {
  588. int j = desc.indexOf(';', i + 1);
  589. if (j < 0)
  590. badMethod();
  591. exprType = CLASS;
  592. className = desc.substring(i + 1, j);
  593. }
  594. else {
  595. exprType = MemberResolver.descToType(c);
  596. className = null;
  597. }
  598. int etype = exprType;
  599. if (isStatic) {
  600. if (popTarget) {
  601. if (is2word(etype, dim)) {
  602. bytecode.addOpcode(DUP2_X1);
  603. bytecode.addOpcode(POP2);
  604. bytecode.addOpcode(POP);
  605. }
  606. else if (etype == VOID)
  607. bytecode.addOpcode(POP);
  608. else {
  609. bytecode.addOpcode(SWAP);
  610. bytecode.addOpcode(POP);
  611. }
  612. }
  613. }
  614. }
  615. protected void atFieldAssign(Expr expr, int op, ASTree left,
  616. ASTree right, boolean doDup) throws CompileError
  617. {
  618. CtField f = fieldAccess(left, false);
  619. boolean is_static = resultStatic;
  620. if (op != '=' && !is_static)
  621. bytecode.addOpcode(DUP);
  622. int fi;
  623. if (op == '=') {
  624. FieldInfo finfo = f.getFieldInfo2();
  625. setFieldType(finfo);
  626. AccessorMaker maker = isAccessibleField(f, finfo);
  627. if (maker == null)
  628. fi = addFieldrefInfo(f, finfo);
  629. else
  630. fi = 0;
  631. }
  632. else
  633. fi = atFieldRead(f, is_static);
  634. int fType = exprType;
  635. int fDim = arrayDim;
  636. String cname = className;
  637. atAssignCore(expr, op, right, fType, fDim, cname);
  638. boolean is2w = is2word(fType, fDim);
  639. if (doDup) {
  640. int dup_code;
  641. if (is_static)
  642. dup_code = (is2w ? DUP2 : DUP);
  643. else
  644. dup_code = (is2w ? DUP2_X1 : DUP_X1);
  645. bytecode.addOpcode(dup_code);
  646. }
  647. atFieldAssignCore(f, is_static, fi, is2w);
  648. exprType = fType;
  649. arrayDim = fDim;
  650. className = cname;
  651. }
  652. /* If fi == 0, the field must be a private field in an enclosing class.
  653. */
  654. private void atFieldAssignCore(CtField f, boolean is_static, int fi,
  655. boolean is2byte) throws CompileError {
  656. if (fi != 0) {
  657. if (is_static) {
  658. bytecode.add(PUTSTATIC);
  659. bytecode.growStack(is2byte ? -2 : -1);
  660. }
  661. else {
  662. bytecode.add(PUTFIELD);
  663. bytecode.growStack(is2byte ? -3 : -2);
  664. }
  665. bytecode.addIndex(fi);
  666. }
  667. else {
  668. CtClass declClass = f.getDeclaringClass();
  669. AccessorMaker maker = declClass.getAccessorMaker();
  670. // make should be non null.
  671. FieldInfo finfo = f.getFieldInfo2();
  672. MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
  673. bytecode.addInvokestatic(declClass, minfo.getName(),
  674. minfo.getDescriptor());
  675. }
  676. }
  677. /* overwritten in JvstCodeGen.
  678. */
  679. public void atMember(Member mem) throws CompileError {
  680. atFieldRead(mem);
  681. }
  682. protected void atFieldRead(ASTree expr) throws CompileError
  683. {
  684. CtField f = fieldAccess(expr, true);
  685. if (f == null) {
  686. atArrayLength(expr);
  687. return;
  688. }
  689. boolean is_static = resultStatic;
  690. ASTree cexpr = TypeChecker.getConstantFieldValue(f);
  691. if (cexpr == null)
  692. atFieldRead(f, is_static);
  693. else {
  694. cexpr.accept(this);
  695. setFieldType(f.getFieldInfo2());
  696. }
  697. }
  698. private void atArrayLength(ASTree expr) throws CompileError {
  699. if (arrayDim == 0)
  700. throw new CompileError(".length applied to a non array");
  701. bytecode.addOpcode(ARRAYLENGTH);
  702. exprType = INT;
  703. arrayDim = 0;
  704. }
  705. /**
  706. * Generates bytecode for reading a field value.
  707. * It returns a fieldref_info index or zero if the field is a private
  708. * one declared in an enclosing class.
  709. */
  710. private int atFieldRead(CtField f, boolean isStatic) throws CompileError {
  711. FieldInfo finfo = f.getFieldInfo2();
  712. boolean is2byte = setFieldType(finfo);
  713. AccessorMaker maker = isAccessibleField(f, finfo);
  714. if (maker != null) {
  715. MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
  716. bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(),
  717. minfo.getDescriptor());
  718. return 0;
  719. }
  720. else {
  721. int fi = addFieldrefInfo(f, finfo);
  722. if (isStatic) {
  723. bytecode.add(GETSTATIC);
  724. bytecode.growStack(is2byte ? 2 : 1);
  725. }
  726. else {
  727. bytecode.add(GETFIELD);
  728. bytecode.growStack(is2byte ? 1 : 0);
  729. }
  730. bytecode.addIndex(fi);
  731. return fi;
  732. }
  733. }
  734. /**
  735. * Returns null if the field is accessible. Otherwise, it throws
  736. * an exception or it returns AccessorMaker if the field is a private
  737. * one declared in an enclosing class.
  738. */
  739. private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
  740. throws CompileError
  741. {
  742. if (AccessFlag.isPrivate(finfo.getAccessFlags())
  743. && f.getDeclaringClass() != thisClass) {
  744. CtClass declClass = f.getDeclaringClass();
  745. if (isEnclosing(declClass, thisClass)) {
  746. AccessorMaker maker = declClass.getAccessorMaker();
  747. if (maker != null)
  748. return maker;
  749. else
  750. throw new CompileError("fatal error. bug?");
  751. }
  752. else
  753. throw new CompileError("Field " + f.getName() + " in "
  754. + declClass.getName() + " is private.");
  755. }
  756. return null; // accessible field
  757. }
  758. /**
  759. * Sets exprType, arrayDim, and className.
  760. *
  761. * @return true if the field type is long or double.
  762. */
  763. private boolean setFieldType(FieldInfo finfo) throws CompileError {
  764. String type = finfo.getDescriptor();
  765. int i = 0;
  766. int dim = 0;
  767. char c = type.charAt(i);
  768. while (c == '[') {
  769. ++dim;
  770. c = type.charAt(++i);
  771. }
  772. arrayDim = dim;
  773. exprType = MemberResolver.descToType(c);
  774. if (c == 'L')
  775. className = type.substring(i + 1, type.indexOf(';', i + 1));
  776. else
  777. className = null;
  778. boolean is2byte = (c == 'J' || c == 'D');
  779. return is2byte;
  780. }
  781. private int addFieldrefInfo(CtField f, FieldInfo finfo) {
  782. ConstPool cp = bytecode.getConstPool();
  783. String cname = f.getDeclaringClass().getName();
  784. int ci = cp.addClassInfo(cname);
  785. String name = finfo.getName();
  786. String type = finfo.getDescriptor();
  787. return cp.addFieldrefInfo(ci, name, type);
  788. }
  789. protected void atFieldPlusPlus(int token, boolean isPost,
  790. ASTree oprand, Expr expr, boolean doDup)
  791. throws CompileError
  792. {
  793. CtField f = fieldAccess(oprand, false);
  794. boolean is_static = resultStatic;
  795. if (!is_static)
  796. bytecode.addOpcode(DUP);
  797. int fi = atFieldRead(f, is_static);
  798. int t = exprType;
  799. boolean is2w = is2word(t, arrayDim);
  800. int dup_code;
  801. if (is_static)
  802. dup_code = (is2w ? DUP2 : DUP);
  803. else
  804. dup_code = (is2w ? DUP2_X1 : DUP_X1);
  805. atPlusPlusCore(dup_code, doDup, token, isPost, expr);
  806. atFieldAssignCore(f, is_static, fi, is2w);
  807. }
  808. /* This method also returns a value in resultStatic.
  809. *
  810. * @param acceptLength true if array length is acceptable
  811. */
  812. protected CtField fieldAccess(ASTree expr, boolean acceptLength)
  813. throws CompileError
  814. {
  815. if (expr instanceof Member) {
  816. String name = ((Member)expr).get();
  817. CtField f = null;
  818. try {
  819. f = thisClass.getField(name);
  820. }
  821. catch (NotFoundException e) {
  822. // EXPR might be part of a static member access?
  823. throw new NoFieldException(name, expr);
  824. }
  825. boolean is_static = Modifier.isStatic(f.getModifiers());
  826. if (!is_static)
  827. if (inStaticMethod)
  828. throw new CompileError(
  829. "not available in a static method: " + name);
  830. else
  831. bytecode.addAload(0); // this
  832. resultStatic = is_static;
  833. return f;
  834. }
  835. else if (expr instanceof Expr) {
  836. Expr e = (Expr)expr;
  837. int op = e.getOperator();
  838. if (op == MEMBER) {
  839. /* static member by # (extension by Javassist)
  840. * For example, if int.class is parsed, the resulting tree
  841. * is (# "java.lang.Integer" "TYPE").
  842. */
  843. CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(),
  844. (Symbol)e.oprand2());
  845. resultStatic = true;
  846. return f;
  847. }
  848. else if (op == '.') {
  849. CtField f = null;
  850. try {
  851. e.oprand1().accept(this);
  852. /* Don't call lookupFieldByJvmName2().
  853. * The left operand of . is not a class name but
  854. * a normal expression.
  855. */
  856. if (exprType == CLASS && arrayDim == 0)
  857. f = resolver.lookupFieldByJvmName(className,
  858. (Symbol)e.oprand2());
  859. else if (acceptLength && arrayDim > 0
  860. && ((Symbol)e.oprand2()).get().equals("length"))
  861. return null; // expr is an array length.
  862. else
  863. badLvalue();
  864. boolean is_static = Modifier.isStatic(f.getModifiers());
  865. if (is_static)
  866. bytecode.addOpcode(POP);
  867. resultStatic = is_static;
  868. return f;
  869. }
  870. catch (NoFieldException nfe) {
  871. if (nfe.getExpr() != e.oprand1())
  872. throw nfe;
  873. /* EXPR should be a static field.
  874. * If EXPR might be part of a qualified class name,
  875. * lookupFieldByJvmName2() throws NoFieldException.
  876. */
  877. Symbol fname = (Symbol)e.oprand2();
  878. String cname = nfe.getField();
  879. f = resolver.lookupFieldByJvmName2(cname, fname, expr);
  880. resolver.recordPackage(cname);
  881. resultStatic = true;
  882. return f;
  883. }
  884. }
  885. else
  886. badLvalue();
  887. }
  888. else
  889. badLvalue();
  890. resultStatic = false;
  891. return null; // never reach
  892. }
  893. private static void badLvalue() throws CompileError {
  894. throw new CompileError("bad l-value");
  895. }
  896. public CtClass[] makeParamList(MethodDecl md) throws CompileError {
  897. CtClass[] params;
  898. ASTList plist = md.getParams();
  899. if (plist == null)
  900. params = new CtClass[0];
  901. else {
  902. int i = 0;
  903. params = new CtClass[plist.length()];
  904. while (plist != null) {
  905. params[i++] = resolver.lookupClass((Declarator)plist.head());
  906. plist = plist.tail();
  907. }
  908. }
  909. return params;
  910. }
  911. public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
  912. CtClass[] clist;
  913. ASTList list = md.getThrows();
  914. if (list == null)
  915. return null;
  916. else {
  917. int i = 0;
  918. clist = new CtClass[list.length()];
  919. while (list != null) {
  920. clist[i++] = resolver.lookupClassByName((ASTList)list.head());
  921. list = list.tail();
  922. }
  923. return clist;
  924. }
  925. }
  926. /* Converts a class name into a JVM-internal representation.
  927. *
  928. * It may also expand a simple class name to java.lang.*.
  929. * For example, this converts Object into java/lang/Object.
  930. */
  931. protected String resolveClassName(ASTList name) throws CompileError {
  932. return resolver.resolveClassName(name);
  933. }
  934. /* Expands a simple class name to java.lang.*.
  935. * For example, this converts Object into java/lang/Object.
  936. */
  937. protected String resolveClassName(String jvmName) throws CompileError {
  938. return resolver.resolveJvmClassName(jvmName);
  939. }
  940. }