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.

JvstCodeGen.java 21KB

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