/*
* Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.java.util.jar.pack;
import java.io.*;
import java.util.*;
import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass;
import com.sun.java.util.jar.pack.ConstantPool.*;
/**
* Writer for a class file that is incorporated into a package.
* @author John Rose
*/
class ClassWriter implements Constants {
int verbose;
Package pkg;
Class cls;
DataOutputStream out;
Index cpIndex;
ClassWriter(Class cls, OutputStream out) throws IOException {
this.pkg = cls.getPackage();
this.cls = cls;
this.verbose = pkg.verbose;
this.out = new DataOutputStream(new BufferedOutputStream(out));
this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap());
this.cpIndex.flattenSigs = true;
if (verbose > 1)
Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString()));
}
private void writeShort(int x) throws IOException {
out.writeShort(x);
}
private void writeInt(int x) throws IOException {
out.writeInt(x);
}
/** Write a 2-byte int representing a CP entry, using the local cpIndex. */
private void writeRef(Entry e) throws IOException {
int i = (e == null) ? 0 : cpIndex.indexOf(e);
writeShort(i);
}
void write() throws IOException {
boolean ok = false;
try {
if (verbose > 1) Utils.log.fine("...writing "+cls);
writeMagicNumbers();
writeConstantPool();
writeHeader();
writeMembers(false); // fields
writeMembers(true); // methods
writeAttributes(ATTR_CONTEXT_CLASS, cls);
/* Closing here will cause all the underlying
streams to close, Causing the jar stream
to close prematurely, instead we just flush.
out.close();
*/
out.flush();
ok = true;
} finally {
if (!ok) {
Utils.log.warning("Error on output of "+cls);
}
}
}
void writeMagicNumbers() throws IOException {
writeInt(cls.magic);
writeShort(cls.minver);
writeShort(cls.majver);
}
void writeConstantPool() throws IOException {
Entry[] cpMap = cls.cpMap;
writeShort(cpMap.length);
for (int i = 0; i < cpMap.length; i++) {
Entry e = cpMap[i];
assert((e == null) == (i == 0 || cpMap[i-1] != null && cpMap[i-1].isDoubleWord()));
if (e == null) continue;
byte tag = e.getTag();
if (verbose > 2) Utils.log.fine(" CP["+i+"] = "+e);
out.write(tag);
switch (tag) {
case CONSTANT_Signature:
assert(false); // should not reach here
break;
case CONSTANT_Utf8:
out.writeUTF(e.stringValue());
break;
case CONSTANT_Integer:
out.writeInt(((NumberEntry)e).numberValue().intValue());
break;
case CONSTANT_Float:
float fval = ((NumberEntry)e).numberValue().floatValue();
out.writeInt(Float.floatToRawIntBits(fval));
break;
case CONSTANT_Long:
out.writeLong(((NumberEntry)e).numberValue().longValue());
break;
case CONSTANT_Double:
double dval = ((NumberEntry)e).numberValue().doubleValue();
out.writeLong(Double.doubleToRawLongBits(dval));
break;
case CONSTANT_Class:
case CONSTANT_String:
writeRef(e.getRef(0));
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType:
writeRef(e.getRef(0));
writeRef(e.getRef(1));
break;
default:
throw new IOException("Bad constant pool tag "+tag);
}
}
}
void writeHeader() throws IOException {
writeShort(cls.flags);
writeRef(cls.thisClass);
writeRef(cls.superClass);
writeShort(cls.interfaces.length);
for (int i = 0; i < cls.interfaces.length; i++) {
writeRef(cls.interfaces[i]);
}
}
void writeMembers(boolean doMethods) throws IOException {
List mems;
if (!doMethods)
mems = cls.getFields();
else
mems = cls.getMethods();
writeShort(mems.size());
for (Iterator i = mems.iterator(); i.hasNext(); ) {
Class.Member m = (Class.Member) i.next();
writeMember(m, doMethods);
}
}
void writeMember(Class.Member m, boolean doMethod) throws IOException {
if (verbose > 2) Utils.log.fine("writeMember "+m);
writeShort(m.flags);
writeRef(m.getDescriptor().nameRef);
writeRef(m.getDescriptor().typeRef);
writeAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
m);
}
// handy buffer for collecting attrs
ByteArrayOutputStream buf = new ByteArrayOutputStream();
DataOutputStream bufOut = new DataOutputStream(buf);
void writeAttributes(int ctype, Attribute.Holder h) throws IOException {
if (h.attributes == null) {
writeShort(0); // attribute size
return;
}
writeShort(h.attributes.size());
for (Iterator i = h.attributes.iterator(); i.hasNext(); ) {
Attribute a = (Attribute) i.next();
a.finishRefs(cpIndex);
writeRef(a.getNameRef());
if (a.layout() == Package.attrCodeEmpty ||
a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired.
DataOutputStream savedOut = out;
assert(out != bufOut);
buf.reset();
out = bufOut;
if (a.name() == "Code") {
Class.Method m = (Class.Method) h;
writeCode(m.code);
} else {
assert(h == cls);
writeInnerClasses(cls);
}
out = savedOut;
if (verbose > 2)
Utils.log.fine("Attribute "+a.name()+" ["+buf.size()+"]");
writeInt(buf.size());
buf.writeTo(out);
} else {
if (verbose > 2)
Utils.log.fine("Attribute "+a.name()+" ["+a.size()+"]");
writeInt(a.size());
out.write(a.bytes());
}
}
}
void writeCode(Code code) throws IOException {
code.finishRefs(cpIndex);
writeShort(code.max_stack);
writeShort(code.max_locals);
writeInt(code.bytes.length);
out.write(code.bytes);
int nh = code.getHandlerCount();
writeShort(nh);
for (int i = 0; i < nh; i++) {
writeShort(code.handler_start[i]);
writeShort(code.handler_end[i]);
writeShort(code.handler_catch[i]);
writeRef(code.handler_class[i]);
}
writeAttributes(ATTR_CONTEXT_CODE, code);
}
void writeInnerClasses(Class cls) throws IOException {
List ics = cls.getInnerClasses();
writeShort(ics.size());
for (Iterator i = ics.iterator(); i.hasNext(); ) {
InnerClass ic = (InnerClass) i.next();
writeRef(ic.thisClass);
writeRef(ic.outerClass);
writeRef(ic.name);
writeShort(ic.flags);
}
}
}