1 /* 2 * Copyright (c) 2015, 2016, 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 package jdk.internal.jimage.decompressor; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.ByteArrayOutputStream; 29 import java.io.DataInputStream; 30 import java.io.DataOutputStream; 31 import java.io.IOException; 32 import java.nio.ByteBuffer; 33 import java.nio.ByteOrder; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.Properties; 37 38 /** 39 * 40 * A Decompressor that reconstructs the constant pool of classes. 41 * 42 * @implNote This class needs to maintain JDK 8 source compatibility. 43 * 44 * It is used internally in the JDK to implement jimage/jrtfs access, 45 * but also compiled and delivered as part of the jrtfs.jar to support access 46 * to the jimage file provided by the shipped JDK by tools running on JDK 8. 47 */ 48 public class StringSharingDecompressor implements ResourceDecompressor { 49 50 public static final int EXTERNALIZED_STRING = 23; 51 public static final int EXTERNALIZED_STRING_DESCRIPTOR = 25; 52 53 private static final int CONSTANT_Utf8 = 1; 54 private static final int CONSTANT_Integer = 3; 55 private static final int CONSTANT_Float = 4; 56 private static final int CONSTANT_Long = 5; 57 private static final int CONSTANT_Double = 6; 58 private static final int CONSTANT_Class = 7; 59 private static final int CONSTANT_String = 8; 60 private static final int CONSTANT_Fieldref = 9; 61 private static final int CONSTANT_Methodref = 10; 62 private static final int CONSTANT_InterfaceMethodref = 11; 63 private static final int CONSTANT_NameAndType = 12; 64 private static final int CONSTANT_MethodHandle = 15; 65 private static final int CONSTANT_MethodType = 16; 66 private static final int CONSTANT_InvokeDynamic = 18; 67 private static final int CONSTANT_Module = 19; 68 private static final int CONSTANT_Package = 20; 69 70 private static final int[] SIZES = new int[21]; 71 72 static { 73 74 //SIZES[CONSTANT_Utf8] = XXX; 75 SIZES[CONSTANT_Integer] = 4; 76 SIZES[CONSTANT_Float] = 4; 77 SIZES[CONSTANT_Long] = 8; 78 SIZES[CONSTANT_Double] = 8; 79 SIZES[CONSTANT_Class] = 2; 80 SIZES[CONSTANT_String] = 2; 81 SIZES[CONSTANT_Fieldref] = 4; 82 SIZES[CONSTANT_Methodref] = 4; 83 SIZES[CONSTANT_InterfaceMethodref] = 4; 84 SIZES[CONSTANT_NameAndType] = 4; 85 SIZES[CONSTANT_MethodHandle] = 3; 86 SIZES[CONSTANT_MethodType] = 2; 87 SIZES[CONSTANT_InvokeDynamic] = 4; 88 SIZES[CONSTANT_Module] = 2; 89 SIZES[CONSTANT_Package] = 2; 90 } 91 92 public static int[] getSizes() { 93 return SIZES.clone(); 94 } 95 96 @SuppressWarnings("fallthrough") 97 public static byte[] normalize(StringsProvider provider, byte[] transformed, 98 int offset) throws IOException { 99 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(transformed, 100 offset, transformed.length - offset)); 101 ByteArrayOutputStream outStream = new ByteArrayOutputStream(transformed.length); 102 DataOutputStream out = new DataOutputStream(outStream); 103 byte[] header = new byte[8]; //maginc/4, minor/2, major/2 104 stream.readFully(header); 105 out.write(header); 106 int count = stream.readUnsignedShort(); 107 out.writeShort(count); 108 for (int i = 1; i < count; i++) { 109 int tag = stream.readUnsignedByte(); 110 byte[] arr; 111 switch (tag) { 112 case CONSTANT_Utf8: { 113 out.write(tag); 114 String utf = stream.readUTF(); 115 out.writeUTF(utf); 116 break; 117 } 118 119 case EXTERNALIZED_STRING: { 120 int index = CompressIndexes.readInt(stream); 121 String orig = provider.getString(index); 122 out.write(CONSTANT_Utf8); 123 out.writeUTF(orig); 124 break; 125 } 126 127 case EXTERNALIZED_STRING_DESCRIPTOR: { 128 String orig = reconstruct(provider, stream); 129 out.write(CONSTANT_Utf8); 130 out.writeUTF(orig); 131 break; 132 } 133 case CONSTANT_Long: 134 case CONSTANT_Double: { 135 i++; 136 } 137 default: { 138 out.write(tag); 139 int size = SIZES[tag]; 140 arr = new byte[size]; 141 stream.readFully(arr); 142 out.write(arr); 143 } 144 } 145 } 146 out.write(transformed, transformed.length - stream.available(), 147 stream.available()); 148 out.flush(); 149 150 return outStream.toByteArray(); 151 } 152 153 private static String reconstruct(StringsProvider reader, DataInputStream cr) 154 throws IOException { 155 int descIndex = CompressIndexes.readInt(cr); 156 String desc = reader.getString(descIndex); 157 byte[] encodedDesc = getEncoded(desc); 158 int indexes_length = CompressIndexes.readInt(cr); 159 byte[] bytes = new byte[indexes_length]; 160 cr.readFully(bytes); 161 List<Integer> indices = CompressIndexes.decompressFlow(bytes); 162 ByteBuffer buffer = ByteBuffer.allocate(encodedDesc.length * 2); 163 buffer.order(ByteOrder.BIG_ENDIAN); 164 int argIndex = 0; 165 for (byte c : encodedDesc) { 166 if (c == 'L') { 167 buffer = safeAdd(buffer, c); 168 int index = indices.get(argIndex); 169 argIndex += 1; 170 String pkg = reader.getString(index); 171 if (pkg.length() > 0) { 172 pkg = pkg + "/"; 173 byte[] encoded = getEncoded(pkg); 174 buffer = safeAdd(buffer, encoded); 175 } 176 int classIndex = indices.get(argIndex); 177 argIndex += 1; 178 String clazz = reader.getString(classIndex); 179 byte[] encoded = getEncoded(clazz); 180 buffer = safeAdd(buffer, encoded); 181 } else { 182 buffer = safeAdd(buffer, c); 183 } 184 } 185 186 byte[] encoded = buffer.array(); 187 ByteBuffer result = ByteBuffer.allocate(encoded.length + 2); 188 result.order(ByteOrder.BIG_ENDIAN); 189 result.putShort((short) buffer.position()); 190 result.put(encoded, 0, buffer.position()); 191 ByteArrayInputStream stream = new ByteArrayInputStream(result.array()); 192 DataInputStream inStream = new DataInputStream(stream); 193 String str = inStream.readUTF(); 194 return str; 195 } 196 197 public static byte[] getEncoded(String pre) throws IOException { 198 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 199 DataOutputStream resultOut = new DataOutputStream(resultStream); 200 resultOut.writeUTF(pre); 201 byte[] content = resultStream.toByteArray(); 202 // first 2 items are length; 203 if (content.length <= 2) { 204 return new byte[0]; 205 } 206 return Arrays.copyOfRange(content, 2, content.length); 207 } 208 209 private static ByteBuffer safeAdd(ByteBuffer current, byte b) { 210 byte[] bytes = {b}; 211 return safeAdd(current, bytes); 212 } 213 214 private static ByteBuffer safeAdd(ByteBuffer current, byte[] bytes) { 215 if (current.remaining() < bytes.length) { 216 ByteBuffer newBuffer = ByteBuffer.allocate((current.capacity() 217 + bytes.length) * 2); 218 newBuffer.order(ByteOrder.BIG_ENDIAN); 219 newBuffer.put(current.array(), 0, current.position()); 220 current = newBuffer; 221 } 222 current.put(bytes); 223 return current; 224 } 225 226 @Override 227 public String getName() { 228 return StringSharingDecompressorFactory.NAME; 229 } 230 231 public StringSharingDecompressor(Properties properties) { 232 233 } 234 235 @Override 236 public byte[] decompress(StringsProvider reader, byte[] content, 237 int offset, long originalSize) throws Exception { 238 return normalize(reader, content, offset); 239 } 240 }