/* ____ ____ ____ ____ ______ *\
** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
** /_____/\____/\___/\____/____/ **
\* */
// $Id$
package scalac.symtab.classfile;
import java.util.HashMap;
import java.io.PrintStream;
import scalac.*;
import scalac.atree.AConstant;
import scalac.util.*;
import ch.epfl.lamp.util.Position;
import scalac.symtab.*;
import Symbol.*;
import Type.*;
public class UnPickle implements Kinds, Modifiers, EntryTags, TypeTags {
/***************************************************
* Symbol table attribute format: see EntryTags.java
*/
static final boolean debug = true;
Symbol classroot;
Symbol moduleroot;
byte[] bytes;
int bp;
Name sourceName;
int[] index;
Object[] entries;
int paramFlags;
Global global;
UnPickle(Symbol root, byte[] data, Name sourceName) {
global = Global.instance;
if (root.isConstructor()) {
this.classroot = root.constructorClass();
this.moduleroot = classroot.module();
} else if (root.isType()) {
this.classroot = root;
this.moduleroot = classroot.module();
} else {
this.moduleroot = root;
this.classroot = root.owner().lookup(root.name.toTypeName());
}
if (root != moduleroot && moduleroot.isModule()) {
moduleroot.moduleClass().setInfo(Type.NoType);
}
if (global.debug)
global.log(
"unpickle " + root + " " + classroot + " " + moduleroot + " " +
moduleroot.moduleClass() + " " + moduleroot.moduleClass().primaryConstructor());
this.bytes = data;
this.bp = 0;
this.sourceName = sourceName;
index = new int[readNat()];
for (int i = 0; i < index.length; i++) {
index[i] = bp;
bp++;
bp = readNat() + bp;
}
entries = new Object[index.length];
if (global.debug) print(System.out);
for (int i = 0; i < index.length; i++) {
if (isSymbolEntry(i)) getSymbol(i);
}
if (global.debug) global.log("unpickled " + root + ":" + root.rawInfo());//debug
if (!root.isInitialized())
throw new BadSignature(this, "it does not define " + root);
if (moduleroot.isModule() && moduleroot.moduleClass().type() == Type.NoType) {
moduleroot.setInfo(Type.NoType);
}
}
int readByte() {
return bytes[bp++];
}
int readNat() {
int b;
int x = 0;
do {
b = readByte();
x = (x << 7) + (b & 0x7f);
} while ((b & 0x80) != 0);
return x;
}
long readLong(int n) {
long x = 0;
for (int i = 0; i < n; i++) {
x = (x << 8) + (readByte() & 0xff);
}
int leading = 64 - (n * 8);
return x << leading >> leading;
}
boolean isTypeEntry(int i) {
int tag = bytes[index[i]];
return (firstTypeTag <= tag && tag <= lastTypeTag) || tag == NOpre;
}
boolean isSymbolEntry(int i) {
int tag = bytes[index[i]];
return (firstSymTag <= tag && tag <= lastSymTag);
}
Name getName(int n) {
if (entries[n] == null) {
int savedBp = bp;
bp = index[n];
int tag = bytes[bp++];
int len = readNat();
Name name = Name.fromAscii(bytes, bp, len);
switch (tag) {
case TERMname : entries[n] = name; break;
case TYPEname : entries[n] = name.toTypeName(); break;
default: throw new BadSignature(this);
}
bp = savedBp;
}
return (Name) entries[n];
}
Name readNameRef() {
return getName(readNat());
}
String decode(Name name) {
if (name.isTypeName()) return "type " + NameTransformer.decode(name);
else return "value " + NameTransformer.decode(name);
}
void enterSymbol(Symbol sym) {
/*
if (global.debug) {
global.log("entering " + sym + ":" + sym.type() + " in " + sym.owner());//debug
if (sym.kind == CLASS)
global.log("primconstr = " + sym.primaryConstructor());
}
*/
if ((sym.flags & ALTERNATIVE) != 0) {
sym.flags &= ~ALTERNATIVE;
} else {
Symbol owner = sym.owner();
if (owner.kind == CLASS &&
!sym.isConstructor() && !sym.isModuleClass()) {
Scope scope = owner.info().members();
Symbol other = scope.lookup(sym.name);
if (other == Symbol.NONE) {
scope.enter(sym);
} else {
assert sym == other
: "double enter: " + other + ":" + other.rawFirstInfo() + "," + sym + ":" + sym.rawFirstInfo();
}
}
}
}
Symbol getSymbol(int n) {
if (entries[n] == null) {
int savedBp = bp;
bp = index[n];
int tag = bytes[bp++];
int end = readNat() + bp;
Symbol sym;
Symbol owner;
switch (tag) {
case NONEsym:
entries[n] = sym = Symbol.NONE;
break;
case EXTref:
case EXTMODCLASSref:
Name name = readNameRef();
if (bp == end) {
owner = Global.instance.definitions.ROOT_CLASS;
} else {
assert bp < end;
owner = readSymbolRef();
}
if (name == Names.ROOT && owner == Symbol.NONE) {
sym = Global.instance.definitions.ROOT_CLASS;
if (tag == EXTref) sym = sym;
// !!! line above is usefull for the transition
// !!! after some time, replace it by the following line:
// !!! assert tag != EXTref;
} else {
sym = owner.info().lookup(name);
if (tag == EXTMODCLASSref) {
/*
if (sym.kind == VAL)
switch (sym.type()) {
case OverloadedType(Symbol[] alts, _):
for (int i = 0; i < alts.length; i++)
if (alts[i].isModule()) sym = alts[i];
}
*/
assert sym.isModule();
sym = sym.moduleClass();
}
}
entries[n] = sym;
if (sym.kind == NONE) {
if (global.debug)
global.log(owner.info().members().toString());
throw new BadSignature(this,
"reference " + decode(name) + " of " + owner +
" refers to nonexisting symbol.");
}
break;
default:
assert isSymbolEntry(n) : n;
Name name = readNameRef();
if (global.debug)
global.log("reading " + name + " at " + n);
owner = readSymbolRef();
if (entries[n] == null) {
int flags = readNat();
int inforef = readNat();
switch (tag) {
case TYPEsym:
entries[n] = sym = new AbsTypeSymbol(
Position.NOPOS, name, owner, flags);
sym.setInfo(getType(inforef));
sym.setLoBound(readTypeRef());
break;
case ALIASsym:
entries[n] = sym = new AliasTypeSymbol(
Position.NOPOS, name, owner, flags);
sym.setInfo(getType(inforef));
Symbol constr = readSymbolRef();
break;
case CLASSsym:
entries[n] = sym = new ClassSymbol(
Position.NOPOS, name, owner, flags);
Symbol clr = ((flags & MODUL) == 0) ? classroot
: moduleroot.moduleClass();
if (name == clr.name && owner == clr.owner()) {
if (global.debug) global.log("overwriting " + clr);
sym.copyTo(clr);
entries[n] = sym = clr;
}
sym.setInfo(getType(inforef));
sym.setTypeOfThis(readTypeRef());
Symbol constr = readSymbolRef();
assert constr == sym.allConstructors();
Symbol[] alts = constr.alternativeSymbols();
for (int i = 0; i < alts.length; i++)
((TermSymbol)alts[i]).makeConstructor((ClassSymbol)sym);
break;
case VALsym:
if (bp < end) {
Symbol tsym = readSymbolRef();
if (name == Names.CONSTRUCTOR) {
entries[n] = sym = tsym.allConstructors();
sym.flags = flags;
} else {
assert (flags & MODUL) != 0 : name;
entries[n] = sym = new TermSymbol(
Position.NOPOS, name, owner, flags)
.makeModule((ClassSymbol) tsym);
}
} else {
entries[n] = sym = new TermSymbol(
Position.NOPOS, name, owner, flags);
}
if (name == moduleroot.name && owner == moduleroot.owner()) {
if (global.debug)
global.log("overwriting " + moduleroot);
sym.copyTo(moduleroot);
entries[n] = sym = moduleroot;
}
Type tp = getType(inforef);
sym.setInfo(tp.setOwner(sym));
break;
default:
throw new BadSignature(this);
}
enterSymbol(sym);
}
}
bp = savedBp;
}
return (Symbol) entries[n];
}
Symbol readSymbolRef() {
return getSymbol(readNat());
}
Symbol[] readSymbolRefs(int end) {
return readSymbolRefs(0, end);
}
Symbol[] readSymbolRefs(int nread, int end) {
if (bp == end) {
return new Symbol[nread];
} else {
assert bp < end;
int bp0 = bp;
int ref = readNat();
if (isSymbolEntry(ref)) {
Symbol s = getSymbol(ref);
Symbol[] ss = readSymbolRefs(nread+1, end);
ss[nread] = s;
return ss;
} else {
bp = bp0;
return new Symbol[nread];
}
}
}
Type getType(int n) {
if (entries[n] == null) {
int savedBp = bp;
bp = index[n];
int tag = bytes[bp++];
int end = readNat() + bp;
Type tpe;
switch (tag) {
case NOtpe:
tpe = Type.NoType;
break;
case NOpre:
tpe = Type.NoPrefix;
break;
case THIStpe:
Symbol sym = readSymbolRef();
tpe = (sym.kind == NONE) ? Type.NoPrefix : Type.ThisType(sym);
// !!! code above is usefull for the transition
// !!! after some time, replace it by the following line:
// !!! tpe = Type.ThisType(readSymbolRef());
break;
case SINGLEtpe:
Type prefix = readTypeRef();
Symbol symbol = readSymbolRef();
tpe = symbol.isRoot() ? symbol.thisType() : Type.singleType(prefix, symbol);
// !!! code above is usefull for the transition
// !!! after some time, replace it by the following line:
// !!! tpe = Type.singleType(readTypeRef(), readSymbolRef());
break;
case CONSTANTtpe:
Type base = readTypeRef();
AConstant value = readConstantRef();
tpe = new Type.ConstantType(base, value);
break;
case TYPEREFtpe:
tpe = Type.newTypeRefUnsafe( // !!!
readTypeRef(), readSymbolRef(), readTypeRefs(end));
break;
case COMPOUNDtpe:
Symbol[] clazzs = readSymbolRefs(end);
assert clazzs.length == 1;
Type[] parents = readTypeRefs(end);
tpe = Type.compoundType(parents, new Scope(), clazzs[0]);
break;
case METHODtpe:
Type restype = readTypeRef();
int bp1 = bp;
Type[] argtypes = readTypeRefs(end);
int[] flags = new int[argtypes.length];
bp = bp1;
readFlags(flags);
Symbol[] params = new Symbol[argtypes.length];
for (int i = 0; i < argtypes.length; i++) {
params[i] = new TermSymbol(
Position.NOPOS, Name.fromString("$" + i),
Symbol.NONE, PARAM | flags[i]);
params[i].setInfo(argtypes[i]);
}
tpe = Type.MethodType(params, restype);
break;
case POLYtpe:
Type restype = readTypeRef();
tpe = Type.PolyType(readSymbolRefs(end), restype);
break;
case OVERLOADEDtpe:
int bp0 = bp;
Symbol[] alts = readSymbolRefs(end);
int bp1 = bp;
Type[] alttypes = readTypeRefs(end);
assert alts.length == alttypes.length
: alts.length + "!=" + alttypes.length +
" at " + bp0 + "/" + bp1 + "/" + bp;
for (int i = 0; i < alts.length; i++)
alttypes[i] = alttypes[i].setOwner(alts[i]);
tpe = Type.OverloadedType(alts, alttypes);
break;
case FLAGGEDtpe:
readNat(); // skip flags
tpe = readTypeRef();
break;
default:
throw new BadSignature(this);
}
entries[n] = tpe;
bp = savedBp;
}
return (Type) entries[n];
}
Type readTypeRef() {
return getType(readNat());
}
Type[] readTypeRefs(int end) {
return readTypeRefs(0, end);
}
Type[] readTypeRefs(int nread, int end) {
if (bp == end) {
return new Type[nread];
} else {
assert bp < end : bp + ">" + end;
int bp0 = bp;
int ref = readNat();
if (isTypeEntry(ref)) {
Type t = getType(ref);
Type[] ts = readTypeRefs(nread + 1, end);
ts[nread] = t;
return ts;
} else {
bp = bp0;
return new Type[nread];
}
}
}
void readFlags(int[] flags) {
for (int i = 0; i < flags.length; i++)
flags[i] = getFlags(readNat());
}
int getFlags(int n) {
int savedBp = bp;
bp = index[n];
int tag = bytes[bp++];
int end = readNat() + bp;
int flags = (tag == FLAGGEDtpe) ? decodeFlags(readNat()) : 0;
bp = savedBp;
return flags;
}
private static int decodeFlags(int n) {
int flags = 0;
if ((n & REPEATEDflag) != 0) flags |= REPEATED;
if ((n & DEFflag) != 0) flags |= DEF;
return flags;
}
AConstant readConstant() {
int tag = bytes[bp++];
int len = readNat();
switch (tag) {
case LITERALunit:
return AConstant.UNIT;
case LITERALboolean:
return AConstant.BOOLEAN(readByte() == 0 ? false : true);
case LITERALbyte:
return AConstant.BYTE((byte)readLong(len));
case LITERALshort:
return AConstant.SHORT((short)readLong(len));
case LITERALchar:
return AConstant.CHAR((char)readLong(len));
case LITERALint:
return AConstant.INT((int)readLong(len));
case LITERALlong:
return AConstant.LONG(readLong(len));
case LITERALfloat:
return AConstant.FLOAT(Float.intBitsToFloat((int)readLong(len)));
case LITERALdouble:
return AConstant.DOUBLE(Double.longBitsToDouble(readLong(len)));
case LITERALstring:
return AConstant.STRING(readNameRef().toString());
case LITERALnull:
return AConstant.NULL;
case LITERALzero:
return AConstant.ZERO;
default:
throw Debug.abort("illegal tag: " + tag);
}
}
AConstant readConstantRef() {
int n = readNat();
int savedBp = bp;
bp = index[n];
AConstant constant = readConstant();
bp = savedBp;
return constant;
}
public static class BadSignature extends java.lang.Error {
public BadSignature(UnPickle outer, String msg) {
super("symbol data " + outer.sourceName +
" could not be loaded; " + msg);
}
public BadSignature(UnPickle outer) {
this(outer, "malformed signature at " + outer.bp);
}
}
// --- print symbl files -------------------------------------------------
private static String tag2string(int tag) {
switch (tag) {
case TERMname: return "TERMname";
case TYPEname: return "TYPEname";
case NONEsym: return "NONEsym";
case TYPEsym: return "TYPEsym";
case ALIASsym: return "ALIASsym";
case CLASSsym: return "CLASSsym";
case VALsym: return "VALsym";
case EXTref: return "EXTref";
case EXTMODCLASSref: return "EXTMODCLASSref";
case NOtpe: return "NOtpe";
case THIStpe: return "THIStpe";
case SINGLEtpe: return "SINGLEtpe";
case TYPEREFtpe: return "TYPEREFtpe";
case CONSTANTtpe: return "CONSTANTtpe";
case COMPOUNDtpe: return "COMPOUNDtpe";
case METHODtpe: return "METHODtpe";
case POLYtpe: return "POLYtpe";
case OVERLOADEDtpe: return "OVERLOADEDtpe";
case UNBOXEDtpe: return "UNBOXEDtpe";
case UNBOXEDARRAYtpe: return "UNBOXEDARRAYtpe";
case FLAGGEDtpe: return "FLAGGEDtpe";
case ERRORtpe: return "ERRORtpe";
case LITERALunit: return "LITERALunit";
case LITERALboolean: return "LITERALboolean";
case LITERALbyte: return "LITERALbyte";
case LITERALshort: return "LITERALshort";
case LITERALchar: return "LITERALchar";
case LITERALint: return "LITERALint";
case LITERALlong: return "LITERALlong";
case LITERALfloat: return "LITERALfloat";
case LITERALdouble: return "LITERALdouble";
case LITERALstring: return "LITERALstring";
case LITERALnull: return "LITERALnull";
case LITERALzero: return "LITERALzero";
default: return "***BAD TAG***(" + tag + ")";
}
}
private void print(PrintStream out) {
out.println("symbl attribute for " + classroot + ":");
for (int i = 0; i < index.length; i++) {
out.print(i + "," + index[i] + ": ");
bp = index[i];
int tag = readByte();
out.print(tag2string(tag));
int len = readNat();
int end = len + bp;
out.print(" " + len);
switch (tag) {
case TERMname:
case TYPEname:
out.print(" " +
SourceRepresentation.ascii2string(bytes, bp, len));
bp = end;
break;
case NONEsym:
break;
case TYPEsym:
case ALIASsym:
case CLASSsym:
case VALsym:
out.print(" " + readNat()); //name
out.print(" " + readNat()); //owner
out.print(" " + Integer.toHexString(readNat())); //flags
out.print(" " + readNat()); //type
break;
case FLAGGEDtpe:
out.print(" " + Integer.toHexString(readNat())); //flags
}
while (bp < end) out.print(" " + readNat());
out.println();
}
}
}