/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.symtab.classfile; import ch.epfl.lamp.util.Position; import java.util.HashMap; import java.io.*; import scalac.Global; import scalac.ApplicationError; import scalac.util.*; import scalac.symtab.*; import Symbol.*; import Type.*; public class Pickle implements Kinds, Modifiers, EntryTags { final static boolean debug = false; /*************************************************** * Symbol table attribute format: see EntryTags.java */ public byte[] bytes; private int bp; private Name rootname; private Symbol rootowner; private HashMap index; private Object[] entries; private int ep; /** Write symbol table info for root. * root must be either a module or a class. */ public Pickle() { index = new HashMap(); entries = new Object[256]; ep = 0; } /** Pickle all symbols descending from `root'. */ public void add(Symbol root) { if (root.pos != Position.NOPOS) { if (Global.instance.debug) System.out.println("pickling " + root); if (index.get(root) == null) { this.rootname = root.name.toTermName(); this.rootowner = root.owner(); putSymbol(root); } } } /** Finalize pickler with given fullname. */ public void pickle() { bytes = new byte[4096]; bp = 0; writeAttr(); this.index = null; this.entries = null; } /** The number of elements defined in `bytes'. */ public int size() { return bp; } /** Create output file with given extension for given class. */ public File outputFile(Name fullname, String extension) { if (Global.instance.outpath != null) { return new File( Global.instance.outpath, SourceRepresentation.externalizeFileName(fullname) + extension); } else { String s = fullname.toString(); int i = s.length(); while (i > 0 && s.charAt(i - 1) != '.') i--; return new File(s.substring(i) + extension); } } private void createPath(File f) { try { f.createNewFile(); } catch (IOException ex) { f.getParentFile().mkdirs(); try { f.createNewFile(); } catch (IOException ex1) { } } } public void writeFile(Name fullname) { File outfile = outputFile(fullname, ".symbl"); try { createPath(outfile); FileOutputStream out = new FileOutputStream(outfile); out.write(bytes, 0, bp); out.close(); Global.instance.operation("wrote " + outfile); } catch (IOException ex) { System.err.println("error writing " + outfile); } } /* ************************************************** * Phase 1: Build entry table ************************************************* */ /** Is root in symbol.owner*? */ private boolean isLocal(Symbol sym) { return sym.name.toTermName() == rootname && sym.owner() == rootowner || (sym.kind != NONE && isLocal(sym.owner())); } /** Store entry `e' in index at next available position unless it it * already there. Return true iff entry is new. */ private boolean putEntry(Object e) { Integer n = (Integer) index.get(e); if (n == null) { //System.out.println("entry " + e);//DEBUG if (ep == entries.length) { Object[] entries1 = new Object[ep * 2]; System.arraycopy(entries, 0, entries1, 0, ep); entries = entries1; } entries[ep] = e; index.put(e, new Integer(ep)); ep++; return true; } else { return false; } } /** Store symbol in index. If symbol is local, also store * everything it refers to. */ private void putSymbol(Symbol sym) { if (putEntry(sym)) { if (debug) System.out.println("put " + sym); if (isLocal(sym)) { putEntry(sym.name); putSymbol(sym.owner()); putType(sym.info()); switch (sym.kind) { case TYPE: putType(sym.loBound()); break; case ALIAS: break; case CLASS: putType(sym.typeOfThis()); putSymbol(sym.allConstructors()); for (Scope.SymbolIterator it = sym.members().iterator(); it.hasNext();) putSymbol(it.next()); break; case VAL: if (sym.isPrimaryConstructor()) putSymbol(sym.primaryConstructorClass()); else if (sym.isModule()) putSymbol(sym.moduleClass()); break; default: throw new ApplicationError(); } } else if (sym.kind != NONE) { putEntry(sym.isModuleClass() ? sym.name.toTermName() : sym.name); if (sym.owner() != Global.instance.definitions.ROOT_CLASS) putSymbol(sym.owner()); } } } private void putSymbols(Symbol[] syms) { for (int i = 0; i < syms.length; i++) putSymbol(syms[i]); } /** Store type and everythig it refers to in index. */ private void putType(Type tp) { if (putEntry(tp)) { switch (tp) { case NoType: break; case ThisType(Symbol sym): putSymbol(sym); break; case SingleType(Type pre, Symbol sym): putType(pre); putSymbol(sym); break; case TypeRef(Type pre, Symbol sym, Type[] args): putType(pre); putSymbol(sym); putTypes(args); break; case CompoundType(Type[] parents, Scope members): putSymbol(tp.symbol()); putTypes(parents); break; case MethodType(Symbol[] vparams, Type result): putType(result); for (int i = 0; i < vparams.length; i++) { Type ptype = vparams[i].type(); putType(ptype); int pflags = vparams[i].flags; if ((pflags & (COVARIANT | CONTRAVARIANT | REPEATED | DEF)) != 0) putEntry(new FlagsAndType(encodeFlags(pflags), ptype)); } break; case PolyType(Symbol[] tparams, Type result): putType(result); putSymbols(tparams); break; case OverloadedType(Symbol[] alts, Type[] alttypes): for (int i = 0; i < alts.length; i++) alts[i].flags |= ALTERNATIVE; putSymbols(alts); putTypes(alttypes); break; default: throw new ApplicationError(); } } } private void putTypes(Type[] tps) { for (int i = 0; i < tps.length; i++) putType(tps[i]); } /* ************************************************** * Phase 2: Write byte array ************************************************* */ private void resizeTo(int size) { byte[] bytes1 = new byte[size]; System.arraycopy(bytes, 0, bytes1, 0, bp); bytes = bytes1; } /** Write a byte of data */ private void writeByte(int b) { if (bp == bytes.length) resizeTo(bytes.length * 2); bytes[bp++] = (byte)b; if (debug) System.out.print(b + " "); } /** Write a natural number in big endian format, base 128. * All but the last digits have bit 0x80 set. */ private void writeNat(int x) { int y = x >>> 7; if (y != 0) writeNatPrefix(y); writeByte(x & 0x7f); } private void writeNatPrefix(int x) { int y = x >>> 7; if (y != 0) writeNatPrefix(y); writeByte((x & 0x7f) | 0x80); } /** Write a natural number at `pos' * If number is more than one byte, shift rest of array to make space. */ private void patchNat(int pos, int x) { bytes[pos] = (byte) (x & 0x7f); int y = x >>> 7; if (y != 0) patchNatPrefix(pos, y); } private void patchNatPrefix(int pos, int x) { writeByte(0); System.arraycopy(bytes, pos, bytes, pos+1, bp - (pos+1)); bytes[pos] = (byte) ((x & 0x7f) | 0x80); int y = x >>> 7; if (y != 0) patchNatPrefix(pos, y); } /** Write a reference to object, i.e., the object's number in the index. */ private void writeRef(Object ref) { Integer i = (Integer) index.get(ref); assert i != null : ref + " " + ref.getClass(); writeNat(i.intValue()); } private void writeRefs(Object[] es) { for (int i = 0; i < es.length; i++) writeRef(es[i]); } /** Write a name entry. Names are stored in Utf8 format. */ private void writeName(Name name) { writeByte(name.isTermName() ? TERMname : TYPEname); writeByte(0); // space for length while (bp + name.length() > bytes.length) resizeTo(bytes.length * 2); name.copyAscii(bytes, bp); if (debug) System.out.print(name); bp = bp + name.length(); } /** Write a symbol entry. */ private void writeSymbol(Symbol sym) { if (debug) System.out.println("write " + sym); if (isLocal(sym)) { switch (sym.kind) { case TYPE: writeByte(TYPEsym); break; case ALIAS: writeByte(ALIASsym); break; case CLASS: writeByte(CLASSsym); break; case VAL: writeByte(VALsym); break; default: throw new ApplicationError(); } writeByte(0); // space for length writeRef(sym.name); writeRef(sym.owner()); writeNat(sym.flags); writeRef(sym.info()); switch (sym.kind) { case TYPE: writeRef(sym.loBound()); break; case ALIAS: break; case CLASS: writeRef(sym.typeOfThis()); writeRef(sym.allConstructors()); break; case VAL: if (sym.isPrimaryConstructor()) writeRef(sym.primaryConstructorClass()); else if (sym.isModule()) writeRef(sym.moduleClass()); break; } } else if (sym.kind == NONE) { writeByte(NONEsym); writeByte(0); // space for length } else { if (sym.isModuleClass()) { writeByte(EXTMODCLASSref); writeByte(0); // space for length writeRef(sym.name.toTermName()); } else { writeByte(EXTref); writeByte(0); // space for length writeRef(sym.name); } if (sym.owner() != Global.instance.definitions.ROOT_CLASS) writeRef(sym.owner()); } sym.flags &= ~ALTERNATIVE; } /** Write a type entry. */ private void writeType(Type tp) { switch (tp) { case NoType: writeByte(NOtpe); writeByte(0); // space for length break; case ThisType(Symbol sym): writeByte(THIStpe); writeByte(0); // space for length writeRef(sym); break; case SingleType(Type pre, Symbol sym): writeByte(SINGLEtpe); writeByte(0); // space for length writeRef(pre); writeRef(sym); break; case TypeRef(Type pre, Symbol sym, Type[] args): writeByte(TYPEREFtpe); writeByte(0); // space for length writeRef(pre); writeRef(sym); writeRefs(args); break; case CompoundType(Type[] parents, Scope members): writeByte(COMPOUNDtpe); writeByte(0); // space for length writeRef(tp.symbol()); writeRefs(parents); break; case MethodType(Symbol[] vparams, Type result): writeByte(METHODtpe); writeByte(0); // space for length writeRef(result); for (int i = 0; i < vparams.length; i++) { Type ptype = vparams[i].type(); int pflags = vparams[i].flags; if ((pflags & (COVARIANT | CONTRAVARIANT | REPEATED | DEF)) != 0) writeRef(new FlagsAndType(encodeFlags(pflags), ptype)); else writeRef(ptype); } break; case PolyType(Symbol[] tparams, Type result): writeByte(POLYtpe); writeByte(0); // space for length writeRef(result); writeRefs(tparams); break; case OverloadedType(Symbol[] alts, Type[] alttypes): writeByte(OVERLOADEDtpe); writeByte(0); // space for length writeRefs(alts); writeRefs(alttypes); break; default: throw new ApplicationError(); } } private void writeFlagsAndType(FlagsAndType ft) { writeByte(FLAGGEDtpe); writeByte(0); // space for length writeNat(ft.flags); writeRef(ft.type); } private void writeEntry(Object e) { int startpos = bp; if (e instanceof Symbol) writeSymbol((Symbol) e); else if (e instanceof Type) writeType((Type) e); else if (e instanceof Name) writeName((Name) e); else if (e instanceof FlagsAndType) writeFlagsAndType((FlagsAndType) e); else throw new ApplicationError(); patchNat(startpos + 1, bp - (startpos + 2)); } private void writeAttr() { writeNat(ep); for (int i = 0; i < ep; i++) { if (debug) System.out.print(i + "," + bp + ": "); writeEntry(entries[i]); if (debug) System.out.print("(" + entries[i] + ")"); if (debug) System.out.println(); } } private static int encodeFlags(int flags) { int n = 0; if ((flags & COVARIANT) != 0) n |= COVARflag; if ((flags & CONTRAVARIANT) != 0) n |= CONTRAVARflag; if ((flags & REPEATED) != 0) n |= REPEATEDflag; if ((flags & DEF) != 0) n |= DEFflag; return n; } static class FlagsAndType { int flags; Type type; FlagsAndType(int flags, Type type) { this.flags = flags; this.type = type; } public boolean equals(Object other) { if (other instanceof FlagsAndType) { FlagsAndType that = (FlagsAndType) other; return this.type.equals(that.type) && this.flags == that.flags; } else { return false; } } public int hashCode() { return 37 + ((flags * 41) ^ type.hashCode()); } } }