/* Expands a simple class name to java.lang.*. | /* Expands a simple class name to java.lang.*. | ||||
* For example, this converts Object into java/lang/Object. | * For example, this converts Object into java/lang/Object. | ||||
*/ | */ | ||||
protected abstract String resolveClassName(String jvmClassName) | |||||
protected abstract String resolveClassName(String jvmClassName, int lineNumber) | |||||
throws CompileError; | throws CompileError; | ||||
/** | /** | ||||
@Override | @Override | ||||
public void atDeclarator(Declarator d) throws CompileError { | public void atDeclarator(Declarator d) throws CompileError { | ||||
d.setLocalVar(getMaxLocals()); | d.setLocalVar(getMaxLocals()); | ||||
d.setClassName(resolveClassName(d.getClassName())); | |||||
d.setClassName(resolveClassName(d.getClassName(), d.getLineNumber())); | |||||
int size; | int size; | ||||
if (is2word(d.getType(), d.getArrayDim())) | if (is2word(d.getType(), d.getArrayDim())) | ||||
int i = cname.indexOf("[L"); | int i = cname.indexOf("[L"); | ||||
if (i >= 0) { | if (i >= 0) { | ||||
String name = cname.substring(i + 2, cname.length() - 1); | String name = cname.substring(i + 2, cname.length() - 1); | ||||
String name2 = resolveClassName(name); | |||||
String name2 = resolveClassName(name, expr.getLineNumber()); | |||||
if (!name.equals(name2)) { | if (!name.equals(name2)) { | ||||
/* For example, to obtain String[].class, | /* For example, to obtain String[].class, | ||||
* "[Ljava.lang.String;" (not "[Ljava/lang/String"!) | * "[Ljava.lang.String;" (not "[Ljava/lang/String"!) | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
cname = resolveClassName(MemberResolver.javaToJvmName(cname)); | |||||
cname = resolveClassName(MemberResolver.javaToJvmName(cname), expr.getLineNumber()); | |||||
cname = MemberResolver.jvmToJavaName(cname); | cname = MemberResolver.jvmToJavaName(cname); | ||||
} | } | ||||
reason = String.format("line %d: %s", lineNumber, s); | reason = String.format("line %d: %s", lineNumber, s); | ||||
} | } | ||||
public CompileError(String s) { | |||||
private CompileError(String s) { | |||||
reason = s; | reason = s; | ||||
lex = null; | lex = null; | ||||
} | } |
*/ | */ | ||||
public CtMember compile(String src) throws CompileError { | public CtMember compile(String src) throws CompileError { | ||||
int startLine = gen.thisClass.getLinesCount(); | int startLine = gen.thisClass.getLinesCount(); | ||||
Parser p = new Parser(new Lex(src, startLine)); | |||||
Lex lex = new Lex(src, startLine); | |||||
Parser p = new Parser(lex); | |||||
ASTList mem = p.parseMember1(stable); | ASTList mem = p.parseMember1(stable); | ||||
try { | try { | ||||
if (mem instanceof FieldDecl) | if (mem instanceof FieldDecl) | ||||
decl.getClassFile2()); | decl.getClassFile2()); | ||||
return cb; | return cb; | ||||
} | } | ||||
catch (BadBytecode bb) { | |||||
throw new CompileError(bb.getMessage()); | |||||
} | |||||
catch (CannotCompileException e) { | |||||
throw new CompileError(e.getMessage()); | |||||
catch (BadBytecode | CannotCompileException bb) { | |||||
throw new CompileError(bb.getMessage(), lex.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
return method; | return method; | ||||
} | } | ||||
catch (NotFoundException e) { | catch (NotFoundException e) { | ||||
throw new CompileError(e.toString()); | |||||
throw new CompileError(e.toString(), md.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
Stmnt s = p.parseStatement(stb); | Stmnt s = p.parseStatement(stb); | ||||
if (p.hasMore()) | if (p.hasMore()) | ||||
throw new CompileError( | throw new CompileError( | ||||
"the method/constructor body must be surrounded by {}"); | |||||
"the method/constructor body must be surrounded by {}", s.getLineNumber()); | |||||
boolean callSuper = false; | boolean callSuper = false; | ||||
if (method instanceof CtConstructor) | if (method instanceof CtConstructor) | ||||
return bytecode; | return bytecode; | ||||
} | } | ||||
catch (NotFoundException e) { | catch (NotFoundException e) { | ||||
throw new CompileError(e.toString()); | |||||
throw new CompileError(e.toString(), -1); | |||||
} | } | ||||
} | } | ||||
ProceedHandler h = new ProceedHandler() { | ProceedHandler h = new ProceedHandler() { | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
ASTree expr = new Member(m, texpr.getLineNumber()); | ASTree expr = new Member(m, texpr.getLineNumber()); | ||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker check, ASTList args) | |||||
public void setReturnType(JvstTypeChecker check, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
ASTree expr = new Member(m, texpr.getLineNumber()); | ASTree expr = new Member(m, texpr.getLineNumber()); | ||||
ProceedHandler h = new ProceedHandler() { | ProceedHandler h = new ProceedHandler() { | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
Expr expr = Expr.make(TokenId.MEMBER, | Expr expr = Expr.make(TokenId.MEMBER, | ||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker check, ASTList args) | |||||
public void setReturnType(JvstTypeChecker check, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
Expr expr = Expr.make(TokenId.MEMBER, | Expr expr = Expr.make(TokenId.MEMBER, | ||||
ProceedHandler h = new ProceedHandler() { | ProceedHandler h = new ProceedHandler() { | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
gen.compileInvokeSpecial(texpr, methodIndex, descriptor, args); | gen.compileInvokeSpecial(texpr, methodIndex, descriptor, args); | ||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.compileInvokeSpecial(texpr, classname, methodname, descriptor, args); | c.compileInvokeSpecial(texpr, classname, methodname, descriptor, args); |
if (arrayDim != 1 || exprType != CLASS) | if (arrayDim != 1 || exprType != CLASS) | ||||
throw new CompileError("invalid type for " + paramArrayName, expr.getLineNumber()); | throw new CompileError("invalid type for " + paramArrayName, expr.getLineNumber()); | ||||
atAssignParamList(paramTypeList, bytecode); | |||||
atAssignParamList(paramTypeList, bytecode, expr.getLineNumber()); | |||||
if (!doDup) | if (!doDup) | ||||
bytecode.addOpcode(POP); | bytecode.addOpcode(POP); | ||||
} | } | ||||
super.atFieldAssign(expr, op, left, right, doDup); | super.atFieldAssign(expr, op, left, right, doDup); | ||||
} | } | ||||
protected void atAssignParamList(CtClass[] params, Bytecode code) | |||||
protected void atAssignParamList(CtClass[] params, Bytecode code, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (params == null) | if (params == null) | ||||
code.addOpcode(DUP); | code.addOpcode(DUP); | ||||
code.addIconst(i); | code.addIconst(i); | ||||
code.addOpcode(AALOAD); | code.addOpcode(AALOAD); | ||||
compileUnwrapValue(params[i], code); | |||||
compileUnwrapValue(params[i], code, lineNumber); | |||||
code.addStore(varNo, params[i]); | code.addStore(varNo, params[i]); | ||||
varNo += is2word(exprType, arrayDim) ? 2 : 1; | varNo += is2word(exprType, arrayDim) ? 2 : 1; | ||||
} | } | ||||
protected void atCastToRtype(CastExpr expr) throws CompileError { | protected void atCastToRtype(CastExpr expr) throws CompileError { | ||||
expr.getOprand().accept(this); | expr.getOprand().accept(this); | ||||
if (exprType == VOID || isRefType(exprType) || arrayDim > 0) | if (exprType == VOID || isRefType(exprType) || arrayDim > 0) | ||||
compileUnwrapValue(returnType, bytecode); | |||||
compileUnwrapValue(returnType, bytecode, 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(), expr.getLineNumber()); | |||||
atNumCastExpr(exprType, destType); | atNumCastExpr(exprType, destType); | ||||
exprType = destType; | exprType = destType; | ||||
arrayDim = 0; | arrayDim = 0; | ||||
if (isRefType(exprType) || arrayDim > 0) | if (isRefType(exprType) || arrayDim > 0) | ||||
return; // Object type. do nothing. | return; // Object type. do nothing. | ||||
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); | |||||
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className, expr.getLineNumber()); | |||||
if (clazz instanceof CtPrimitiveType) { | if (clazz instanceof CtPrimitiveType) { | ||||
CtPrimitiveType pt = (CtPrimitiveType)clazz; | CtPrimitiveType pt = (CtPrimitiveType)clazz; | ||||
String wrapper = pt.getWrapperName(); | String wrapper = pt.getWrapperName(); | ||||
if (method instanceof Member) { | if (method instanceof Member) { | ||||
String name = ((Member)method).get(); | String name = ((Member)method).get(); | ||||
if (procHandler != null && name.equals(proceedName)) { | if (procHandler != null && name.equals(proceedName)) { | ||||
procHandler.doit(this, bytecode, (ASTList)expr.oprand2()); | |||||
procHandler.doit(this, bytecode, (ASTList)expr.oprand2(), expr.getLineNumber()); | |||||
return; | return; | ||||
} | } | ||||
else if (name.equals(cflowName)) { | else if (name.equals(cflowName)) { | ||||
for (int k = 0; k < n; ++k) { | for (int k = 0; k < n; ++k) { | ||||
CtClass p = params[k]; | CtClass p = params[k]; | ||||
regno += bytecode.addLoad(regno, p); | regno += bytecode.addLoad(regno, p); | ||||
setType(p); | |||||
setType(p, a.getLineNumber()); | |||||
types[i] = exprType; | types[i] = exprType; | ||||
dims[i] = arrayDim; | dims[i] = arrayDim; | ||||
cnames[i] = className; | cnames[i] = className; | ||||
atMethodArgs(args, new int[nargs], new int[nargs], | atMethodArgs(args, new int[nargs], new int[nargs], | ||||
new String[nargs]); | new String[nargs]); | ||||
bytecode.addInvokespecial(methodIndex, descriptor); | bytecode.addInvokespecial(methodIndex, descriptor); | ||||
setReturnType(descriptor, false, false); | |||||
setReturnType(descriptor, false, false, target.getLineNumber()); | |||||
addNullIfVoid(); | addNullIfVoid(); | ||||
} | } | ||||
className = jvmJavaLangObject; | className = jvmJavaLangObject; | ||||
} | } | ||||
else | else | ||||
setType(cc); | |||||
setType(cc, cc.getLinesCount()); | |||||
Declarator decl | Declarator decl | ||||
= new Declarator(exprType, className, arrayDim, | = new Declarator(exprType, className, arrayDim, | ||||
while ((c = typeDesc.charAt(dim)) == '[') | while ((c = typeDesc.charAt(dim)) == '[') | ||||
++dim; | ++dim; | ||||
int type = MemberResolver.descToType(c); | |||||
int type = MemberResolver.descToType(c, -1); | |||||
String cname = null; | String cname = null; | ||||
if (type == CLASS) { | if (type == CLASS) { | ||||
if (dim == 0) | if (dim == 0) | ||||
return 8; | return 8; | ||||
} | } | ||||
protected void compileUnwrapValue(CtClass type, Bytecode code) | |||||
protected void compileUnwrapValue(CtClass type, Bytecode code, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (type == CtClass.voidType) { | if (type == CtClass.voidType) { | ||||
} | } | ||||
if (exprType == VOID) | if (exprType == VOID) | ||||
throw new CompileError("invalid type for " + returnCastName); | |||||
throw new CompileError("invalid type for " + returnCastName, lineNumber); | |||||
if (type instanceof CtPrimitiveType) { | if (type instanceof CtPrimitiveType) { | ||||
CtPrimitiveType pt = (CtPrimitiveType)type; | CtPrimitiveType pt = (CtPrimitiveType)type; | ||||
code.addCheckcast(wrapper); | code.addCheckcast(wrapper); | ||||
code.addInvokevirtual(wrapper, pt.getGetMethodName(), | code.addInvokevirtual(wrapper, pt.getGetMethodName(), | ||||
pt.getGetMethodDescriptor()); | pt.getGetMethodDescriptor()); | ||||
setType(type); | |||||
setType(type, lineNumber); | |||||
} | } | ||||
else { | else { | ||||
code.addCheckcast(type); | code.addCheckcast(type); | ||||
setType(type); | |||||
setType(type, lineNumber); | |||||
} | } | ||||
} | } | ||||
/* Sets exprType, arrayDim, and className; | /* Sets exprType, arrayDim, and className; | ||||
* If type is void, then this method does nothing. | * If type is void, then this method does nothing. | ||||
*/ | */ | ||||
public void setType(CtClass type) throws CompileError { | |||||
setType(type, 0); | |||||
public void setType(CtClass type, int lineNumber) throws CompileError { | |||||
setType(type, 0, lineNumber); | |||||
} | } | ||||
private void setType(CtClass type, int dim) throws CompileError { | |||||
private void setType(CtClass type, int dim, int lineNumber) throws CompileError { | |||||
if (type.isPrimitive()) { | if (type.isPrimitive()) { | ||||
CtPrimitiveType pt = (CtPrimitiveType)type; | CtPrimitiveType pt = (CtPrimitiveType)type; | ||||
exprType = MemberResolver.descToType(pt.getDescriptor()); | |||||
exprType = MemberResolver.descToType(pt.getDescriptor(), lineNumber); | |||||
arrayDim = dim; | arrayDim = dim; | ||||
className = null; | className = null; | ||||
} | } | ||||
else if (type.isArray()) | else if (type.isArray()) | ||||
try { | try { | ||||
setType(type.getComponentType(), dim + 1); | |||||
setType(type.getComponentType(), dim + 1, lineNumber); | |||||
} | } | ||||
catch (NotFoundException e) { | catch (NotFoundException e) { | ||||
throw new CompileError("undefined type: " + type.getName()); | |||||
throw new CompileError("undefined type: " + type.getName(), lineNumber); | |||||
} | } | ||||
else { | else { | ||||
exprType = CLASS; | exprType = CLASS; | ||||
if (type instanceof CtPrimitiveType) { | if (type instanceof CtPrimitiveType) { | ||||
CtPrimitiveType pt = (CtPrimitiveType)type; | CtPrimitiveType pt = (CtPrimitiveType)type; | ||||
atNumCastExpr(exprType, | atNumCastExpr(exprType, | ||||
MemberResolver.descToType(pt.getDescriptor())); | |||||
MemberResolver.descToType(pt.getDescriptor(), type.getLinesCount() - 1)); | |||||
} | } | ||||
else | else | ||||
throw new CompileError("type mismatch"); | |||||
throw new CompileError("type mismatch", type.getLinesCount() - 1); | |||||
} | } | ||||
} | } |
compileUnwrapValue(returnType, expr.getLineNumber()); | 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(), expr.getLineNumber()); | |||||
exprType = destType; | exprType = destType; | ||||
arrayDim = 0; | arrayDim = 0; | ||||
className = null; | className = null; | ||||
if (CodeGen.isRefType(exprType) || arrayDim > 0) | if (CodeGen.isRefType(exprType) || arrayDim > 0) | ||||
return; // Object type. do nothing. | return; // Object type. do nothing. | ||||
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); | |||||
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className, expr.getLineNumber()); | |||||
if (clazz instanceof CtPrimitiveType) { | if (clazz instanceof CtPrimitiveType) { | ||||
exprType = CLASS; | exprType = CLASS; | ||||
arrayDim = 0; | arrayDim = 0; | ||||
if (codeGen.procHandler != null | if (codeGen.procHandler != null | ||||
&& name.equals(codeGen.proceedName)) { | && name.equals(codeGen.proceedName)) { | ||||
codeGen.procHandler.setReturnType(this, | codeGen.procHandler.setReturnType(this, | ||||
(ASTList)expr.oprand2()); | |||||
(ASTList)expr.oprand2(), expr.getLineNumber()); | |||||
return; | return; | ||||
} | } | ||||
else if (name.equals(JvstCodeGen.cflowName)) { | else if (name.equals(JvstCodeGen.cflowName)) { | ||||
int n = params.length; | int n = params.length; | ||||
for (int k = 0; k < n; ++k) { | for (int k = 0; k < n; ++k) { | ||||
CtClass p = params[k]; | CtClass p = params[k]; | ||||
setType(p); | |||||
setType(p, a.getLineNumber()); | |||||
types[i] = exprType; | types[i] = exprType; | ||||
dims[i] = arrayDim; | dims[i] = arrayDim; | ||||
cnames[i] = className; | cnames[i] = className; | ||||
int nargs = getMethodArgsLength(args); | int nargs = getMethodArgsLength(args); | ||||
atMethodArgs(args, new int[nargs], new int[nargs], | atMethodArgs(args, new int[nargs], new int[nargs], | ||||
new String[nargs]); | new String[nargs]); | ||||
setReturnType(descriptor); | |||||
setReturnType(descriptor, target.getLineNumber()); | |||||
addNullIfVoid(); | addNullIfVoid(); | ||||
} | } | ||||
if (type == CtClass.voidType) | if (type == CtClass.voidType) | ||||
addNullIfVoid(); | addNullIfVoid(); | ||||
else | else | ||||
setType(type); | |||||
setType(type, lineNumber); | |||||
} | } | ||||
/* Sets exprType, arrayDim, and className; | /* Sets exprType, arrayDim, and className; | ||||
* If type is void, then this method does nothing. | * If type is void, then this method does nothing. | ||||
*/ | */ | ||||
public void setType(CtClass type) throws CompileError { | |||||
setType(type, 0); | |||||
public void setType(CtClass type, int lineNumber) throws CompileError { | |||||
setType(type, 0, lineNumber); | |||||
} | } | ||||
private void setType(CtClass type, int dim) throws CompileError { | |||||
private void setType(CtClass type, int dim, int lineNumber) throws CompileError { | |||||
if (type.isPrimitive()) { | if (type.isPrimitive()) { | ||||
CtPrimitiveType pt = (CtPrimitiveType)type; | CtPrimitiveType pt = (CtPrimitiveType)type; | ||||
exprType = MemberResolver.descToType(pt.getDescriptor()); | |||||
exprType = MemberResolver.descToType(pt.getDescriptor(), lineNumber); | |||||
arrayDim = dim; | arrayDim = dim; | ||||
className = null; | className = null; | ||||
} | } | ||||
else if (type.isArray()) | else if (type.isArray()) | ||||
try { | try { | ||||
setType(type.getComponentType(), dim + 1); | |||||
setType(type.getComponentType(), dim + 1, lineNumber); | |||||
} | } | ||||
catch (NotFoundException e) { | catch (NotFoundException e) { | ||||
throw new CompileError("undefined type: " + type.getName()); | |||||
throw new CompileError("undefined type: " + type.getName(), lineNumber); | |||||
} | } | ||||
else { | else { | ||||
exprType = CLASS; | exprType = CLASS; |
decl.setLocalVar(var); | decl.setLocalVar(var); | ||||
CtClass type = resolver.lookupClassByJvmName(decl.getClassName()); | |||||
CtClass type = resolver.lookupClassByJvmName(decl.getClassName(), st.getLineNumber()); | |||||
decl.setClassName(MemberResolver.javaToJvmName(type.getName())); | decl.setClassName(MemberResolver.javaToJvmName(type.getName())); | ||||
bc.addExceptionHandler(start, end, bc.currentPc(), type); | bc.addExceptionHandler(start, end, bc.currentPc(), type); | ||||
bc.growStack(1); | bc.growStack(1); | ||||
String elementClass; | String elementClass; | ||||
if (type == CLASS) { | if (type == CLASS) { | ||||
elementClass = resolveClassName(jvmClassname); | |||||
elementClass = resolveClassName(jvmClassname, lineNumber); | |||||
bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); | bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); | ||||
} | } | ||||
else { | else { | ||||
int op = e.getOperator(); | int op = e.getOperator(); | ||||
if (op == MEMBER) { // static method | if (op == MEMBER) { // static method | ||||
targetClass | targetClass | ||||
= resolver.lookupClass(((Symbol)e.oprand1()).get(), false); | |||||
= resolver.lookupClass(((Symbol)e.oprand1()).get(), false, expr.getLineNumber()); | |||||
isStatic = true; | isStatic = true; | ||||
} | } | ||||
else if (op == '.') { | else if (op == '.') { | ||||
} | } | ||||
if (arrayDim > 0) | if (arrayDim > 0) | ||||
targetClass = resolver.lookupClass(javaLangObject, true); | |||||
targetClass = resolver.lookupClass(javaLangObject, true, expr.getLineNumber()); | |||||
else if (exprType == CLASS /* && arrayDim == 0 */) | else if (exprType == CLASS /* && arrayDim == 0 */) | ||||
targetClass = resolver.lookupClassByJvmName(className); | |||||
targetClass = resolver.lookupClassByJvmName(className, expr.getLineNumber()); | |||||
else | else | ||||
badMethod(); | |||||
badMethod(e.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
badMethod(); | |||||
badMethod(expr.getLineNumber()); | |||||
} | } | ||||
else | else | ||||
fatal(expr.getLineNumber()); | fatal(expr.getLineNumber()); | ||||
aload0pos, cached); | aload0pos, cached); | ||||
} | } | ||||
private static void badMethod() throws CompileError { | |||||
throw new CompileError("bad method"); | |||||
private static void badMethod(int lineNumber) throws CompileError { | |||||
throw new CompileError("bad method", lineNumber); | |||||
} | } | ||||
/* | /* | ||||
if (mname.equals(MethodInfo.nameInit)) { | if (mname.equals(MethodInfo.nameInit)) { | ||||
isSpecial = true; | isSpecial = true; | ||||
if (declClass != targetClass) | if (declClass != targetClass) | ||||
throw new CompileError("no such constructor: " + targetClass.getName()); | |||||
throw new CompileError("no such constructor: " + targetClass.getName(), targetClass.getLinesCount() - 1); | |||||
if (declClass != thisClass && AccessFlag.isPrivate(acc)) { | if (declClass != thisClass && AccessFlag.isPrivate(acc)) { | ||||
if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_8 | if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_8 | ||||
} | } | ||||
else | else | ||||
if (isStatic) | if (isStatic) | ||||
throw new CompileError(mname + " is not static"); | |||||
throw new CompileError(mname + " is not static", targetClass.getLinesCount() - 1); | |||||
else | else | ||||
bytecode.addInvokevirtual(declClass, mname, desc); | bytecode.addInvokevirtual(declClass, mname, desc); | ||||
} | } | ||||
setReturnType(desc, isStatic, popTarget); | |||||
setReturnType(desc, isStatic, popTarget, targetClass.getLinesCount() - 1); | |||||
} | } | ||||
/* | /* | ||||
} | } | ||||
throw new CompileError("Method " + methodName | throw new CompileError("Method " + methodName | ||||
+ " is private"); | |||||
+ " is private", declClass.getLinesCount() - 1); | |||||
} | } | ||||
/* | /* | ||||
} | } | ||||
throw new CompileError("the called constructor is private in " | throw new CompileError("the called constructor is private in " | ||||
+ declClass.getName()); | |||||
+ declClass.getName(), declClass.getLinesCount() - 1); | |||||
} | } | ||||
private boolean isEnclosing(CtClass outer, CtClass inner) { | private boolean isEnclosing(CtClass outer, CtClass inner) { | ||||
} | } | ||||
} | } | ||||
void setReturnType(String desc, boolean isStatic, boolean popTarget) | |||||
void setReturnType(String desc, boolean isStatic, boolean popTarget, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
int i = desc.indexOf(')'); | int i = desc.indexOf(')'); | ||||
if (i < 0) | if (i < 0) | ||||
badMethod(); | |||||
badMethod(lineNumber); | |||||
char c = desc.charAt(++i); | char c = desc.charAt(++i); | ||||
int dim = 0; | int dim = 0; | ||||
if (c == 'L') { | if (c == 'L') { | ||||
int j = desc.indexOf(';', i + 1); | int j = desc.indexOf(';', i + 1); | ||||
if (j < 0) | if (j < 0) | ||||
badMethod(); | |||||
badMethod(lineNumber); | |||||
exprType = CLASS; | exprType = CLASS; | ||||
className = desc.substring(i + 1, j); | className = desc.substring(i + 1, j); | ||||
} | } | ||||
else { | else { | ||||
exprType = MemberResolver.descToType(c); | |||||
exprType = MemberResolver.descToType(c, lineNumber); | |||||
className = null; | className = null; | ||||
} | } | ||||
int fi; | int fi; | ||||
if (op == '=') { | if (op == '=') { | ||||
FieldInfo finfo = f.getFieldInfo2(); | FieldInfo finfo = f.getFieldInfo2(); | ||||
setFieldType(finfo); | |||||
setFieldType(finfo, expr.getLineNumber()); | |||||
AccessorMaker maker = isAccessibleField(f, finfo, expr.getLineNumber()); | AccessorMaker maker = isAccessibleField(f, finfo, expr.getLineNumber()); | ||||
if (maker == null) | if (maker == null) | ||||
fi = addFieldrefInfo(f, finfo); | fi = addFieldrefInfo(f, finfo); | ||||
atFieldRead(f, is_static,expr.getLineNumber() ); | atFieldRead(f, is_static,expr.getLineNumber() ); | ||||
else { | else { | ||||
cexpr.accept(this); | cexpr.accept(this); | ||||
setFieldType(f.getFieldInfo2()); | |||||
setFieldType(f.getFieldInfo2(), expr.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
*/ | */ | ||||
private int atFieldRead(CtField f, boolean isStatic, int lineNumber) 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, lineNumber); | |||||
AccessorMaker maker = isAccessibleField(f, finfo, lineNumber); | AccessorMaker maker = isAccessibleField(f, finfo, lineNumber); | ||||
if (maker != null) { | if (maker != null) { | ||||
MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); | MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); | ||||
* | * | ||||
* @return true if the field type is long or double. | * @return true if the field type is long or double. | ||||
*/ | */ | ||||
private boolean setFieldType(FieldInfo finfo) throws CompileError { | |||||
private boolean setFieldType(FieldInfo finfo, int lineNumber) throws CompileError { | |||||
String type = finfo.getDescriptor(); | String type = finfo.getDescriptor(); | ||||
int i = 0; | int i = 0; | ||||
} | } | ||||
arrayDim = dim; | arrayDim = dim; | ||||
exprType = MemberResolver.descToType(c); | |||||
exprType = MemberResolver.descToType(c, lineNumber); | |||||
if (c == 'L') | if (c == 'L') | ||||
className = type.substring(i + 1, type.indexOf(';', i + 1)); | className = type.substring(i + 1, type.indexOf(';', i + 1)); | ||||
* For example, this converts Object into java/lang/Object. | * For example, this converts Object into java/lang/Object. | ||||
*/ | */ | ||||
@Override | @Override | ||||
protected String resolveClassName(String jvmName) throws CompileError { | |||||
return resolver.resolveJvmClassName(jvmName); | |||||
protected String resolveClassName(String jvmName, int lineNumber) throws CompileError { | |||||
return resolver.resolveJvmClassName(jvmName, lineNumber); | |||||
} | } | ||||
} | } |
public ClassPool getClassPool() { return classPool; } | public ClassPool getClassPool() { return classPool; } | ||||
private static void fatal() throws CompileError { | |||||
throw new CompileError("fatal"); | |||||
private static void fatal(int lineNumber) throws CompileError { | |||||
throw new CompileError("fatal", lineNumber); | |||||
} | } | ||||
public static class Method { | public static class Method { | ||||
if (current != null && clazz == currentClass) | if (current != null && clazz == currentClass) | ||||
if (current.getName().equals(methodName)) { | if (current.getName().equals(methodName)) { | ||||
int res = compareSignature(current.getDescriptor(), | int res = compareSignature(current.getDescriptor(), | ||||
argTypes, argDims, argClassNames); | |||||
argTypes, argDims, argClassNames, currentClass.getLinesCount() - 1); | |||||
if (res != NO) { | if (res != NO) { | ||||
Method r = new Method(clazz, current, res); | Method r = new Method(clazz, current, res); | ||||
if (res == YES) | if (res == YES) | ||||
if (minfo.getName().equals(methodName) | if (minfo.getName().equals(methodName) | ||||
&& (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0) { | && (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0) { | ||||
int res = compareSignature(minfo.getDescriptor(), | int res = compareSignature(minfo.getDescriptor(), | ||||
argTypes, argDims, argClassNames); | |||||
argTypes, argDims, argClassNames, clazz.getLinesCount() - 1); | |||||
if (res != NO) { | if (res != NO) { | ||||
Method r = new Method(clazz, minfo, res); | Method r = new Method(clazz, minfo, res); | ||||
if (res == YES) | if (res == YES) | ||||
* of parameter types that do not exactly match. | * of parameter types that do not exactly match. | ||||
*/ | */ | ||||
private int compareSignature(String desc, int[] argTypes, | private int compareSignature(String desc, int[] argTypes, | ||||
int[] argDims, String[] argClassNames) | |||||
int[] argDims, String[] argClassNames, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
int result = YES; | int result = YES; | ||||
String cname = desc.substring(i, j); | String cname = desc.substring(i, j); | ||||
if (!cname.equals(argClassNames[n])) { | if (!cname.equals(argClassNames[n])) { | ||||
CtClass clazz = lookupClassByJvmName(argClassNames[n]); | |||||
CtClass clazz = lookupClassByJvmName(argClassNames[n], lineNumber); | |||||
try { | try { | ||||
if (clazz.subtypeOf(lookupClassByJvmName(cname))) | |||||
if (clazz.subtypeOf(lookupClassByJvmName(cname, lineNumber))) | |||||
result++; | result++; | ||||
else | else | ||||
return NO; | return NO; | ||||
i = j + 1; | i = j + 1; | ||||
} | } | ||||
else { | else { | ||||
int t = descToType(c); | |||||
int t = descToType(c, lineNumber); | |||||
int at = argTypes[n]; | int at = argTypes[n]; | ||||
if (t != at) | if (t != at) | ||||
if (t == INT | if (t == INT | ||||
* Only used by fieldAccess() in MemberCodeGen and TypeChecker. | * Only used by fieldAccess() in MemberCodeGen and TypeChecker. | ||||
* | * | ||||
* @param jvmClassName a JVM class name. e.g. java/lang/String | * @param jvmClassName a JVM class name. e.g. java/lang/String | ||||
* @see #lookupClass(String, boolean) | |||||
* @see #lookupClass(String, boolean, int) | |||||
*/ | */ | ||||
public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, | public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, | ||||
ASTree expr) throws NoFieldException | ASTree expr) throws NoFieldException | ||||
String field = fieldSym.get(); | String field = fieldSym.get(); | ||||
CtClass cc = null; | CtClass cc = null; | ||||
try { | try { | ||||
cc = lookupClass(jvmToJavaName(jvmClassName), true); | |||||
cc = lookupClass(jvmToJavaName(jvmClassName), true, expr.getLineNumber()); | |||||
} | } | ||||
catch (CompileError e) { | catch (CompileError e) { | ||||
// EXPR might be part of a qualified class name. | // EXPR might be part of a qualified class name. | ||||
public CtField lookupField(String className, Symbol fieldName) | public CtField lookupField(String className, Symbol fieldName) | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
CtClass cc = lookupClass(className, false); | |||||
CtClass cc = lookupClass(className, false, fieldName.getLineNumber()); | |||||
try { | try { | ||||
return cc.getField(fieldName.get()); | return cc.getField(fieldName.get()); | ||||
} | } | ||||
catch (NotFoundException e) {} | catch (NotFoundException e) {} | ||||
throw new CompileError("no such field: " + fieldName.get()); | |||||
throw new CompileError("no such field: " + fieldName.get(), fieldName.getLineNumber()); | |||||
} | } | ||||
public CtClass lookupClassByName(ASTList name) throws CompileError { | public CtClass lookupClassByName(ASTList name) throws CompileError { | ||||
return lookupClass(Declarator.astToClassName(name, '.'), false); | |||||
return lookupClass(Declarator.astToClassName(name, '.'), false, name.getLineNumber()); | |||||
} | } | ||||
public CtClass lookupClassByJvmName(String jvmName) throws CompileError { | |||||
return lookupClass(jvmToJavaName(jvmName), false); | |||||
public CtClass lookupClassByJvmName(String jvmName, int lineNumber) throws CompileError { | |||||
return lookupClass(jvmToJavaName(jvmName), false, lineNumber); | |||||
} | } | ||||
public CtClass lookupClass(Declarator decl) throws CompileError { | public CtClass lookupClass(Declarator decl) throws CompileError { | ||||
return lookupClass(decl.getType(), decl.getArrayDim(), | return lookupClass(decl.getType(), decl.getArrayDim(), | ||||
decl.getClassName()); | |||||
decl.getClassName(), decl.getLineNumber()); | |||||
} | } | ||||
/** | /** | ||||
* @param classname jvm class name. | |||||
* @param classname jvm class name. | |||||
* @param lineNumber | |||||
*/ | */ | ||||
public CtClass lookupClass(int type, int dim, String classname) | |||||
public CtClass lookupClass(int type, int dim, String classname, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
String cname = ""; | String cname = ""; | ||||
CtClass clazz; | CtClass clazz; | ||||
if (type == CLASS) { | if (type == CLASS) { | ||||
clazz = lookupClassByJvmName(classname); | |||||
clazz = lookupClassByJvmName(classname, lineNumber); | |||||
if (dim > 0) | if (dim > 0) | ||||
cname = clazz.getName(); | cname = clazz.getName(); | ||||
else | else | ||||
return clazz; | return clazz; | ||||
} | } | ||||
else | else | ||||
cname = getTypeName(type); | |||||
cname = getTypeName(type, lineNumber); | |||||
while (dim-- > 0) | while (dim-- > 0) | ||||
cname += "[]"; | cname += "[]"; | ||||
return lookupClass(cname, false); | |||||
return lookupClass(cname, false, lineNumber); | |||||
} | } | ||||
/* | /* | ||||
* type cannot be CLASS | * type cannot be CLASS | ||||
*/ | */ | ||||
static String getTypeName(int type) throws CompileError { | |||||
static String getTypeName(int type, int lineNumber) throws CompileError { | |||||
String cname = ""; | String cname = ""; | ||||
switch (type) { | switch (type) { | ||||
case BOOLEAN : | case BOOLEAN : | ||||
cname = "void"; | cname = "void"; | ||||
break; | break; | ||||
default : | default : | ||||
fatal(); | |||||
fatal(lineNumber); | |||||
} | } | ||||
return cname; | return cname; | ||||
} | } | ||||
/** | /** | ||||
* @param name a qualified class name. e.g. java.lang.String | |||||
* @param name a qualified class name. e.g. java.lang.String | |||||
* @param lineNumber | |||||
*/ | */ | ||||
public CtClass lookupClass(String name, boolean notCheckInner) | |||||
public CtClass lookupClass(String name, boolean notCheckInner, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
Map<String,String> cache = getInvalidNames(); | Map<String,String> cache = getInvalidNames(); | ||||
String found = cache.get(name); | String found = cache.get(name); | ||||
if (found == INVALID) | if (found == INVALID) | ||||
throw new CompileError("no such class: " + name); | |||||
throw new CompileError("no such class: " + name, lineNumber); | |||||
else if (found != null) | else if (found != null) | ||||
try { | try { | ||||
return classPool.get(found); | return classPool.get(found); | ||||
cc = lookupClass0(name, notCheckInner); | cc = lookupClass0(name, notCheckInner); | ||||
} | } | ||||
catch (NotFoundException e) { | catch (NotFoundException e) { | ||||
cc = searchImports(name); | |||||
cc = searchImports(name, lineNumber); | |||||
} | } | ||||
cache.put(name, cc.getName()); | cache.put(name, cc.getName()); | ||||
return ht; | return ht; | ||||
} | } | ||||
private CtClass searchImports(String orgName) | |||||
private CtClass searchImports(String orgName, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (orgName.indexOf('.') < 0) { | if (orgName.indexOf('.') < 0) { | ||||
} | } | ||||
getInvalidNames().put(orgName, INVALID); | getInvalidNames().put(orgName, INVALID); | ||||
throw new CompileError("no such class: " + orgName); | |||||
throw new CompileError("no such class: " + orgName, lineNumber); | |||||
} | } | ||||
private CtClass lookupClass0(String classname, boolean notCheckInner) | private CtClass lookupClass0(String classname, boolean notCheckInner) | ||||
/* Expands a simple class name to java.lang.*. | /* Expands a simple class name to java.lang.*. | ||||
* For example, this converts Object into java/lang/Object. | * For example, this converts Object into java/lang/Object. | ||||
*/ | */ | ||||
public String resolveJvmClassName(String jvmName) throws CompileError { | |||||
public String resolveJvmClassName(String jvmName, int lineNumber) throws CompileError { | |||||
if (jvmName == null) | if (jvmName == null) | ||||
return null; | return null; | ||||
return javaToJvmName(lookupClassByJvmName(jvmName).getName()); | |||||
return javaToJvmName(lookupClassByJvmName(jvmName, lineNumber).getName()); | |||||
} | } | ||||
public static CtClass getSuperclass(CtClass c) throws CompileError { | public static CtClass getSuperclass(CtClass c) throws CompileError { | ||||
} | } | ||||
catch (NotFoundException e) {} | catch (NotFoundException e) {} | ||||
throw new CompileError("cannot find the super class of " | throw new CompileError("cannot find the super class of " | ||||
+ c.getName()); | |||||
+ c.getName(), c.getLinesCount() - 1); | |||||
} | } | ||||
public static CtClass getSuperInterface(CtClass c, String interfaceName) | public static CtClass getSuperInterface(CtClass c, String interfaceName) | ||||
return intfs[i]; | return intfs[i]; | ||||
} catch (NotFoundException e) {} | } catch (NotFoundException e) {} | ||||
throw new CompileError("cannot find the super interface " + interfaceName | throw new CompileError("cannot find the super interface " + interfaceName | ||||
+ " of " + c.getName()); | |||||
+ " of " + c.getName(), c.getLinesCount() - 1); | |||||
} | } | ||||
public static String javaToJvmName(String classname) { | public static String javaToJvmName(String classname) { | ||||
return classname.replace('/', '.'); | return classname.replace('/', '.'); | ||||
} | } | ||||
public static int descToType(char c) throws CompileError { | |||||
public static int descToType(char c, int lineNumber) throws CompileError { | |||||
switch (c) { | switch (c) { | ||||
case 'Z' : | case 'Z' : | ||||
return BOOLEAN; | return BOOLEAN; | ||||
case '[' : | case '[' : | ||||
return CLASS; | return CLASS; | ||||
default : | default : | ||||
fatal(); | |||||
fatal(lineNumber); | |||||
return VOID; // never reach here | return VOID; // never reach here | ||||
} | } | ||||
} | } |
* @see javassist.compiler.JvstCodeGen#setProceedHandler(ProceedHandler, String) | * @see javassist.compiler.JvstCodeGen#setProceedHandler(ProceedHandler, String) | ||||
*/ | */ | ||||
public interface ProceedHandler { | public interface ProceedHandler { | ||||
void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError; | |||||
void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError; | |||||
void doit(JvstCodeGen gen, Bytecode b, ASTList args, int lineNumber) throws CompileError; | |||||
void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) throws CompileError; | |||||
} | } |
* into a String object. | * into a String object. | ||||
*/ | */ | ||||
protected static String argTypesToString(int[] types, int[] dims, | protected static String argTypesToString(int[] types, int[] dims, | ||||
String[] cnames) { | |||||
String[] cnames, int lineNumber) { | |||||
StringBuilder sbuf = new StringBuilder(); | StringBuilder sbuf = new StringBuilder(); | ||||
sbuf.append('('); | sbuf.append('('); | ||||
int n = types.length; | int n = types.length; | ||||
if (n > 0) { | if (n > 0) { | ||||
int i = 0; | int i = 0; | ||||
while (true) { | while (true) { | ||||
typeToString(sbuf, types[i], dims[i], cnames[i]); | |||||
typeToString(sbuf, types[i], dims[i], cnames[i], lineNumber); | |||||
if (++i < n) | if (++i < n) | ||||
sbuf.append(','); | sbuf.append(','); | ||||
else | else | ||||
* into a String object. | * into a String object. | ||||
*/ | */ | ||||
protected static StringBuilder typeToString(StringBuilder sbuf, | protected static StringBuilder typeToString(StringBuilder sbuf, | ||||
int type, int dim, String cname) { | |||||
int type, int dim, String cname, int lineNumber) { | |||||
String s; | String s; | ||||
if (type == CLASS) | if (type == CLASS) | ||||
s = MemberResolver.jvmToJavaName(cname); | s = MemberResolver.jvmToJavaName(cname); | ||||
s = "Object"; | s = "Object"; | ||||
else | else | ||||
try { | try { | ||||
s = MemberResolver.getTypeName(type); | |||||
s = MemberResolver.getTypeName(type, lineNumber); | |||||
} | } | ||||
catch (CompileError e) { | catch (CompileError e) { | ||||
s = "?"; | s = "?"; | ||||
/* Expands a simple class name to java.lang.*. | /* Expands a simple class name to java.lang.*. | ||||
* For example, this converts Object into java/lang/Object. | * For example, this converts Object into java/lang/Object. | ||||
*/ | */ | ||||
protected String resolveClassName(String jvmName) throws CompileError { | |||||
return resolver.resolveJvmClassName(jvmName); | |||||
protected String resolveClassName(String jvmName, int lineNumber) throws CompileError { | |||||
return resolver.resolveJvmClassName(jvmName, lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
CtClass clazz = resolver.lookupClassByName(expr.getClassName()); | CtClass clazz = resolver.lookupClassByName(expr.getClassName()); | ||||
String cname = clazz.getName(); | String cname = clazz.getName(); | ||||
ASTList args = expr.getArguments(); | ASTList args = expr.getArguments(); | ||||
atMethodCallCore(clazz, MethodInfo.nameInit, args); | |||||
atMethodCallCore(clazz, MethodInfo.nameInit, args, expr.getLineNumber()); | |||||
exprType = CLASS; | exprType = CLASS; | ||||
arrayDim = 0; | arrayDim = 0; | ||||
className = MemberResolver.javaToJvmName(cname); | className = MemberResolver.javaToJvmName(cname); | ||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
CtField f = fieldAccess(left); | CtField f = fieldAccess(left); | ||||
atFieldRead(f); | |||||
atFieldRead(f, expr.getLineNumber()); | |||||
int fType = exprType; | int fType = exprType; | ||||
int fDim = arrayDim; | int fDim = arrayDim; | ||||
String cname = className; | String cname = className; | ||||
if (op == MEMBER) // static method | if (op == MEMBER) // static method | ||||
targetClass | targetClass | ||||
= resolver.lookupClass(((Symbol)e.oprand1()).get(), | = resolver.lookupClass(((Symbol)e.oprand1()).get(), | ||||
false); | |||||
false, e.getLineNumber()); | |||||
else if (op == '.') { | else if (op == '.') { | ||||
ASTree target = e.oprand1(); | ASTree target = e.oprand1(); | ||||
String classFollowedByDotSuper = isDotSuper(target); | String classFollowedByDotSuper = isDotSuper(target); | ||||
} | } | ||||
if (arrayDim > 0) | if (arrayDim > 0) | ||||
targetClass = resolver.lookupClass(javaLangObject, true); | |||||
targetClass = resolver.lookupClass(javaLangObject, true, e.getLineNumber()); | |||||
else if (exprType == CLASS /* && arrayDim == 0 */) | else if (exprType == CLASS /* && arrayDim == 0 */) | ||||
targetClass = resolver.lookupClassByJvmName(className); | |||||
targetClass = resolver.lookupClassByJvmName(className, e.getLineNumber()); | |||||
else | else | ||||
badMethod(); | |||||
badMethod(e.getLineNumber()); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
badMethod(); | |||||
badMethod(expr.getLineNumber()); | |||||
} | } | ||||
else | else | ||||
fatal(expr.getLineNumber()); | fatal(expr.getLineNumber()); | ||||
MemberResolver.Method minfo | MemberResolver.Method minfo | ||||
= atMethodCallCore(targetClass, mname, args); | |||||
= atMethodCallCore(targetClass, mname, args, expr.getLineNumber()); | |||||
expr.setMethod(minfo); | expr.setMethod(minfo); | ||||
} | } | ||||
private static void badMethod() throws CompileError { | |||||
throw new CompileError("bad method"); | |||||
private static void badMethod(int lineNumber) throws CompileError { | |||||
throw new CompileError("bad method", lineNumber); | |||||
} | } | ||||
/** | /** | ||||
* and the MethodInfo of that method. Never null. | * and the MethodInfo of that method. Never null. | ||||
*/ | */ | ||||
public MemberResolver.Method atMethodCallCore(CtClass targetClass, | public MemberResolver.Method atMethodCallCore(CtClass targetClass, | ||||
String mname, ASTList args) | |||||
String mname, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
int nargs = getMethodArgsLength(args); | int nargs = getMethodArgsLength(args); | ||||
mname, types, dims, cnames); | mname, types, dims, cnames); | ||||
if (found == null) { | if (found == null) { | ||||
String clazz = targetClass.getName(); | String clazz = targetClass.getName(); | ||||
String signature = argTypesToString(types, dims, cnames); | |||||
String signature = argTypesToString(types, dims, cnames, lineNumber); | |||||
String msg; | String msg; | ||||
if (mname.equals(MethodInfo.nameInit)) | if (mname.equals(MethodInfo.nameInit)) | ||||
msg = "cannot find constructor " + clazz + signature; | msg = "cannot find constructor " + clazz + signature; | ||||
else | else | ||||
msg = mname + signature + " not found in " + clazz; | msg = mname + signature + " not found in " + clazz; | ||||
throw new CompileError(msg); | |||||
throw new CompileError(msg, lineNumber); | |||||
} | } | ||||
String desc = found.info.getDescriptor(); | String desc = found.info.getDescriptor(); | ||||
setReturnType(desc); | |||||
setReturnType(desc, lineNumber); | |||||
return found; | return found; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void setReturnType(String desc) throws CompileError { | |||||
void setReturnType(String desc, int lineNumber) throws CompileError { | |||||
int i = desc.indexOf(')'); | int i = desc.indexOf(')'); | ||||
if (i < 0) | if (i < 0) | ||||
badMethod(); | |||||
badMethod(lineNumber); | |||||
char c = desc.charAt(++i); | char c = desc.charAt(++i); | ||||
int dim = 0; | int dim = 0; | ||||
if (c == 'L') { | if (c == 'L') { | ||||
int j = desc.indexOf(';', i + 1); | int j = desc.indexOf(';', i + 1); | ||||
if (j < 0) | if (j < 0) | ||||
badMethod(); | |||||
badMethod(lineNumber); | |||||
exprType = CLASS; | exprType = CLASS; | ||||
className = desc.substring(i + 1, j); | className = desc.substring(i + 1, j); | ||||
} | } | ||||
else { | else { | ||||
exprType = MemberResolver.descToType(c); | |||||
exprType = MemberResolver.descToType(c, lineNumber); | |||||
className = null; | className = null; | ||||
} | } | ||||
} | } | ||||
private void atFieldRead(ASTree expr) throws CompileError { | private void atFieldRead(ASTree expr) throws CompileError { | ||||
atFieldRead(fieldAccess(expr)); | |||||
atFieldRead(fieldAccess(expr), expr.getLineNumber()); | |||||
} | } | ||||
private void atFieldRead(CtField f) throws CompileError { | |||||
private void atFieldRead(CtField f, int lineNumber) throws CompileError { | |||||
FieldInfo finfo = f.getFieldInfo2(); | FieldInfo finfo = f.getFieldInfo2(); | ||||
String type = finfo.getDescriptor(); | String type = finfo.getDescriptor(); | ||||
} | } | ||||
arrayDim = dim; | arrayDim = dim; | ||||
exprType = MemberResolver.descToType(c); | |||||
exprType = MemberResolver.descToType(c, lineNumber); | |||||
if (c == 'L') | if (c == 'L') | ||||
className = type.substring(i + 1, type.indexOf(';', i + 1)); | className = type.substring(i + 1, type.indexOf(';', i + 1)); | ||||
protected void atFieldPlusPlus(ASTree oprand) throws CompileError | protected void atFieldPlusPlus(ASTree oprand) throws CompileError | ||||
{ | { | ||||
CtField f = fieldAccess(oprand); | CtField f = fieldAccess(oprand); | ||||
atFieldRead(f); | |||||
atFieldRead(f, oprand.getLineNumber()); | |||||
int t = exprType; | int t = exprType; | ||||
if (t == INT || t == BYTE || t == CHAR || t == SHORT) | if (t == INT || t == BYTE || t == CHAR || t == SHORT) | ||||
exprType = INT; | exprType = INT; |
} | } | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (gen.getMethodArgsLength(args) != 1) | if (gen.getMethodArgsLength(args) != 1) | ||||
throw new CompileError(Javac.proceedName | throw new CompileError(Javac.proceedName | ||||
+ "() cannot take more than one parameter " | + "() cannot take more than one parameter " | ||||
+ "for cast"); | |||||
+ "for cast", lineNumber); | |||||
gen.atMethodArgs(args, new int[1], new int[1], new String[1]); | gen.atMethodArgs(args, new int[1], new int[1], new String[1]); | ||||
bytecode.addOpcode(Opcode.CHECKCAST); | bytecode.addOpcode(Opcode.CHECKCAST); | ||||
bytecode.addIndex(index); | bytecode.addIndex(index); | ||||
gen.setType(retType); | |||||
gen.setType(retType, lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.atMethodArgs(args, new int[1], new int[1], new String[1]); | c.atMethodArgs(args, new int[1], new int[1], new String[1]); | ||||
c.setType(retType); | |||||
c.setType(retType, lineNumber); | |||||
} | } | ||||
} | } | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (args != null && !gen.isParamListName(args)) | if (args != null && !gen.isParamListName(args)) | ||||
throw new CompileError(Javac.proceedName | throw new CompileError(Javac.proceedName | ||||
+ "() cannot take a parameter for field reading"); | |||||
+ "() cannot take a parameter for field reading", lineNumber); | |||||
int stack; | int stack; | ||||
if (isStatic(opcode)) | if (isStatic(opcode)) | ||||
bytecode.add(opcode); | bytecode.add(opcode); | ||||
bytecode.addIndex(index); | bytecode.addIndex(index); | ||||
bytecode.growStack(stack); | bytecode.growStack(stack); | ||||
gen.setType(fieldType); | |||||
gen.setType(fieldType, lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.setType(fieldType); | |||||
c.setType(fieldType, lineNumber); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (gen.getMethodArgsLength(args) != 1) | if (gen.getMethodArgsLength(args) != 1) | ||||
throw new CompileError(Javac.proceedName | throw new CompileError(Javac.proceedName | ||||
+ "() cannot take more than one parameter " | + "() cannot take more than one parameter " | ||||
+ "for field writing"); | |||||
+ "for field writing", lineNumber); | |||||
int stack; | int stack; | ||||
if (isStatic(opcode)) | if (isStatic(opcode)) | ||||
bytecode.add(opcode); | bytecode.add(opcode); | ||||
bytecode.addIndex(index); | bytecode.addIndex(index); | ||||
bytecode.growStack(stack); | bytecode.growStack(stack); | ||||
gen.setType(CtClass.voidType); | |||||
gen.setType(CtClass.voidType, lineNumber); | |||||
gen.addNullIfVoid(); | gen.addNullIfVoid(); | ||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.atMethodArgs(args, new int[1], new int[1], new String[1]); | c.atMethodArgs(args, new int[1], new int[1], new String[1]); | ||||
c.setType(CtClass.voidType); | |||||
c.setType(CtClass.voidType, lineNumber); | |||||
c.addNullIfVoid(); | c.addNullIfVoid(); | ||||
} | } | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
if (gen.getMethodArgsLength(args) != 1) | if (gen.getMethodArgsLength(args) != 1) | ||||
throw new CompileError(Javac.proceedName | throw new CompileError(Javac.proceedName | ||||
+ "() cannot take more than one parameter " | + "() cannot take more than one parameter " | ||||
+ "for instanceof"); | |||||
+ "for instanceof", lineNumber); | |||||
gen.atMethodArgs(args, new int[1], new int[1], new String[1]); | gen.atMethodArgs(args, new int[1], new int[1], new String[1]); | ||||
bytecode.addOpcode(Opcode.INSTANCEOF); | bytecode.addOpcode(Opcode.INSTANCEOF); | ||||
bytecode.addIndex(index); | bytecode.addIndex(index); | ||||
gen.setType(CtClass.booleanType); | |||||
gen.setType(CtClass.booleanType, lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.atMethodArgs(args, new int[1], new int[1], new String[1]); | c.atMethodArgs(args, new int[1], new int[1], new String[1]); | ||||
c.setType(CtClass.booleanType); | |||||
c.setType(CtClass.booleanType, lineNumber); | |||||
} | } | ||||
} | } | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
int num = gen.getMethodArgsLength(args); | int num = gen.getMethodArgsLength(args); | ||||
if (num != dimension) | if (num != dimension) | ||||
throw new CompileError(Javac.proceedName | throw new CompileError(Javac.proceedName | ||||
+ "() with a wrong number of parameters"); | |||||
+ "() with a wrong number of parameters", lineNumber); | |||||
gen.atMethodArgs(args, new int[num], | gen.atMethodArgs(args, new int[num], | ||||
new int[num], new String[num]); | new int[num], new String[num]); | ||||
bytecode.growStack(1 - dimension); | bytecode.growStack(1 - dimension); | ||||
} | } | ||||
gen.setType(arrayType); | |||||
gen.setType(arrayType, lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.setType(arrayType); | |||||
c.setType(arrayType, lineNumber); | |||||
} | } | ||||
} | } | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) | |||||
public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
bytecode.addOpcode(NEW); | bytecode.addOpcode(NEW); | ||||
bytecode.addOpcode(DUP); | bytecode.addOpcode(DUP); | ||||
gen.atMethodCallCore(newType, MethodInfo.nameInit, args, | gen.atMethodCallCore(newType, MethodInfo.nameInit, args, | ||||
false, true, -1, null); | false, true, -1, null); | ||||
gen.setType(newType); | |||||
gen.setType(newType, lineNumber); | |||||
} | } | ||||
@Override | @Override | ||||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||||
public void setReturnType(JvstTypeChecker c, ASTList args, int lineNumber) | |||||
throws CompileError | throws CompileError | ||||
{ | { | ||||
c.atMethodCallCore(newType, MethodInfo.nameInit, args); | |||||
c.setType(newType); | |||||
c.atMethodCallCore(newType, MethodInfo.nameInit, args, lineNumber); | |||||
c.setType(newType, lineNumber); | |||||
} | } | ||||
} | } | ||||
} | } |
"}"), "line 3: syntax error near \" return\n}\""); | "}"), "line 3: syntax error near \" return\n}\""); | ||||
} | } | ||||
public void testUndevField() { | |||||
doTestCompile(String.join("\n", | |||||
"public void run() {", | |||||
" foo = 5;", | |||||
"}"), "line 2: no such field: foo"); | |||||
} | |||||
public void testUndevMethod() { | |||||
doTestCompile(String.join("\n", | |||||
"public void run() {", | |||||
" foo();", | |||||
"}"), "line 2: foo() not found in javassist.LineNumberCompileTest2"); | |||||
} | |||||
public void testException() { | public void testException() { | ||||
doTestRuntime(String.join("\n", | doTestRuntime(String.join("\n", | ||||
"public void run() {", | "public void run() {", |
public void testArgTypesToString() { | public void testArgTypesToString() { | ||||
String s; | String s; | ||||
s = TypeChecker.argTypesToString(new int[0], new int[0], new String[0]); | |||||
s = TypeChecker.argTypesToString(new int[0], new int[0], new String[0], 0); | |||||
assertEquals("()", s); | assertEquals("()", s); | ||||
s = TypeChecker.argTypesToString(new int[] { TokenId.INT, TokenId.CHAR, TokenId.CLASS }, | s = TypeChecker.argTypesToString(new int[] { TokenId.INT, TokenId.CHAR, TokenId.CLASS }, | ||||
new int[] { 0, 1, 0 }, | new int[] { 0, 1, 0 }, | ||||
new String[] { null, null, "String" }); | |||||
new String[] { null, null, "String" }, 0); | |||||
assertEquals("(int,char[],String)", s); | assertEquals("(int,char[],String)", s); | ||||
} | } | ||||