1/*
2 * Copyright (c) 2001, 2012, 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
26package com.sun.java.util.jar.pack;
27
28
29import com.sun.java.util.jar.pack.ConstantPool.Entry;
30import com.sun.java.util.jar.pack.ConstantPool.Index;
31import com.sun.java.util.jar.pack.ConstantPool.NumberEntry;
32import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
33import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
34import com.sun.java.util.jar.pack.Package.Class;
35import com.sun.java.util.jar.pack.Package.InnerClass;
36import java.io.BufferedOutputStream;
37import java.io.ByteArrayOutputStream;
38import java.io.DataOutputStream;
39import java.io.IOException;
40import java.io.OutputStream;
41import java.util.List;
42import static com.sun.java.util.jar.pack.Constants.*;
43/**
44 * Writer for a class file that is incorporated into a package.
45 * @author John Rose
46 */
47class ClassWriter {
48    int verbose;
49
50    Package pkg;
51    Class cls;
52    DataOutputStream out;
53    Index cpIndex;
54    Index bsmIndex;
55
56    ClassWriter(Class cls, OutputStream out) throws IOException {
57        this.pkg = cls.getPackage();
58        this.cls = cls;
59        this.verbose = pkg.verbose;
60        this.out = new DataOutputStream(new BufferedOutputStream(out));
61        this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap());
62        this.cpIndex.flattenSigs = true;
63        if (cls.hasBootstrapMethods()) {
64            this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods",
65                                                   cls.getBootstrapMethodMap());
66        }
67        if (verbose > 1)
68            Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString()));
69    }
70
71    private void writeShort(int x) throws IOException {
72        out.writeShort(x);
73    }
74
75    private void writeInt(int x) throws IOException {
76        out.writeInt(x);
77    }
78
79    /** Write a 2-byte int representing a CP entry, using the local cpIndex. */
80    private void writeRef(Entry e) throws IOException {
81        writeRef(e, cpIndex);
82    }
83
84    /** Write a 2-byte int representing a CP entry, using the given cpIndex. */
85    private void writeRef(Entry e, Index cpIndex) throws IOException {
86        int i = (e == null) ? 0 : cpIndex.indexOf(e);
87        writeShort(i);
88    }
89
90    void write() throws IOException {
91        boolean ok = false;
92        try {
93            if (verbose > 1)  Utils.log.fine("...writing "+cls);
94            writeMagicNumbers();
95            writeConstantPool();
96            writeHeader();
97            writeMembers(false);  // fields
98            writeMembers(true);   // methods
99            writeAttributes(ATTR_CONTEXT_CLASS, cls);
100            /* Closing here will cause all the underlying
101               streams to close, Causing the jar stream
102               to close prematurely, instead we just flush.
103               out.close();
104             */
105            out.flush();
106            ok = true;
107        } finally {
108            if (!ok) {
109                Utils.log.warning("Error on output of "+cls);
110            }
111        }
112    }
113
114    void writeMagicNumbers() throws IOException {
115        writeInt(cls.magic);
116        writeShort(cls.version.minor);
117        writeShort(cls.version.major);
118    }
119
120    void writeConstantPool() throws IOException {
121        Entry[] cpMap = cls.cpMap;
122        writeShort(cpMap.length);
123        for (int i = 0; i < cpMap.length; i++) {
124            Entry e = cpMap[i];
125            assert((e == null) == (i == 0 || cpMap[i-1] != null && cpMap[i-1].isDoubleWord()));
126            if (e == null)  continue;
127            byte tag = e.getTag();
128            if (verbose > 2)  Utils.log.fine("   CP["+i+"] = "+e);
129            out.write(tag);
130            switch (tag) {
131                case CONSTANT_Signature:
132                    throw new AssertionError("CP should have Signatures remapped to Utf8");
133                case CONSTANT_Utf8:
134                    out.writeUTF(e.stringValue());
135                    break;
136                case CONSTANT_Integer:
137                    out.writeInt(((NumberEntry)e).numberValue().intValue());
138                    break;
139                case CONSTANT_Float:
140                    float fval = ((NumberEntry)e).numberValue().floatValue();
141                    out.writeInt(Float.floatToRawIntBits(fval));
142                    break;
143                case CONSTANT_Long:
144                    out.writeLong(((NumberEntry)e).numberValue().longValue());
145                    break;
146                case CONSTANT_Double:
147                    double dval = ((NumberEntry)e).numberValue().doubleValue();
148                    out.writeLong(Double.doubleToRawLongBits(dval));
149                    break;
150                case CONSTANT_Class:
151                case CONSTANT_String:
152                case CONSTANT_MethodType:
153                    writeRef(e.getRef(0));
154                    break;
155                case CONSTANT_MethodHandle:
156                    MethodHandleEntry mhe = (MethodHandleEntry) e;
157                    out.writeByte(mhe.refKind);
158                    writeRef(mhe.getRef(0));
159                    break;
160                case CONSTANT_Fieldref:
161                case CONSTANT_Methodref:
162                case CONSTANT_InterfaceMethodref:
163                case CONSTANT_NameandType:
164                    writeRef(e.getRef(0));
165                    writeRef(e.getRef(1));
166                    break;
167                case CONSTANT_InvokeDynamic:
168                    writeRef(e.getRef(0), bsmIndex);
169                    writeRef(e.getRef(1));
170                    break;
171                case CONSTANT_BootstrapMethod:
172                    throw new AssertionError("CP should have BootstrapMethods moved to side-table");
173                default:
174                    throw new IOException("Bad constant pool tag "+tag);
175            }
176        }
177    }
178
179    void writeHeader() throws IOException {
180        writeShort(cls.flags);
181        writeRef(cls.thisClass);
182        writeRef(cls.superClass);
183        writeShort(cls.interfaces.length);
184        for (int i = 0; i < cls.interfaces.length; i++) {
185            writeRef(cls.interfaces[i]);
186        }
187    }
188
189    void writeMembers(boolean doMethods) throws IOException {
190        List<? extends Class.Member> mems;
191        if (!doMethods)
192            mems = cls.getFields();
193        else
194            mems = cls.getMethods();
195        writeShort(mems.size());
196        for (Class.Member m : mems) {
197            writeMember(m, doMethods);
198        }
199    }
200
201    void writeMember(Class.Member m, boolean doMethod) throws IOException {
202        if (verbose > 2)  Utils.log.fine("writeMember "+m);
203        writeShort(m.flags);
204        writeRef(m.getDescriptor().nameRef);
205        writeRef(m.getDescriptor().typeRef);
206        writeAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
207                        m);
208    }
209
210    private void reorderBSMandICS(Attribute.Holder h) {
211        Attribute bsmAttr = h.getAttribute(Package.attrBootstrapMethodsEmpty);
212        if (bsmAttr == null) return;
213
214        Attribute icsAttr = h.getAttribute(Package.attrInnerClassesEmpty);
215        if (icsAttr == null) return;
216
217        int bsmidx = h.attributes.indexOf(bsmAttr);
218        int icsidx = h.attributes.indexOf(icsAttr);
219        if (bsmidx > icsidx) {
220            h.attributes.remove(bsmAttr);
221            h.attributes.add(icsidx, bsmAttr);
222        }
223        return;
224    }
225
226    // handy buffer for collecting attrs
227    ByteArrayOutputStream buf    = new ByteArrayOutputStream();
228    DataOutputStream      bufOut = new DataOutputStream(buf);
229
230    void writeAttributes(int ctype, Attribute.Holder h) throws IOException {
231        if (h.attributes == null) {
232            writeShort(0);  // attribute size
233            return;
234        }
235        // there may be cases if an InnerClass attribute is explicit, then the
236        // ordering could be wrong, fix the ordering before we write it out.
237        if (h instanceof Package.Class)
238            reorderBSMandICS(h);
239
240        writeShort(h.attributes.size());
241        for (Attribute a : h.attributes) {
242            a.finishRefs(cpIndex);
243            writeRef(a.getNameRef());
244            if (a.layout() == Package.attrCodeEmpty ||
245                a.layout() == Package.attrBootstrapMethodsEmpty ||
246                a.layout() == Package.attrInnerClassesEmpty) {
247                // These are hardwired.
248                DataOutputStream savedOut = out;
249                assert(out != bufOut);
250                buf.reset();
251                out = bufOut;
252                if ("Code".equals(a.name())) {
253                    Class.Method m = (Class.Method) h;
254                    writeCode(m.code);
255                } else if ("BootstrapMethods".equals(a.name())) {
256                    assert(h == cls);
257                    writeBootstrapMethods(cls);
258                } else if ("InnerClasses".equals(a.name())) {
259                    assert(h == cls);
260                    writeInnerClasses(cls);
261                } else {
262                    throw new AssertionError();
263                }
264                out = savedOut;
265                if (verbose > 2)
266                    Utils.log.fine("Attribute "+a.name()+" ["+buf.size()+"]");
267                writeInt(buf.size());
268                buf.writeTo(out);
269            } else {
270                if (verbose > 2)
271                    Utils.log.fine("Attribute "+a.name()+" ["+a.size()+"]");
272                writeInt(a.size());
273                out.write(a.bytes());
274            }
275        }
276    }
277
278    void writeCode(Code code) throws IOException {
279        code.finishRefs(cpIndex);
280        writeShort(code.max_stack);
281        writeShort(code.max_locals);
282        writeInt(code.bytes.length);
283        out.write(code.bytes);
284        int nh = code.getHandlerCount();
285        writeShort(nh);
286        for (int i = 0; i < nh; i++) {
287             writeShort(code.handler_start[i]);
288             writeShort(code.handler_end[i]);
289             writeShort(code.handler_catch[i]);
290             writeRef(code.handler_class[i]);
291        }
292        writeAttributes(ATTR_CONTEXT_CODE, code);
293    }
294
295    void writeBootstrapMethods(Class cls) throws IOException {
296        List<BootstrapMethodEntry> bsms = cls.getBootstrapMethods();
297        writeShort(bsms.size());
298        for (BootstrapMethodEntry e : bsms) {
299            writeRef(e.bsmRef);
300            writeShort(e.argRefs.length);
301            for (Entry argRef : e.argRefs) {
302                writeRef(argRef);
303            }
304        }
305    }
306
307    void writeInnerClasses(Class cls) throws IOException {
308        List<InnerClass> ics = cls.getInnerClasses();
309        writeShort(ics.size());
310        for (InnerClass ic : ics) {
311            writeRef(ic.thisClass);
312            writeRef(ic.outerClass);
313            writeRef(ic.name);
314            writeShort(ic.flags);
315        }
316    }
317}
318