/**
* The version number of this release.
*/
- public static final String version = "3.0 alpha 1";
+ public static final String version = "3.0 RC1";
/**
* Prints the version number and the copyright notice.
return (accflags & ~(PROTECTED | PUBLIC | PRIVATE));
}
+ /**
+ * Returns true if the access flags include the public bit.
+ */
+ public static boolean isPublic(int accflags) {
+ return (accflags & PUBLIC) != 0;
+ }
+
+ /**
+ * Returns true if the access flags include the protected bit.
+ */
+ public static boolean isProtected(int accflags) {
+ return (accflags & PROTECTED) != 0;
+ }
+
+ /**
+ * Returns true if the access flags include the private bit.
+ */
+ public static boolean isPrivate(int accflags) {
+ return (accflags & PRIVATE) != 0;
+ }
+
+ /**
+ * Returns true if the access flags include neither public, protected,
+ * or private.
+ */
+ public static boolean isPackage(int accflags) {
+ return (accflags & (PROTECTED | PUBLIC | PRIVATE)) == 0;
+ }
+
/**
* Clears a specified bit in <code>accflags</code>.
*/
* @see javassist.CtClass#getClassFile()
*/
public final class ClassFile {
+ int major, minor; // version number
ConstPool constPool;
int thisClass;
int accessFlags;
*/
public ClassFile(boolean isInterface,
String classname, String superclass) {
+ major = 45;
+ minor = 3; // JDK 1.1 or later
constPool = new ConstPool(classname);
thisClass = constPool.getThisClassInfo();
if (isInterface)
if (magic != 0xCAFEBABE)
throw new IOException("non class file");
- int major = in.readUnsignedShort();
- int minor = in.readUnsignedShort();
+ minor = in.readUnsignedShort();
+ major = in.readUnsignedShort();
constPool = new ConstPool(in);
accessFlags = in.readUnsignedShort();
thisClass = in.readUnsignedShort();
int i, n;
out.writeInt(0xCAFEBABE); // magic
- out.writeShort(3); // major version
- out.writeShort(45); // minor version
+ out.writeShort(minor); // minor version
+ out.writeShort(major); // major version
constPool.write(out); // constant pool
out.writeShort(accessFlags);
out.writeShort(thisClass);
int mod
= AccessFlag.toModifier(cf.getAccessFlags()
& ~AccessFlag.SYNCHRONIZED);
+ out.println("major: " + cf.major + ", minor: " + cf.minor);
out.println(Modifier.toString(mod) + " class "
+ cf.getName() + " extends " + cf.getSuperclass());
return accName; // already exists.
ClassFile cf = clazz.getClassFile(); // turn on the modified flag.
- do {
- accName = "access$" + uniqueNumber++;
- } while (cf.getMethod(accName) != null);
-
+ accName = findAccessorName(cf);
try {
ConstPool cp = cf.getConstPool();
ClassPool pool = clazz.getClassPool();
accessors.put(key, accName);
return accName;
}
+
+ /**
+ * Returns the method_info representing the added getter.
+ */
+ public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static)
+ throws CompileError
+ {
+ String fieldName = finfo.getName();
+ String key = fieldName + ":getter";
+ Object res = accessors.get(key);
+ if (res != null)
+ return (MethodInfo)res; // already exists.
+
+ ClassFile cf = clazz.getClassFile(); // turn on the modified flag.
+ String accName = findAccessorName(cf);
+ try {
+ ConstPool cp = cf.getConstPool();
+ ClassPool pool = clazz.getClassPool();
+ String fieldType = finfo.getDescriptor();
+ String accDesc;
+ if (is_static)
+ accDesc = "()" + fieldType;
+ else
+ accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType;
+
+ MethodInfo minfo = new MethodInfo(cp, accName, accDesc);
+ minfo.setAccessFlags(AccessFlag.STATIC);
+ minfo.addAttribute(new SyntheticAttribute(cp));
+ Bytecode code = new Bytecode(cp);
+ if (is_static) {
+ code.addGetstatic(Bytecode.THIS, fieldName, fieldType);
+ }
+ else {
+ code.addAload(0);
+ code.addGetfield(Bytecode.THIS, fieldName, fieldType);
+ code.setMaxLocals(1);
+ }
+
+ code.addReturn(Descriptor.toCtClass(fieldType, pool));
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ cf.addMethod(minfo);
+ accessors.put(key, minfo);
+ return minfo;
+ }
+ catch (CannotCompileException e) {
+ throw new CompileError(e);
+ }
+ catch (NotFoundException e) {
+ throw new CompileError(e);
+ }
+ }
+
+ /**
+ * Returns the method_info representing the added setter.
+ */
+ public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static)
+ throws CompileError
+ {
+ String fieldName = finfo.getName();
+ String key = fieldName + ":setter";
+ Object res = accessors.get(key);
+ if (res != null)
+ return (MethodInfo)res; // already exists.
+
+ ClassFile cf = clazz.getClassFile(); // turn on the modified flag.
+ String accName = findAccessorName(cf);
+ try {
+ ConstPool cp = cf.getConstPool();
+ ClassPool pool = clazz.getClassPool();
+ String fieldType = finfo.getDescriptor();
+ String accDesc;
+ if (is_static)
+ accDesc = "(" + fieldType + ")V";
+ else
+ accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V";
+
+ MethodInfo minfo = new MethodInfo(cp, accName, accDesc);
+ minfo.setAccessFlags(AccessFlag.STATIC);
+ minfo.addAttribute(new SyntheticAttribute(cp));
+ Bytecode code = new Bytecode(cp);
+ int reg;
+ if (is_static) {
+ reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool));
+ code.addPutstatic(Bytecode.THIS, fieldName, fieldType);
+ }
+ else {
+ code.addAload(0);
+ reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool))
+ + 1;
+ code.addPutfield(Bytecode.THIS, fieldName, fieldType);
+ }
+
+ code.addReturn(null);
+ code.setMaxLocals(reg);
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ cf.addMethod(minfo);
+ accessors.put(key, minfo);
+ return minfo;
+ }
+ catch (CannotCompileException e) {
+ throw new CompileError(e);
+ }
+ catch (NotFoundException e) {
+ throw new CompileError(e);
+ }
+ }
+
+ private String findAccessorName(ClassFile cf) {
+ String accName;
+ do {
+ accName = "access$" + uniqueNumber++;
+ } while (cf.getMethod(accName) != null);
+ return accName;
+ }
}
int token = expr.getOperator();
ASTree oprand = expr.oprand1();
- if (token == '.')
- if (((Symbol)expr.oprand2()).get().equals("length"))
+ if (token == '.') {
+ String member = ((Symbol)expr.oprand2()).get();
+ if (member.equals("length"))
atArrayLength(expr);
+ else if (member.equals("class"))
+ atClassObject(expr); // .class
else
atFieldRead(expr);
+ }
else if (token == MEMBER) { // field read
- if (!atClassObject(expr)) // .class
+ String member = ((Symbol)expr.oprand2()).get();
+ if (member.equals("class"))
+ atClassObject(expr); // .class
+ else
atFieldRead(expr);
}
else if (token == ARRAY)
protected abstract void atFieldRead(ASTree expr) throws CompileError;
- public boolean atClassObject(Expr expr) throws CompileError {
- if (!((Symbol)expr.oprand2()).get().equals("class"))
- return false;
-
- if (resolveClassName((ASTList)expr.oprand1()) == null)
- return false;
+ public void atClassObject(Expr expr) throws CompileError {
+ ASTree op1 = expr.oprand1();
+ String cname;
+ if (op1 instanceof ASTList)
+ cname = Declarator.astToClassName((ASTList)op1, '/');
+ else
+ cname = ((Symbol)op1).get();
- throw new CompileError(".class is not supported");
+ cname = resolveClassName(cname);
+ throw new CompileError(".class is not supported: " + cname);
}
public void atArrayLength(Expr expr) throws CompileError {
if (declClass != targetClass)
throw new CompileError("no such a constructor");
}
- else if ((acc & AccessFlag.PRIVATE) != 0) {
+ else if (AccessFlag.isPrivate(acc))
if (declClass == thisClass)
isSpecial = true;
else {
throw new CompileError("Method " + orgName
+ " is private");
}
- }
boolean popTarget = false;
if ((acc & AccessFlag.STATIC) != 0) {
if (op != '=' && !is_static)
bytecode.addOpcode(DUP);
- int fi = atFieldRead(f, is_static, op == '=');
+ int fi;
+ if (op == '=') {
+ FieldInfo finfo = f.getFieldInfo2();
+ setFieldType(finfo);
+ AccessorMaker maker = isAccessibleField(f, finfo);
+ if (maker == null)
+ fi = addFieldrefInfo(f, finfo);
+ else
+ fi = 0;
+ }
+ else
+ fi = atFieldRead(f, is_static);
+
int fType = exprType;
int fDim = arrayDim;
String cname = className;
bytecode.addOpcode(dup_code);
}
- if (is_static) {
- bytecode.add(PUTSTATIC);
- bytecode.growStack(is2w ? -2 : -1);
- }
- else {
- bytecode.add(PUTFIELD);
- bytecode.growStack(is2w ? -3 : -2);
- }
+ atFieldAssignCore(f, is_static, fi, is2w);
- bytecode.addIndex(fi);
exprType = fType;
arrayDim = fDim;
className = cname;
}
+ /* If fi == 0, the field must be a private field in an enclosing class.
+ */
+ private void atFieldAssignCore(CtField f, boolean is_static, int fi,
+ boolean is2byte) throws CompileError {
+ if (fi != 0) {
+ if (is_static) {
+ bytecode.add(PUTSTATIC);
+ bytecode.growStack(is2byte ? -2 : -1);
+ }
+ else {
+ bytecode.add(PUTFIELD);
+ bytecode.growStack(is2byte ? -3 : -2);
+ }
+
+ bytecode.addIndex(fi);
+ }
+ else {
+ CtClass declClass = f.getDeclaringClass();
+ AccessorMaker maker = declClass.getAccessorMaker();
+ // make should be non null.
+ FieldInfo finfo = f.getFieldInfo2();
+ MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
+ bytecode.addInvokestatic(declClass, minfo.getName(),
+ minfo.getDescriptor());
+ }
+ }
+
/* overwritten in JvstCodeGen.
*/
public void atMember(Member mem) throws CompileError {
{
CtField f = fieldAccess(expr);
boolean is_static = resultStatic;
- atFieldRead(f, is_static, false);
+ atFieldRead(f, is_static);
}
- private int atFieldRead(CtField f, boolean isStatic, boolean noRead)
+ /**
+ * Generates bytecode for reading a field value.
+ * It returns a fieldref_info index or zero if the field is a private
+ * one declared in an enclosing class.
+ */
+ private int atFieldRead(CtField f, boolean isStatic) throws CompileError {
+ FieldInfo finfo = f.getFieldInfo2();
+ boolean is2byte = setFieldType(finfo);
+ AccessorMaker maker = isAccessibleField(f, finfo);
+ if (maker != null) {
+ MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
+ bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(),
+ minfo.getDescriptor());
+ return 0;
+ }
+ else {
+ int fi = addFieldrefInfo(f, finfo);
+ if (isStatic) {
+ bytecode.add(GETSTATIC);
+ bytecode.growStack(is2byte ? 2 : 1);
+ }
+ else {
+ bytecode.add(GETFIELD);
+ bytecode.growStack(is2byte ? 1 : 0);
+ }
+
+ bytecode.addIndex(fi);
+ return fi;
+ }
+ }
+
+ /**
+ * Returns null if the field is accessible. Otherwise, it throws
+ * an exception or it returns AccessorMaker if the field is a private
+ * one declared in an enclosing class.
+ */
+ private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
throws CompileError
{
- FieldInfo finfo = f.getFieldInfo2();
- String type = finfo.getDescriptor();
+ if (AccessFlag.isPrivate(finfo.getAccessFlags())
+ && f.getDeclaringClass() != thisClass) {
+ CtClass declClass = f.getDeclaringClass();
+ if (isEnclosing(declClass, thisClass)) {
+ AccessorMaker maker = declClass.getAccessorMaker();
+ if (maker != null)
+ return maker;
+ else
+ throw new CompileError("fatal error. bug?");
+ }
+ else
+ throw new CompileError("Field " + f.getName() + " in "
+ + declClass.getName() + " is private.");
+ }
+
+ return null; // accessible field
+ }
- int fi = addFieldrefInfo(f, finfo, type);
+ /**
+ * Sets exprType, arrayDim, and className.
+ *
+ * @return true if the field type is long or double.
+ */
+ private boolean setFieldType(FieldInfo finfo) throws CompileError {
+ String type = finfo.getDescriptor();
int i = 0;
int dim = 0;
}
arrayDim = dim;
- boolean is2byte = (c == 'J' || c == 'D');
exprType = MemberResolver.descToType(c);
if (c == 'L')
else
className = null;
- if (noRead)
- return fi;
-
- if (isStatic) {
- bytecode.add(GETSTATIC);
- bytecode.growStack(is2byte ? 2 : 1);
- }
- else {
- bytecode.add(GETFIELD);
- bytecode.growStack(is2byte ? 1 : 0);
- }
-
- bytecode.addIndex(fi);
- return fi;
+ boolean is2byte = (c == 'J' || c == 'D');
+ return is2byte;
}
- protected int addFieldrefInfo(CtField f, FieldInfo finfo, String type) {
+ private int addFieldrefInfo(CtField f, FieldInfo finfo) {
ConstPool cp = bytecode.getConstPool();
String cname = f.getDeclaringClass().getName();
int ci = cp.addClassInfo(cname);
String name = finfo.getName();
+ String type = finfo.getDescriptor();
return cp.addFieldrefInfo(ci, name, type);
}
if (!is_static)
bytecode.addOpcode(DUP);
- int fi = atFieldRead(f, is_static, false);
+ int fi = atFieldRead(f, is_static);
int t = exprType;
boolean is2w = is2word(t, arrayDim);
dup_code = (is2w ? DUP2_X1 : DUP_X1);
atPlusPlusCore(dup_code, doDup, token, isPost, expr);
-
- if (is_static) {
- bytecode.add(PUTSTATIC);
- bytecode.growStack(is2w ? -2 : -1);
- }
- else {
- bytecode.add(PUTFIELD);
- bytecode.growStack(is2w ? -3 : -2);
- }
-
- bytecode.addIndex(fi);
+ atFieldAssignCore(f, is_static, fi, is2w);
}
/* This method also returns a value in resultStatic.
break;
case '.' :
lex.get();
- if (lex.get() != Identifier)
+ t = lex.get();
+ if (t == CLASS)
+ str = "class";
+ else if (t == Identifier)
+ str = lex.getString();
+ else
throw new CompileError("missing member name", lex);
- expr = Expr.make('.', expr, new Member(lex.getString()));
+ expr = Expr.make('.', expr, new Member(str));
break;
case '#' :
lex.get();
static final String javaLangObject = "java.lang.Object";
static final String jvmJavaLangObject = "java/lang/Object";
static final String jvmJavaLangString = "java/lang/String";
+ static final String jvmJavaLangClass = "java/lang/Class";
/* The following fields are used by atXXX() methods
* for returning the type of the compiled expression.
int token = expr.getOperator();
ASTree oprand = expr.oprand1();
- if (token == '.')
- if (((Symbol)expr.oprand2()).get().equals("length"))
+ if (token == '.') {
+ String member = ((Symbol)expr.oprand2()).get();
+ if (member.equals("length"))
atArrayLength(expr);
+ else if (member.equals("class"))
+ atClassObject(expr); // .class
else
atFieldRead(expr);
+ }
else if (token == MEMBER) { // field read
- if (!atClassObject(expr)) // .class
+ String member = ((Symbol)expr.oprand2()).get();
+ if (member.equals("class"))
+ atClassObject(expr); // .class
+ else
atFieldRead(expr);
}
else if (token == ARRAY)
throw new CompileError("bad filed access");
}
- public boolean atClassObject(Expr expr) throws CompileError {
- if (!((Symbol)expr.oprand2()).get().equals("class"))
- return false;
-
- if (resolveClassName((ASTList)expr.oprand1()) == null)
- return false;
-
- return true;
+ public void atClassObject(Expr expr) throws CompileError {
+ exprType = CLASS;
+ arrayDim = 0;
+ className =jvmJavaLangClass;
}
public void atArrayLength(Expr expr) throws CompileError {
return null;
StringBuffer sbuf = new StringBuffer();
+ astToClassName(sbuf, name, sep);
+ return sbuf.toString();
+ }
+
+ private static void astToClassName(StringBuffer sbuf, ASTList name,
+ char sep) {
for (;;) {
- sbuf.append(((Symbol)name.head()).get());
+ ASTree h = name.head();
+ if (h instanceof Symbol)
+ sbuf.append(((Symbol)h).get());
+ else if (h instanceof ASTList)
+ astToClassName(sbuf, (ASTList)h, sep);
+
name = name.tail();
if (name == null)
break;
sbuf.append(sep);
}
-
- return sbuf.toString();
}
}