blob: da48df9af86fcb0eea4cba8a706ef065a28bac6b [file] [log] [blame]
/*
* Copyright (c) 2001, 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.util.jar.pack;
import com.sun.java.util.jar.pack.ConstantPool.Entry;
import com.sun.java.util.jar.pack.ConstantPool.Index;
import com.sun.java.util.jar.pack.ConstantPool.NumberEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Writer for a class file that is incorporated into a package.
* @author John Rose
*/
class ClassWriter {
int verbose;
Package pkg;
Class cls;
DataOutputStream out;
Index cpIndex;
Index bsmIndex;
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 (cls.hasBootstrapMethods()) {
this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods",
cls.getBootstrapMethodMap());
}
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 {
writeRef(e, cpIndex);
}
/** Write a 2-byte int representing a CP entry, using the given cpIndex. */
private void writeRef(Entry e, Index cpIndex) 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.version.minor);
writeShort(cls.version.major);
}
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:
throw new AssertionError("CP should have Signatures remapped to Utf8");
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:
case CONSTANT_MethodType:
writeRef(e.getRef(0));
break;
case CONSTANT_MethodHandle:
MethodHandleEntry mhe = (MethodHandleEntry) e;
out.writeByte(mhe.refKind);
writeRef(mhe.getRef(0));
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType:
writeRef(e.getRef(0));
writeRef(e.getRef(1));
break;
case CONSTANT_InvokeDynamic:
writeRef(e.getRef(0), bsmIndex);
writeRef(e.getRef(1));
break;
case CONSTANT_BootstrapMethod:
throw new AssertionError("CP should have BootstrapMethods moved to side-table");
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<? extends Class.Member> mems;
if (!doMethods)
mems = cls.getFields();
else
mems = cls.getMethods();
writeShort(mems.size());
for (Class.Member m : mems) {
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);
}
private void reorderBSMandICS(Attribute.Holder h) {
Attribute bsmAttr = h.getAttribute(Package.attrBootstrapMethodsEmpty);
if (bsmAttr == null) return;
Attribute icsAttr = h.getAttribute(Package.attrInnerClassesEmpty);
if (icsAttr == null) return;
int bsmidx = h.attributes.indexOf(bsmAttr);
int icsidx = h.attributes.indexOf(icsAttr);
if (bsmidx > icsidx) {
h.attributes.remove(bsmAttr);
h.attributes.add(icsidx, bsmAttr);
}
return;
}
// 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;
}
// there may be cases if an InnerClass attribute is explicit, then the
// ordering could be wrong, fix the ordering before we write it out.
if (h instanceof Package.Class)
reorderBSMandICS(h);
writeShort(h.attributes.size());
for (Attribute a : h.attributes) {
a.finishRefs(cpIndex);
writeRef(a.getNameRef());
if (a.layout() == Package.attrCodeEmpty ||
a.layout() == Package.attrBootstrapMethodsEmpty ||
a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired.
DataOutputStream savedOut = out;
assert(out != bufOut);
buf.reset();
out = bufOut;
if ("Code".equals(a.name())) {
Class.Method m = (Class.Method) h;
writeCode(m.code);
} else if ("BootstrapMethods".equals(a.name())) {
assert(h == cls);
writeBootstrapMethods(cls);
} else if ("InnerClasses".equals(a.name())) {
assert(h == cls);
writeInnerClasses(cls);
} else {
throw new AssertionError();
}
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 writeBootstrapMethods(Class cls) throws IOException {
List<BootstrapMethodEntry> bsms = cls.getBootstrapMethods();
writeShort(bsms.size());
for (BootstrapMethodEntry e : bsms) {
writeRef(e.bsmRef);
writeShort(e.argRefs.length);
for (Entry argRef : e.argRefs) {
writeRef(argRef);
}
}
}
void writeInnerClasses(Class cls) throws IOException {
List<InnerClass> ics = cls.getInnerClasses();
writeShort(ics.size());
for (InnerClass ic : ics) {
writeRef(ic.thisClass);
writeRef(ic.outerClass);
writeRef(ic.name);
writeShort(ic.flags);
}
}
}