1 /* 2 * Copyright (c) 2014, 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 26 package jdk.internal.jimage; 27 28 import java.nio.ByteBuffer; 29 import java.util.Objects; 30 31 /** 32 * @implNote This class needs to maintain JDK 8 source compatibility. 33 * 34 * It is used internally in the JDK to implement jimage/jrtfs access, 35 * but also compiled and delivered as part of the jrtfs.jar to support access 36 * to the jimage file provided by the shipped JDK by tools running on JDK 8. 37 */ 38 public class ImageLocation { 39 public static final int ATTRIBUTE_END = 0; 40 public static final int ATTRIBUTE_MODULE = 1; 41 public static final int ATTRIBUTE_PARENT = 2; 42 public static final int ATTRIBUTE_BASE = 3; 43 public static final int ATTRIBUTE_EXTENSION = 4; 44 public static final int ATTRIBUTE_OFFSET = 5; 45 public static final int ATTRIBUTE_COMPRESSED = 6; 46 public static final int ATTRIBUTE_UNCOMPRESSED = 7; 47 public static final int ATTRIBUTE_COUNT = 8; 48 49 protected final long[] attributes; 50 51 protected final ImageStrings strings; 52 53 public ImageLocation(long[] attributes, ImageStrings strings) { 54 this.attributes = Objects.requireNonNull(attributes); 55 this.strings = Objects.requireNonNull(strings); 56 } 57 58 ImageStrings getStrings() { 59 return strings; 60 } 61 62 static long[] decompress(ByteBuffer bytes) { 63 Objects.requireNonNull(bytes); 64 long[] attributes = new long[ATTRIBUTE_COUNT]; 65 66 if (bytes != null) { 67 while (bytes.hasRemaining()) { 68 int data = bytes.get() & 0xFF; 69 int kind = data >>> 3; 70 71 if (kind == ATTRIBUTE_END) { 72 break; 73 } 74 75 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 76 throw new InternalError( 77 "Invalid jimage attribute kind: " + kind); 78 } 79 80 int length = (data & 0x7) + 1; 81 long value = 0; 82 83 for (int j = 0; j < length; j++) { 84 value <<= 8; 85 86 if (!bytes.hasRemaining()) { 87 throw new InternalError("Missing jimage attribute data"); 88 } 89 90 value |= bytes.get() & 0xFF; 91 } 92 93 attributes[kind] = value; 94 } 95 } 96 97 return attributes; 98 } 99 100 public static byte[] compress(long[] attributes) { 101 Objects.requireNonNull(attributes); 102 ImageStream stream = new ImageStream(16); 103 104 for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) { 105 long value = attributes[kind]; 106 107 if (value != 0) { 108 int n = (63 - Long.numberOfLeadingZeros(value)) >> 3; 109 stream.put((kind << 3) | n); 110 111 for (int i = n; i >= 0; i--) { 112 stream.put((int)(value >> (i << 3))); 113 } 114 } 115 } 116 117 stream.put(ATTRIBUTE_END << 3); 118 119 return stream.toArray(); 120 } 121 122 public boolean verify(String name) { 123 return verify(name, attributes, strings); 124 } 125 126 /** 127 * A simpler verification would be {@code name.equals(getFullName())}, but 128 * by not creating the full name and enabling early returns we allocate 129 * fewer objects. Could possibly be made allocation free by extending 130 * ImageStrings to test if strings at an offset match the name region. 131 */ 132 static boolean verify(String name, final long[] attributes, 133 final ImageStrings strings) { 134 Objects.requireNonNull(name); 135 final int length = name.length(); 136 int index = 0; 137 int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; 138 if (moduleOffset != 0) { 139 String module = strings.get(moduleOffset); 140 final int moduleLen = module.length(); 141 index = moduleLen + 1; 142 if (length <= index 143 || name.charAt(0) != '/' 144 || !name.regionMatches(1, module, 0, moduleLen) 145 || name.charAt(index++) != '/') { 146 return false; 147 } 148 } 149 150 int parentOffset = (int)attributes[ATTRIBUTE_PARENT]; 151 if (parentOffset != 0) { 152 String parent = strings.get(parentOffset); 153 final int parentLen = parent.length(); 154 if (!name.regionMatches(index, parent, 0, parentLen)) { 155 return false; 156 } 157 index += parentLen; 158 if (length <= index || name.charAt(index++) != '/') { 159 return false; 160 } 161 } 162 163 String base = strings.get((int)attributes[ATTRIBUTE_BASE]); 164 final int baseLen = base.length(); 165 if (!name.regionMatches(index, base, 0, baseLen)) { 166 return false; 167 } 168 index += baseLen; 169 170 int extOffset = (int)attributes[ATTRIBUTE_EXTENSION]; 171 if (extOffset != 0) { 172 String extension = strings.get(extOffset); 173 int extLen = extension.length(); 174 if (length <= index 175 || name.charAt(index++) != '.' 176 || !name.regionMatches(index, extension, 0, extLen)) { 177 return false; 178 } 179 index += extLen; 180 } 181 return length == index; 182 } 183 184 long getAttribute(int kind) { 185 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 186 throw new InternalError( 187 "Invalid jimage attribute kind: " + kind); 188 } 189 190 return attributes[kind]; 191 } 192 193 String getAttributeString(int kind) { 194 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 195 throw new InternalError( 196 "Invalid jimage attribute kind: " + kind); 197 } 198 199 return getStrings().get((int)attributes[kind]); 200 } 201 202 public String getModule() { 203 return getAttributeString(ATTRIBUTE_MODULE); 204 } 205 206 public int getModuleOffset() { 207 return (int)getAttribute(ATTRIBUTE_MODULE); 208 } 209 210 public String getBase() { 211 return getAttributeString(ATTRIBUTE_BASE); 212 } 213 214 public int getBaseOffset() { 215 return (int)getAttribute(ATTRIBUTE_BASE); 216 } 217 218 public String getParent() { 219 return getAttributeString(ATTRIBUTE_PARENT); 220 } 221 222 public int getParentOffset() { 223 return (int)getAttribute(ATTRIBUTE_PARENT); 224 } 225 226 public String getExtension() { 227 return getAttributeString(ATTRIBUTE_EXTENSION); 228 } 229 230 public int getExtensionOffset() { 231 return (int)getAttribute(ATTRIBUTE_EXTENSION); 232 } 233 234 public String getFullName() { 235 return getFullName(false); 236 } 237 238 public String getFullName(boolean modulesPrefix) { 239 StringBuilder builder = new StringBuilder(); 240 241 if (getModuleOffset() != 0) { 242 if (modulesPrefix) { 243 builder.append("/modules"); 244 } 245 246 builder.append('/'); 247 builder.append(getModule()); 248 builder.append('/'); 249 } 250 251 if (getParentOffset() != 0) { 252 builder.append(getParent()); 253 builder.append('/'); 254 } 255 256 builder.append(getBase()); 257 258 if (getExtensionOffset() != 0) { 259 builder.append('.'); 260 builder.append(getExtension()); 261 } 262 263 return builder.toString(); 264 } 265 266 String buildName(boolean includeModule, boolean includeParent, 267 boolean includeName) { 268 StringBuilder builder = new StringBuilder(); 269 270 if (includeModule && getModuleOffset() != 0) { 271 builder.append("/modules/"); 272 builder.append(getModule()); 273 } 274 275 if (includeParent && getParentOffset() != 0) { 276 builder.append('/'); 277 builder.append(getParent()); 278 } 279 280 if (includeName) { 281 if (includeModule || includeParent) { 282 builder.append('/'); 283 } 284 285 builder.append(getBase()); 286 287 if (getExtensionOffset() != 0) { 288 builder.append('.'); 289 builder.append(getExtension()); 290 } 291 } 292 293 return builder.toString(); 294 } 295 296 public long getContentOffset() { 297 return getAttribute(ATTRIBUTE_OFFSET); 298 } 299 300 public long getCompressedSize() { 301 return getAttribute(ATTRIBUTE_COMPRESSED); 302 } 303 304 public long getUncompressedSize() { 305 return getAttribute(ATTRIBUTE_UNCOMPRESSED); 306 } 307 308 static ImageLocation readFrom(BasicImageReader reader, int offset) { 309 Objects.requireNonNull(reader); 310 long[] attributes = reader.getAttributes(offset); 311 ImageStringsReader strings = reader.getStrings(); 312 313 return new ImageLocation(attributes, strings); 314 } 315 }