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 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  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 (exprType == VOID || isRefType(exprType) || arrayDim > 0)
  168. compileUnwrapValue(returnType, bytecode);
  169. else if (returnType instanceof CtPrimitiveType) {
  170. CtPrimitiveType pt = (CtPrimitiveType)returnType;
  171. int destType = jvmTypeNameToExprType(pt.getDescriptor());
  172. atNumCastExpr(exprType, destType);
  173. exprType = destType;
  174. arrayDim = 0;
  175. className = null;
  176. }
  177. else
  178. throw new CompileError("invalid cast");
  179. }
  180. protected void atCastToWrapper(CastExpr expr) throws CompileError {
  181. expr.getOprand().accept(this);
  182. if (isRefType(exprType) || arrayDim > 0)
  183. return; // Object type. do nothing.
  184. CtClass clazz = lookupClass(exprType, arrayDim, className);
  185. if (clazz instanceof CtPrimitiveType) {
  186. CtPrimitiveType pt = (CtPrimitiveType)clazz;
  187. String wrapper = pt.getWrapperName();
  188. bytecode.addNew(wrapper); // new <wrapper>
  189. bytecode.addOpcode(DUP); // dup
  190. if (pt.getDataSize() > 1)
  191. bytecode.addOpcode(DUP2_X2); // dup2_x2
  192. else
  193. bytecode.addOpcode(DUP2_X1); // dup2_x1
  194. bytecode.addOpcode(POP2); // pop2
  195. bytecode.addInvokespecial(wrapper, "<init>",
  196. "(" + pt.getDescriptor() + ")V");
  197. // invokespecial
  198. exprType = CLASS;
  199. arrayDim = 0;
  200. className = jvmJavaLangObject;
  201. }
  202. }
  203. /* Delegates to a ProcHandler object if the method call is
  204. * $proceed(). It may process $cflow().
  205. */
  206. protected void atMethodCall(Expr expr) throws CompileError {
  207. ASTree method = expr.oprand1();
  208. if (method instanceof Member) {
  209. String name = ((Member)method).get();
  210. if (procHandler != null && name.equals(proceedName)) {
  211. procHandler.doit(this, bytecode, (ASTList)expr.oprand2());
  212. return;
  213. }
  214. else if (name.equals(cflowName)) {
  215. atCflow((ASTList)expr.oprand2());
  216. return;
  217. }
  218. }
  219. super.atMethodCall(expr);
  220. }
  221. /* To support $cflow().
  222. */
  223. protected void atCflow(ASTList cname) throws CompileError {
  224. StringBuffer sbuf = new StringBuffer();
  225. if (cname == null || cname.tail() != null)
  226. throw new CompileError("bad " + cflowName);
  227. makeCflowName(sbuf, cname.head());
  228. String name = sbuf.toString();
  229. Object[] names = classPool.lookupCflow(name);
  230. if (names == null)
  231. throw new CompileError("no such a " + cflowName + ": " + name);
  232. bytecode.addGetstatic((String)names[0], (String)names[1],
  233. "Ljavassist/runtime/Cflow;");
  234. bytecode.addInvokevirtual("javassist.runtime.Cflow",
  235. "value", "()I");
  236. exprType = INT;
  237. arrayDim = 0;
  238. className = null;
  239. }
  240. /* Syntax:
  241. *
  242. * <cflow> : $cflow '(' <cflow name> ')'
  243. * <cflow name> : <identifier> ('.' <identifier>)*
  244. */
  245. private static void makeCflowName(StringBuffer sbuf, ASTree name)
  246. throws CompileError
  247. {
  248. if (name instanceof Symbol) {
  249. sbuf.append(((Symbol)name).get());
  250. return;
  251. }
  252. else if (name instanceof Expr) {
  253. Expr expr = (Expr)name;
  254. if (expr.getOperator() == '.') {
  255. makeCflowName(sbuf, expr.oprand1());
  256. sbuf.append('.');
  257. makeCflowName(sbuf, expr.oprand2());
  258. return;
  259. }
  260. }
  261. throw new CompileError("bad " + cflowName);
  262. }
  263. /* To support $$. ($$) is equivalent to ($1, ..., $n).
  264. * It can be used only as a parameter list of method call.
  265. */
  266. public boolean isParamListName(ASTList args) {
  267. if (paramTypeList != null
  268. && args != null && args.tail() == null) {
  269. ASTree left = args.head();
  270. return (left instanceof Member
  271. && ((Member)left).get().equals(paramListName));
  272. }
  273. else
  274. return false;
  275. }
  276. /*
  277. public int atMethodArgsLength(ASTList args) {
  278. if (!isParamListName(args))
  279. return super.atMethodArgsLength(args);
  280. return paramTypeList.length;
  281. }
  282. */
  283. public int atMethodArgsLength(ASTList args) {
  284. String pname = paramListName;
  285. int n = 0;
  286. while (args != null) {
  287. ASTree a = args.head();
  288. if (a instanceof Member && ((Member)a).get().equals(pname)) {
  289. if (paramTypeList != null)
  290. n += paramTypeList.length;
  291. }
  292. else
  293. ++n;
  294. args = args.tail();
  295. }
  296. return n;
  297. }
  298. public void atMethodArgs(ASTList args, int[] types, int[] dims,
  299. String[] cnames) throws CompileError {
  300. CtClass[] params = paramTypeList;
  301. String pname = paramListName;
  302. int i = 0;
  303. while (args != null) {
  304. ASTree a = args.head();
  305. if (a instanceof Member && ((Member)a).get().equals(pname)) {
  306. if (params != null) {
  307. int n = params.length;
  308. int regno = indexOfParam1();
  309. for (int k = 0; k < n; ++k) {
  310. CtClass p = params[k];
  311. regno += bytecode.addLoad(regno, p);
  312. setType(p);
  313. types[i] = exprType;
  314. dims[i] = arrayDim;
  315. cnames[i] = className;
  316. ++i;
  317. }
  318. }
  319. }
  320. else {
  321. a.accept(this);
  322. types[i] = exprType;
  323. dims[i] = arrayDim;
  324. cnames[i] = className;
  325. ++i;
  326. }
  327. args = args.tail();
  328. }
  329. }
  330. /*
  331. public void atMethodArgs(ASTList args, int[] types, int[] dims,
  332. String[] cnames) throws CompileError {
  333. if (!isParamListName(args)) {
  334. super.atMethodArgs(args, types, dims, cnames);
  335. return;
  336. }
  337. CtClass[] params = paramTypeList;
  338. if (params == null)
  339. return;
  340. int n = params.length;
  341. int regno = indexOfParam1();
  342. for (int i = 0; i < n; ++i) {
  343. CtClass p = params[i];
  344. regno += bytecode.addLoad(regno, p);
  345. setType(p);
  346. types[i] = exprType;
  347. dims[i] = arrayDim;
  348. cnames[i] = className;
  349. }
  350. }
  351. */
  352. /*
  353. * Makes it valid to write "return <expr>;" for a void method.
  354. */
  355. protected void atReturnStmnt(Stmnt st) throws CompileError {
  356. ASTree result = st.getLeft();
  357. if (result != null && returnType == CtClass.voidType) {
  358. result.accept(this);
  359. if (is2word(exprType, arrayDim))
  360. bytecode.addOpcode(POP2);
  361. else if (exprType != VOID)
  362. bytecode.addOpcode(POP);
  363. result = null;
  364. }
  365. atReturnStmnt2(result);
  366. }
  367. /**
  368. * Makes a cast to the return type ($r) available.
  369. * It also enables $_.
  370. *
  371. * <p>If the return type is void, ($r) does nothing.
  372. * The type of $_ is java.lang.Object.
  373. *
  374. * @param resultName null if $_ is not used.
  375. * @return -1 or the variable index assigned to $_.
  376. */
  377. public int recordReturnType(CtClass type, String castName,
  378. String resultName, SymbolTable tbl) throws CompileError
  379. {
  380. returnType = type;
  381. returnCastName = castName;
  382. returnVarName = resultName;
  383. if (resultName == null)
  384. return -1;
  385. else {
  386. int varNo = getMaxLocals();
  387. int locals = varNo + recordVar(type, resultName, varNo, tbl);
  388. setMaxLocals(locals);
  389. return varNo;
  390. }
  391. }
  392. /**
  393. * Makes $type available.
  394. */
  395. public void recordType(CtClass t) {
  396. dollarType = t;
  397. }
  398. /**
  399. * Makes method parameters $0, $1, ..., $args, and $$ available.
  400. * $0 is equivalent to THIS if the method is not static. Otherwise,
  401. * if the method is static, then $0 is not available.
  402. */
  403. public void recordParams(CtClass[] params, boolean isStatic,
  404. String prefix, String paramVarName,
  405. String paramsName, SymbolTable tbl)
  406. throws CompileError
  407. {
  408. recordParams(params, isStatic, prefix, paramVarName,
  409. paramsName, !isStatic, 0, getThisName(), tbl);
  410. }
  411. /**
  412. * Makes method parameters $0, $1, ..., $args, and $$ available.
  413. * $0 is available only if use0 is true. It might not be equivalent
  414. * to THIS.
  415. *
  416. * @paaram use0 true if $0 is used.
  417. * @param paramBase the register number of $0 (use0 is true)
  418. * or $1 (otherwise).
  419. * @param target the class of $0. If use0 is false, target
  420. * can be null.
  421. * @param isStatic true if the method in which the compiled bytecode
  422. * is embedded is static.
  423. */
  424. public void recordParams(CtClass[] params, boolean isStatic,
  425. String prefix, String paramVarName,
  426. String paramsName, boolean use0,
  427. int paramBase, String target,
  428. SymbolTable tbl)
  429. throws CompileError
  430. {
  431. int varNo;
  432. paramTypeList = params;
  433. paramArrayName = paramVarName;
  434. paramListName = paramsName;
  435. paramVarBase = paramBase;
  436. useParam0 = use0;
  437. param0Type = jvmToJavaName(target);
  438. inStaticMethod = isStatic;
  439. varNo = paramBase;
  440. if (use0) {
  441. String varName = prefix + "0";
  442. Declarator decl
  443. = new Declarator(CLASS, javaToJvmName(target), 0, varNo++,
  444. new Symbol(varName));
  445. tbl.append(varName, decl);
  446. }
  447. for (int i = 0; i < params.length; ++i)
  448. varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl);
  449. if (getMaxLocals() < varNo)
  450. setMaxLocals(varNo);
  451. }
  452. /**
  453. * Makes the given variable name available.
  454. *
  455. * @param type variable type
  456. * @param varName variable name
  457. */
  458. public int recordVariable(CtClass type, String varName, SymbolTable tbl)
  459. throws CompileError
  460. {
  461. if (varName == null)
  462. return -1;
  463. else {
  464. int varNo = getMaxLocals();
  465. int locals = varNo + recordVar(type, varName, varNo, tbl);
  466. setMaxLocals(locals);
  467. return varNo;
  468. }
  469. }
  470. private int recordVar(CtClass cc, String varName, int varNo,
  471. SymbolTable tbl) throws CompileError
  472. {
  473. if (cc == CtClass.voidType) {
  474. exprType = CLASS;
  475. arrayDim = 0;
  476. className = jvmJavaLangObject;
  477. }
  478. else
  479. setType(cc);
  480. Declarator decl
  481. = new Declarator(exprType, className, arrayDim,
  482. varNo, new Symbol(varName));
  483. tbl.append(varName, decl);
  484. return is2word(exprType, arrayDim) ? 2 : 1;
  485. }
  486. /* compileParameterList() returns the stack size used
  487. * by the produced code.
  488. *
  489. * This method correctly computes the max_stack value.
  490. *
  491. * @param regno the index of the local variable in which
  492. * the first argument is received.
  493. * (0: static method, 1: regular method.)
  494. */
  495. public static int compileParameterList(Bytecode code,
  496. CtClass[] params, int regno) {
  497. if (params == null) {
  498. code.addIconst(0); // iconst_0
  499. code.addAnewarray(javaLangObject); // anewarray Object
  500. return 1;
  501. }
  502. else {
  503. CtClass[] args = new CtClass[1];
  504. int n = params.length;
  505. code.addIconst(n); // iconst_<n>
  506. code.addAnewarray(javaLangObject); // anewarray Object
  507. for (int i = 0; i < n; ++i) {
  508. code.addOpcode(Bytecode.DUP); // dup
  509. code.addIconst(i); // iconst_<i>
  510. if (params[i].isPrimitive()) {
  511. CtPrimitiveType pt = (CtPrimitiveType)params[i];
  512. String wrapper = pt.getWrapperName();
  513. code.addNew(wrapper); // new <wrapper>
  514. code.addOpcode(Bytecode.DUP); // dup
  515. int s = code.addLoad(regno, pt); // ?load <regno>
  516. regno += s;
  517. args[0] = pt;
  518. code.addInvokespecial(wrapper, "<init>",
  519. Descriptor.ofMethod(CtClass.voidType, args));
  520. // invokespecial
  521. }
  522. else {
  523. code.addAload(regno); // aload <regno>
  524. ++regno;
  525. }
  526. code.addOpcode(Bytecode.AASTORE); // aastore
  527. }
  528. return 8;
  529. }
  530. }
  531. protected void compileUnwrapValue(CtClass type, Bytecode code)
  532. throws CompileError
  533. {
  534. if (type == CtClass.voidType) {
  535. addNullIfVoid();
  536. return;
  537. }
  538. if (exprType == VOID)
  539. throw new CompileError("invalid type for " + returnCastName);
  540. if (type instanceof CtPrimitiveType) {
  541. CtPrimitiveType pt = (CtPrimitiveType)type;
  542. // pt is not voidType.
  543. String wrapper = pt.getWrapperName();
  544. code.addCheckcast(wrapper);
  545. code.addInvokevirtual(wrapper, pt.getGetMethodName(),
  546. pt.getGetMethodDescriptor());
  547. setType(type);
  548. }
  549. else {
  550. code.addCheckcast(type);
  551. setType(type);
  552. }
  553. }
  554. /* Sets exprType, arrayDim, and className;
  555. * If type is void, then this method does nothing.
  556. */
  557. public void setType(CtClass type) throws CompileError {
  558. setType(type, 0);
  559. }
  560. private void setType(CtClass type, int dim) throws CompileError {
  561. if (type.isPrimitive()) {
  562. CtPrimitiveType pt = (CtPrimitiveType)type;
  563. exprType = descToType(pt.getDescriptor());
  564. arrayDim = dim;
  565. className = null;
  566. }
  567. else if (type.isArray())
  568. try {
  569. setType(type.getComponentType(), dim + 1);
  570. }
  571. catch (NotFoundException e) {
  572. throw new CompileError("undefined type: " + type.getName());
  573. }
  574. else {
  575. exprType = CLASS;
  576. arrayDim = dim;
  577. className = javaToJvmName(type.getName());
  578. }
  579. }
  580. /* Performs implicit coercion from exprType to type.
  581. */
  582. public void doNumCast(CtClass type) throws CompileError {
  583. if (arrayDim == 0 && !isRefType(exprType))
  584. if (type instanceof CtPrimitiveType) {
  585. CtPrimitiveType pt = (CtPrimitiveType)type;
  586. atNumCastExpr(exprType, descToType(pt.getDescriptor()));
  587. }
  588. else
  589. throw new CompileError("type mismatch");
  590. }
  591. }