attributes.add(sm); | attributes.add(sm); | ||||
} | } | ||||
/** | |||||
* Adds a line number table. If another copy of line number table | |||||
* is already contained, the old one is removed. | |||||
* | |||||
* @param lnt the line number table added to this code attribute. | |||||
* If it is null, a new line number is not added. | |||||
* Only the old line number is removed. | |||||
*/ | |||||
public void setAttribute(LineNumberAttribute lnt) { | |||||
AttributeInfo.remove(attributes, LineNumberAttribute.tag); | |||||
if (lnt != null) | |||||
attributes.add(lnt); | |||||
} | |||||
/** | /** | ||||
* Copies code. | * Copies code. | ||||
*/ | */ |
super(cp, n, in); | super(cp, n, in); | ||||
} | } | ||||
private LineNumberAttribute(ConstPool cp, byte[] i) { | |||||
LineNumberAttribute(ConstPool cp, byte[] i) { | |||||
super(cp, tag, i); | super(cp, tag, i); | ||||
} | } | ||||
package javassist.bytecode; | |||||
import javassist.compiler.ast.ASTree; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.DataOutputStream; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
public class LineNumberAttributeBuilder { | |||||
private final HashMap<Integer, Integer> map = new HashMap<>(); | |||||
public void put(int newPc, ASTree tree) { | |||||
if (tree != null) | |||||
put(newPc, tree.getLineNumber()); | |||||
} | |||||
private void put(int newPc, int lineNum) { | |||||
Integer pc = map.get(lineNum); | |||||
if (pc == null || newPc < pc) { | |||||
map.put(lineNum, newPc); | |||||
} | |||||
} | |||||
public LineNumberAttribute build(ConstPool cp) { | |||||
int size = map.size(); | |||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(size * 4 + 2); | |||||
DataOutputStream dos = new DataOutputStream(bos)) { | |||||
dos.writeShort(size); | |||||
for (Map.Entry<Integer, Integer> entry : map.entrySet()) { | |||||
dos.writeShort(entry.getValue()); | |||||
dos.writeShort(entry.getKey()); | |||||
} | |||||
return new LineNumberAttribute(cp, bos.toByteArray()); | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
} |
typeChecker = checker; | typeChecker = checker; | ||||
} | } | ||||
protected static void fatal() throws CompileError { | |||||
throw new CompileError("fatal"); | |||||
protected static void fatal(int lineNumber) throws CompileError { | |||||
throw new CompileError("fatal", lineNumber); | |||||
} | } | ||||
public static boolean is2word(int type, int dim) { | public static boolean is2word(int type, int dim) { | ||||
} | } | ||||
@Override | @Override | ||||
public void atASTList(ASTList n) throws CompileError { fatal(); } | |||||
public void atASTList(ASTList n) throws CompileError { fatal(n.getLineNumber()); } | |||||
@Override | @Override | ||||
public void atPair(Pair n) throws CompileError { fatal(); } | |||||
public void atPair(Pair n) throws CompileError { fatal(n.getLineNumber()); } | |||||
@Override | @Override | ||||
public void atSymbol(Symbol n) throws CompileError { fatal(); } | |||||
public void atSymbol(Symbol n) throws CompileError { fatal(n.getLineNumber()); } | |||||
@Override | @Override | ||||
public void atFieldDecl(FieldDecl field) throws CompileError { | public void atFieldDecl(FieldDecl field) throws CompileError { | ||||
hasReturned = true; | hasReturned = true; | ||||
} | } | ||||
else | else | ||||
throw new CompileError("no return statement"); | |||||
throw new CompileError("no return statement", s.getLineNumber()); | |||||
} | } | ||||
private boolean needsSuperCall(Stmnt body) throws CompileError { | private boolean needsSuperCall(Stmnt body) throws CompileError { | ||||
// LABEL, SWITCH label stament might be null?. | // LABEL, SWITCH label stament might be null?. | ||||
hasReturned = false; | hasReturned = false; | ||||
throw new CompileError( | throw new CompileError( | ||||
"sorry, not supported statement: TokenId " + op); | |||||
"sorry, not supported statement: TokenId " + op, st.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
if (op == DEFAULT) | if (op == DEFAULT) | ||||
defaultPc = bytecode.currentPc(); | defaultPc = bytecode.currentPc(); | ||||
else if (op != CASE) | else if (op != CASE) | ||||
fatal(); | |||||
fatal(st.getLineNumber()); | |||||
else { | else { | ||||
int curPos = bytecode.currentPc(); | int curPos = bytecode.currentPc(); | ||||
long caseLabel; | long caseLabel; | ||||
expr = TypeChecker.stripPlusExpr(expr); | expr = TypeChecker.stripPlusExpr(expr); | ||||
if (expr instanceof IntConst) | if (expr instanceof IntConst) | ||||
return (int)((IntConst)expr).get(); | return (int)((IntConst)expr).get(); | ||||
throw new CompileError("bad case label"); | |||||
throw new CompileError("bad case label", expr.getLineNumber()); | |||||
} | } | ||||
private int computeStringLabel(ASTree expr, int tmpVar, List<Integer> gotoDefaults) | private int computeStringLabel(ASTree expr, int tmpVar, List<Integer> gotoDefaults) | ||||
gotoDefaults.add(pc); | gotoDefaults.add(pc); | ||||
return (int)label.hashCode(); | return (int)label.hashCode(); | ||||
} | } | ||||
throw new CompileError("bad case label"); | |||||
throw new CompileError("bad case label", expr.getLineNumber()); | |||||
} | } | ||||
private void atBreakStmnt(Stmnt st, boolean notCont) | private void atBreakStmnt(Stmnt st, boolean notCont) | ||||
{ | { | ||||
if (st.head() != null) | if (st.head() != null) | ||||
throw new CompileError( | throw new CompileError( | ||||
"sorry, not support labeled break or continue"); | |||||
"sorry, not support labeled break or continue", st.getLineNumber()); | |||||
bytecode.addOpcode(Opcode.GOTO); | bytecode.addOpcode(Opcode.GOTO); | ||||
Integer pc = Integer.valueOf(bytecode.currentPc()); | Integer pc = Integer.valueOf(bytecode.currentPc()); | ||||
ASTree e = st.getLeft(); | ASTree e = st.getLeft(); | ||||
compileExpr(e); | compileExpr(e); | ||||
if (exprType != CLASS || arrayDim > 0) | if (exprType != CLASS || arrayDim > 0) | ||||
throw new CompileError("bad throw statement"); | |||||
throw new CompileError("bad throw statement", st.getLineNumber()); | |||||
bytecode.addOpcode(ATHROW); | bytecode.addOpcode(ATHROW); | ||||
hasReturned = true; | hasReturned = true; | ||||
compileExpr(st.head()); | compileExpr(st.head()); | ||||
if (exprType != CLASS && arrayDim == 0) | if (exprType != CLASS && arrayDim == 0) | ||||
throw new CompileError("bad type expr for synchronized block"); | |||||
throw new CompileError("bad type expr for synchronized block", st.getLineNumber()); | |||||
Bytecode bc = bytecode; | Bytecode bc = bytecode; | ||||
final int var = bc.getMaxLocals(); | final int var = bc.getMaxLocals(); | ||||
if (getListSize(breakList) != nbreaks | if (getListSize(breakList) != nbreaks | ||||
|| getListSize(continueList) != ncontinues) | || getListSize(continueList) != ncontinues) | ||||
throw new CompileError( | throw new CompileError( | ||||
"sorry, cannot break/continue in synchronized block"); | |||||
"sorry, cannot break/continue in synchronized block", st.getLineNumber()); | |||||
} | } | ||||
private static int getListSize(List<Integer> list) { | private static int getListSize(List<Integer> list) { | ||||
else | else | ||||
msg = "incompatible type for " + expr.getName(); | msg = "incompatible type for " + expr.getName(); | ||||
throw new CompileError(msg); | |||||
throw new CompileError(msg, expr.getLineNumber()); | |||||
} | } | ||||
/* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. | /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. | ||||
int token = assignOps[op - MOD_E]; | int token = assignOps[op - MOD_E]; | ||||
int k = lookupBinOp(token); | int k = lookupBinOp(token); | ||||
if (k < 0) | if (k < 0) | ||||
fatal(); | |||||
fatal(right.getLineNumber()); | |||||
atArithBinExpr(expr, token, k, type); | atArithBinExpr(expr, token, k, type); | ||||
} | } | ||||
if (!jvmJavaLangString.equals(cname)) | if (!jvmJavaLangString.equals(cname)) | ||||
badAssign(expr); | badAssign(expr); | ||||
convToString(type, dim); // the value might be null. | |||||
convToString(type, dim, expr.getLineNumber()); // the value might be null. | |||||
right.accept(this); | right.accept(this); | ||||
convToString(exprType, arrayDim); | |||||
convToString(exprType, arrayDim, expr.getLineNumber()); | |||||
bytecode.addInvokevirtual(javaLangString, "concat", | bytecode.addInvokevirtual(javaLangString, "concat", | ||||
"(Ljava/lang/String;)Ljava/lang/String;"); | "(Ljava/lang/String;)Ljava/lang/String;"); | ||||
exprType = CLASS; | exprType = CLASS; | ||||
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); | bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); | ||||
expr.elseExpr().accept(this); | expr.elseExpr().accept(this); | ||||
if (dim1 != arrayDim) | if (dim1 != arrayDim) | ||||
throw new CompileError("type mismatch in ?:"); | |||||
throw new CompileError("type mismatch in ?:", expr.getLineNumber()); | |||||
bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); | bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); | ||||
} | } | ||||
String cname1 = className; | String cname1 = className; | ||||
right.accept(this); | right.accept(this); | ||||
if (dim1 != arrayDim) | if (dim1 != arrayDim) | ||||
throw new CompileError("incompatible array types"); | |||||
throw new CompileError("incompatible array types", expr.getLineNumber()); | |||||
if (token == '+' && dim1 == 0 | if (token == '+' && dim1 == 0 | ||||
&& (type1 == CLASS || exprType == CLASS)) | && (type1 == CLASS || exprType == CLASS)) | ||||
= (type2 == CLASS && jvmJavaLangString.equals(className)); | = (type2 == CLASS && jvmJavaLangString.equals(className)); | ||||
if (type2Is2) | if (type2Is2) | ||||
convToString(type2, dim2); | |||||
convToString(type2, dim2, expr.getLineNumber()); | |||||
if (is2word(type1, dim1)) { | if (is2word(type1, dim1)) { | ||||
bytecode.addOpcode(DUP_X2); | bytecode.addOpcode(DUP_X2); | ||||
bytecode.addOpcode(SWAP); | bytecode.addOpcode(SWAP); | ||||
// even if type1 is String, the left operand might be null. | // even if type1 is String, the left operand might be null. | ||||
convToString(type1, dim1); | |||||
convToString(type1, dim1, expr.getLineNumber()); | |||||
bytecode.addOpcode(SWAP); | bytecode.addOpcode(SWAP); | ||||
if (!type2Is2 && !type2IsString) | if (!type2Is2 && !type2IsString) | ||||
convToString(type2, dim2); | |||||
convToString(type2, dim2, expr.getLineNumber()); | |||||
bytecode.addInvokevirtual(javaLangString, "concat", | bytecode.addInvokevirtual(javaLangString, "concat", | ||||
"(Ljava/lang/String;)Ljava/lang/String;"); | "(Ljava/lang/String;)Ljava/lang/String;"); | ||||
className = jvmJavaLangString; | className = jvmJavaLangString; | ||||
} | } | ||||
private void convToString(int type, int dim) throws CompileError { | |||||
private void convToString(int type, int dim, int lineNumber) throws CompileError { | |||||
final String method = "valueOf"; | final String method = "valueOf"; | ||||
if (isRefType(type) || dim > 0) | if (isRefType(type) || dim > 0) | ||||
bytecode.addInvokestatic(javaLangString, method, | bytecode.addInvokestatic(javaLangString, method, | ||||
"(C)Ljava/lang/String;"); | "(C)Ljava/lang/String;"); | ||||
else if (type == VOID) | else if (type == VOID) | ||||
throw new CompileError("void type expression"); | |||||
throw new CompileError("void type expression", lineNumber); | |||||
else /* INT, BYTE, SHORT */ | else /* INT, BYTE, SHORT */ | ||||
bytecode.addInvokestatic(javaLangString, method, | bytecode.addInvokestatic(javaLangString, method, | ||||
"(I)Ljava/lang/String;"); | "(I)Ljava/lang/String;"); | ||||
else { // others | else { // others | ||||
expr.accept(this); | expr.accept(this); | ||||
if (exprType != BOOLEAN || arrayDim != 0) | if (exprType != BOOLEAN || arrayDim != 0) | ||||
throw new CompileError("boolean expr is required"); | |||||
throw new CompileError("boolean expr is required", expr.getLineNumber()); | |||||
bytecode.addOpcode(branchIf ? IFNE : IFEQ); | bytecode.addOpcode(branchIf ? IFNE : IFEQ); | ||||
} | } | ||||
expr.oprand2().accept(this); | expr.oprand2().accept(this); | ||||
if (dim1 != arrayDim) | if (dim1 != arrayDim) | ||||
if (type1 != NULL && exprType != NULL) | if (type1 != NULL && exprType != NULL) | ||||
throw new CompileError("incompatible array types"); | |||||
throw new CompileError("incompatible array types", expr.getLineNumber()); | |||||
else if (exprType == NULL) | else if (exprType == NULL) | ||||
arrayDim = dim1; | arrayDim = dim1; | ||||
else if (p == P_LONG) | else if (p == P_LONG) | ||||
bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: < | bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: < | ||||
else | else | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
int[] op = ifOp2; | int[] op = ifOp2; | ||||
for (int i = 0; i < op.length; i += 3) | for (int i = 0; i < op.length; i += 3) | ||||
} | } | ||||
protected static void badTypes(Expr expr) throws CompileError { | protected static void badTypes(Expr expr) throws CompileError { | ||||
throw new CompileError("invalid types for " + expr.getName()); | |||||
throw new CompileError("invalid types for " + expr.getName(), expr.getLineNumber()); | |||||
} | } | ||||
private static final int P_DOUBLE = 0; | private static final int P_DOUBLE = 0; | ||||
bytecode.addOpcode(SWAP); | bytecode.addOpcode(SWAP); | ||||
} | } | ||||
else | else | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
} | } | ||||
else if (op != NOP) | else if (op != NOP) | ||||
bytecode.addOpcode(op); | bytecode.addOpcode(op); | ||||
int srcDim = arrayDim; | int srcDim = arrayDim; | ||||
if (invalidDim(srcType, arrayDim, className, type, dim, name, true) | if (invalidDim(srcType, arrayDim, className, type, dim, name, true) | ||||
|| srcType == VOID || type == VOID) | || srcType == VOID || type == VOID) | ||||
throw new CompileError(msg); | |||||
throw new CompileError(msg, expr.getLineNumber()); | |||||
if (type == CLASS) { | if (type == CLASS) { | ||||
if (!isRefType(srcType) && srcDim == 0) | if (!isRefType(srcType) && srcDim == 0) | ||||
throw new CompileError(msg); | |||||
throw new CompileError(msg, expr.getLineNumber()); | |||||
return toJvmArrayName(name, dim); | return toJvmArrayName(name, dim); | ||||
} | } | ||||
bytecode.addIconst(0); | bytecode.addIconst(0); | ||||
} | } | ||||
else if (token == CALL) // method call | else if (token == CALL) // method call | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
else { | else { | ||||
expr.oprand1().accept(this); | expr.oprand1().accept(this); | ||||
int type = typePrecedence(exprType); | int type = typePrecedence(exprType); | ||||
// do nothing. ignore. | // do nothing. ignore. | ||||
} | } | ||||
else | else | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
protected static void badType(Expr expr) throws CompileError { | protected static void badType(Expr expr) throws CompileError { | ||||
throw new CompileError("invalid type for " + expr.getName()); | |||||
throw new CompileError("invalid type for " + expr.getName(), expr.getLineNumber()); | |||||
} | } | ||||
@Override | @Override | ||||
public void atClassObject(Expr expr) throws CompileError { | public void atClassObject(Expr expr) throws CompileError { | ||||
ASTree op1 = expr.oprand1(); | ASTree op1 = expr.oprand1(); | ||||
if (!(op1 instanceof Symbol)) | if (!(op1 instanceof Symbol)) | ||||
throw new CompileError("fatal error: badly parsed .class expr"); | |||||
throw new CompileError("fatal error: badly parsed .class expr", expr.getLineNumber()); | |||||
String cname = ((Symbol)op1).get(); | String cname = ((Symbol)op1).get(); | ||||
if (cname.startsWith("[")) { | if (cname.startsWith("[")) { | ||||
int type = exprType; | int type = exprType; | ||||
int dim = arrayDim; | int dim = arrayDim; | ||||
if (dim == 0) | if (dim == 0) | ||||
throw new CompileError("bad array access"); | |||||
throw new CompileError("bad array access", array.getLineNumber()); | |||||
String cname = className; | String cname = className; | ||||
index.accept(this); | index.accept(this); | ||||
if (typePrecedence(exprType) != P_INT || arrayDim > 0) | if (typePrecedence(exprType) != P_INT || arrayDim > 0) | ||||
throw new CompileError("bad array index"); | |||||
throw new CompileError("bad array index", array.getLineNumber()); | |||||
exprType = type; | exprType = type; | ||||
arrayDim = dim - 1; | arrayDim = dim - 1; | ||||
case SUPER : | case SUPER : | ||||
if (inStaticMethod) | if (inStaticMethod) | ||||
throw new CompileError("not-available: " | throw new CompileError("not-available: " | ||||
+ (token == THIS ? "this" : "super")); | |||||
+ (token == THIS ? "this" : "super"), k.getLineNumber()); | |||||
bytecode.addAload(0); | bytecode.addAload(0); | ||||
exprType = CLASS; | exprType = CLASS; | ||||
className = getSuperName(); | className = getSuperName(); | ||||
break; | break; | ||||
default : | default : | ||||
fatal(); | |||||
fatal(k.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
private Lex lex; | private Lex lex; | ||||
private String reason; | private String reason; | ||||
private int lineNumber = -1; | |||||
public CompileError(String s, Lex l) { | public CompileError(String s, Lex l) { | ||||
reason = s; | |||||
this(s, l.getLineNumber()); | |||||
lex = l; | lex = l; | ||||
} | } | ||||
public CompileError(String s, int lineNumber) { | |||||
this.lineNumber = lineNumber; | |||||
reason = String.format("line %d: %s", lineNumber, s); | |||||
} | |||||
public CompileError(String s) { | public CompileError(String s) { | ||||
reason = s; | reason = s; | ||||
lex = null; | lex = null; | ||||
public Lex getLex() { return lex; } | public Lex getLex() { return lex; } | ||||
public int getLineNumber() { | |||||
return lineNumber; | |||||
} | |||||
@Override | @Override | ||||
public String getMessage() { | public String getMessage() { | ||||
return reason; | return reason; |
import javassist.compiler.ast.Symbol; | import javassist.compiler.ast.Symbol; | ||||
public class Javac { | public class Javac { | ||||
JvstCodeGen gen; | |||||
JvstCodeGenWitlLineNumber gen; | |||||
SymbolTable stable; | SymbolTable stable; | ||||
private Bytecode bytecode; | private Bytecode bytecode; | ||||
* belongs to. | * belongs to. | ||||
*/ | */ | ||||
public Javac(Bytecode b, CtClass thisClass) { | public Javac(Bytecode b, CtClass thisClass) { | ||||
gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool()); | |||||
gen = new JvstCodeGenWitlLineNumber(b, thisClass, thisClass.getClassPool()); | |||||
stable = new SymbolTable(); | stable = new SymbolTable(); | ||||
bytecode = b; | bytecode = b; | ||||
} | } | ||||
gen.getThisClass()); | gen.getThisClass()); | ||||
cons.setModifiers(mod); | cons.setModifiers(mod); | ||||
md.accept(gen); | md.accept(gen); | ||||
cons.getMethodInfo().setCodeAttribute( | |||||
bytecode.toCodeAttribute()); | |||||
CodeAttribute cattr = bytecode.toCodeAttribute(); | |||||
cattr.setAttribute(gen.toLineNumberAttribute()); | |||||
cons.getMethodInfo().setCodeAttribute(cattr); | |||||
cons.setExceptionTypes(tlist); | cons.setExceptionTypes(tlist); | ||||
return cons; | return cons; | ||||
} | } | ||||
method.setModifiers(mod); | method.setModifiers(mod); | ||||
gen.setThisMethod(method); | gen.setThisMethod(method); | ||||
md.accept(gen); | md.accept(gen); | ||||
if (md.getBody() != null) | |||||
method.getMethodInfo().setCodeAttribute( | |||||
bytecode.toCodeAttribute()); | |||||
else | |||||
if (md.getBody() != null) { | |||||
CodeAttribute cattr = bytecode.toCodeAttribute(); | |||||
cattr.setAttribute(gen.toLineNumberAttribute()); | |||||
method.getMethodInfo().setCodeAttribute(cattr); | |||||
} else | |||||
method.setModifiers(mod | Modifier.ABSTRACT); | method.setModifiers(mod | Modifier.ABSTRACT); | ||||
method.setExceptionTypes(tlist); | method.setExceptionTypes(tlist); | ||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args) | public void doit(JvstCodeGen gen, Bytecode b, ASTList args) | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
ASTree expr = new Member(m); | |||||
ASTree expr = new Member(m, texpr.getLineNumber()); | |||||
if (texpr != null) | if (texpr != null) | ||||
expr = Expr.make('.', texpr, expr); | |||||
expr = Expr.make('.', texpr, expr, texpr.getLineNumber()); | |||||
expr = CallExpr.makeCall(expr, args); | |||||
expr = CallExpr.makeCall(expr, args, texpr.getLineNumber()); | |||||
gen.compileExpr(expr); | gen.compileExpr(expr); | ||||
gen.addNullIfVoid(); | gen.addNullIfVoid(); | ||||
} | } | ||||
public void setReturnType(JvstTypeChecker check, ASTList args) | public void setReturnType(JvstTypeChecker check, ASTList args) | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
ASTree expr = new Member(m); | |||||
ASTree expr = new Member(m, texpr.getLineNumber()); | |||||
if (texpr != null) | if (texpr != null) | ||||
expr = Expr.make('.', texpr, expr); | |||||
expr = Expr.make('.', texpr, expr, texpr.getLineNumber()); | |||||
expr = CallExpr.makeCall(expr, args); | |||||
expr = CallExpr.makeCall(expr, args, texpr.getLineNumber()); | |||||
expr.accept(check); | expr.accept(check); | ||||
check.addNullIfVoid(); | check.addNullIfVoid(); | ||||
} | } | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
Expr expr = Expr.make(TokenId.MEMBER, | Expr expr = Expr.make(TokenId.MEMBER, | ||||
new Symbol(c), new Member(m)); | |||||
expr = CallExpr.makeCall(expr, args); | |||||
new Symbol(c, args.getLineNumber()), new Member(m, args.getLineNumber()), args.getLineNumber()); | |||||
expr = CallExpr.makeCall(expr, args, args.getLineNumber()); | |||||
gen.compileExpr(expr); | gen.compileExpr(expr); | ||||
gen.addNullIfVoid(); | gen.addNullIfVoid(); | ||||
} | } | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
Expr expr = Expr.make(TokenId.MEMBER, | Expr expr = Expr.make(TokenId.MEMBER, | ||||
new Symbol(c), new Member(m)); | |||||
expr = CallExpr.makeCall(expr, args); | |||||
new Symbol(c, args.getLineNumber()), new Member(m, args.getLineNumber()), args.getLineNumber()); | |||||
expr = CallExpr.makeCall(expr, args, args.getLineNumber()); | |||||
expr.accept(check); | expr.accept(check); | ||||
check.addNullIfVoid(); | check.addNullIfVoid(); | ||||
} | } |
} | } | ||||
else if (name.equals(dollarTypeName)) { | else if (name.equals(dollarTypeName)) { | ||||
if (dollarType == null) | if (dollarType == null) | ||||
throw new CompileError(dollarTypeName + " is not available"); | |||||
throw new CompileError(dollarTypeName + " is not available", mem.getLineNumber()); | |||||
bytecode.addLdc(Descriptor.of(dollarType)); | bytecode.addLdc(Descriptor.of(dollarType)); | ||||
callGetType("getType"); | callGetType("getType"); | ||||
} | } | ||||
else if (name.equals(clazzName)) { | else if (name.equals(clazzName)) { | ||||
if (param0Type == null) | if (param0Type == null) | ||||
throw new CompileError(clazzName + " is not available"); | |||||
throw new CompileError(clazzName + " is not available", mem.getLineNumber()); | |||||
bytecode.addLdc(param0Type); | bytecode.addLdc(param0Type); | ||||
callGetType("getClazz"); | callGetType("getClazz"); | ||||
if (left instanceof Member | if (left instanceof Member | ||||
&& ((Member)left).get().equals(paramArrayName)) { | && ((Member)left).get().equals(paramArrayName)) { | ||||
if (op != '=') | if (op != '=') | ||||
throw new CompileError("bad operator for " + paramArrayName); | |||||
throw new CompileError("bad operator for " + paramArrayName, expr.getLineNumber()); | |||||
right.accept(this); | right.accept(this); | ||||
if (arrayDim != 1 || exprType != CLASS) | if (arrayDim != 1 || exprType != CLASS) | ||||
throw new CompileError("invalid type for " + paramArrayName); | |||||
throw new CompileError("invalid type for " + paramArrayName, expr.getLineNumber()); | |||||
atAssignParamList(paramTypeList, bytecode); | atAssignParamList(paramTypeList, bytecode); | ||||
if (!doDup) | if (!doDup) | ||||
className = null; | className = null; | ||||
} | } | ||||
else | else | ||||
throw new CompileError("invalid cast"); | |||||
throw new CompileError("invalid cast", expr.getLineNumber()); | |||||
} | } | ||||
protected void atCastToWrapper(CastExpr expr) throws CompileError { | protected void atCastToWrapper(CastExpr expr) throws CompileError { | ||||
return; | return; | ||||
} | } | ||||
else if (name.equals(cflowName)) { | else if (name.equals(cflowName)) { | ||||
atCflow((ASTList)expr.oprand2()); | |||||
atCflow((ASTList)expr.oprand2(), expr.getLineNumber()); | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
/* To support $cflow(). | /* To support $cflow(). | ||||
*/ | */ | ||||
protected void atCflow(ASTList cname) throws CompileError { | |||||
protected void atCflow(ASTList cname, int lineNumber) throws CompileError { | |||||
StringBuilder sbuf = new StringBuilder(); | StringBuilder sbuf = new StringBuilder(); | ||||
if (cname == null || cname.tail() != null) | if (cname == null || cname.tail() != null) | ||||
throw new CompileError("bad " + cflowName); | |||||
throw new CompileError("bad " + cflowName, lineNumber); | |||||
makeCflowName(sbuf, cname.head()); | makeCflowName(sbuf, cname.head()); | ||||
String name = sbuf.toString(); | String name = sbuf.toString(); | ||||
Object[] names = resolver.getClassPool().lookupCflow(name); | Object[] names = resolver.getClassPool().lookupCflow(name); | ||||
if (names == null) | if (names == null) | ||||
throw new CompileError("no such " + cflowName + ": " + name); | |||||
throw new CompileError("no such " + cflowName + ": " + name, lineNumber); | |||||
bytecode.addGetstatic((String)names[0], (String)names[1], | bytecode.addGetstatic((String)names[0], (String)names[1], | ||||
"Ljavassist/runtime/Cflow;"); | "Ljavassist/runtime/Cflow;"); | ||||
} | } | ||||
} | } | ||||
throw new CompileError("bad " + cflowName); | |||||
throw new CompileError("bad " + cflowName, name.getLineNumber()); | |||||
} | } | ||||
/* To support $$. ($$) is equivalent to ($1, ..., $n). | /* To support $$. ($$) is equivalent to ($1, ..., $n). | ||||
String varName = prefix + "0"; | String varName = prefix + "0"; | ||||
Declarator decl | Declarator decl | ||||
= new Declarator(CLASS, MemberResolver.javaToJvmName(target), | = new Declarator(CLASS, MemberResolver.javaToJvmName(target), | ||||
0, varNo++, new Symbol(varName)); | |||||
0, varNo++, new Symbol(varName, 0), 0); | |||||
tbl.append(varName, decl); | tbl.append(varName, decl); | ||||
} | } | ||||
Declarator decl | Declarator decl | ||||
= new Declarator(exprType, className, arrayDim, | = new Declarator(exprType, className, arrayDim, | ||||
varNo, new Symbol(varName)); | |||||
varNo, new Symbol(varName, 0), 0); | |||||
tbl.append(varName, decl); | tbl.append(varName, decl); | ||||
return is2word(exprType, arrayDim) ? 2 : 1; | return is2word(exprType, arrayDim) ? 2 : 1; | ||||
} | } | ||||
} | } | ||||
Declarator decl | Declarator decl | ||||
= new Declarator(type, cname, dim, varNo, new Symbol(varName)); | |||||
= new Declarator(type, cname, dim, varNo, new Symbol(varName, 0), 0); | |||||
tbl.append(varName, decl); | tbl.append(varName, decl); | ||||
} | } | ||||
package javassist.compiler; | |||||
import javassist.ClassPool; | |||||
import javassist.CtClass; | |||||
import javassist.bytecode.Bytecode; | |||||
import javassist.bytecode.LineNumberAttribute; | |||||
import javassist.bytecode.LineNumberAttributeBuilder; | |||||
import javassist.compiler.ast.*; | |||||
public class JvstCodeGenWitlLineNumber extends JvstCodeGen { | |||||
private final LineNumberAttributeBuilder lineNumberAttributeBuilder = new LineNumberAttributeBuilder(); | |||||
public JvstCodeGenWitlLineNumber(Bytecode b, CtClass cc, ClassPool cp) { | |||||
super(b, cc, cp); | |||||
} | |||||
public LineNumberAttribute toLineNumberAttribute() { | |||||
return lineNumberAttributeBuilder.build(bytecode.getConstPool()); | |||||
} | |||||
@Override | |||||
public void atASTList(ASTList n) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), n); | |||||
super.atASTList(n); | |||||
} | |||||
@Override | |||||
public void atPair(Pair n) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), n); | |||||
super.atPair(n); | |||||
} | |||||
@Override | |||||
public void atSymbol(Symbol n) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), n); | |||||
super.atSymbol(n); | |||||
} | |||||
@Override | |||||
public void atFieldDecl(FieldDecl field) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), field); | |||||
super.atFieldDecl(field); | |||||
} | |||||
@Override | |||||
public void atMethodDecl(MethodDecl method) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), method); | |||||
super.atMethodDecl(method); | |||||
} | |||||
@Override | |||||
public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), s); | |||||
super.atMethodBody(s, isCons, isVoid); | |||||
} | |||||
@Override | |||||
public void atStmnt(Stmnt st) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), st); | |||||
super.atStmnt(st); | |||||
} | |||||
@Override | |||||
public void atDeclarator(Declarator d) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), d); | |||||
super.atDeclarator(d); | |||||
} | |||||
@Override | |||||
public void atAssignExpr(AssignExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atAssignExpr(expr); | |||||
} | |||||
@Override | |||||
protected void atAssignExpr(AssignExpr expr, boolean doDup) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atAssignExpr(expr, doDup); | |||||
} | |||||
@Override | |||||
protected void atAssignCore(Expr expr, int op, ASTree right, int type, int dim, String cname) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atAssignCore(expr, op, right, type, dim, cname); | |||||
} | |||||
@Override | |||||
public void atCondExpr(CondExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atCondExpr(expr); | |||||
} | |||||
@Override | |||||
public void atBinExpr(BinExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atBinExpr(expr); | |||||
} | |||||
@Override | |||||
public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atInstanceOfExpr(expr); | |||||
} | |||||
@Override | |||||
public void atExpr(Expr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atExpr(expr); | |||||
} | |||||
@Override | |||||
public void atClassObject(Expr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atClassObject(expr); | |||||
} | |||||
@Override | |||||
public void atArrayRead(ASTree array, ASTree index) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), array); | |||||
super.atArrayRead(array, index); | |||||
} | |||||
@Override | |||||
protected void arrayAccess(ASTree array, ASTree index) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), array); | |||||
super.arrayAccess(array, index); | |||||
} | |||||
@Override | |||||
public void atArrayPlusPlus(int token, boolean isPost, Expr expr, boolean doDup) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atArrayPlusPlus(token, isPost, expr, doDup); | |||||
} | |||||
@Override | |||||
protected void atPlusPlusCore(int dup_code, boolean doDup, int token, boolean isPost, Expr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atPlusPlusCore(dup_code, doDup, token, isPost, expr); | |||||
} | |||||
@Override | |||||
public void atVariable(Variable v) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), v); | |||||
super.atVariable(v); | |||||
} | |||||
@Override | |||||
public void atKeyword(Keyword k) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), k); | |||||
super.atKeyword(k); | |||||
} | |||||
@Override | |||||
public void atStringL(javassist.compiler.ast.StringL s) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), s); | |||||
super.atStringL(s); | |||||
} | |||||
@Override | |||||
public void atIntConst(IntConst i) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), i); | |||||
super.atIntConst(i); | |||||
} | |||||
@Override | |||||
public void atDoubleConst(DoubleConst d) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), d); | |||||
super.atDoubleConst(d); | |||||
} | |||||
@Override | |||||
public void atMember(Member mem) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), mem); | |||||
super.atMember(mem); | |||||
} | |||||
@Override | |||||
protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right, boolean doDup) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atFieldAssign(expr, op, left, right, doDup); | |||||
} | |||||
@Override | |||||
public void atCastExpr(CastExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atCastExpr(expr); | |||||
} | |||||
@Override | |||||
protected void atCastToRtype(CastExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atCastToRtype(expr); | |||||
} | |||||
@Override | |||||
protected void atCastToWrapper(CastExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atCastToWrapper(expr); | |||||
} | |||||
@Override | |||||
public void atCallExpr(CallExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atCallExpr(expr); | |||||
} | |||||
@Override | |||||
protected void atCflow(ASTList cname, int lineNumber) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), cname); | |||||
super.atCflow(cname, lineNumber); | |||||
} | |||||
@Override | |||||
protected void atTryStmnt(Stmnt st) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), st); | |||||
super.atTryStmnt(st); | |||||
} | |||||
@Override | |||||
public void atNewExpr(NewExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atNewExpr(expr); | |||||
} | |||||
@Override | |||||
public void atNewArrayExpr(NewExpr expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atNewArrayExpr(expr); | |||||
} | |||||
@Override | |||||
protected void atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), init); | |||||
super.atArrayVariableAssign(init, varType, varArray, varClass); | |||||
} | |||||
@Override | |||||
public void atArrayInit(ArrayInit init) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), init); | |||||
super.atArrayInit(init); | |||||
} | |||||
@Override | |||||
protected void atMultiNewArray(int type, ASTList classname, ASTList size) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), classname); | |||||
super.atMultiNewArray(type, classname, size); | |||||
} | |||||
@Override | |||||
public void atMethodCallCore(CtClass targetClass, String mname, ASTList args, boolean isStatic, boolean isSpecial, int aload0pos, MemberResolver.Method found) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), args); | |||||
super.atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, aload0pos, found); | |||||
} | |||||
@Override | |||||
protected void atFieldRead(ASTree expr) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atFieldRead(expr); | |||||
} | |||||
@Override | |||||
protected void atFieldPlusPlus(int token, boolean isPost, ASTree oprand, Expr expr, boolean doDup) throws CompileError { | |||||
lineNumberAttributeBuilder.put(bytecode.currentPc(), expr); | |||||
super.atFieldPlusPlus(token, isPost, oprand, expr, doDup); | |||||
} | |||||
} |
int n = params.length; | int n = params.length; | ||||
for (int i = 0; i < n; ++i) | for (int i = 0; i < n; ++i) | ||||
compileUnwrapValue(params[i]); | |||||
compileUnwrapValue(params[i], expr.getLineNumber()); | |||||
} | } | ||||
else | else | ||||
super.atFieldAssign(expr, op, left, right); | super.atFieldAssign(expr, op, left, right); | ||||
CtClass returnType = codeGen.returnType; | CtClass returnType = codeGen.returnType; | ||||
expr.getOprand().accept(this); | expr.getOprand().accept(this); | ||||
if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0) | if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0) | ||||
compileUnwrapValue(returnType); | |||||
compileUnwrapValue(returnType, expr.getLineNumber()); | |||||
else if (returnType instanceof CtPrimitiveType) { | else if (returnType instanceof CtPrimitiveType) { | ||||
CtPrimitiveType pt = (CtPrimitiveType)returnType; | CtPrimitiveType pt = (CtPrimitiveType)returnType; | ||||
int destType = MemberResolver.descToType(pt.getDescriptor()); | int destType = MemberResolver.descToType(pt.getDescriptor()); | ||||
addNullIfVoid(); | addNullIfVoid(); | ||||
} | } | ||||
protected void compileUnwrapValue(CtClass type) throws CompileError | |||||
protected void compileUnwrapValue(CtClass type, int lineNumber) throws CompileError | |||||
{ | { | ||||
if (type == CtClass.voidType) | if (type == CtClass.voidType) | ||||
addNullIfVoid(); | addNullIfVoid(); |
ungetc(c); | ungetc(c); | ||||
c = '/'; | c = '/'; | ||||
} | } | ||||
} | |||||
} else if (c == '\n') | |||||
++lineNumber; | |||||
} while(isBlank(c)); | } while(isBlank(c)); | ||||
return c; | return c; | ||||
} | } | ||||
lastChar = -1; | lastChar = -1; | ||||
return c; | return c; | ||||
} | } | ||||
public int getLineNumber() { | |||||
return lineNumber + 1; | |||||
} | |||||
} | } |
body.accept(this); | body.accept(this); | ||||
int end = bc.currentPc(); | int end = bc.currentPc(); | ||||
if (start == end) | if (start == end) | ||||
throw new CompileError("empty try block"); | |||||
throw new CompileError("empty try block", st.getLineNumber()); | |||||
boolean tryNotReturn = !hasReturned; | boolean tryNotReturn = !hasReturned; | ||||
if (tryNotReturn) { | if (tryNotReturn) { | ||||
if (init != null) | if (init != null) | ||||
throw new CompileError( | throw new CompileError( | ||||
"sorry, multi-dimensional array initializer " + | "sorry, multi-dimensional array initializer " + | ||||
"for new is not supported"); | |||||
"for new is not supported", expr.getLineNumber()); | |||||
atMultiNewArray(type, classname, size); | atMultiNewArray(type, classname, size); | ||||
return; | return; | ||||
} | } | ||||
ASTree sizeExpr = size.head(); | ASTree sizeExpr = size.head(); | ||||
atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init); | |||||
atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init, expr.getLineNumber()); | |||||
} | } | ||||
private void atNewArrayExpr2(int type, ASTree sizeExpr, | private void atNewArrayExpr2(int type, ASTree sizeExpr, | ||||
String jvmClassname, ArrayInit init) throws CompileError { | |||||
String jvmClassname, ArrayInit init, int lineNumber) throws CompileError { | |||||
if (init == null) | if (init == null) | ||||
if (sizeExpr == null) | if (sizeExpr == null) | ||||
throw new CompileError("no array size"); | |||||
throw new CompileError("no array size", lineNumber); | |||||
else | else | ||||
sizeExpr.accept(this); | sizeExpr.accept(this); | ||||
else | else | ||||
bytecode.addIconst(s); | bytecode.addIconst(s); | ||||
} | } | ||||
else | else | ||||
throw new CompileError("unnecessary array size specified for new"); | |||||
throw new CompileError("unnecessary array size specified for new", lineNumber); | |||||
String elementClass; | String elementClass; | ||||
if (type == CLASS) { | if (type == CLASS) { | ||||
atype = T_LONG; | atype = T_LONG; | ||||
break; | break; | ||||
default : | default : | ||||
badNewExpr(); | |||||
badNewExpr(lineNumber); | |||||
break; | break; | ||||
} | } | ||||
className = elementClass; | className = elementClass; | ||||
} | } | ||||
private static void badNewExpr() throws CompileError { | |||||
throw new CompileError("bad new expression"); | |||||
private static void badNewExpr(int lineNumber) throws CompileError { | |||||
throw new CompileError("bad new expression", lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
protected void atArrayVariableAssign(ArrayInit init, int varType, | protected void atArrayVariableAssign(ArrayInit init, int varType, | ||||
int varArray, String varClass) throws CompileError { | int varArray, String varClass) throws CompileError { | ||||
atNewArrayExpr2(varType, null, varClass, init); | |||||
atNewArrayExpr2(varType, null, varClass, init, init.getLineNumber()); | |||||
} | } | ||||
@Override | @Override | ||||
public void atArrayInit(ArrayInit init) throws CompileError { | public void atArrayInit(ArrayInit init) throws CompileError { | ||||
throw new CompileError("array initializer is not supported"); | |||||
throw new CompileError("array initializer is not supported", init.getLineNumber()); | |||||
} | } | ||||
protected void atMultiNewArray(int type, ASTList classname, ASTList size) | protected void atMultiNewArray(int type, ASTList classname, ASTList size) | ||||
++count; | ++count; | ||||
s.accept(this); | s.accept(this); | ||||
if (exprType != INT) | if (exprType != INT) | ||||
throw new CompileError("bad type for array size"); | |||||
throw new CompileError("bad type for array size", classname.getLineNumber()); | |||||
} | } | ||||
String desc; | String desc; | ||||
mname = MethodInfo.nameInit; // <init> | mname = MethodInfo.nameInit; // <init> | ||||
targetClass = thisClass; | targetClass = thisClass; | ||||
if (inStaticMethod) | if (inStaticMethod) | ||||
throw new CompileError("a constructor cannot be static"); | |||||
throw new CompileError("a constructor cannot be static", expr.getLineNumber()); | |||||
bytecode.addAload(0); // this | bytecode.addAload(0); // this | ||||
if (((Keyword)method).get() == SUPER) | if (((Keyword)method).get() == SUPER) | ||||
badMethod(); | badMethod(); | ||||
} | } | ||||
else | else | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, | atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, | ||||
aload0pos, cached); | aload0pos, cached); | ||||
msg = "Method " + mname + " not found in " | msg = "Method " + mname + " not found in " | ||||
+ targetClass.getName(); | + targetClass.getName(); | ||||
throw new CompileError(msg); | |||||
throw new CompileError(msg, args.getLineNumber()); | |||||
} | } | ||||
atMethodCallCore2(targetClass, mname, isStatic, isSpecial, | atMethodCallCore2(targetClass, mname, isStatic, isSpecial, | ||||
if (op == '=') { | if (op == '=') { | ||||
FieldInfo finfo = f.getFieldInfo2(); | FieldInfo finfo = f.getFieldInfo2(); | ||||
setFieldType(finfo); | setFieldType(finfo); | ||||
AccessorMaker maker = isAccessibleField(f, finfo); | |||||
AccessorMaker maker = isAccessibleField(f, finfo, expr.getLineNumber()); | |||||
if (maker == null) | if (maker == null) | ||||
fi = addFieldrefInfo(f, finfo); | fi = addFieldrefInfo(f, finfo); | ||||
else | else | ||||
fi = 0; | fi = 0; | ||||
} | } | ||||
else | else | ||||
fi = atFieldRead(f, is_static); | |||||
fi = atFieldRead(f, is_static, expr.getLineNumber()); | |||||
int fType = exprType; | int fType = exprType; | ||||
int fDim = arrayDim; | int fDim = arrayDim; | ||||
} | } | ||||
boolean is_static = resultStatic; | boolean is_static = resultStatic; | ||||
ASTree cexpr = TypeChecker.getConstantFieldValue(f); | |||||
ASTree cexpr = TypeChecker.getConstantFieldValue(f, expr.getLineNumber()); | |||||
if (cexpr == null) | if (cexpr == null) | ||||
atFieldRead(f, is_static); | |||||
atFieldRead(f, is_static,expr.getLineNumber() ); | |||||
else { | else { | ||||
cexpr.accept(this); | cexpr.accept(this); | ||||
setFieldType(f.getFieldInfo2()); | setFieldType(f.getFieldInfo2()); | ||||
private void atArrayLength(ASTree expr) throws CompileError { | private void atArrayLength(ASTree expr) throws CompileError { | ||||
if (arrayDim == 0) | if (arrayDim == 0) | ||||
throw new CompileError(".length applied to a non array"); | |||||
throw new CompileError(".length applied to a non array", expr.getLineNumber()); | |||||
bytecode.addOpcode(ARRAYLENGTH); | bytecode.addOpcode(ARRAYLENGTH); | ||||
exprType = INT; | exprType = INT; | ||||
* It returns a fieldref_info index or zero if the field is a private | * It returns a fieldref_info index or zero if the field is a private | ||||
* one declared in an enclosing class. | * one declared in an enclosing class. | ||||
*/ | */ | ||||
private int atFieldRead(CtField f, boolean isStatic) throws CompileError { | |||||
private int atFieldRead(CtField f, boolean isStatic, int lineNumber) throws CompileError { | |||||
FieldInfo finfo = f.getFieldInfo2(); | FieldInfo finfo = f.getFieldInfo2(); | ||||
boolean is2byte = setFieldType(finfo); | boolean is2byte = setFieldType(finfo); | ||||
AccessorMaker maker = isAccessibleField(f, finfo); | |||||
AccessorMaker maker = isAccessibleField(f, finfo, lineNumber); | |||||
if (maker != null) { | if (maker != null) { | ||||
MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); | MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); | ||||
bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), | bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), | ||||
* an exception or it returns AccessorMaker if the field is a private | * an exception or it returns AccessorMaker if the field is a private | ||||
* one declared in an enclosing class. | * one declared in an enclosing class. | ||||
*/ | */ | ||||
private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo) | |||||
private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (AccessFlag.isPrivate(finfo.getAccessFlags()) | if (AccessFlag.isPrivate(finfo.getAccessFlags()) | ||||
return maker; | return maker; | ||||
} | } | ||||
throw new CompileError("Field " + f.getName() + " in " | throw new CompileError("Field " + f.getName() + " in " | ||||
+ declClass.getName() + " is private."); | |||||
+ declClass.getName() + " is private.", lineNumber); | |||||
} | } | ||||
return null; // accessible field | return null; // accessible field | ||||
if (!is_static) | if (!is_static) | ||||
bytecode.addOpcode(DUP); | bytecode.addOpcode(DUP); | ||||
int fi = atFieldRead(f, is_static); | |||||
int fi = atFieldRead(f, is_static, oprand.getLineNumber()); | |||||
int t = exprType; | int t = exprType; | ||||
boolean is2w = is2word(t, arrayDim); | boolean is2w = is2word(t, arrayDim); | ||||
if (!is_static) | if (!is_static) | ||||
if (inStaticMethod) | if (inStaticMethod) | ||||
throw new CompileError( | throw new CompileError( | ||||
"not available in a static method: " + name); | |||||
"not available in a static method: " + name, expr.getLineNumber()); | |||||
else | else | ||||
bytecode.addAload(0); // this | bytecode.addAload(0); // this | ||||
&& ((Symbol)e.oprand2()).get().equals("length")) | && ((Symbol)e.oprand2()).get().equals("length")) | ||||
return null; // expr is an array length. | return null; // expr is an array length. | ||||
else | else | ||||
badLvalue(); | |||||
badLvalue(expr.getLineNumber()); | |||||
boolean is_static = Modifier.isStatic(f.getModifiers()); | boolean is_static = Modifier.isStatic(f.getModifiers()); | ||||
if (is_static) | if (is_static) | ||||
} | } | ||||
} | } | ||||
else | else | ||||
badLvalue(); | |||||
badLvalue(expr.getLineNumber()); | |||||
} | } | ||||
else | else | ||||
badLvalue(); | |||||
badLvalue(expr.getLineNumber()); | |||||
resultStatic = false; | resultStatic = false; | ||||
return null; // never reach | return null; // never reach | ||||
} | } | ||||
private static void badLvalue() throws CompileError { | |||||
throw new CompileError("bad l-value"); | |||||
private static void badLvalue(int lineNumber) throws CompileError { | |||||
throw new CompileError("bad l-value", lineNumber); | |||||
} | } | ||||
public CtClass[] makeParamList(MethodDecl md) throws CompileError { | public CtClass[] makeParamList(MethodDecl md) throws CompileError { |
/* NAME must be JVM-internal representation. | /* NAME must be JVM-internal representation. | ||||
*/ | */ | ||||
public NoFieldException(String name, ASTree e) { | public NoFieldException(String name, ASTree e) { | ||||
super("no such field: " + name); | |||||
super("no such field: " + name, e.getLineNumber()); | |||||
fieldName = name; | fieldName = name; | ||||
expr = e; | expr = e; | ||||
} | } |
Declarator d; | Declarator d; | ||||
boolean isConstructor = false; | boolean isConstructor = false; | ||||
if (lex.lookAhead() == Identifier && lex.lookAhead(1) == '(') { | if (lex.lookAhead() == Identifier && lex.lookAhead(1) == '(') { | ||||
d = new Declarator(VOID, 0); | |||||
d = new Declarator(VOID, 0, lex.getLineNumber()); | |||||
isConstructor = true; | isConstructor = true; | ||||
} | } | ||||
else | else | ||||
else | else | ||||
name = lex.getString(); | name = lex.getString(); | ||||
d.setVariable(new Symbol(name)); | |||||
d.setVariable(new Symbol(name, lex.getLineNumber())); | |||||
if (isConstructor || lex.lookAhead() == '(') | if (isConstructor || lex.lookAhead() == '(') | ||||
return parseMethod1(tbl, isConstructor, mods, d); | return parseMethod1(tbl, isConstructor, mods, d); | ||||
return parseField(tbl, mods, d); | return parseField(tbl, mods, d); | ||||
int c = lex.get(); | int c = lex.get(); | ||||
if (c == ';') | if (c == ';') | ||||
return new FieldDecl(mods, new ASTList(d, new ASTList(expr))); | |||||
return new FieldDecl(mods, new ASTList(d, new ASTList(expr, lex.getLineNumber()), lex.getLineNumber()), lex.getLineNumber()); | |||||
else if (c == ',') | else if (c == ',') | ||||
throw new CompileError( | throw new CompileError( | ||||
"only one field can be declared in one declaration", lex); | "only one field can be declared in one declaration", lex); | ||||
ASTList parms = null; | ASTList parms = null; | ||||
if (lex.lookAhead() != ')') | if (lex.lookAhead() != ')') | ||||
while (true) { | while (true) { | ||||
parms = ASTList.append(parms, parseFormalParam(tbl)); | |||||
parms = ASTList.append(parms, parseFormalParam(tbl), lex.getLineNumber()); | |||||
int t = lex.lookAhead(); | int t = lex.lookAhead(); | ||||
if (t == ',') | if (t == ',') | ||||
lex.get(); | lex.get(); | ||||
if (lex.lookAhead() == THROWS) { | if (lex.lookAhead() == THROWS) { | ||||
lex.get(); | lex.get(); | ||||
while (true) { | while (true) { | ||||
throwsList = ASTList.append(throwsList, parseClassType(tbl)); | |||||
throwsList = ASTList.append(throwsList, parseClassType(tbl), lex.getLineNumber()); | |||||
if (lex.lookAhead() == ',') | if (lex.lookAhead() == ',') | ||||
lex.get(); | lex.get(); | ||||
else | else | ||||
} | } | ||||
return new MethodDecl(mods, new ASTList(d, | return new MethodDecl(mods, new ASTList(d, | ||||
ASTList.make(parms, throwsList, null))); | |||||
ASTList.make(parms, throwsList, null, lex.getLineNumber()), lex.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
/* Parses a method body. | /* Parses a method body. | ||||
else { | else { | ||||
body = parseBlock(tbl); | body = parseBlock(tbl); | ||||
if (body == null) | if (body == null) | ||||
body = new Stmnt(BLOCK); | |||||
body = new Stmnt(BLOCK, lex.getLineNumber()); | |||||
} | } | ||||
md.sublist(4).setHead(body); | md.sublist(4).setHead(body); | ||||
if (t == ABSTRACT || t == FINAL || t == PUBLIC || t == PROTECTED | if (t == ABSTRACT || t == FINAL || t == PUBLIC || t == PROTECTED | ||||
|| t == PRIVATE || t == SYNCHRONIZED || t == STATIC | || t == PRIVATE || t == SYNCHRONIZED || t == STATIC | ||||
|| t == VOLATILE || t == TRANSIENT || t == STRICT) | || t == VOLATILE || t == TRANSIENT || t == STRICT) | ||||
list = new ASTList(new Keyword(lex.get()), list); | |||||
list = new ASTList(new Keyword(lex.get(), lex.getLineNumber()), list, lex.getLineNumber()); | |||||
else | else | ||||
break; | break; | ||||
} | } | ||||
if (isBuiltinType(t) || t == VOID) { | if (isBuiltinType(t) || t == VOID) { | ||||
lex.get(); // primitive type | lex.get(); // primitive type | ||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
return new Declarator(t, dim); | |||||
return new Declarator(t, dim, lex.getLineNumber()); | |||||
} | } | ||||
ASTList name = parseClassType(tbl); | ASTList name = parseClassType(tbl); | ||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
return new Declarator(name, dim); | |||||
return new Declarator(name, dim, lex.getLineNumber()); | |||||
} | } | ||||
private static boolean isBuiltinType(int t) { | private static boolean isBuiltinType(int t) { | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
String name = lex.getString(); | String name = lex.getString(); | ||||
d.setVariable(new Symbol(name)); | |||||
d.setVariable(new Symbol(name, lex.getLineNumber())); | |||||
d.addArrayDim(parseArrayDimension()); | d.addArrayDim(parseArrayDimension()); | ||||
tbl.append(name, d); | tbl.append(name, d); | ||||
return d; | return d; | ||||
return parseBlock(tbl); | return parseBlock(tbl); | ||||
else if (t == ';') { | else if (t == ';') { | ||||
lex.get(); | lex.get(); | ||||
return new Stmnt(BLOCK); // empty statement | |||||
return new Stmnt(BLOCK, lex.getLineNumber()); // empty statement | |||||
} | } | ||||
else if (t == Identifier && lex.lookAhead(1) == ':') { | else if (t == Identifier && lex.lookAhead(1) == ':') { | ||||
lex.get(); // Identifier | lex.get(); // Identifier | ||||
String label = lex.getString(); | String label = lex.getString(); | ||||
lex.get(); // ':' | lex.get(); // ':' | ||||
return Stmnt.make(LABEL, new Symbol(label), parseStatement(tbl)); | |||||
return Stmnt.make(LABEL, new Symbol(label, lex.getLineNumber()), parseStatement(tbl), lex.getLineNumber()); | |||||
} | } | ||||
else if (t == IF) | else if (t == IF) | ||||
return parseIf(tbl); | return parseIf(tbl); | ||||
while (lex.lookAhead() != '}') { | while (lex.lookAhead() != '}') { | ||||
Stmnt s = parseStatement(tbl2); | Stmnt s = parseStatement(tbl2); | ||||
if (s != null) | if (s != null) | ||||
body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s)); | |||||
body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s, lex.getLineNumber())); | |||||
} | } | ||||
lex.get(); // '}' | lex.get(); // '}' | ||||
if (body == null) | if (body == null) | ||||
return new Stmnt(BLOCK); // empty block | |||||
return new Stmnt(BLOCK, lex.getLineNumber()); // empty block | |||||
return body; | return body; | ||||
} | } | ||||
else | else | ||||
elsep = null; | elsep = null; | ||||
return new Stmnt(t, expr, new ASTList(thenp, new ASTList(elsep))); | |||||
return new Stmnt(t, expr, new ASTList(thenp, new ASTList(elsep, lex.getLineNumber()), lex.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
/* while.statement : WHILE "(" expression ")" statement | /* while.statement : WHILE "(" expression ")" statement | ||||
int t = lex.get(); // WHILE | int t = lex.get(); // WHILE | ||||
ASTree expr = parseParExpression(tbl); | ASTree expr = parseParExpression(tbl); | ||||
Stmnt body = parseStatement(tbl); | Stmnt body = parseStatement(tbl); | ||||
return new Stmnt(t, expr, body); | |||||
return new Stmnt(t, expr, body, lex.getLineNumber()); | |||||
} | } | ||||
/* do.statement : DO statement WHILE "(" expression ")" ";" | /* do.statement : DO statement WHILE "(" expression ")" ";" | ||||
if (lex.get() != ')' || lex.get() != ';') | if (lex.get() != ')' || lex.get() != ';') | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
return new Stmnt(t, expr, body); | |||||
return new Stmnt(t, expr, body, lex.getLineNumber()); | |||||
} | } | ||||
/* for.statement : FOR "(" decl.or.expr expression ";" expression ")" | /* for.statement : FOR "(" decl.or.expr expression ";" expression ")" | ||||
Stmnt body = parseStatement(tbl2); | Stmnt body = parseStatement(tbl2); | ||||
return new Stmnt(t, expr1, new ASTList(expr2, | return new Stmnt(t, expr1, new ASTList(expr2, | ||||
new ASTList(expr3, body))); | |||||
new ASTList(expr3, body, lex.getLineNumber()), lex.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
/* switch.statement : SWITCH "(" expression ")" "{" switch.block "}" | /* switch.statement : SWITCH "(" expression ")" "{" switch.block "}" | ||||
int t = lex.get(); // SWITCH | int t = lex.get(); // SWITCH | ||||
ASTree expr = parseParExpression(tbl); | ASTree expr = parseParExpression(tbl); | ||||
Stmnt body = parseSwitchBlock(tbl); | Stmnt body = parseSwitchBlock(tbl); | ||||
return new Stmnt(t, expr, body); | |||||
return new Stmnt(t, expr, body, lex.getLineNumber()); | |||||
} | } | ||||
private Stmnt parseSwitchBlock(SymbolTable tbl) throws CompileError { | private Stmnt parseSwitchBlock(SymbolTable tbl) throws CompileError { | ||||
throw new CompileError("no case or default in a switch block", | throw new CompileError("no case or default in a switch block", | ||||
lex); | lex); | ||||
Stmnt body = new Stmnt(BLOCK, s); | |||||
Stmnt body = new Stmnt(BLOCK, s, lex.getLineNumber()); | |||||
while (lex.lookAhead() != '}') { | while (lex.lookAhead() != '}') { | ||||
Stmnt s2 = parseStmntOrCase(tbl2); | Stmnt s2 = parseStmntOrCase(tbl2); | ||||
if (s2 != null) { | if (s2 != null) { | ||||
int op2 = s2.getOperator(); | int op2 = s2.getOperator(); | ||||
if (op2 == CASE || op2 == DEFAULT) { | if (op2 == CASE || op2 == DEFAULT) { | ||||
body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s2)); | |||||
body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s2, lex.getLineNumber())); | |||||
s = s2; | s = s2; | ||||
} | } | ||||
else | else | ||||
s = (Stmnt)ASTList.concat(s, new Stmnt(BLOCK, s2)); | |||||
s = (Stmnt)ASTList.concat(s, new Stmnt(BLOCK, s2, lex.getLineNumber())); | |||||
} | } | ||||
} | } | ||||
lex.get(); | lex.get(); | ||||
Stmnt s; | Stmnt s; | ||||
if (t == CASE) | if (t == CASE) | ||||
s = new Stmnt(t, parseExpression(tbl)); | |||||
s = new Stmnt(t, parseExpression(tbl), lex.getLineNumber()); | |||||
else | else | ||||
s = new Stmnt(DEFAULT); | |||||
s = new Stmnt(DEFAULT, lex.getLineNumber()); | |||||
if (lex.get() != ':') | if (lex.get() != ':') | ||||
throw new CompileError(": is missing", lex); | throw new CompileError(": is missing", lex); | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
Stmnt body = parseBlock(tbl); | Stmnt body = parseBlock(tbl); | ||||
return new Stmnt(t, expr, body); | |||||
return new Stmnt(t, expr, body, lex.getLineNumber()); | |||||
} | } | ||||
/* try.statement | /* try.statement | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
Stmnt b = parseBlock(tbl2); | Stmnt b = parseBlock(tbl2); | ||||
catchList = ASTList.append(catchList, new Pair(d, b)); | |||||
catchList = ASTList.append(catchList, new Pair(d, b), lex.getLineNumber()); | |||||
} | } | ||||
Stmnt finallyBlock = null; | Stmnt finallyBlock = null; | ||||
finallyBlock = parseBlock(tbl); | finallyBlock = parseBlock(tbl); | ||||
} | } | ||||
return Stmnt.make(TRY, block, catchList, finallyBlock); | |||||
return Stmnt.make(TRY, block, catchList, finallyBlock, lex.getLineNumber()); | |||||
} | } | ||||
/* return.statement : RETURN [ expression ] ";" | /* return.statement : RETURN [ expression ] ";" | ||||
*/ | */ | ||||
private Stmnt parseReturn(SymbolTable tbl) throws CompileError { | private Stmnt parseReturn(SymbolTable tbl) throws CompileError { | ||||
int t = lex.get(); // RETURN | int t = lex.get(); // RETURN | ||||
Stmnt s = new Stmnt(t); | |||||
Stmnt s = new Stmnt(t, lex.getLineNumber()); | |||||
if (lex.lookAhead() != ';') | if (lex.lookAhead() != ';') | ||||
s.setLeft(parseExpression(tbl)); | s.setLeft(parseExpression(tbl)); | ||||
if (lex.get() != ';') | if (lex.get() != ';') | ||||
throw new CompileError("; is missing", lex); | throw new CompileError("; is missing", lex); | ||||
return new Stmnt(t, expr); | |||||
return new Stmnt(t, expr, lex.getLineNumber()); | |||||
} | } | ||||
/* break.statement : BREAK [ Identifier ] ";" | /* break.statement : BREAK [ Identifier ] ";" | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
int t = lex.get(); // CONTINUE | int t = lex.get(); // CONTINUE | ||||
Stmnt s = new Stmnt(t); | |||||
Stmnt s = new Stmnt(t, lex.getLineNumber()); | |||||
int t2 = lex.get(); | int t2 = lex.get(); | ||||
if (t2 == Identifier) { | if (t2 == Identifier) { | ||||
s.setLeft(new Symbol(lex.getString())); | |||||
s.setLeft(new Symbol(lex.getString(), lex.getLineNumber())); | |||||
t2 = lex.get(); | t2 = lex.get(); | ||||
} | } | ||||
if (isBuiltinType(t)) { | if (isBuiltinType(t)) { | ||||
t = lex.get(); | t = lex.get(); | ||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
return parseDeclarators(tbl, new Declarator(t, dim)); | |||||
return parseDeclarators(tbl, new Declarator(t, dim, lex.getLineNumber())); | |||||
} | } | ||||
else if (t == Identifier) { | else if (t == Identifier) { | ||||
int i = nextIsClassType(0); | int i = nextIsClassType(0); | ||||
if (lex.lookAhead(i) == Identifier) { | if (lex.lookAhead(i) == Identifier) { | ||||
ASTList name = parseClassType(tbl); | ASTList name = parseClassType(tbl); | ||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
return parseDeclarators(tbl, new Declarator(name, dim)); | |||||
return parseDeclarators(tbl, new Declarator(name, dim, lex.getLineNumber())); | |||||
} | } | ||||
} | } | ||||
if (exprList) | if (exprList) | ||||
expr = parseExprList(tbl); | expr = parseExprList(tbl); | ||||
else | else | ||||
expr = new Stmnt(EXPR, parseExpression(tbl)); | |||||
expr = new Stmnt(EXPR, parseExpression(tbl), lex.getLineNumber()); | |||||
if (lex.get() != ';') | if (lex.get() != ';') | ||||
throw new CompileError("; is missing", lex); | throw new CompileError("; is missing", lex); | ||||
private Stmnt parseExprList(SymbolTable tbl) throws CompileError { | private Stmnt parseExprList(SymbolTable tbl) throws CompileError { | ||||
Stmnt expr = null; | Stmnt expr = null; | ||||
for (;;) { | for (;;) { | ||||
Stmnt e = new Stmnt(EXPR, parseExpression(tbl)); | |||||
expr = (Stmnt)ASTList.concat(expr, new Stmnt(BLOCK, e)); | |||||
Stmnt e = new Stmnt(EXPR, parseExpression(tbl), lex.getLineNumber()); | |||||
expr = (Stmnt)ASTList.concat(expr, new Stmnt(BLOCK, e, lex.getLineNumber())); | |||||
if (lex.lookAhead() == ',') | if (lex.lookAhead() == ',') | ||||
lex.get(); | lex.get(); | ||||
else | else | ||||
Stmnt decl = null; | Stmnt decl = null; | ||||
for (;;) { | for (;;) { | ||||
decl = (Stmnt)ASTList.concat(decl, | decl = (Stmnt)ASTList.concat(decl, | ||||
new Stmnt(DECL, parseDeclarator(tbl, d))); | |||||
new Stmnt(DECL, parseDeclarator(tbl, d), lex.getLineNumber())); | |||||
int t = lex.get(); | int t = lex.get(); | ||||
if (t == ';') | if (t == ';') | ||||
return decl; | return decl; | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
String name = lex.getString(); | String name = lex.getString(); | ||||
Symbol symbol = new Symbol(name); | |||||
Symbol symbol = new Symbol(name, lex.getLineNumber()); | |||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
ASTree init = null; | ASTree init = null; | ||||
if (lex.lookAhead() == '=') { | if (lex.lookAhead() == '=') { | ||||
init = parseInitializer(tbl); | init = parseInitializer(tbl); | ||||
} | } | ||||
Declarator decl = d.make(symbol, dim, init); | |||||
Declarator decl = d.make(symbol, dim, init, lex.getLineNumber()); | |||||
tbl.append(name, decl); | tbl.append(name, decl); | ||||
return decl; | return decl; | ||||
} | } | ||||
lex.get(); // '{' | lex.get(); // '{' | ||||
if(lex.lookAhead() == '}'){ | if(lex.lookAhead() == '}'){ | ||||
lex.get(); | lex.get(); | ||||
return new ArrayInit(null); | |||||
return new ArrayInit(null, lex.getLineNumber()); | |||||
} | } | ||||
ASTree expr = parseExpression(tbl); | ASTree expr = parseExpression(tbl); | ||||
ArrayInit init = new ArrayInit(expr); | |||||
ArrayInit init = new ArrayInit(expr, lex.getLineNumber()); | |||||
while (lex.lookAhead() == ',') { | while (lex.lookAhead() == ',') { | ||||
lex.get(); | lex.get(); | ||||
expr = parseExpression(tbl); | expr = parseExpression(tbl); | ||||
ASTList.append(init, expr); | |||||
ASTList.append(init, expr, lex.getLineNumber()); | |||||
} | } | ||||
if (lex.get() != '}') | if (lex.get() != '}') | ||||
int t = lex.get(); | int t = lex.get(); | ||||
ASTree right = parseExpression(tbl); | ASTree right = parseExpression(tbl); | ||||
return AssignExpr.makeAssign(t, left, right); | |||||
return AssignExpr.makeAssign(t, left, right, lex.getLineNumber()); | |||||
} | } | ||||
private static boolean isAssignOp(int t) { | private static boolean isAssignOp(int t) { | ||||
throw new CompileError(": is missing", lex); | throw new CompileError(": is missing", lex); | ||||
ASTree elseExpr = parseExpression(tbl); | ASTree elseExpr = parseExpression(tbl); | ||||
return new CondExpr(cond, thenExpr, elseExpr); | |||||
return new CondExpr(cond, thenExpr, elseExpr, lex.getLineNumber()); | |||||
} | } | ||||
return cond; | return cond; | ||||
} | } | ||||
if (isBuiltinType(t)) { | if (isBuiltinType(t)) { | ||||
lex.get(); // primitive type | lex.get(); // primitive type | ||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
return new InstanceOfExpr(t, dim, expr); | |||||
return new InstanceOfExpr(t, dim, expr, lex.getLineNumber()); | |||||
} | } | ||||
ASTList name = parseClassType(tbl); | ASTList name = parseClassType(tbl); | ||||
int dim = parseArrayDimension(); | int dim = parseArrayDimension(); | ||||
return new InstanceOfExpr(name, dim, expr); | |||||
return new InstanceOfExpr(name, dim, expr, lex.getLineNumber()); | |||||
} | } | ||||
private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec) | private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec) | ||||
if (p2 != 0 && prec > p2) | if (p2 != 0 && prec > p2) | ||||
expr2 = binaryExpr2(tbl, expr2, p2); | expr2 = binaryExpr2(tbl, expr2, p2); | ||||
else | else | ||||
return BinExpr.makeBin(t, expr, expr2); | |||||
return BinExpr.makeBin(t, expr, expr2, lex.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
case IntConstant : | case IntConstant : | ||||
case CharConstant : | case CharConstant : | ||||
lex.get(); | lex.get(); | ||||
return new IntConst(-lex.getLong(), t2); | |||||
return new IntConst(-lex.getLong(), t2, lex.getLineNumber()); | |||||
case DoubleConstant : | case DoubleConstant : | ||||
case FloatConstant : | case FloatConstant : | ||||
lex.get(); | lex.get(); | ||||
return new DoubleConst(-lex.getDouble(), t2); | |||||
return new DoubleConst(-lex.getDouble(), t2, lex.getLineNumber()); | |||||
default : | default : | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return Expr.make(t, parseUnaryExpr(tbl)); | |||||
return Expr.make(t, parseUnaryExpr(tbl), lex.getLineNumber()); | |||||
case '(' : | case '(' : | ||||
return parseCast(tbl); | return parseCast(tbl); | ||||
default : | default : | ||||
if (lex.get() != ')') | if (lex.get() != ')') | ||||
throw new CompileError(") is missing", lex); | throw new CompileError(") is missing", lex); | ||||
return new CastExpr(t, dim, parseUnaryExpr(tbl)); | |||||
return new CastExpr(t, dim, parseUnaryExpr(tbl), lex.getLineNumber()); | |||||
} | } | ||||
else if (t == Identifier && nextIsClassCast()) { | else if (t == Identifier && nextIsClassCast()) { | ||||
lex.get(); // '(' | lex.get(); // '(' | ||||
if (lex.get() != ')') | if (lex.get() != ')') | ||||
throw new CompileError(") is missing", lex); | throw new CompileError(") is missing", lex); | ||||
return new CastExpr(name, dim, parseUnaryExpr(tbl)); | |||||
return new CastExpr(name, dim, parseUnaryExpr(tbl), lex.getLineNumber()); | |||||
} | } | ||||
else | else | ||||
return parsePostfix(tbl); | return parsePostfix(tbl); | ||||
if (lex.get() != Identifier) | if (lex.get() != Identifier) | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
list = ASTList.append(list, new Symbol(lex.getString())); | |||||
list = ASTList.append(list, new Symbol(lex.getString(), lex.getLineNumber()), lex.getLineNumber()); | |||||
if (lex.lookAhead() == '.') | if (lex.lookAhead() == '.') | ||||
lex.get(); | lex.get(); | ||||
else | else | ||||
case IntConstant : | case IntConstant : | ||||
case CharConstant : | case CharConstant : | ||||
lex.get(); | lex.get(); | ||||
return new IntConst(lex.getLong(), token); | |||||
return new IntConst(lex.getLong(), token, lex.getLineNumber()); | |||||
case DoubleConstant : | case DoubleConstant : | ||||
case FloatConstant : | case FloatConstant : | ||||
lex.get(); | lex.get(); | ||||
return new DoubleConst(lex.getDouble(), token); | |||||
return new DoubleConst(lex.getDouble(), token, lex.getLineNumber()); | |||||
default : | default : | ||||
break; | break; | ||||
} | } | ||||
if (index == null) | if (index == null) | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
expr = Expr.make(ARRAY, expr, index); | |||||
expr = Expr.make(ARRAY, expr, index, lex.getLineNumber()); | |||||
} | } | ||||
break; | break; | ||||
case PLUSPLUS : | case PLUSPLUS : | ||||
case MINUSMINUS : | case MINUSMINUS : | ||||
t = lex.get(); | t = lex.get(); | ||||
expr = Expr.make(t, null, expr); | |||||
expr = Expr.make(t, null, expr, lex.getLineNumber()); | |||||
break; | break; | ||||
case '.' : | case '.' : | ||||
lex.get(); | lex.get(); | ||||
if (t == CLASS) | if (t == CLASS) | ||||
expr = parseDotClass(expr, 0); | expr = parseDotClass(expr, 0); | ||||
else if (t == SUPER) | else if (t == SUPER) | ||||
expr = Expr.make('.', new Symbol(toClassName(expr)), new Keyword(t)); | |||||
expr = Expr.make('.', new Symbol(toClassName(expr), lex.getLineNumber()), new Keyword(t, lex.getLineNumber()), lex.getLineNumber()); | |||||
else if (t == Identifier) { | else if (t == Identifier) { | ||||
str = lex.getString(); | str = lex.getString(); | ||||
expr = Expr.make('.', expr, new Member(str)); | |||||
expr = Expr.make('.', expr, new Member(str, lex.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
else | else | ||||
throw new CompileError("missing member name", lex); | throw new CompileError("missing member name", lex); | ||||
throw new CompileError("missing static member name", lex); | throw new CompileError("missing static member name", lex); | ||||
str = lex.getString(); | str = lex.getString(); | ||||
expr = Expr.make(MEMBER, new Symbol(toClassName(expr)), | |||||
new Member(str)); | |||||
expr = Expr.make(MEMBER, new Symbol(toClassName(expr), lex.getLineNumber()), | |||||
new Member(str, lex.getLineNumber()), lex.getLineNumber()); | |||||
break; | break; | ||||
default : | default : | ||||
return expr; | return expr; | ||||
cname = sbuf.toString(); | cname = sbuf.toString(); | ||||
} | } | ||||
return Expr.make('.', new Symbol(cname), new Member("class")); | |||||
return Expr.make('.', new Symbol(cname, className.getLineNumber()), new Member("class", className.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
/* Parses a .class expression on a built-in type. For example, | /* Parses a .class expression on a built-in type. For example, | ||||
{ | { | ||||
if (dim > 0) { | if (dim > 0) { | ||||
String cname = CodeGen.toJvmTypeName(builtinType, dim); | String cname = CodeGen.toJvmTypeName(builtinType, dim); | ||||
return Expr.make('.', new Symbol(cname), new Member("class")); | |||||
return Expr.make('.', new Symbol(cname, lex.getLineNumber()), new Member("class", lex.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
String cname; | String cname; | ||||
switch(builtinType) { | switch(builtinType) { | ||||
break; | break; | ||||
default : | default : | ||||
throw new CompileError("invalid builtin type: " | throw new CompileError("invalid builtin type: " | ||||
+ builtinType); | |||||
+ builtinType, lex); | |||||
} | } | ||||
return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE")); | |||||
return Expr.make(MEMBER, new Symbol(cname, lex.getLineNumber()), new Member("TYPE", lex.getLineNumber()), lex.getLineNumber()); | |||||
} | } | ||||
/* method.call : method.expr "(" argument.list ")" | /* method.call : method.expr "(" argument.list ")" | ||||
throw new SyntaxError(lex); | throw new SyntaxError(lex); | ||||
} | } | ||||
return CallExpr.makeCall(expr, parseArgumentList(tbl)); | |||||
return CallExpr.makeCall(expr, parseArgumentList(tbl), lex.getLineNumber()); | |||||
} | } | ||||
private String toClassName(ASTree name) | private String toClassName(ASTree name) | ||||
case TRUE : | case TRUE : | ||||
case FALSE : | case FALSE : | ||||
case NULL : | case NULL : | ||||
return new Keyword(t); | |||||
return new Keyword(t, lex.getLineNumber()); | |||||
case Identifier : | case Identifier : | ||||
name = lex.getString(); | name = lex.getString(); | ||||
decl = tbl.lookup(name); | decl = tbl.lookup(name); | ||||
if (decl == null) | if (decl == null) | ||||
return new Member(name); // this or static member | |||||
return new Variable(name, decl); // local variable | |||||
return new Member(name, lex.getLineNumber()); // this or static member | |||||
return new Variable(name, decl, lex.getLineNumber()); // local variable | |||||
case StringL : | case StringL : | ||||
return new StringL(lex.getString()); | |||||
return new StringL(lex.getString(), lex.getLineNumber()); | |||||
case NEW : | case NEW : | ||||
return parseNew(tbl); | return parseNew(tbl); | ||||
case '(' : | case '(' : | ||||
if (lex.lookAhead() == '{') | if (lex.lookAhead() == '{') | ||||
init = parseArrayInitializer(tbl); | init = parseArrayInitializer(tbl); | ||||
return new NewExpr(t, size, init); | |||||
return new NewExpr(t, size, init, lex.getLineNumber()); | |||||
} | } | ||||
else if (t == Identifier) { | else if (t == Identifier) { | ||||
ASTList name = parseClassType(tbl); | ASTList name = parseClassType(tbl); | ||||
t = lex.lookAhead(); | t = lex.lookAhead(); | ||||
if (t == '(') { | if (t == '(') { | ||||
ASTList args = parseArgumentList(tbl); | ASTList args = parseArgumentList(tbl); | ||||
return new NewExpr(name, args); | |||||
return new NewExpr(name, args, lex.getLineNumber()); | |||||
} | } | ||||
else if (t == '[') { | else if (t == '[') { | ||||
ASTList size = parseArraySize(tbl); | ASTList size = parseArraySize(tbl); | ||||
if (lex.lookAhead() == '{') | if (lex.lookAhead() == '{') | ||||
init = parseArrayInitializer(tbl); | init = parseArrayInitializer(tbl); | ||||
return NewExpr.makeObjectArray(name, size, init); | |||||
return NewExpr.makeObjectArray(name, size, init, lex.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
private ASTList parseArraySize(SymbolTable tbl) throws CompileError { | private ASTList parseArraySize(SymbolTable tbl) throws CompileError { | ||||
ASTList list = null; | ASTList list = null; | ||||
while (lex.lookAhead() == '[') | while (lex.lookAhead() == '[') | ||||
list = ASTList.append(list, parseArrayIndex(tbl)); | |||||
list = ASTList.append(list, parseArrayIndex(tbl), lex.getLineNumber()); | |||||
return list; | return list; | ||||
} | } | ||||
ASTList list = null; | ASTList list = null; | ||||
if (lex.lookAhead() != ')') | if (lex.lookAhead() != ')') | ||||
for (;;) { | for (;;) { | ||||
list = ASTList.append(list, parseExpression(tbl)); | |||||
list = ASTList.append(list, parseExpression(tbl), lex.getLineNumber()); | |||||
if (lex.lookAhead() == ',') | if (lex.lookAhead() == ',') | ||||
lex.get(); | lex.get(); | ||||
else | else |
thisMethod = m; | thisMethod = m; | ||||
} | } | ||||
protected static void fatal() throws CompileError { | |||||
throw new CompileError("fatal"); | |||||
protected static void fatal(int lineLum) throws CompileError { | |||||
throw new CompileError("fatal", lineLum); | |||||
} | } | ||||
/** | /** | ||||
if (dim1 == 0 && dim1 == arrayDim) | if (dim1 == 0 && dim1 == arrayDim) | ||||
if (CodeGen.rightIsStrong(type1, exprType)) | if (CodeGen.rightIsStrong(type1, exprType)) | ||||
expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); | |||||
expr.setThen(new CastExpr(exprType, 0, expr.thenExpr(), expr.getLineNumber())); | |||||
else if (CodeGen.rightIsStrong(exprType, type1)) { | else if (CodeGen.rightIsStrong(exprType, type1)) { | ||||
expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); | |||||
expr.setElse(new CastExpr(type1, 0, expr.elseExpr(), expr.getLineNumber())); | |||||
exprType = type1; | exprType = type1; | ||||
} | } | ||||
} | } | ||||
* an expression using StringBuffer. | * an expression using StringBuffer. | ||||
*/ | */ | ||||
e = CallExpr.makeCall(Expr.make('.', e, | e = CallExpr.makeCall(Expr.make('.', e, | ||||
new Member("toString")), null); | |||||
new Member("toString", expr.getLineNumber()), expr.getLineNumber()), null, expr.getLineNumber()); | |||||
expr.setOprand1(e); | expr.setOprand1(e); | ||||
expr.setOprand2(null); // <---- look at this! | expr.setOprand2(null); // <---- look at this! | ||||
className = jvmJavaLangString; | className = jvmJavaLangString; | ||||
if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) | if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) | ||||
|| (exprType == CLASS && arrayDim == 0 | || (exprType == CLASS && arrayDim == 0 | ||||
&& jvmJavaLangString.equals(className))) { | && jvmJavaLangString.equals(className))) { | ||||
ASTList sbufClass = ASTList.make(new Symbol("java"), | |||||
new Symbol("lang"), new Symbol("StringBuffer")); | |||||
ASTree e = new NewExpr(sbufClass, null); | |||||
int lineNum = expr.getLineNumber(); | |||||
ASTList sbufClass = ASTList.make(new Symbol("java", lineNum), | |||||
new Symbol("lang", lineNum), new Symbol("StringBuffer", lineNum), lineNum); | |||||
ASTree e = new NewExpr(sbufClass, null, lineNum); | |||||
exprType = CLASS; | exprType = CLASS; | ||||
arrayDim = 0; | arrayDim = 0; | ||||
className = "java/lang/StringBuffer"; | className = "java/lang/StringBuffer"; | ||||
ASTree newExpr = null; | ASTree newExpr = null; | ||||
if (left instanceof StringL && right instanceof StringL && op == '+') | if (left instanceof StringL && right instanceof StringL && op == '+') | ||||
newExpr = new StringL(((StringL)left).get() | newExpr = new StringL(((StringL)left).get() | ||||
+ ((StringL)right).get()); | |||||
+ ((StringL)right).get(), left.getLineNumber()); | |||||
else if (left instanceof IntConst) | else if (left instanceof IntConst) | ||||
newExpr = ((IntConst)left).compute(op, right); | newExpr = ((IntConst)left).compute(op, right); | ||||
else if (left instanceof DoubleConst) | else if (left instanceof DoubleConst) | ||||
Expr e = (Expr)expr; | Expr e = (Expr)expr; | ||||
int op = e.getOperator(); | int op = e.getOperator(); | ||||
if (op == MEMBER) { | if (op == MEMBER) { | ||||
ASTree cexpr = getConstantFieldValue((Member)e.oprand2()); | |||||
ASTree cexpr = getConstantFieldValue((Member)e.oprand2(), expr.getLineNumber()); | |||||
if (cexpr != null) | if (cexpr != null) | ||||
return cexpr; | return cexpr; | ||||
} | } | ||||
return e.getLeft(); | return e.getLeft(); | ||||
} | } | ||||
else if (expr instanceof Member) { | else if (expr instanceof Member) { | ||||
ASTree cexpr = getConstantFieldValue((Member)expr); | |||||
ASTree cexpr = getConstantFieldValue((Member)expr, expr.getLineNumber()); | |||||
if (cexpr != null) | if (cexpr != null) | ||||
return cexpr; | return cexpr; | ||||
} | } | ||||
* If MEM is a static final field, this method returns a constant | * If MEM is a static final field, this method returns a constant | ||||
* expression representing the value of that field. | * expression representing the value of that field. | ||||
*/ | */ | ||||
private static ASTree getConstantFieldValue(Member mem) { | |||||
return getConstantFieldValue(mem.getField()); | |||||
private static ASTree getConstantFieldValue(Member mem, int lineNumber) { | |||||
return getConstantFieldValue(mem.getField(), lineNumber); | |||||
} | } | ||||
public static ASTree getConstantFieldValue(CtField f) { | |||||
public static ASTree getConstantFieldValue(CtField f, int lineNumber) { | |||||
if (f == null) | if (f == null) | ||||
return null; | return null; | ||||
return null; | return null; | ||||
if (value instanceof String) | if (value instanceof String) | ||||
return new StringL((String)value); | |||||
return new StringL((String)value, lineNumber); | |||||
else if (value instanceof Double || value instanceof Float) { | else if (value instanceof Double || value instanceof Float) { | ||||
int token = (value instanceof Double) | int token = (value instanceof Double) | ||||
? DoubleConstant : FloatConstant; | ? DoubleConstant : FloatConstant; | ||||
return new DoubleConst(((Number)value).doubleValue(), token); | |||||
return new DoubleConst(((Number)value).doubleValue(), token, lineNumber); | |||||
} | } | ||||
else if (value instanceof Number) { | else if (value instanceof Number) { | ||||
int token = (value instanceof Long) ? LongConstant : IntConstant; | int token = (value instanceof Long) ? LongConstant : IntConstant; | ||||
return new IntConst(((Number)value).longValue(), token); | |||||
return new IntConst(((Number)value).longValue(), token, lineNumber); | |||||
} | } | ||||
else if (value instanceof Boolean) | else if (value instanceof Boolean) | ||||
return new Keyword(((Boolean)value).booleanValue() | return new Keyword(((Boolean)value).booleanValue() | ||||
? TokenId.TRUE : TokenId.FALSE); | |||||
? TokenId.TRUE : TokenId.FALSE, lineNumber); | |||||
else | else | ||||
return null; | return null; | ||||
} | } | ||||
} | } | ||||
private static Expr makeAppendCall(ASTree target, ASTree arg) { | private static Expr makeAppendCall(ASTree target, ASTree arg) { | ||||
return CallExpr.makeCall(Expr.make('.', target, new Member("append")), | |||||
new ASTList(arg)); | |||||
return CallExpr.makeCall(Expr.make('.', target, new Member("append", target.getLineNumber()), target.getLineNumber()), | |||||
new ASTList(arg, target.getLineNumber()), target.getLineNumber()); | |||||
} | } | ||||
private void computeBinExprType(BinExpr expr, int token, int type1) | private void computeBinExprType(BinExpr expr, int token, int type1) | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (CodeGen.rightIsStrong(type1, type2)) | if (CodeGen.rightIsStrong(type1, type2)) | ||||
expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); | |||||
expr.setLeft(new CastExpr(type2, 0, expr.oprand1(), expr.getLineNumber())); | |||||
else | else | ||||
exprType = type1; | exprType = type1; | ||||
} | } | ||||
else if (token == '!') | else if (token == '!') | ||||
booleanExpr(expr); | booleanExpr(expr); | ||||
else if (token == CALL) // method call | else if (token == CALL) // method call | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
else { | else { | ||||
oprand.accept(this); | oprand.accept(this); | ||||
if (!isConstant(expr, token, oprand)) | if (!isConstant(expr, token, oprand)) | ||||
className = nfe.getField(); // JVM-internal | className = nfe.getField(); // JVM-internal | ||||
e.setOperator(MEMBER); | e.setOperator(MEMBER); | ||||
e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( | e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( | ||||
className))); | |||||
className), e.getLineNumber())); | |||||
} | } | ||||
if (arrayDim > 0) | if (arrayDim > 0) | ||||
badMethod(); | badMethod(); | ||||
} | } | ||||
else | else | ||||
fatal(); | |||||
fatal(expr.getLineNumber()); | |||||
MemberResolver.Method minfo | MemberResolver.Method minfo | ||||
= atMethodCallCore(targetClass, mname, args); | = atMethodCallCore(targetClass, mname, args); | ||||
} | } | ||||
} | } | ||||
throw new CompileError("bad field access"); | |||||
throw new CompileError("bad field access", expr.getLineNumber()); | |||||
} | } | ||||
private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { | private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { | ||||
Member fname = (Member)e.oprand2(); | Member fname = (Member)e.oprand2(); | ||||
CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); | CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); | ||||
e.setOperator(MEMBER); | e.setOperator(MEMBER); | ||||
e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName))); | |||||
e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName), e.getLineNumber())); | |||||
fname.setField(f); | fname.setField(f); | ||||
return f; | return f; | ||||
} | } | ||||
className = getSuperName(); | className = getSuperName(); | ||||
break; | break; | ||||
default : | default : | ||||
fatal(); | |||||
fatal(k.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
private ASTree left; | private ASTree left; | ||||
private ASTList right; | private ASTList right; | ||||
public ASTList(ASTree _head, ASTList _tail) { | |||||
public ASTList(ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(lineNumber); | |||||
left = _head; | left = _head; | ||||
right = _tail; | right = _tail; | ||||
} | } | ||||
public ASTList(ASTree _head) { | |||||
left = _head; | |||||
right = null; | |||||
public ASTList(ASTree _head, int lineNumber) { | |||||
this(_head, null, lineNumber); | |||||
} | } | ||||
public static ASTList make(ASTree e1, ASTree e2, ASTree e3) { | |||||
return new ASTList(e1, new ASTList(e2, new ASTList(e3))); | |||||
public static ASTList make(ASTree e1, ASTree e2, ASTree e3 , int lineNumber) { | |||||
return new ASTList(e1, new ASTList(e2, new ASTList(e3, lineNumber), lineNumber), lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
/** | /** | ||||
* Appends an object to a list. | * Appends an object to a list. | ||||
*/ | */ | ||||
public static ASTList append(ASTList a, ASTree b) { | |||||
return concat(a, new ASTList(b)); | |||||
public static ASTList append(ASTList a, ASTree b, int lineNumber) { | |||||
return concat(a, new ASTList(b, lineNumber)); | |||||
} | } | ||||
/** | /** |
/** default serialVersionUID */ | /** default serialVersionUID */ | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
private final int lineNumber; | |||||
public ASTree(int lineNumber) { | |||||
this.lineNumber = lineNumber; | |||||
} | |||||
public ASTree getLeft() { return null; } | public ASTree getLeft() { return null; } | ||||
public ASTree getRight() { return null; } | public ASTree getRight() { return null; } | ||||
String name = getClass().getName(); | String name = getClass().getName(); | ||||
return name.substring(name.lastIndexOf('.') + 1); | return name.substring(name.lastIndexOf('.') + 1); | ||||
} | } | ||||
public int getLineNumber() { | |||||
return lineNumber; | |||||
} | |||||
} | } |
/** | /** | ||||
* Constructs an object. | * Constructs an object. | ||||
* @param firstElement maybe null when the initializer is <code>{}</code> (empty). | |||||
* | |||||
* @param firstElement maybe null when the initializer is <code>{}</code> (empty). | |||||
* @param lineNumber | |||||
*/ | */ | ||||
public ArrayInit(ASTree firstElement) { | |||||
super(firstElement); | |||||
public ArrayInit(ASTree firstElement, int lineNumber) { | |||||
super(firstElement, lineNumber); | |||||
} | } | ||||
/** | /** |
/** default serialVersionUID */ | /** default serialVersionUID */ | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
private AssignExpr(int op, ASTree _head, ASTList _tail) { | |||||
super(op, _head, _tail); | |||||
private AssignExpr(int op, ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(op, _head, _tail, lineNumber); | |||||
} | } | ||||
public static AssignExpr makeAssign(int op, ASTree oprand1, | public static AssignExpr makeAssign(int op, ASTree oprand1, | ||||
ASTree oprand2) { | |||||
return new AssignExpr(op, oprand1, new ASTList(oprand2)); | |||||
ASTree oprand2, int lineNumber) { | |||||
return new AssignExpr(op, oprand1, new ASTList(oprand2, lineNumber), lineNumber); | |||||
} | } | ||||
@Override | @Override |
/** default serialVersionUID */ | /** default serialVersionUID */ | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
private BinExpr(int op, ASTree _head, ASTList _tail) { | |||||
super(op, _head, _tail); | |||||
private BinExpr(int op, ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(op, _head, _tail, lineNumber); | |||||
} | } | ||||
public static BinExpr makeBin(int op, ASTree oprand1, ASTree oprand2) { | |||||
return new BinExpr(op, oprand1, new ASTList(oprand2)); | |||||
public static BinExpr makeBin(int op, ASTree oprand1, ASTree oprand2, int lineNumber) { | |||||
return new BinExpr(op, oprand1, new ASTList(oprand2, lineNumber), lineNumber); | |||||
} | } | ||||
@Override | @Override |
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
private MemberResolver.Method method; // cached result of lookupMethod() | private MemberResolver.Method method; // cached result of lookupMethod() | ||||
private CallExpr(ASTree _head, ASTList _tail) { | |||||
super(TokenId.CALL, _head, _tail); | |||||
private CallExpr(ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(TokenId.CALL, _head, _tail, lineNumber); | |||||
method = null; | method = null; | ||||
} | } | ||||
return method; | return method; | ||||
} | } | ||||
public static CallExpr makeCall(ASTree target, ASTree args) { | |||||
return new CallExpr(target, new ASTList(args)); | |||||
public static CallExpr makeCall(ASTree target, ASTree args, int lineNumber) { | |||||
return new CallExpr(target, new ASTList(args, lineNumber), lineNumber); | |||||
} | } | ||||
@Override | @Override |
protected int castType; | protected int castType; | ||||
protected int arrayDim; | protected int arrayDim; | ||||
public CastExpr(ASTList className, int dim, ASTree expr) { | |||||
super(className, new ASTList(expr)); | |||||
public CastExpr(ASTList className, int dim, ASTree expr, int lineNumber) { | |||||
super(className, new ASTList(expr, lineNumber), lineNumber); | |||||
castType = CLASS; | castType = CLASS; | ||||
arrayDim = dim; | arrayDim = dim; | ||||
} | } | ||||
public CastExpr(int type, int dim, ASTree expr) { | |||||
super(null, new ASTList(expr)); | |||||
public CastExpr(int type, int dim, ASTree expr, int lineNumber) { | |||||
super(null, new ASTList(expr, lineNumber), lineNumber); | |||||
castType = type; | castType = type; | ||||
arrayDim = dim; | arrayDim = dim; | ||||
} | } |
/** default serialVersionUID */ | /** default serialVersionUID */ | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
public CondExpr(ASTree cond, ASTree thenp, ASTree elsep) { | |||||
super(cond, new ASTList(thenp, new ASTList(elsep))); | |||||
public CondExpr(ASTree cond, ASTree thenp, ASTree elsep, int lineNumber) { | |||||
super(cond, new ASTList(thenp, new ASTList(elsep, lineNumber), lineNumber), lineNumber); | |||||
} | } | ||||
public ASTree condExpr() { return head(); } | public ASTree condExpr() { return head(); } |
protected int localVar; | protected int localVar; | ||||
protected String qualifiedClass; // JVM-internal representation | protected String qualifiedClass; // JVM-internal representation | ||||
public Declarator(int type, int dim) { | |||||
super(null); | |||||
public Declarator(int type, int dim, int lineNumber) { | |||||
super(null, lineNumber); | |||||
varType = type; | varType = type; | ||||
arrayDim = dim; | arrayDim = dim; | ||||
localVar = -1; | localVar = -1; | ||||
qualifiedClass = null; | qualifiedClass = null; | ||||
} | } | ||||
public Declarator(ASTList className, int dim) { | |||||
super(null); | |||||
public Declarator(ASTList className, int dim, int lineNumber) { | |||||
super(null, lineNumber); | |||||
varType = CLASS; | varType = CLASS; | ||||
arrayDim = dim; | arrayDim = dim; | ||||
localVar = -1; | localVar = -1; | ||||
/* For declaring a pre-defined? local variable. | /* For declaring a pre-defined? local variable. | ||||
*/ | */ | ||||
public Declarator(int type, String jvmClassName, int dim, | public Declarator(int type, String jvmClassName, int dim, | ||||
int var, Symbol sym) { | |||||
super(null); | |||||
int var, Symbol sym, int lineNumber) { | |||||
super(null, lineNumber); | |||||
varType = type; | varType = type; | ||||
arrayDim = dim; | arrayDim = dim; | ||||
localVar = var; | localVar = var; | ||||
qualifiedClass = jvmClassName; | qualifiedClass = jvmClassName; | ||||
setLeft(sym); | setLeft(sym); | ||||
append(this, null); // initializer | |||||
append(this, null, lineNumber); // initializer | |||||
} | } | ||||
public Declarator make(Symbol sym, int dim, ASTree init) { | |||||
Declarator d = new Declarator(this.varType, this.arrayDim + dim); | |||||
public Declarator make(Symbol sym, int dim, ASTree init, int lineNumber) { | |||||
Declarator d = new Declarator(this.varType, this.arrayDim + dim, lineNumber); | |||||
d.qualifiedClass = this.qualifiedClass; | d.qualifiedClass = this.qualifiedClass; | ||||
d.setLeft(sym); | d.setLeft(sym); | ||||
append(d, init); | |||||
append(d, init, lineNumber); | |||||
return d; | return d; | ||||
} | } | ||||
protected double value; | protected double value; | ||||
protected int type; | protected int type; | ||||
public DoubleConst(double v, int tokenId) { value = v; type = tokenId; } | |||||
public DoubleConst(double v, int tokenId, int lineNumber) { | |||||
super(lineNumber); | |||||
value = v; | |||||
type = tokenId; | |||||
} | |||||
public double get() { return value; } | public double get() { return value; } | ||||
else | else | ||||
newType = TokenId.FloatConstant; | newType = TokenId.FloatConstant; | ||||
return compute(op, this.value, right.value, newType); | |||||
return compute(op, this.value, right.value, newType, getLineNumber()); | |||||
} | } | ||||
private DoubleConst compute0(int op, IntConst right) { | private DoubleConst compute0(int op, IntConst right) { | ||||
return compute(op, this.value, right.value, this.type); | |||||
return compute(op, this.value, right.value, this.type, getLineNumber()); | |||||
} | } | ||||
private static DoubleConst compute(int op, double value1, double value2, | private static DoubleConst compute(int op, double value1, double value2, | ||||
int newType) | |||||
int newType, int lineNumber) | |||||
{ | { | ||||
double newValue; | double newValue; | ||||
switch (op) { | switch (op) { | ||||
return null; | return null; | ||||
} | } | ||||
return new DoubleConst(newValue, newType); | |||||
return new DoubleConst(newValue, newType, lineNumber); | |||||
} | } | ||||
} | } |
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
protected int operatorId; | protected int operatorId; | ||||
Expr(int op, ASTree _head, ASTList _tail) { | |||||
super(_head, _tail); | |||||
Expr(int op, ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(_head, _tail, lineNumber); | |||||
operatorId = op; | operatorId = op; | ||||
} | } | ||||
Expr(int op, ASTree _head) { | |||||
super(_head); | |||||
Expr(int op, ASTree _head, int lineNumber) { | |||||
super(_head, lineNumber); | |||||
operatorId = op; | operatorId = op; | ||||
} | } | ||||
public static Expr make(int op, ASTree oprand1, ASTree oprand2) { | |||||
return new Expr(op, oprand1, new ASTList(oprand2)); | |||||
public static Expr make(int op, ASTree oprand1, ASTree oprand2, int lineNumber) { | |||||
return new Expr(op, oprand1, new ASTList(oprand2, lineNumber), lineNumber); | |||||
} | } | ||||
public static Expr make(int op, ASTree oprand1) { | |||||
return new Expr(op, oprand1); | |||||
public static Expr make(int op, ASTree oprand1, int lineNumber) { | |||||
return new Expr(op, oprand1, lineNumber); | |||||
} | } | ||||
public int getOperator() { return operatorId; } | public int getOperator() { return operatorId; } |
/** default serialVersionUID */ | /** default serialVersionUID */ | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
public FieldDecl(ASTree _head, ASTList _tail) { | |||||
super(_head, _tail); | |||||
public FieldDecl(ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(_head, _tail, lineNumber); | |||||
} | } | ||||
public ASTList getModifiers() { return (ASTList)getLeft(); } | public ASTList getModifiers() { return (ASTList)getLeft(); } |
/** default serialVersionUID */ | /** default serialVersionUID */ | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
public InstanceOfExpr(ASTList className, int dim, ASTree expr) { | |||||
super(className, dim, expr); | |||||
public InstanceOfExpr(ASTList className, int dim, ASTree expr, int lineNumber) { | |||||
super(className, dim, expr, lineNumber); | |||||
} | } | ||||
public InstanceOfExpr(int type, int dim, ASTree expr) { | |||||
super(type, dim, expr); | |||||
public InstanceOfExpr(int type, int dim, ASTree expr, int lineNumber) { | |||||
super(type, dim, expr, lineNumber); | |||||
} | } | ||||
@Override | @Override |
protected long value; | protected long value; | ||||
protected int type; | protected int type; | ||||
public IntConst(long v, int tokenId) { value = v; type = tokenId; } | |||||
public IntConst(long v, int tokenId, int lineNumber) { | |||||
super(lineNumber); | |||||
value = v; | |||||
type = tokenId; | |||||
} | |||||
public long get() { return value; } | public long get() { return value; } | ||||
return null; | return null; | ||||
} | } | ||||
return new IntConst(newValue, newType); | |||||
return new IntConst(newValue, newType, right.getLineNumber()); | |||||
} | } | ||||
private DoubleConst compute0(int op, DoubleConst right) { | private DoubleConst compute0(int op, DoubleConst right) { | ||||
return null; | return null; | ||||
} | } | ||||
return new DoubleConst(newValue, right.type); | |||||
return new DoubleConst(newValue, right.type, right.getLineNumber()); | |||||
} | } | ||||
} | } |
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
protected int tokenId; | protected int tokenId; | ||||
public Keyword(int token) { | |||||
public Keyword(int token, int lineNumber) { | |||||
super(lineNumber); | |||||
tokenId = token; | tokenId = token; | ||||
} | } | ||||
// this is used to obtain the value of a static final field. | // this is used to obtain the value of a static final field. | ||||
private CtField field; | private CtField field; | ||||
public Member(String name) { | |||||
super(name); | |||||
public Member(String name, int lineNumber) { | |||||
super(name, lineNumber); | |||||
field = null; | field = null; | ||||
} | } | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
public static final String initName = "<init>"; | public static final String initName = "<init>"; | ||||
public MethodDecl(ASTree _head, ASTList _tail) { | |||||
super(_head, _tail); | |||||
public MethodDecl(ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(_head, _tail, lineNumber); | |||||
} | } | ||||
public boolean isConstructor() { | public boolean isConstructor() { |
protected boolean newArray; | protected boolean newArray; | ||||
protected int arrayType; | protected int arrayType; | ||||
public NewExpr(ASTList className, ASTList args) { | |||||
super(className, new ASTList(args)); | |||||
public NewExpr(ASTList className, ASTList args, int lineNumber) { | |||||
super(className, new ASTList(args, lineNumber), lineNumber); | |||||
newArray = false; | newArray = false; | ||||
arrayType = CLASS; | arrayType = CLASS; | ||||
} | } | ||||
public NewExpr(int type, ASTList arraySize, ArrayInit init) { | |||||
super(null, new ASTList(arraySize)); | |||||
public NewExpr(int type, ASTList arraySize, ArrayInit init, int lineNumber) { | |||||
super(null, new ASTList(arraySize, lineNumber), lineNumber); | |||||
newArray = true; | newArray = true; | ||||
arrayType = type; | arrayType = type; | ||||
if (init != null) | if (init != null) | ||||
append(this, init); | |||||
append(this, init, lineNumber); | |||||
} | } | ||||
public static NewExpr makeObjectArray(ASTList className, | public static NewExpr makeObjectArray(ASTList className, | ||||
ASTList arraySize, ArrayInit init) { | |||||
NewExpr e = new NewExpr(className, arraySize); | |||||
ASTList arraySize, ArrayInit init, int lineNumber) { | |||||
NewExpr e = new NewExpr(className, arraySize, lineNumber); | |||||
e.newArray = true; | e.newArray = true; | ||||
if (init != null) | if (init != null) | ||||
append(e, init); | |||||
append(e, init, lineNumber); | |||||
return e; | return e; | ||||
} | } |
protected ASTree left, right; | protected ASTree left, right; | ||||
public Pair(ASTree _left, ASTree _right) { | public Pair(ASTree _left, ASTree _right) { | ||||
super(_left.getLineNumber()); | |||||
left = _left; | left = _left; | ||||
right = _right; | right = _right; | ||||
} | } |
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
protected int operatorId; | protected int operatorId; | ||||
public Stmnt(int op, ASTree _head, ASTList _tail) { | |||||
super(_head, _tail); | |||||
public Stmnt(int op, ASTree _head, ASTList _tail, int lineNumber) { | |||||
super(_head, _tail, lineNumber); | |||||
operatorId = op; | operatorId = op; | ||||
} | } | ||||
public Stmnt(int op, ASTree _head) { | |||||
super(_head); | |||||
public Stmnt(int op, ASTree _head, int lineNumber) { | |||||
super(_head, lineNumber); | |||||
operatorId = op; | operatorId = op; | ||||
} | } | ||||
public Stmnt(int op) { | |||||
this(op, null); | |||||
public Stmnt(int op, int lineNumber) { | |||||
this(op, null, lineNumber); | |||||
} | } | ||||
public static Stmnt make(int op, ASTree oprand1, ASTree oprand2) { | |||||
return new Stmnt(op, oprand1, new ASTList(oprand2)); | |||||
public static Stmnt make(int op, ASTree oprand1, ASTree oprand2, int lineNumber) { | |||||
return new Stmnt(op, oprand1, new ASTList(oprand2, lineNumber), lineNumber); | |||||
} | } | ||||
public static Stmnt make(int op, ASTree op1, ASTree op2, ASTree op3) { | |||||
return new Stmnt(op, op1, new ASTList(op2, new ASTList(op3))); | |||||
public static Stmnt make(int op, ASTree op1, ASTree op2, ASTree op3, int lineNumber) { | |||||
return new Stmnt(op, op1, new ASTList(op2, new ASTList(op3, lineNumber), lineNumber), lineNumber); | |||||
} | } | ||||
@Override | @Override |
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
protected String text; | protected String text; | ||||
public StringL(String t) { | |||||
public StringL(String t, int lineNumber) { | |||||
super(lineNumber); | |||||
text = t; | text = t; | ||||
} | } | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
protected String identifier; | protected String identifier; | ||||
public Symbol(String sym) { | |||||
public Symbol(String sym, int lineNumber) { | |||||
super(lineNumber); | |||||
identifier = sym; | identifier = sym; | ||||
} | } | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
protected Declarator declarator; | protected Declarator declarator; | ||||
public Variable(String sym, Declarator d) { | |||||
super(sym); | |||||
public Variable(String sym, Declarator d, int lineNumber) { | |||||
super(sym, lineNumber); | |||||
declarator = d; | declarator = d; | ||||
} | } | ||||
assertEquals(20, pc.line); | assertEquals(20, pc.line); | ||||
} | } | ||||
public void testLineNumberCompiler() { | |||||
CtClass testClass = loader.makeClass("javassist.bytecode.LineNumberCompilerClass"); | |||||
String run = String.join("\n", | |||||
"public void run() {", | |||||
" return", | |||||
"}"); | |||||
try { | |||||
testClass.addMethod(CtMethod.make(run, testClass)); | |||||
} catch (CannotCompileException e) { | |||||
assertEquals("line 3: syntax error near \" return\n}\"", e.getCause().getMessage()); | |||||
return; | |||||
} | |||||
fail("should not happen"); | |||||
} | |||||
public void testLineNumberException() { | |||||
CtClass testClass = loader.makeClass("javassist.bytecode.LineNumberExceptionClass"); | |||||
String run = String.join("\n", | |||||
"public void run() {", | |||||
" throw new java.lang.RuntimeException();", | |||||
"}"); | |||||
try { | |||||
testClass.addInterface(loader.get("java.lang.Runnable")); | |||||
testClass.addMethod(CtMethod.make(run, testClass)); | |||||
Class cls = testClass.toClass(BytecodeTest.class); | |||||
var runnable = (Runnable) cls.getConstructor().newInstance(); | |||||
runnable.run(); | |||||
} catch (Exception e) { | |||||
var lineNum = e.getStackTrace()[0].getLineNumber(); | |||||
assertEquals("Line number should be right", 2, lineNum); | |||||
return; | |||||
} | |||||
fail("should not happen"); | |||||
} | |||||
public void testRenameClass() throws Exception { | public void testRenameClass() throws Exception { | ||||
CtClass cc = loader.get("test1.RenameClass"); | CtClass cc = loader.get("test1.RenameClass"); | ||||
cc.replaceClassName("test1.RenameClass2", "java.lang.String"); | cc.replaceClassName("test1.RenameClass2", "java.lang.String"); |