J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2003 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package com.sun.java.util.jar.pack; |
| 27 | |
| 28 | import java.io.*; |
| 29 | import java.util.*; |
| 30 | import com.sun.java.util.jar.pack.ConstantPool.*; |
| 31 | |
| 32 | /** |
| 33 | * Collection of relocatable constant pool references. |
| 34 | * It operates with respect to a particular byte array, |
| 35 | * and stores some of its state in the bytes themselves. |
| 36 | * <p> |
| 37 | * As a Collection, it can be iterated over, but it is not a List, |
| 38 | * since it does not natively support indexed access. |
| 39 | * <p> |
| 40 | * |
| 41 | * @author John Rose |
| 42 | */ |
| 43 | class Fixups extends AbstractCollection implements Constants { |
| 44 | byte[] bytes; // the subject of the relocations |
| 45 | int head; // desc locating first reloc |
| 46 | int tail; // desc locating last reloc |
| 47 | int size; // number of relocations |
| 48 | Entry[] entries; // [0..size-1] relocations |
| 49 | int[] bigDescs; // descs which cannot be stored in the bytes |
| 50 | |
| 51 | // A "desc" (descriptor) is a bit-encoded pair of a location |
| 52 | // and format. Every fixup occurs at a "desc". Until final |
| 53 | // patching, bytes addressed by descs may also be used to |
| 54 | // link this data structure together. If the bytes are missing, |
| 55 | // or if the "desc" is too large to encode in the bytes, |
| 56 | // it is kept in the bigDescs array. |
| 57 | |
| 58 | Fixups(byte[] bytes) { |
| 59 | this.bytes = bytes; |
| 60 | entries = new Entry[3]; |
| 61 | bigDescs = noBigDescs; |
| 62 | } |
| 63 | Fixups() { |
| 64 | // If there are no bytes, all descs are kept in bigDescs. |
| 65 | this((byte[])null); |
| 66 | } |
| 67 | Fixups(byte[] bytes, Collection fixups) { |
| 68 | this(bytes); |
| 69 | addAll(fixups); |
| 70 | } |
| 71 | Fixups(Collection fixups) { |
| 72 | this((byte[])null); |
| 73 | addAll(fixups); |
| 74 | } |
| 75 | |
| 76 | private static final int MINBIGSIZE = 1; |
| 77 | // cleverly share empty bigDescs: |
| 78 | private static int[] noBigDescs = {MINBIGSIZE}; |
| 79 | |
| 80 | public int size() { |
| 81 | return size; |
| 82 | } |
| 83 | |
| 84 | public void trimToSize() { |
| 85 | if (size != entries.length) { |
| 86 | Entry[] oldEntries = entries; |
| 87 | entries = new Entry[size]; |
| 88 | System.arraycopy(oldEntries, 0, entries, 0, size); |
| 89 | } |
| 90 | int bigSize = bigDescs[BIGSIZE]; |
| 91 | if (bigSize == MINBIGSIZE) { |
| 92 | bigDescs = noBigDescs; |
| 93 | } else if (bigSize != bigDescs.length) { |
| 94 | int[] oldBigDescs = bigDescs; |
| 95 | bigDescs = new int[bigSize]; |
| 96 | System.arraycopy(oldBigDescs, 0, bigDescs, 0, bigSize); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | public void visitRefs(Collection refs) { |
| 101 | for (int i = 0; i < size; i++) { |
| 102 | refs.add(entries[i]); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | public void clear() { |
| 107 | if (bytes != null) { |
| 108 | // Clean the bytes: |
| 109 | for (Iterator i = iterator(); i.hasNext(); ) { |
| 110 | Fixup fx = (Fixup) i.next(); |
| 111 | //System.out.println("clean "+fx); |
| 112 | storeIndex(fx.location(), fx.format(), 0); |
| 113 | } |
| 114 | } |
| 115 | size = 0; |
| 116 | if (bigDescs != noBigDescs) |
| 117 | bigDescs[BIGSIZE] = MINBIGSIZE; |
| 118 | // do not trim to size, however |
| 119 | } |
| 120 | |
| 121 | public byte[] getBytes() { |
| 122 | return bytes; |
| 123 | } |
| 124 | |
| 125 | public void setBytes(byte[] newBytes) { |
| 126 | if (bytes == newBytes) return; |
| 127 | ArrayList old = null; |
| 128 | assert((old = new ArrayList(this)) != null); |
| 129 | if (bytes == null || newBytes == null) { |
| 130 | // One or the other representations is deficient. |
| 131 | // Construct a checkpoint. |
| 132 | ArrayList save = new ArrayList(this); |
| 133 | clear(); |
| 134 | bytes = newBytes; |
| 135 | addAll(save); |
| 136 | } else { |
| 137 | // assume newBytes is some sort of bitwise copy of the old bytes |
| 138 | bytes = newBytes; |
| 139 | } |
| 140 | assert(old.equals(new ArrayList(this))); |
| 141 | } |
| 142 | |
| 143 | static final int LOC_SHIFT = 1; |
| 144 | static final int FMT_MASK = 0x1; |
| 145 | static final byte UNUSED_BYTE = 0; |
| 146 | static final byte OVERFLOW_BYTE = -1; |
| 147 | // fill pointer of bigDescs array is in element [0] |
| 148 | static final int BIGSIZE = 0; |
| 149 | |
| 150 | // Format values: |
| 151 | public static final int U2_FORMAT = 0; |
| 152 | public static final int U1_FORMAT = 1; |
| 153 | |
| 154 | // Special values for the static methods. |
| 155 | private static final int SPECIAL_LOC = 0; |
| 156 | private static final int SPECIAL_FMT = U2_FORMAT; |
| 157 | |
| 158 | static int fmtLen(int fmt) { return 1+(fmt-U1_FORMAT)/(U2_FORMAT-U1_FORMAT); } |
| 159 | static int descLoc(int desc) { return desc >>> LOC_SHIFT; } |
| 160 | static int descFmt(int desc) { return desc & FMT_MASK; } |
| 161 | static int descEnd(int desc) { return descLoc(desc) + fmtLen(descFmt(desc)); } |
| 162 | static int makeDesc(int loc, int fmt) { |
| 163 | int desc = (loc << LOC_SHIFT) | fmt; |
| 164 | assert(descLoc(desc) == loc); |
| 165 | assert(descFmt(desc) == fmt); |
| 166 | return desc; |
| 167 | } |
| 168 | int fetchDesc(int loc, int fmt) { |
| 169 | byte b1 = bytes[loc]; |
| 170 | assert(b1 != OVERFLOW_BYTE); |
| 171 | int value; |
| 172 | if (fmt == U2_FORMAT) { |
| 173 | byte b2 = bytes[loc+1]; |
| 174 | value = ((b1 & 0xFF) << 8) + (b2 & 0xFF); |
| 175 | } else { |
| 176 | value = (b1 & 0xFF); |
| 177 | } |
| 178 | // Stored loc field is difference between its own loc and next loc. |
| 179 | return value + (loc << LOC_SHIFT); |
| 180 | } |
| 181 | boolean storeDesc(int loc, int fmt, int desc) { |
| 182 | if (bytes == null) |
| 183 | return false; |
| 184 | int value = desc - (loc << LOC_SHIFT); |
| 185 | byte b1, b2; |
| 186 | switch (fmt) { |
| 187 | case U2_FORMAT: |
| 188 | assert(bytes[loc+0] == UNUSED_BYTE); |
| 189 | assert(bytes[loc+1] == UNUSED_BYTE); |
| 190 | b1 = (byte)(value >> 8); |
| 191 | b2 = (byte)(value >> 0); |
| 192 | if (value == (value & 0xFFFF) && b1 != OVERFLOW_BYTE) { |
| 193 | bytes[loc+0] = b1; |
| 194 | bytes[loc+1] = b2; |
| 195 | assert(fetchDesc(loc, fmt) == desc); |
| 196 | return true; |
| 197 | } |
| 198 | break; |
| 199 | case U1_FORMAT: |
| 200 | assert(bytes[loc] == UNUSED_BYTE); |
| 201 | b1 = (byte)value; |
| 202 | if (value == (value & 0xFF) && b1 != OVERFLOW_BYTE) { |
| 203 | bytes[loc] = b1; |
| 204 | assert(fetchDesc(loc, fmt) == desc); |
| 205 | return true; |
| 206 | } |
| 207 | break; |
| 208 | default: assert(false); |
| 209 | } |
| 210 | // Failure. Caller must allocate a bigDesc. |
| 211 | bytes[loc] = OVERFLOW_BYTE; |
| 212 | assert(fmt==U1_FORMAT || (bytes[loc+1]=(byte)bigDescs[BIGSIZE])!=999); |
| 213 | return false; |
| 214 | } |
| 215 | void storeIndex(int loc, int fmt, int value) { |
| 216 | storeIndex(bytes, loc, fmt, value); |
| 217 | } |
| 218 | static |
| 219 | void storeIndex(byte[] bytes, int loc, int fmt, int value) { |
| 220 | switch (fmt) { |
| 221 | case U2_FORMAT: |
| 222 | assert(value == (value & 0xFFFF)) : (value); |
| 223 | bytes[loc+0] = (byte)(value >> 8); |
| 224 | bytes[loc+1] = (byte)(value >> 0); |
| 225 | break; |
| 226 | case U1_FORMAT: |
| 227 | assert(value == (value & 0xFF)) : (value); |
| 228 | bytes[loc] = (byte)value; |
| 229 | break; |
| 230 | default: assert(false); |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /** Simple and necessary tuple to present each fixup. */ |
| 235 | public static |
| 236 | class Fixup implements Comparable { |
| 237 | int desc; // location and format of reloc |
| 238 | Entry entry; // which entry to plug into the bytes |
| 239 | Fixup(int desc, Entry entry) { |
| 240 | this.desc = desc; |
| 241 | this.entry = entry; |
| 242 | } |
| 243 | public Fixup(int loc, int fmt, Entry entry) { |
| 244 | this.desc = makeDesc(loc, fmt); |
| 245 | this.entry = entry; |
| 246 | } |
| 247 | public int location() { return descLoc(desc); } |
| 248 | public int format() { return descFmt(desc); } |
| 249 | public Entry entry() { return entry; } |
| 250 | public int compareTo(Fixup that) { |
| 251 | // Ordering depends only on location. |
| 252 | return this.location() - that.location(); |
| 253 | } |
| 254 | public int compareTo(Object that) { |
| 255 | return compareTo((Fixup)that); |
| 256 | } |
| 257 | public boolean equals(Object x) { |
| 258 | if (!(x instanceof Fixup)) return false; |
| 259 | Fixup that = (Fixup) x; |
| 260 | return this.desc == that.desc && this.entry == that.entry; |
| 261 | } |
| 262 | public String toString() { |
| 263 | return "@"+location()+(format()==U1_FORMAT?".1":"")+"="+entry; |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | private |
| 268 | class Itr implements Iterator { |
| 269 | int index = 0; // index into entries |
| 270 | int bigIndex = BIGSIZE+1; // index into bigDescs |
| 271 | int next = head; // desc pointing to next fixup |
| 272 | public boolean hasNext() { return index < size; } |
| 273 | public void remove() { throw new UnsupportedOperationException(); } |
| 274 | public Object next() { |
| 275 | int thisIndex = index; |
| 276 | return new Fixup(nextDesc(), entries[thisIndex]); |
| 277 | } |
| 278 | int nextDesc() { |
| 279 | int thisIndex = index++; |
| 280 | int thisDesc = next; |
| 281 | if (index < size) { |
| 282 | // Fetch next desc eagerly, in case this fixup gets finalized. |
| 283 | int loc = descLoc(thisDesc); |
| 284 | int fmt = descFmt(thisDesc); |
| 285 | if (bytes != null && bytes[loc] != OVERFLOW_BYTE) { |
| 286 | next = fetchDesc(loc, fmt); |
| 287 | } else { |
| 288 | // The unused extra byte is "asserted" to be equal to BI. |
| 289 | // This helps keep the overflow descs in sync. |
| 290 | assert(fmt==U1_FORMAT || bytes == null || bytes[loc+1]==(byte)bigIndex); |
| 291 | next = bigDescs[bigIndex++]; |
| 292 | } |
| 293 | } |
| 294 | return thisDesc; |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | public Iterator iterator() { |
| 299 | return new Itr(); |
| 300 | } |
| 301 | public void add(int location, int format, Entry entry) { |
| 302 | addDesc(makeDesc(location, format), entry); |
| 303 | } |
| 304 | public boolean add(Fixup f) { |
| 305 | addDesc(f.desc, f.entry); |
| 306 | return true; |
| 307 | } |
| 308 | public boolean add(Object fixup) { |
| 309 | return add((Fixup) fixup); |
| 310 | } |
| 311 | public boolean addAll(Collection c) { |
| 312 | if (c instanceof Fixups) { |
| 313 | // Use knowledge of Itr structure to avoid building little structs. |
| 314 | Fixups that = (Fixups) c; |
| 315 | if (that.size == 0) return false; |
| 316 | if (this.size == 0 && entries.length < that.size) |
| 317 | growEntries(that.size); // presize exactly |
| 318 | Entry[] thatEntries = that.entries; |
| 319 | for (Itr i = that.new Itr(); i.hasNext(); ) { |
| 320 | int ni = i.index; |
| 321 | addDesc(i.nextDesc(), thatEntries[ni]); |
| 322 | } |
| 323 | return true; |
| 324 | } else { |
| 325 | return super.addAll(c); |
| 326 | } |
| 327 | } |
| 328 | // Here is how things get added: |
| 329 | private void addDesc(int thisDesc, Entry entry) { |
| 330 | if (entries.length == size) |
| 331 | growEntries(size * 2); |
| 332 | entries[size] = entry; |
| 333 | if (size == 0) { |
| 334 | head = tail = thisDesc; |
| 335 | } else { |
| 336 | int prevDesc = tail; |
| 337 | // Store new desc in previous tail. |
| 338 | int prevLoc = descLoc(prevDesc); |
| 339 | int prevFmt = descFmt(prevDesc); |
| 340 | int prevLen = fmtLen(prevFmt); |
| 341 | int thisLoc = descLoc(thisDesc); |
| 342 | // The collection must go in ascending order, and not overlap. |
| 343 | if (thisLoc < prevLoc + prevLen) |
| 344 | badOverlap(thisLoc); |
| 345 | tail = thisDesc; |
| 346 | if (!storeDesc(prevLoc, prevFmt, thisDesc)) { |
| 347 | // overflow |
| 348 | int bigSize = bigDescs[BIGSIZE]; |
| 349 | if (bigDescs.length == bigSize) |
| 350 | growBigDescs(); |
| 351 | //System.out.println("bigDescs["+bigSize+"] = "+thisDesc); |
| 352 | bigDescs[bigSize++] = thisDesc; |
| 353 | bigDescs[BIGSIZE] = bigSize; |
| 354 | } |
| 355 | } |
| 356 | size += 1; |
| 357 | } |
| 358 | private void badOverlap(int thisLoc) { |
| 359 | throw new IllegalArgumentException("locs must be ascending and must not overlap: "+thisLoc+" >> "+this); |
| 360 | } |
| 361 | |
| 362 | private void growEntries(int newSize) { |
| 363 | Entry[] oldEntries = entries; |
| 364 | entries = new Entry[Math.max(3, newSize)]; |
| 365 | System.arraycopy(oldEntries, 0, entries, 0, oldEntries.length); |
| 366 | } |
| 367 | private void growBigDescs() { |
| 368 | int[] oldBigDescs = bigDescs; |
| 369 | bigDescs = new int[oldBigDescs.length * 2]; |
| 370 | System.arraycopy(oldBigDescs, 0, bigDescs, 0, oldBigDescs.length); |
| 371 | } |
| 372 | |
| 373 | /// Static methods that optimize the use of this class. |
| 374 | public static |
| 375 | Object add(Object prevFixups, |
| 376 | byte[] bytes, int loc, int fmt, |
| 377 | Entry e) { |
| 378 | Fixups f; |
| 379 | if (prevFixups == null) { |
| 380 | if (loc == SPECIAL_LOC && fmt == SPECIAL_FMT) { |
| 381 | // Special convention: If the attribute has a |
| 382 | // U2 relocation at position zero, store the Entry |
| 383 | // rather than building a Fixups structure. |
| 384 | return e; |
| 385 | } |
| 386 | f = new Fixups(bytes); |
| 387 | } else if (!(prevFixups instanceof Fixups)) { |
| 388 | // Recognize the special convention: |
| 389 | Entry firstEntry = (Entry) prevFixups; |
| 390 | f = new Fixups(bytes); |
| 391 | f.add(SPECIAL_LOC, SPECIAL_FMT, firstEntry); |
| 392 | } else { |
| 393 | f = (Fixups) prevFixups; |
| 394 | assert(f.bytes == bytes); |
| 395 | } |
| 396 | f.add(loc, fmt, e); |
| 397 | return f; |
| 398 | } |
| 399 | |
| 400 | public static |
| 401 | void setBytes(Object fixups, byte[] bytes) { |
| 402 | if (fixups instanceof Fixups) { |
| 403 | Fixups f = (Fixups) fixups; |
| 404 | f.setBytes(bytes); |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | public static |
| 409 | Object trimToSize(Object fixups) { |
| 410 | if (fixups instanceof Fixups) { |
| 411 | Fixups f = (Fixups) fixups; |
| 412 | f.trimToSize(); |
| 413 | if (f.size() == 0) |
| 414 | fixups = null; |
| 415 | } |
| 416 | return fixups; |
| 417 | } |
| 418 | |
| 419 | // Iterate over all the references in this set of fixups. |
| 420 | public static |
| 421 | void visitRefs(Object fixups, Collection refs) { |
| 422 | if (fixups == null) { |
| 423 | } else if (!(fixups instanceof Fixups)) { |
| 424 | // Special convention; see above. |
| 425 | refs.add((Entry) fixups); |
| 426 | } else { |
| 427 | Fixups f = (Fixups) fixups; |
| 428 | f.visitRefs(refs); |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | // Clear out this set of fixups by replacing each reference |
| 433 | // by a hardcoded coding of its reference, drawn from ix. |
| 434 | public static |
| 435 | void finishRefs(Object fixups, byte[] bytes, ConstantPool.Index ix) { |
| 436 | if (fixups == null) |
| 437 | return; |
| 438 | if (!(fixups instanceof Fixups)) { |
| 439 | // Special convention; see above. |
| 440 | int index = ix.indexOf((Entry) fixups); |
| 441 | storeIndex(bytes, SPECIAL_LOC, SPECIAL_FMT, index); |
| 442 | return; |
| 443 | } |
| 444 | Fixups f = (Fixups) fixups; |
| 445 | assert(f.bytes == bytes); |
| 446 | f.finishRefs(ix); |
| 447 | } |
| 448 | |
| 449 | void finishRefs(ConstantPool.Index ix) { |
| 450 | if (isEmpty()) |
| 451 | return; |
| 452 | for (Iterator i = iterator(); i.hasNext(); ) { |
| 453 | Fixup fx = (Fixup) i.next(); |
| 454 | int index = ix.indexOf(fx.entry); |
| 455 | //System.out.println("finish "+fx+" = "+index); |
| 456 | // Note that the iterator has already fetched the |
| 457 | // bytes we are about to overwrite. |
| 458 | storeIndex(fx.location(), fx.format(), index); |
| 459 | } |
| 460 | // Further iterations should do nothing: |
| 461 | bytes = null; // do not clean them |
| 462 | clear(); |
| 463 | } |
| 464 | |
| 465 | /* |
| 466 | /// Testing. |
| 467 | public static void main(String[] av) { |
| 468 | byte[] bytes = new byte[1 << 20]; |
| 469 | ConstantPool cp = new ConstantPool(); |
| 470 | Fixups f = new Fixups(bytes); |
| 471 | boolean isU1 = false; |
| 472 | int span = 3; |
| 473 | int nextLoc = 0; |
| 474 | int[] locs = new int[100]; |
| 475 | final int[] indexes = new int[100]; |
| 476 | int iptr = 1; |
| 477 | for (int loc = 0; loc < bytes.length; loc++) { |
| 478 | if (loc == nextLoc && loc+1 < bytes.length) { |
| 479 | int fmt = (isU1 ? U1_FORMAT : U2_FORMAT); |
| 480 | Entry e = ConstantPool.getUtf8Entry("L"+loc); |
| 481 | f.add(loc, fmt, e); |
| 482 | isU1 ^= true; |
| 483 | if (iptr < 10) { |
| 484 | // Make it close in. |
| 485 | nextLoc += fmtLen(fmt) + (iptr < 5 ? 0 : 1); |
| 486 | } else { |
| 487 | nextLoc += span; |
| 488 | span = (int)(span * 1.77); |
| 489 | } |
| 490 | // Here are the bytes that would have gone here: |
| 491 | locs[iptr] = loc; |
| 492 | if (fmt == U1_FORMAT) { |
| 493 | indexes[iptr++] = (loc & 0xFF); |
| 494 | } else { |
| 495 | indexes[iptr++] = ((loc & 0xFF) << 8) | ((loc+1) & 0xFF); |
| 496 | ++loc; // skip a byte |
| 497 | } |
| 498 | continue; |
| 499 | } |
| 500 | bytes[loc] = (byte)loc; |
| 501 | } |
| 502 | System.out.println("size="+f.size() |
| 503 | +" overflow="+(f.bigDescs[BIGSIZE]-1)); |
| 504 | System.out.println("Fixups: "+f); |
| 505 | // Test collection contents. |
| 506 | assert(iptr == 1+f.size()); |
| 507 | List l = new ArrayList(f); |
| 508 | Collections.sort(l); // should not change the order |
| 509 | if (!l.equals(new ArrayList(f))) System.out.println("** disordered"); |
| 510 | f.setBytes(null); |
| 511 | if (!l.equals(new ArrayList(f))) System.out.println("** bad set 1"); |
| 512 | f.setBytes(bytes); |
| 513 | if (!l.equals(new ArrayList(f))) System.out.println("** bad set 2"); |
| 514 | Fixups f3 = new Fixups(f); |
| 515 | if (!l.equals(new ArrayList(f3))) System.out.println("** bad set 3"); |
| 516 | Iterator fi = f.iterator(); |
| 517 | for (int i = 1; i < iptr; i++) { |
| 518 | Fixup fx = (Fixup) fi.next(); |
| 519 | if (fx.location() != locs[i]) { |
| 520 | System.out.println("** "+fx+" != "+locs[i]); |
| 521 | } |
| 522 | if (fx.format() == U1_FORMAT) |
| 523 | System.out.println(fx+" -> "+bytes[locs[i]]); |
| 524 | else |
| 525 | System.out.println(fx+" -> "+bytes[locs[i]]+" "+bytes[locs[i]+1]); |
| 526 | } |
| 527 | assert(!fi.hasNext()); |
| 528 | indexes[0] = 1; // like iptr |
| 529 | Index ix = new Index("ix") { |
| 530 | public int indexOf(Entry e) { |
| 531 | return indexes[indexes[0]++]; |
| 532 | } |
| 533 | }; |
| 534 | f.finishRefs(ix); |
| 535 | for (int loc = 0; loc < bytes.length; loc++) { |
| 536 | if (bytes[loc] != (byte)loc) { |
| 537 | System.out.println("** ["+loc+"] = "+bytes[loc]+" != "+(byte)loc); |
| 538 | } |
| 539 | } |
| 540 | } |
| 541 | //*/ |
| 542 | } |