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.

TypeChecker.java 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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. package javassist.compiler;
  17. import javassist.ClassPool;
  18. import javassist.CtClass;
  19. import javassist.CtField;
  20. import javassist.Modifier;
  21. import javassist.NotFoundException;
  22. import javassist.bytecode.FieldInfo;
  23. import javassist.bytecode.MethodInfo;
  24. import javassist.bytecode.Opcode;
  25. import javassist.compiler.ast.ASTList;
  26. import javassist.compiler.ast.ASTree;
  27. import javassist.compiler.ast.ArrayInit;
  28. import javassist.compiler.ast.AssignExpr;
  29. import javassist.compiler.ast.BinExpr;
  30. import javassist.compiler.ast.CallExpr;
  31. import javassist.compiler.ast.CastExpr;
  32. import javassist.compiler.ast.CondExpr;
  33. import javassist.compiler.ast.Declarator;
  34. import javassist.compiler.ast.DoubleConst;
  35. import javassist.compiler.ast.Expr;
  36. import javassist.compiler.ast.InstanceOfExpr;
  37. import javassist.compiler.ast.IntConst;
  38. import javassist.compiler.ast.Keyword;
  39. import javassist.compiler.ast.Member;
  40. import javassist.compiler.ast.NewExpr;
  41. import javassist.compiler.ast.StringL;
  42. import javassist.compiler.ast.Symbol;
  43. import javassist.compiler.ast.Variable;
  44. import javassist.compiler.ast.Visitor;
  45. public class TypeChecker extends Visitor implements Opcode, TokenId {
  46. static final String javaLangObject = "java.lang.Object";
  47. static final String jvmJavaLangObject = "java/lang/Object";
  48. static final String jvmJavaLangString = "java/lang/String";
  49. static final String jvmJavaLangClass = "java/lang/Class";
  50. /* The following fields are used by atXXX() methods
  51. * for returning the type of the compiled expression.
  52. */
  53. protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ...
  54. protected int arrayDim;
  55. protected String className; // JVM-internal representation
  56. protected MemberResolver resolver;
  57. protected CtClass thisClass;
  58. protected MethodInfo thisMethod;
  59. public TypeChecker(CtClass cc, ClassPool cp) {
  60. resolver = new MemberResolver(cp);
  61. thisClass = cc;
  62. thisMethod = null;
  63. }
  64. /*
  65. * Converts an array of tuples of exprType, arrayDim, and className
  66. * into a String object.
  67. */
  68. protected static String argTypesToString(int[] types, int[] dims,
  69. String[] cnames, int lineNumber) {
  70. StringBuilder sbuf = new StringBuilder();
  71. sbuf.append('(');
  72. int n = types.length;
  73. if (n > 0) {
  74. int i = 0;
  75. while (true) {
  76. typeToString(sbuf, types[i], dims[i], cnames[i], lineNumber);
  77. if (++i < n)
  78. sbuf.append(',');
  79. else
  80. break;
  81. }
  82. }
  83. sbuf.append(')');
  84. return sbuf.toString();
  85. }
  86. /*
  87. * Converts a tuple of exprType, arrayDim, and className
  88. * into a String object.
  89. */
  90. protected static StringBuilder typeToString(StringBuilder sbuf,
  91. int type, int dim, String cname, int lineNumber) {
  92. String s;
  93. if (type == CLASS)
  94. s = MemberResolver.jvmToJavaName(cname);
  95. else if (type == NULL)
  96. s = "Object";
  97. else
  98. try {
  99. s = MemberResolver.getTypeName(type, lineNumber);
  100. }
  101. catch (CompileError e) {
  102. s = "?";
  103. }
  104. sbuf.append(s);
  105. while (dim-- > 0)
  106. sbuf.append("[]");
  107. return sbuf;
  108. }
  109. /**
  110. * Records the currently compiled method.
  111. */
  112. public void setThisMethod(MethodInfo m) {
  113. thisMethod = m;
  114. }
  115. protected static void fatal(int lineLum) throws CompileError {
  116. throw new CompileError("fatal", lineLum);
  117. }
  118. /**
  119. * Returns the JVM-internal representation of this class name.
  120. */
  121. protected String getThisName() {
  122. return MemberResolver.javaToJvmName(thisClass.getName());
  123. }
  124. /**
  125. * Returns the JVM-internal representation of this super class name.
  126. */
  127. protected String getSuperName() throws CompileError {
  128. return MemberResolver.javaToJvmName(
  129. MemberResolver.getSuperclass(thisClass).getName());
  130. }
  131. /* Converts a class name into a JVM-internal representation.
  132. *
  133. * It may also expand a simple class name to java.lang.*.
  134. * For example, this converts Object into java/lang/Object.
  135. */
  136. protected String resolveClassName(ASTList name) throws CompileError {
  137. return resolver.resolveClassName(name);
  138. }
  139. /* Expands a simple class name to java.lang.*.
  140. * For example, this converts Object into java/lang/Object.
  141. */
  142. protected String resolveClassName(String jvmName, int lineNumber) throws CompileError {
  143. return resolver.resolveJvmClassName(jvmName, lineNumber);
  144. }
  145. @Override
  146. public void atNewExpr(NewExpr expr) throws CompileError {
  147. if (expr.isArray())
  148. atNewArrayExpr(expr);
  149. else {
  150. CtClass clazz = resolver.lookupClassByName(expr.getClassName());
  151. String cname = clazz.getName();
  152. ASTList args = expr.getArguments();
  153. atMethodCallCore(clazz, MethodInfo.nameInit, args, expr.getLineNumber());
  154. exprType = CLASS;
  155. arrayDim = 0;
  156. className = MemberResolver.javaToJvmName(cname);
  157. }
  158. }
  159. public void atNewArrayExpr(NewExpr expr) throws CompileError {
  160. int type = expr.getArrayType();
  161. ASTList size = expr.getArraySize();
  162. ASTList classname = expr.getClassName();
  163. ASTree init = expr.getInitializer();
  164. if (init != null)
  165. init.accept(this);
  166. if (size.length() > 1)
  167. atMultiNewArray(type, classname, size);
  168. else {
  169. ASTree sizeExpr = size.head();
  170. if (sizeExpr != null)
  171. sizeExpr.accept(this);
  172. exprType = type;
  173. arrayDim = 1;
  174. if (type == CLASS)
  175. className = resolveClassName(classname);
  176. else
  177. className = null;
  178. }
  179. }
  180. @Override
  181. public void atArrayInit(ArrayInit init) throws CompileError {
  182. ASTList list = init;
  183. while (list != null) {
  184. ASTree h = list.head();
  185. list = list.tail();
  186. if (h != null)
  187. h.accept(this);
  188. }
  189. }
  190. protected void atMultiNewArray(int type, ASTList classname, ASTList size)
  191. throws CompileError
  192. {
  193. @SuppressWarnings("unused")
  194. int count, dim;
  195. dim = size.length();
  196. for (count = 0; size != null; size = size.tail()) {
  197. ASTree s = size.head();
  198. if (s == null)
  199. break; // int[][][] a = new int[3][4][];
  200. ++count;
  201. s.accept(this);
  202. }
  203. exprType = type;
  204. arrayDim = dim;
  205. if (type == CLASS)
  206. className = resolveClassName(classname);
  207. else
  208. className = null;
  209. }
  210. @Override
  211. public void atAssignExpr(AssignExpr expr) throws CompileError {
  212. // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
  213. int op = expr.getOperator();
  214. ASTree left = expr.oprand1();
  215. ASTree right = expr.oprand2();
  216. if (left instanceof Variable)
  217. atVariableAssign(expr, op, (Variable)left,
  218. ((Variable)left).getDeclarator(),
  219. right);
  220. else {
  221. if (left instanceof Expr) {
  222. Expr e = (Expr)left;
  223. if (e.getOperator() == ARRAY) {
  224. atArrayAssign(expr, op, (Expr)left, right);
  225. return;
  226. }
  227. }
  228. atFieldAssign(expr, op, left, right);
  229. }
  230. }
  231. /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
  232. *
  233. * expr and var can be null.
  234. */
  235. private void atVariableAssign(Expr expr, int op, Variable var,
  236. Declarator d, ASTree right)
  237. throws CompileError
  238. {
  239. int varType = d.getType();
  240. int varArray = d.getArrayDim();
  241. String varClass = d.getClassName();
  242. if (op != '=')
  243. atVariable(var);
  244. right.accept(this);
  245. exprType = varType;
  246. arrayDim = varArray;
  247. className = varClass;
  248. }
  249. private void atArrayAssign(Expr expr, int op, Expr array,
  250. ASTree right) throws CompileError
  251. {
  252. atArrayRead(array.oprand1(), array.oprand2());
  253. int aType = exprType;
  254. int aDim = arrayDim;
  255. String cname = className;
  256. right.accept(this);
  257. exprType = aType;
  258. arrayDim = aDim;
  259. className = cname;
  260. }
  261. protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right)
  262. throws CompileError
  263. {
  264. CtField f = fieldAccess(left);
  265. atFieldRead(f, expr.getLineNumber());
  266. int fType = exprType;
  267. int fDim = arrayDim;
  268. String cname = className;
  269. right.accept(this);
  270. exprType = fType;
  271. arrayDim = fDim;
  272. className = cname;
  273. }
  274. @Override
  275. public void atCondExpr(CondExpr expr) throws CompileError {
  276. booleanExpr(expr.condExpr());
  277. expr.thenExpr().accept(this);
  278. int type1 = exprType;
  279. int dim1 = arrayDim;
  280. @SuppressWarnings("unused")
  281. String cname1 = className;
  282. expr.elseExpr().accept(this);
  283. if (dim1 == 0 && dim1 == arrayDim)
  284. if (CodeGen.rightIsStrong(type1, exprType))
  285. expr.setThen(new CastExpr(exprType, 0, expr.thenExpr(), expr.getLineNumber()));
  286. else if (CodeGen.rightIsStrong(exprType, type1)) {
  287. expr.setElse(new CastExpr(type1, 0, expr.elseExpr(), expr.getLineNumber()));
  288. exprType = type1;
  289. }
  290. }
  291. /*
  292. * If atBinExpr() substitutes a new expression for the original
  293. * binary-operator expression, it changes the operator name to '+'
  294. * (if the original is not '+') and sets the new expression to the
  295. * left-hand-side expression and null to the right-hand-side expression.
  296. */
  297. @Override
  298. public void atBinExpr(BinExpr expr) throws CompileError {
  299. int token = expr.getOperator();
  300. int k = CodeGen.lookupBinOp(token);
  301. if (k >= 0) {
  302. /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
  303. */
  304. if (token == '+') {
  305. Expr e = atPlusExpr(expr);
  306. if (e != null) {
  307. /* String concatenation has been translated into
  308. * an expression using StringBuffer.
  309. */
  310. e = CallExpr.makeCall(Expr.make('.', e,
  311. new Member("toString", expr.getLineNumber()), expr.getLineNumber()), null, expr.getLineNumber());
  312. expr.setOprand1(e);
  313. expr.setOprand2(null); // <---- look at this!
  314. className = jvmJavaLangString;
  315. }
  316. }
  317. else {
  318. ASTree left = expr.oprand1();
  319. ASTree right = expr.oprand2();
  320. left.accept(this);
  321. int type1 = exprType;
  322. right.accept(this);
  323. if (!isConstant(expr, token, left, right))
  324. computeBinExprType(expr, token, type1);
  325. }
  326. }
  327. else {
  328. /* equation: &&, ||, ==, !=, <=, >=, <, >
  329. */
  330. booleanExpr(expr);
  331. }
  332. }
  333. /* EXPR must be a + expression.
  334. * atPlusExpr() returns non-null if the given expression is string
  335. * concatenation. The returned value is "new StringBuffer().append..".
  336. */
  337. private Expr atPlusExpr(BinExpr expr) throws CompileError {
  338. ASTree left = expr.oprand1();
  339. ASTree right = expr.oprand2();
  340. if (right == null) {
  341. // this expression has been already type-checked.
  342. // see atBinExpr() above.
  343. left.accept(this);
  344. return null;
  345. }
  346. if (isPlusExpr(left)) {
  347. Expr newExpr = atPlusExpr((BinExpr)left);
  348. if (newExpr != null) {
  349. right.accept(this);
  350. exprType = CLASS;
  351. arrayDim = 0;
  352. className = "java/lang/StringBuffer";
  353. return makeAppendCall(newExpr, right);
  354. }
  355. }
  356. else
  357. left.accept(this);
  358. int type1 = exprType;
  359. int dim1 = arrayDim;
  360. String cname = className;
  361. right.accept(this);
  362. if (isConstant(expr, '+', left, right))
  363. return null;
  364. if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname))
  365. || (exprType == CLASS && arrayDim == 0
  366. && jvmJavaLangString.equals(className))) {
  367. int lineNum = expr.getLineNumber();
  368. ASTList sbufClass = ASTList.make(new Symbol("java", lineNum),
  369. new Symbol("lang", lineNum), new Symbol("StringBuffer", lineNum), lineNum);
  370. ASTree e = new NewExpr(sbufClass, null, lineNum);
  371. exprType = CLASS;
  372. arrayDim = 0;
  373. className = "java/lang/StringBuffer";
  374. return makeAppendCall(makeAppendCall(e, left), right);
  375. }
  376. computeBinExprType(expr, '+', type1);
  377. return null;
  378. }
  379. private boolean isConstant(BinExpr expr, int op, ASTree left,
  380. ASTree right) throws CompileError
  381. {
  382. left = stripPlusExpr(left);
  383. right = stripPlusExpr(right);
  384. ASTree newExpr = null;
  385. if (left instanceof StringL && right instanceof StringL && op == '+')
  386. newExpr = new StringL(((StringL)left).get()
  387. + ((StringL)right).get(), left.getLineNumber());
  388. else if (left instanceof IntConst)
  389. newExpr = ((IntConst)left).compute(op, right);
  390. else if (left instanceof DoubleConst)
  391. newExpr = ((DoubleConst)left).compute(op, right);
  392. if (newExpr == null)
  393. return false; // not a constant expression
  394. expr.setOperator('+');
  395. expr.setOprand1(newExpr);
  396. expr.setOprand2(null);
  397. newExpr.accept(this); // for setting exprType, arrayDim, ...
  398. return true;
  399. }
  400. /* CodeGen.atSwitchStmnt() also calls stripPlusExpr().
  401. */
  402. static ASTree stripPlusExpr(ASTree expr) {
  403. if (expr instanceof BinExpr) {
  404. BinExpr e = (BinExpr)expr;
  405. if (e.getOperator() == '+' && e.oprand2() == null)
  406. return e.getLeft();
  407. }
  408. else if (expr instanceof Expr) { // note: BinExpr extends Expr.
  409. Expr e = (Expr)expr;
  410. int op = e.getOperator();
  411. if (op == MEMBER) {
  412. ASTree cexpr = getConstantFieldValue((Member)e.oprand2(), expr.getLineNumber());
  413. if (cexpr != null)
  414. return cexpr;
  415. }
  416. else if (op == '+' && e.getRight() == null)
  417. return e.getLeft();
  418. }
  419. else if (expr instanceof Member) {
  420. ASTree cexpr = getConstantFieldValue((Member)expr, expr.getLineNumber());
  421. if (cexpr != null)
  422. return cexpr;
  423. }
  424. return expr;
  425. }
  426. /**
  427. * If MEM is a static final field, this method returns a constant
  428. * expression representing the value of that field.
  429. */
  430. private static ASTree getConstantFieldValue(Member mem, int lineNumber) {
  431. return getConstantFieldValue(mem.getField(), lineNumber);
  432. }
  433. public static ASTree getConstantFieldValue(CtField f, int lineNumber) {
  434. if (f == null)
  435. return null;
  436. Object value = f.getConstantValue();
  437. if (value == null)
  438. return null;
  439. if (value instanceof String)
  440. return new StringL((String)value, lineNumber);
  441. else if (value instanceof Double || value instanceof Float) {
  442. int token = (value instanceof Double)
  443. ? DoubleConstant : FloatConstant;
  444. return new DoubleConst(((Number)value).doubleValue(), token, lineNumber);
  445. }
  446. else if (value instanceof Number) {
  447. int token = (value instanceof Long) ? LongConstant : IntConstant;
  448. return new IntConst(((Number)value).longValue(), token, lineNumber);
  449. }
  450. else if (value instanceof Boolean)
  451. return new Keyword(((Boolean)value).booleanValue()
  452. ? TokenId.TRUE : TokenId.FALSE, lineNumber);
  453. else
  454. return null;
  455. }
  456. private static boolean isPlusExpr(ASTree expr) {
  457. if (expr instanceof BinExpr) {
  458. BinExpr bexpr = (BinExpr)expr;
  459. int token = bexpr.getOperator();
  460. return token == '+';
  461. }
  462. return false;
  463. }
  464. private static Expr makeAppendCall(ASTree target, ASTree arg) {
  465. return CallExpr.makeCall(Expr.make('.', target, new Member("append", target.getLineNumber()), target.getLineNumber()),
  466. new ASTList(arg, target.getLineNumber()), target.getLineNumber());
  467. }
  468. private void computeBinExprType(BinExpr expr, int token, int type1)
  469. throws CompileError
  470. {
  471. // arrayDim should be 0.
  472. int type2 = exprType;
  473. if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
  474. exprType = type1;
  475. else
  476. insertCast(expr, type1, type2);
  477. if (CodeGen.isP_INT(exprType) && exprType != BOOLEAN)
  478. exprType = INT; // type1 may be BYTE, ...
  479. }
  480. private void booleanExpr(ASTree expr)
  481. throws CompileError
  482. {
  483. int op = CodeGen.getCompOperator(expr);
  484. if (op == EQ) { // ==, !=, ...
  485. BinExpr bexpr = (BinExpr)expr;
  486. bexpr.oprand1().accept(this);
  487. int type1 = exprType;
  488. int dim1 = arrayDim;
  489. bexpr.oprand2().accept(this);
  490. if (dim1 == 0 && arrayDim == 0)
  491. insertCast(bexpr, type1, exprType);
  492. }
  493. else if (op == '!')
  494. ((Expr)expr).oprand1().accept(this);
  495. else if (op == ANDAND || op == OROR) {
  496. BinExpr bexpr = (BinExpr)expr;
  497. bexpr.oprand1().accept(this);
  498. bexpr.oprand2().accept(this);
  499. }
  500. else // others
  501. expr.accept(this);
  502. exprType = BOOLEAN;
  503. arrayDim = 0;
  504. }
  505. private void insertCast(BinExpr expr, int type1, int type2)
  506. throws CompileError
  507. {
  508. if (CodeGen.rightIsStrong(type1, type2))
  509. expr.setLeft(new CastExpr(type2, 0, expr.oprand1(), expr.getLineNumber()));
  510. else
  511. exprType = type1;
  512. }
  513. @Override
  514. public void atCastExpr(CastExpr expr) throws CompileError {
  515. String cname = resolveClassName(expr.getClassName());
  516. expr.getOprand().accept(this);
  517. exprType = expr.getType();
  518. arrayDim = expr.getArrayDim();
  519. className = cname;
  520. }
  521. @Override
  522. public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
  523. expr.getOprand().accept(this);
  524. exprType = BOOLEAN;
  525. arrayDim = 0;
  526. }
  527. @Override
  528. public void atExpr(Expr expr) throws CompileError {
  529. // array access, member access,
  530. // (unary) +, (unary) -, ++, --, !, ~
  531. int token = expr.getOperator();
  532. ASTree oprand = expr.oprand1();
  533. if (token == '.') {
  534. String member = ((Symbol)expr.oprand2()).get();
  535. if (member.equals("length"))
  536. try {
  537. atArrayLength(expr);
  538. }
  539. catch (NoFieldException nfe) {
  540. // length might be a class or package name.
  541. atFieldRead(expr);
  542. }
  543. else if (member.equals("class"))
  544. atClassObject(expr); // .class
  545. else
  546. atFieldRead(expr);
  547. }
  548. else if (token == MEMBER) { // field read
  549. String member = ((Symbol)expr.oprand2()).get();
  550. if (member.equals("class"))
  551. atClassObject(expr); // .class
  552. else
  553. atFieldRead(expr);
  554. }
  555. else if (token == ARRAY)
  556. atArrayRead(oprand, expr.oprand2());
  557. else if (token == PLUSPLUS || token == MINUSMINUS)
  558. atPlusPlus(token, oprand, expr);
  559. else if (token == '!')
  560. booleanExpr(expr);
  561. else if (token == CALL) // method call
  562. fatal(expr.getLineNumber());
  563. else {
  564. oprand.accept(this);
  565. if (!isConstant(expr, token, oprand))
  566. if (token == '-' || token == '~')
  567. if (CodeGen.isP_INT(exprType))
  568. exprType = INT; // type may be BYTE, ...
  569. }
  570. }
  571. private boolean isConstant(Expr expr, int op, ASTree oprand) {
  572. oprand = stripPlusExpr(oprand);
  573. if (oprand instanceof IntConst) {
  574. IntConst c = (IntConst)oprand;
  575. long v = c.get();
  576. if (op == '-')
  577. v = -v;
  578. else if (op == '~')
  579. v = ~v;
  580. else
  581. return false;
  582. c.set(v);
  583. }
  584. else if (oprand instanceof DoubleConst) {
  585. DoubleConst c = (DoubleConst)oprand;
  586. if (op == '-')
  587. c.set(-c.get());
  588. else
  589. return false;
  590. }
  591. else
  592. return false;
  593. expr.setOperator('+');
  594. return true;
  595. }
  596. @Override
  597. public void atCallExpr(CallExpr expr) throws CompileError {
  598. String mname = null;
  599. CtClass targetClass = null;
  600. ASTree method = expr.oprand1();
  601. ASTList args = (ASTList)expr.oprand2();
  602. if (method instanceof Member) {
  603. mname = ((Member)method).get();
  604. targetClass = thisClass;
  605. }
  606. else if (method instanceof Keyword) { // constructor
  607. mname = MethodInfo.nameInit; // <init>
  608. if (((Keyword)method).get() == SUPER)
  609. targetClass = MemberResolver.getSuperclass(thisClass);
  610. else
  611. targetClass = thisClass;
  612. }
  613. else if (method instanceof Expr) {
  614. Expr e = (Expr)method;
  615. mname = ((Symbol)e.oprand2()).get();
  616. int op = e.getOperator();
  617. if (op == MEMBER) // static method
  618. targetClass
  619. = resolver.lookupClass(((Symbol)e.oprand1()).get(),
  620. false, e.getLineNumber());
  621. else if (op == '.') {
  622. ASTree target = e.oprand1();
  623. String classFollowedByDotSuper = isDotSuper(target);
  624. if (classFollowedByDotSuper != null)
  625. targetClass = MemberResolver.getSuperInterface(thisClass,
  626. classFollowedByDotSuper);
  627. else {
  628. try {
  629. target.accept(this);
  630. }
  631. catch (NoFieldException nfe) {
  632. if (nfe.getExpr() != target)
  633. throw nfe;
  634. // it should be a static method.
  635. exprType = CLASS;
  636. arrayDim = 0;
  637. className = nfe.getField(); // JVM-internal
  638. e.setOperator(MEMBER);
  639. e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(
  640. className), e.getLineNumber()));
  641. }
  642. if (arrayDim > 0)
  643. targetClass = resolver.lookupClass(javaLangObject, true, e.getLineNumber());
  644. else if (exprType == CLASS /* && arrayDim == 0 */)
  645. targetClass = resolver.lookupClassByJvmName(className, e.getLineNumber());
  646. else
  647. badMethod(e.getLineNumber());
  648. }
  649. }
  650. else
  651. badMethod(expr.getLineNumber());
  652. }
  653. else
  654. fatal(expr.getLineNumber());
  655. MemberResolver.Method minfo
  656. = atMethodCallCore(targetClass, mname, args, expr.getLineNumber());
  657. expr.setMethod(minfo);
  658. }
  659. private static void badMethod(int lineNumber) throws CompileError {
  660. throw new CompileError("bad method", lineNumber);
  661. }
  662. /**
  663. * Returns non-null if target is something like Foo.super
  664. * for accessing the default method in an interface.
  665. * Otherwise, null.
  666. *
  667. * @return the class name followed by {@code .super} or null.
  668. */
  669. static String isDotSuper(ASTree target) {
  670. if (target instanceof Expr) {
  671. Expr e = (Expr)target;
  672. if (e.getOperator() == '.') {
  673. ASTree right = e.oprand2();
  674. if (right instanceof Keyword && ((Keyword)right).get() == SUPER)
  675. return ((Symbol)e.oprand1()).get();
  676. }
  677. }
  678. return null;
  679. }
  680. /**
  681. * @return a pair of the class declaring the invoked method
  682. * and the MethodInfo of that method. Never null.
  683. */
  684. public MemberResolver.Method atMethodCallCore(CtClass targetClass,
  685. String mname, ASTList args, int lineNumber)
  686. throws CompileError
  687. {
  688. int nargs = getMethodArgsLength(args);
  689. int[] types = new int[nargs];
  690. int[] dims = new int[nargs];
  691. String[] cnames = new String[nargs];
  692. atMethodArgs(args, types, dims, cnames);
  693. MemberResolver.Method found
  694. = resolver.lookupMethod(targetClass, thisClass, thisMethod,
  695. mname, types, dims, cnames);
  696. if (found == null) {
  697. String clazz = targetClass.getName();
  698. String signature = argTypesToString(types, dims, cnames, lineNumber);
  699. String msg;
  700. if (mname.equals(MethodInfo.nameInit))
  701. msg = "cannot find constructor " + clazz + signature;
  702. else
  703. msg = mname + signature + " not found in " + clazz;
  704. throw new CompileError(msg, lineNumber);
  705. }
  706. String desc = found.info.getDescriptor();
  707. setReturnType(desc, lineNumber);
  708. return found;
  709. }
  710. public int getMethodArgsLength(ASTList args) {
  711. return ASTList.length(args);
  712. }
  713. public void atMethodArgs(ASTList args, int[] types, int[] dims,
  714. String[] cnames) throws CompileError {
  715. int i = 0;
  716. while (args != null) {
  717. ASTree a = args.head();
  718. a.accept(this);
  719. types[i] = exprType;
  720. dims[i] = arrayDim;
  721. cnames[i] = className;
  722. ++i;
  723. args = args.tail();
  724. }
  725. }
  726. void setReturnType(String desc, int lineNumber) throws CompileError {
  727. int i = desc.indexOf(')');
  728. if (i < 0)
  729. badMethod(lineNumber);
  730. char c = desc.charAt(++i);
  731. int dim = 0;
  732. while (c == '[') {
  733. ++dim;
  734. c = desc.charAt(++i);
  735. }
  736. arrayDim = dim;
  737. if (c == 'L') {
  738. int j = desc.indexOf(';', i + 1);
  739. if (j < 0)
  740. badMethod(lineNumber);
  741. exprType = CLASS;
  742. className = desc.substring(i + 1, j);
  743. }
  744. else {
  745. exprType = MemberResolver.descToType(c, lineNumber);
  746. className = null;
  747. }
  748. }
  749. private void atFieldRead(ASTree expr) throws CompileError {
  750. atFieldRead(fieldAccess(expr), expr.getLineNumber());
  751. }
  752. private void atFieldRead(CtField f, int lineNumber) throws CompileError {
  753. FieldInfo finfo = f.getFieldInfo2();
  754. String type = finfo.getDescriptor();
  755. int i = 0;
  756. int dim = 0;
  757. char c = type.charAt(i);
  758. while (c == '[') {
  759. ++dim;
  760. c = type.charAt(++i);
  761. }
  762. arrayDim = dim;
  763. exprType = MemberResolver.descToType(c, lineNumber);
  764. if (c == 'L')
  765. className = type.substring(i + 1, type.indexOf(';', i + 1));
  766. else
  767. className = null;
  768. }
  769. /* if EXPR is to access a static field, fieldAccess() translates EXPR
  770. * into an expression using '#' (MEMBER). For example, it translates
  771. * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation
  772. * speeds up type resolution by MemberCodeGen.
  773. */
  774. protected CtField fieldAccess(ASTree expr) throws CompileError {
  775. if (expr instanceof Member) {
  776. Member mem = (Member)expr;
  777. String name = mem.get();
  778. try {
  779. CtField f = thisClass.getField(name);
  780. if (Modifier.isStatic(f.getModifiers()))
  781. mem.setField(f);
  782. return f;
  783. }
  784. catch (NotFoundException e) {
  785. // EXPR might be part of a static member access?
  786. throw new NoFieldException(name, expr);
  787. }
  788. }
  789. else if (expr instanceof Expr) {
  790. Expr e = (Expr)expr;
  791. int op = e.getOperator();
  792. if (op == MEMBER) {
  793. Member mem = (Member)e.oprand2();
  794. CtField f
  795. = resolver.lookupField(((Symbol)e.oprand1()).get(), mem);
  796. mem.setField(f);
  797. return f;
  798. }
  799. else if (op == '.') {
  800. try {
  801. e.oprand1().accept(this);
  802. }
  803. catch (NoFieldException nfe) {
  804. if (nfe.getExpr() != e.oprand1())
  805. throw nfe;
  806. /* EXPR should be a static field.
  807. * If EXPR might be part of a qualified class name,
  808. * lookupFieldByJvmName2() throws NoFieldException.
  809. */
  810. return fieldAccess2(e, nfe.getField());
  811. }
  812. CompileError err = null;
  813. try {
  814. if (exprType == CLASS && arrayDim == 0)
  815. return resolver.lookupFieldByJvmName(className,
  816. (Symbol)e.oprand2());
  817. }
  818. catch (CompileError ce) {
  819. err = ce;
  820. }
  821. /* If a filed name is the same name as a package's,
  822. * a static member of a class in that package is not
  823. * visible. For example,
  824. *
  825. * class Foo {
  826. * int javassist;
  827. * }
  828. *
  829. * It is impossible to add the following method:
  830. *
  831. * String m() { return javassist.CtClass.intType.toString(); }
  832. *
  833. * because javassist is a field name. However, this is
  834. * often inconvenient, this compiler allows it. The following
  835. * code is for that.
  836. */
  837. ASTree oprnd1 = e.oprand1();
  838. if (oprnd1 instanceof Symbol)
  839. return fieldAccess2(e, ((Symbol)oprnd1).get());
  840. if (err != null)
  841. throw err;
  842. }
  843. }
  844. throw new CompileError("bad field access", expr.getLineNumber());
  845. }
  846. private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError {
  847. Member fname = (Member)e.oprand2();
  848. CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e);
  849. e.setOperator(MEMBER);
  850. e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName), e.getLineNumber()));
  851. fname.setField(f);
  852. return f;
  853. }
  854. public void atClassObject(Expr expr) throws CompileError {
  855. exprType = CLASS;
  856. arrayDim = 0;
  857. className =jvmJavaLangClass;
  858. }
  859. public void atArrayLength(Expr expr) throws CompileError {
  860. expr.oprand1().accept(this);
  861. if (arrayDim == 0)
  862. throw new NoFieldException("length", expr);
  863. exprType = INT;
  864. arrayDim = 0;
  865. }
  866. public void atArrayRead(ASTree array, ASTree index)
  867. throws CompileError
  868. {
  869. array.accept(this);
  870. int type = exprType;
  871. int dim = arrayDim;
  872. String cname = className;
  873. index.accept(this);
  874. exprType = type;
  875. arrayDim = dim - 1;
  876. className = cname;
  877. }
  878. private void atPlusPlus(int token, ASTree oprand, Expr expr)
  879. throws CompileError
  880. {
  881. boolean isPost = oprand == null; // ++i or i++?
  882. if (isPost)
  883. oprand = expr.oprand2();
  884. if (oprand instanceof Variable) {
  885. Declarator d = ((Variable)oprand).getDeclarator();
  886. exprType = d.getType();
  887. arrayDim = d.getArrayDim();
  888. }
  889. else {
  890. if (oprand instanceof Expr) {
  891. Expr e = (Expr)oprand;
  892. if (e.getOperator() == ARRAY) {
  893. atArrayRead(e.oprand1(), e.oprand2());
  894. // arrayDim should be 0.
  895. int t = exprType;
  896. if (t == INT || t == BYTE || t == CHAR || t == SHORT)
  897. exprType = INT;
  898. return;
  899. }
  900. }
  901. atFieldPlusPlus(oprand);
  902. }
  903. }
  904. protected void atFieldPlusPlus(ASTree oprand) throws CompileError
  905. {
  906. CtField f = fieldAccess(oprand);
  907. atFieldRead(f, oprand.getLineNumber());
  908. int t = exprType;
  909. if (t == INT || t == BYTE || t == CHAR || t == SHORT)
  910. exprType = INT;
  911. }
  912. @Override
  913. public void atMember(Member mem) throws CompileError {
  914. atFieldRead(mem);
  915. }
  916. @Override
  917. public void atVariable(Variable v) throws CompileError {
  918. Declarator d = v.getDeclarator();
  919. exprType = d.getType();
  920. arrayDim = d.getArrayDim();
  921. className = d.getClassName();
  922. }
  923. @Override
  924. public void atKeyword(Keyword k) throws CompileError {
  925. arrayDim = 0;
  926. int token = k.get();
  927. switch (token) {
  928. case TRUE :
  929. case FALSE :
  930. exprType = BOOLEAN;
  931. break;
  932. case NULL :
  933. exprType = NULL;
  934. break;
  935. case THIS :
  936. case SUPER :
  937. exprType = CLASS;
  938. if (token == THIS)
  939. className = getThisName();
  940. else
  941. className = getSuperName();
  942. break;
  943. default :
  944. fatal(k.getLineNumber());
  945. }
  946. }
  947. @Override
  948. public void atStringL(StringL s) throws CompileError {
  949. exprType = CLASS;
  950. arrayDim = 0;
  951. className = jvmJavaLangString;
  952. }
  953. @Override
  954. public void atIntConst(IntConst i) throws CompileError {
  955. arrayDim = 0;
  956. int type = i.getType();
  957. if (type == IntConstant || type == CharConstant)
  958. exprType = (type == IntConstant ? INT : CHAR);
  959. else
  960. exprType = LONG;
  961. }
  962. @Override
  963. public void atDoubleConst(DoubleConst d) throws CompileError {
  964. arrayDim = 0;
  965. if (d.getType() == DoubleConstant)
  966. exprType = DOUBLE;
  967. else
  968. exprType = FLOAT;
  969. }
  970. }