1/* 2 * Copyright (c) 2014, 2017, 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 jdk.internal.jimage; 27 28import java.nio.ByteBuffer; 29import 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 */ 38public 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, long[] attributes, ImageStrings strings) { 133 Objects.requireNonNull(name); 134 final int length = name.length(); 135 int index = 0; 136 int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; 137 if (moduleOffset != 0) { 138 String module = strings.get(moduleOffset); 139 final int moduleLen = module.length(); 140 index = moduleLen + 1; 141 if (length <= index 142 || name.charAt(0) != '/' 143 || !name.regionMatches(1, module, 0, moduleLen) 144 || name.charAt(index++) != '/') { 145 return false; 146 } 147 } 148 149 return verifyName(name, index, length, attributes, strings); 150 } 151 152 static boolean verify(String module, String name, long[] attributes, 153 ImageStrings strings) { 154 Objects.requireNonNull(module); 155 Objects.requireNonNull(name); 156 int moduleOffset = (int)attributes[ATTRIBUTE_MODULE]; 157 if (moduleOffset != 0) { 158 if (!module.equals(strings.get(moduleOffset))) { 159 return false; 160 } 161 } 162 163 return verifyName(name, 0, name.length(), attributes, strings); 164 } 165 166 private static boolean verifyName(String name, int index, int length, 167 long[] attributes, ImageStrings strings) { 168 169 int parentOffset = (int) attributes[ATTRIBUTE_PARENT]; 170 if (parentOffset != 0) { 171 String parent = strings.get(parentOffset); 172 final int parentLen = parent.length(); 173 if (!name.regionMatches(index, parent, 0, parentLen)) { 174 return false; 175 } 176 index += parentLen; 177 if (length <= index || name.charAt(index++) != '/') { 178 return false; 179 } 180 } 181 String base = strings.get((int) attributes[ATTRIBUTE_BASE]); 182 final int baseLen = base.length(); 183 if (!name.regionMatches(index, base, 0, baseLen)) { 184 return false; 185 } 186 index += baseLen; 187 int extOffset = (int) attributes[ATTRIBUTE_EXTENSION]; 188 if (extOffset != 0) { 189 String extension = strings.get(extOffset); 190 int extLen = extension.length(); 191 if (length <= index 192 || name.charAt(index++) != '.' 193 || !name.regionMatches(index, extension, 0, extLen)) { 194 return false; 195 } 196 index += extLen; 197 } 198 return length == index; 199 } 200 201 long getAttribute(int kind) { 202 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 203 throw new InternalError( 204 "Invalid jimage attribute kind: " + kind); 205 } 206 207 return attributes[kind]; 208 } 209 210 String getAttributeString(int kind) { 211 if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) { 212 throw new InternalError( 213 "Invalid jimage attribute kind: " + kind); 214 } 215 216 return getStrings().get((int)attributes[kind]); 217 } 218 219 public String getModule() { 220 return getAttributeString(ATTRIBUTE_MODULE); 221 } 222 223 public int getModuleOffset() { 224 return (int)getAttribute(ATTRIBUTE_MODULE); 225 } 226 227 public String getBase() { 228 return getAttributeString(ATTRIBUTE_BASE); 229 } 230 231 public int getBaseOffset() { 232 return (int)getAttribute(ATTRIBUTE_BASE); 233 } 234 235 public String getParent() { 236 return getAttributeString(ATTRIBUTE_PARENT); 237 } 238 239 public int getParentOffset() { 240 return (int)getAttribute(ATTRIBUTE_PARENT); 241 } 242 243 public String getExtension() { 244 return getAttributeString(ATTRIBUTE_EXTENSION); 245 } 246 247 public int getExtensionOffset() { 248 return (int)getAttribute(ATTRIBUTE_EXTENSION); 249 } 250 251 public String getFullName() { 252 return getFullName(false); 253 } 254 255 public String getFullName(boolean modulesPrefix) { 256 StringBuilder builder = new StringBuilder(); 257 258 if (getModuleOffset() != 0) { 259 if (modulesPrefix) { 260 builder.append("/modules"); 261 } 262 263 builder.append('/'); 264 builder.append(getModule()); 265 builder.append('/'); 266 } 267 268 if (getParentOffset() != 0) { 269 builder.append(getParent()); 270 builder.append('/'); 271 } 272 273 builder.append(getBase()); 274 275 if (getExtensionOffset() != 0) { 276 builder.append('.'); 277 builder.append(getExtension()); 278 } 279 280 return builder.toString(); 281 } 282 283 String buildName(boolean includeModule, boolean includeParent, 284 boolean includeName) { 285 StringBuilder builder = new StringBuilder(); 286 287 if (includeModule && getModuleOffset() != 0) { 288 builder.append("/modules/"); 289 builder.append(getModule()); 290 } 291 292 if (includeParent && getParentOffset() != 0) { 293 builder.append('/'); 294 builder.append(getParent()); 295 } 296 297 if (includeName) { 298 if (includeModule || includeParent) { 299 builder.append('/'); 300 } 301 302 builder.append(getBase()); 303 304 if (getExtensionOffset() != 0) { 305 builder.append('.'); 306 builder.append(getExtension()); 307 } 308 } 309 310 return builder.toString(); 311 } 312 313 public long getContentOffset() { 314 return getAttribute(ATTRIBUTE_OFFSET); 315 } 316 317 public long getCompressedSize() { 318 return getAttribute(ATTRIBUTE_COMPRESSED); 319 } 320 321 public long getUncompressedSize() { 322 return getAttribute(ATTRIBUTE_UNCOMPRESSED); 323 } 324 325 static ImageLocation readFrom(BasicImageReader reader, int offset) { 326 Objects.requireNonNull(reader); 327 long[] attributes = reader.getAttributes(offset); 328 ImageStringsReader strings = reader.getStrings(); 329 330 return new ImageLocation(attributes, strings); 331 } 332} 333