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