package x10.sncode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public abstract class Type { static final byte Type_Void = 0; static final byte Type_Ref = 1; static final byte Type_Struct = 2; static final byte Type_Scoped = 3; static final byte Type_Param = 4; static final byte Type_Constrained = 5; static final byte Type_Fun = 6; public abstract String desc(); public abstract void writeInto(ConstantPool cp, ByteBuffer w); public static class ConstrainedType extends Type { private Type base; private Constraint c; public ConstrainedType(Type base, Constraint c) { this.base = base; this.c = c; assert base != null; assert c != null; } public ConstrainedType(ConstantPoolParser cp, ByteBuffer r) throws InvalidClassFileException { this.base = cp.getCPType(r.getCPIndex()); this.c = cp.getCPConstraint(r.getCPIndex()); } @Override public String desc() { return base.desc(); } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Constrained); w.addCPIndex(cp.addCPType(base)); w.addCPIndex(cp.addCPConstraint(c)); } @Override public boolean equals(Object o) { if (o instanceof ConstrainedType) { ConstrainedType t = (ConstrainedType) o; if (base.equals(t.base) && c.equals(t.c)) return true; } return false; } @Override public int hashCode() { return base.hashCode() + c.hashCode() + 1433; } } public static class VoidType extends Type { public VoidType() {} public String toString() { return "void"; } @Override public String desc() { return "V"; } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Void); } @Override public boolean equals(Object o) { return o instanceof VoidType; } @Override public int hashCode() { return 389; } } public static class FunType extends Type { private final List<Type> args; private final Type ret; public FunType(List<Type> a, Type r) { args = a; ret = r; } public FunType(ConstantPoolParser f, ByteBuffer r) throws InvalidClassFileException { int n = r.getCount(); args = new ArrayList<Type>(n); for (int i = 0; i < n; i++) { args.add(f.getCPType(r.getCPIndex())); } ret = f.getCPType(r.getCPIndex()); } public String toString() { return "(" + getArgs().toString().substring(1, getArgs().toString().length() - 1) + ") => " + getRet(); } public List<Type> getArgs() { return args; } public Type getRet() { return ret; } @Override public String desc() { StringBuilder sb = new StringBuilder(); sb.append("("); for (Type t : args) { sb.append(t.desc()); } sb.append(")"); sb.append(ret.desc()); return sb.toString(); } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Fun); w.addCount(args.size()); for (Type t : args) w.addCPIndex(cp.addCPType(t)); w.addCPIndex(cp.addCPType(ret)); } @Override public boolean equals(Object o) { if (o instanceof FunType) { FunType t = (FunType) o; if (t.args.size() != args.size()) return false; if (!t.ret.equals(ret)) return false; for (int i = 0; i < args.size(); i++) if (!t.args.get(i).equals(args.get(i))) return false; return true; } return false; } @Override public int hashCode() { int h = 23907; for (Type t : args) { h += t.hashCode(); } h += ret.hashCode(); return h; } } public static class AnnotatedType extends Type { Type annotated; Type annotation; List<String> args; public AnnotatedType(Type t1, Type t2) { annotated = t1; annotation = t2; args = Collections.EMPTY_LIST; } @Override public String desc() { StringBuilder sb = new StringBuilder(); sb.append("%"); sb.append(annotated.desc()); sb.append(annotation.desc()); if (args.size() > 0) { sb.append("("); for (String t : args) { sb.append(t); } sb.append(");"); } return sb.toString(); } public void writeInto(ConstantPool cp, ByteBuffer w) { annotated.writeInto(cp, w); } @Override public boolean equals(Object o) { if (o instanceof AnnotatedType) { AnnotatedType t = (AnnotatedType) o; if (!t.annotated.equals(annotated)) return false; if (!t.annotation.equals(annotation)) return false; if (t.args.size() != args.size()) return false; for (int i = 0; i < args.size(); i++) if (!t.args.get(i).equals(args.get(i))) return false; return true; } return false; } @Override public int hashCode() { int h = 4905; h += annotated.hashCode(); h += annotation.hashCode(); for (String t : args) { h += t.hashCode(); } return h; } } public static class ScopedType extends Type { private final String name; final List<Type> args; public ScopedType(String s, List<Type> a) { name = s; args = a; } public ScopedType(ConstantPoolParser f, ByteBuffer r) throws InvalidClassFileException { name = f.getCPUtf8(r.getCPIndex()); int n = r.getCount(); args = new ArrayList<Type>(n); for (int i = 0; i < n; i++) { args.add(f.getCPType(r.getCPIndex())); } } public String toString() { return getName() + (args.isEmpty() ? "" : args.toString()); } public String getName() { return name; } @Override public String desc() { StringBuilder sb = new StringBuilder(); sb.append("&"); sb.append(name); if (args.size() > 0) { sb.append("["); for (Type t : args) { sb.append(t.desc()); } sb.append("]"); } sb.append(";"); return sb.toString(); } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Scoped); w.addCPIndex(cp.addCPUtf8(name)); w.addCount(args.size()); for (Type t : args) w.addCPIndex(cp.addCPType(t)); } @Override public boolean equals(Object o) { if (o instanceof ScopedType) { ScopedType t = (ScopedType) o; if (!t.name.equals(name)) return false; if (t.args.size() != args.size()) return false; for (int i = 0; i < args.size(); i++) if (!t.args.get(i).equals(args.get(i))) return false; return true; } return false; } @Override public int hashCode() { int h = "&".hashCode(); h += name.hashCode(); for (Type t : args) { h += t.hashCode(); } return h; } } public static class RefType extends Type { private final String name; final List<Type> args; public RefType(String s, List<Type> a) { name = s; args = a; } public RefType(ConstantPoolParser f, ByteBuffer r) throws InvalidClassFileException { name = f.getCPUtf8(r.getCPIndex()); int n = r.getCount(); args = new ArrayList<Type>(n); for (int i = 0; i < n; i++) { args.add(f.getCPType(r.getCPIndex())); } } public String toString() { return getName() + (args.isEmpty() ? "" : args.toString()); } public String getName() { return name; } @Override public String desc() { StringBuilder sb = new StringBuilder(); sb.append("*"); sb.append(name); if (args.size() > 0) { sb.append("["); for (Type t : args) { sb.append(t.desc()); } sb.append("]"); } sb.append(";"); return sb.toString(); } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Ref); w.addCPIndex(cp.addCPUtf8(name)); w.addCount(args.size()); for (Type t : args) w.addCPIndex(cp.addCPType(t)); } @Override public boolean equals(Object o) { if (o instanceof RefType) { RefType t = (RefType) o; if (!t.name.equals(name)) return false; if (t.args.size() != args.size()) return false; for (int i = 0; i < args.size(); i++) if (!t.args.get(i).equals(args.get(i))) return false; return true; } return false; } @Override public int hashCode() { int h = "*".hashCode(); h += name.hashCode(); for (Type t : args) { h += t.hashCode(); } return h; } } public static class StructType extends Type { final String name; final List<Type> args; public StructType(String s, List<Type> a) { name = s; args = a; } public StructType(ConstantPoolParser f, ByteBuffer r) throws InvalidClassFileException { name = f.getCPUtf8(r.getCPIndex()); int n = r.getCount(); args = new ArrayList<Type>(n); for (int i = 0; i < n; i++) { args.add(f.getCPType(r.getCPIndex())); } } public String toString() { return name + "!" + (args.isEmpty() ? "" : args.toString()); } @Override public String desc() { StringBuilder sb = new StringBuilder(); sb.append("!"); sb.append(name); if (args.size() > 0) { sb.append("["); for (Type t : args) { sb.append(t.desc()); } sb.append("]"); } sb.append(";"); return sb.toString(); } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Struct); w.addCPIndex(cp.addCPUtf8(name)); w.addCount(args.size()); for (Type t : args) w.addCPIndex(cp.addCPType(t)); } @Override public boolean equals(Object o) { if (o instanceof StructType) { StructType t = (StructType) o; if (!t.name.equals(name)) return false; if (t.args.size() != args.size()) return false; for (int i = 0; i < args.size(); i++) if (!t.args.get(i).equals(args.get(i))) return false; return true; } return false; } @Override public int hashCode() { int h = "!".hashCode(); h += name.hashCode(); for (Type t : args) { h += t.hashCode(); } return h; } } public static class ParamType extends Type { final String name; public ParamType(String s) { name = s; } public ParamType(ConstantPoolParser f, ByteBuffer r) throws InvalidClassFileException { name = f.getCPUtf8(r.getCPIndex()); } public String getName() { return name; } public String toString() { return name; } @Override public String desc() { StringBuilder sb = new StringBuilder(); sb.append("?"); sb.append(name); sb.append(";"); return sb.toString(); } public void writeInto(ConstantPool cp, ByteBuffer w) { w.addByte(Type_Param); w.addCPIndex(cp.addCPUtf8(name)); } @Override public boolean equals(Object o) { if (o instanceof ParamType) { ParamType t = (ParamType) o; if (!t.name.equals(name)) return false; return true; } return false; } @Override public int hashCode() { int h = 2389; h += name.hashCode(); return h; } } static int size(ConstantPoolParser f, ByteBuffer b) throws InvalidClassFileException { int o = b.offset(); byte k = b.getByte(o); switch (k) { case Type_Constrained: // tag, type, constraint return 1 + 2 * 4; case Type_Fun: { int n = b.getCount(o + 1); // tag, count, args, ret return 1 + 4 + n * 4 + 4; } case Type_Param: return 1 + 4; case Type_Ref: case Type_Scoped: case Type_Struct: { int n = b.getCount(o + 5); // tag, name, count, args return 1 + 4 + 4 + n * 4; } case Type_Void: return 1; } throw new InvalidClassFileException(b.offset(), "base type"); } static Type readFrom(ConstantPoolParser f, ByteBuffer b) throws InvalidClassFileException { byte k = b.getByte(); switch (k) { case Type_Constrained: return new ConstrainedType(f, b); case Type_Fun: return new FunType(f, b); case Type_Param: return new ParamType(f, b); case Type_Ref: return new RefType(f, b); case Type_Scoped: return new ScopedType(f, b); case Type_Struct: return new StructType(f, b); case Type_Void: return new VoidType(); } throw new InvalidClassFileException(b.offset(), "base type"); } static Type parse(ByteBuffer b, int offset, int len) throws InvalidClassFileException { b.seek(offset); Type t = parse(b); if (b.offset() != offset + len) throw new InvalidClassFileException(offset, "bad type"); return t; } static String parseClassName(ByteBuffer b) throws InvalidClassFileException { String s = b.getUtf8ToDelimiter("[(;"); if (s.length() > 0) { s = s.substring(0, s.length() - 1); b.skip(-1); } else { throw new InvalidClassFileException(b.offset(), "bad class name"); } return s; } static Type parse(ByteBuffer b) throws InvalidClassFileException { int offset = b.offset(); char k = (char) b.getByte(); switch (k) { case 'V': { return new VoidType(); } case '*': { // *C[T1;T2]; String s = parseClassName(b); char last = (char) b.getByte(); if (last == '[') { List<Type> args = parseTypeList(b, ']'); last = (char) b.getByte(); if (last == ']') { last = (char) b.getByte(); if (last == ';') return new RefType(s, args); } } else if (last == ';') { return new RefType(s, Collections.EMPTY_LIST); } break; } case '!': { // !C[T1;T2]; String s = parseClassName(b); char last = (char) b.getByte(); if (last == '[') { List<Type> args = parseTypeList(b, ']'); last = (char) b.getByte(); if (last == ']') { last = (char) b.getByte(); if (last == ';') return new StructType(s, args); } } else if (last == ';') { return new StructType(s, Collections.EMPTY_LIST); } break; } case '&': { // &C[T1;T2]; String s = parseClassName(b); char last = (char) b.getByte(); if (last == '[') { List<Type> args = parseTypeList(b, ']'); last = (char) b.getByte(); if (last == ']') { last = (char) b.getByte(); if (last == ';') return new ScopedType(s, args); } } else if (last == ';') { return new ScopedType(s, Collections.EMPTY_LIST); } break; } case '(': { // (T1;T2;)T3; List<Type> args = parseTypeList(b, ')'); char last = (char) b.getByte(); if (last == ')') { Type ret = parse(b); return new FunType(args, ret); } } case '%': { // %T1;T2;(...) Type t1 = parse(b); char last = (char) b.getByte(); if (last == ';') { Type t2 = parse(b); last = (char) b.getByte(); if (last == ';') { return new AnnotatedType(t1, t2); } } } case '?': { String s = parseClassName(b); char last = (char) b.getByte(); if (last == ';') return new ParamType(s); } } throw new InvalidClassFileException(offset, "bad type " + (char) k); } private static List<Type> parseTypeList(ByteBuffer b, char end) throws InvalidClassFileException { List<Type> types = new ArrayList<Type>(); while (true) { char lookahead = (char) b.getByte(b.offset()); if (lookahead == end) break; Type t = parse(b); types.add(t); } return types; } public static Type parse(String s) throws InvalidClassFileException { return parse(new ByteBuffer(s.getBytes())); } }