1 /*
   2  * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.java.util.jar.pack;
  27 
  28 
  29 import com.sun.java.util.jar.pack.ConstantPool.Entry;
  30 import com.sun.java.util.jar.pack.ConstantPool.Index;
  31 import com.sun.java.util.jar.pack.ConstantPool.NumberEntry;
  32 import com.sun.java.util.jar.pack.Package.Class;
  33 import com.sun.java.util.jar.pack.Package.InnerClass;
  34 import java.io.BufferedOutputStream;
  35 import java.io.ByteArrayOutputStream;
  36 import java.io.DataOutputStream;
  37 import java.io.IOException;
  38 import java.io.OutputStream;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 
  42 /**
  43  * Writer for a class file that is incorporated into a package.
  44  * @author John Rose
  45  */
  46 class ClassWriter implements Constants {
  47     int verbose;
  48 
  49     Package pkg;
  50     Class cls;
  51     DataOutputStream out;
  52     Index cpIndex;
  53 
  54     ClassWriter(Class cls, OutputStream out) throws IOException {
  55         this.pkg = cls.getPackage();
  56         this.cls = cls;
  57         this.verbose = pkg.verbose;
  58         this.out = new DataOutputStream(new BufferedOutputStream(out));
  59         this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap());
  60         this.cpIndex.flattenSigs = true;
  61         if (verbose > 1)
  62             Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString()));
  63     }
  64 
  65     private void writeShort(int x) throws IOException {
  66         out.writeShort(x);
  67     }
  68 
  69     private void writeInt(int x) throws IOException {
  70         out.writeInt(x);
  71     }
  72 
  73     /** Write a 2-byte int representing a CP entry, using the local cpIndex. */
  74     private void writeRef(Entry e) throws IOException {
  75         int i = (e == null) ? 0 : cpIndex.indexOf(e);
  76         writeShort(i);
  77     }
  78 
  79     void write() throws IOException {
  80         boolean ok = false;
  81         try {
  82             if (verbose > 1)  Utils.log.fine("...writing "+cls);
  83             writeMagicNumbers();
  84             writeConstantPool();
  85             writeHeader();
  86             writeMembers(false);  // fields
  87             writeMembers(true);   // methods
  88             writeAttributes(ATTR_CONTEXT_CLASS, cls);
  89             /* Closing here will cause all the underlying
  90                streams to close, Causing the jar stream
  91                to close prematurely, instead we just flush.
  92                out.close();
  93              */
  94             out.flush();
  95             ok = true;
  96         } finally {
  97             if (!ok) {
  98                 Utils.log.warning("Error on output of "+cls);
  99             }
 100         }
 101     }
 102 
 103     void writeMagicNumbers() throws IOException {
 104         writeInt(cls.magic);
 105         writeShort(cls.minver);
 106         writeShort(cls.majver);
 107     }
 108 
 109     void writeConstantPool() throws IOException {
 110         Entry[] cpMap = cls.cpMap;
 111         writeShort(cpMap.length);
 112         for (int i = 0; i < cpMap.length; i++) {
 113             Entry e = cpMap[i];
 114             assert((e == null) == (i == 0 || cpMap[i-1] != null && cpMap[i-1].isDoubleWord()));
 115             if (e == null)  continue;
 116             byte tag = e.getTag();
 117             if (verbose > 2)  Utils.log.fine("   CP["+i+"] = "+e);
 118             out.write(tag);
 119             switch (tag) {
 120                 case CONSTANT_Signature:
 121                     assert(false);  // should not reach here
 122                     break;
 123                 case CONSTANT_Utf8:
 124                     out.writeUTF(e.stringValue());
 125                     break;
 126                 case CONSTANT_Integer:
 127                     out.writeInt(((NumberEntry)e).numberValue().intValue());
 128                     break;
 129                 case CONSTANT_Float:
 130                     float fval = ((NumberEntry)e).numberValue().floatValue();
 131                     out.writeInt(Float.floatToRawIntBits(fval));
 132                     break;
 133                 case CONSTANT_Long:
 134                     out.writeLong(((NumberEntry)e).numberValue().longValue());
 135                     break;
 136                 case CONSTANT_Double:
 137                     double dval = ((NumberEntry)e).numberValue().doubleValue();
 138                     out.writeLong(Double.doubleToRawLongBits(dval));
 139                     break;
 140                 case CONSTANT_Class:
 141                 case CONSTANT_String:
 142                     writeRef(e.getRef(0));
 143                     break;
 144                 case CONSTANT_Fieldref:
 145                 case CONSTANT_Methodref:
 146                 case CONSTANT_InterfaceMethodref:
 147                 case CONSTANT_NameandType:
 148                     writeRef(e.getRef(0));
 149                     writeRef(e.getRef(1));
 150                     break;
 151                 default:
 152                     throw new IOException("Bad constant pool tag "+tag);
 153             }
 154         }
 155     }
 156 
 157     void writeHeader() throws IOException {
 158         writeShort(cls.flags);
 159         writeRef(cls.thisClass);
 160         writeRef(cls.superClass);
 161         writeShort(cls.interfaces.length);
 162         for (int i = 0; i < cls.interfaces.length; i++) {
 163             writeRef(cls.interfaces[i]);
 164         }
 165     }
 166 
 167     void writeMembers(boolean doMethods) throws IOException {
 168         List mems;
 169         if (!doMethods)
 170             mems = cls.getFields();
 171         else
 172             mems = cls.getMethods();
 173         writeShort(mems.size());
 174         for (Iterator i = mems.iterator(); i.hasNext(); ) {
 175             Class.Member m = (Class.Member) i.next();
 176             writeMember(m, doMethods);
 177         }
 178     }
 179 
 180     void writeMember(Class.Member m, boolean doMethod) throws IOException {
 181         if (verbose > 2)  Utils.log.fine("writeMember "+m);
 182         writeShort(m.flags);
 183         writeRef(m.getDescriptor().nameRef);
 184         writeRef(m.getDescriptor().typeRef);
 185         writeAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
 186                         m);
 187     }
 188 
 189     // handy buffer for collecting attrs
 190     ByteArrayOutputStream buf    = new ByteArrayOutputStream();
 191     DataOutputStream      bufOut = new DataOutputStream(buf);
 192 
 193     void writeAttributes(int ctype, Attribute.Holder h) throws IOException {
 194         if (h.attributes == null) {
 195             writeShort(0);  // attribute size
 196             return;
 197         }
 198         writeShort(h.attributes.size());
 199         for (Iterator i = h.attributes.iterator(); i.hasNext(); ) {
 200             Attribute a = (Attribute) i.next();
 201             a.finishRefs(cpIndex);
 202             writeRef(a.getNameRef());
 203             if (a.layout() == Package.attrCodeEmpty ||
 204                 a.layout() == Package.attrInnerClassesEmpty) {
 205                 // These are hardwired.
 206                 DataOutputStream savedOut = out;
 207                 assert(out != bufOut);
 208                 buf.reset();
 209                 out = bufOut;
 210                 if (a.name() == "Code") {
 211                     Class.Method m = (Class.Method) h;
 212                     writeCode(m.code);
 213                 } else {
 214                     assert(h == cls);
 215                     writeInnerClasses(cls);
 216                 }
 217                 out = savedOut;
 218                 if (verbose > 2)
 219                     Utils.log.fine("Attribute "+a.name()+" ["+buf.size()+"]");
 220                 writeInt(buf.size());
 221                 buf.writeTo(out);
 222             } else {
 223                 if (verbose > 2)
 224                     Utils.log.fine("Attribute "+a.name()+" ["+a.size()+"]");
 225                 writeInt(a.size());
 226                 out.write(a.bytes());
 227             }
 228         }
 229     }
 230 
 231     void writeCode(Code code) throws IOException {
 232         code.finishRefs(cpIndex);
 233         writeShort(code.max_stack);
 234         writeShort(code.max_locals);
 235         writeInt(code.bytes.length);
 236         out.write(code.bytes);
 237         int nh = code.getHandlerCount();
 238         writeShort(nh);
 239         for (int i = 0; i < nh; i++) {
 240              writeShort(code.handler_start[i]);
 241              writeShort(code.handler_end[i]);
 242              writeShort(code.handler_catch[i]);
 243              writeRef(code.handler_class[i]);
 244         }
 245         writeAttributes(ATTR_CONTEXT_CODE, code);
 246     }
 247 
 248     void writeInnerClasses(Class cls) throws IOException {
 249         List ics = cls.getInnerClasses();
 250         writeShort(ics.size());
 251         for (Iterator i = ics.iterator(); i.hasNext(); ) {
 252             InnerClass ic = (InnerClass) i.next();
 253             writeRef(ic.thisClass);
 254             writeRef(ic.outerClass);
 255             writeRef(ic.name);
 256             writeShort(ic.flags);
 257         }
 258     }
 259 }