package com.intellij.flex.uiDesigner.abc; import gnu.trove.TIntObjectHashMap; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.WritableByteChannel; import java.util.List; import static com.intellij.flex.uiDesigner.abc.ActionBlockConstants.*; final class IndexHistory { public static final int INT = 0; public static final int UINT = 1; public static final int DOUBLE = 2; public static final int STRING = 3; public static final int NS = 4; public static final int NS_SET = 5; public static final int MULTINAME = 6; private final int[] map; private final PoolPart[] poolParts = new PoolPart[7]; private final WritableDataBuffer in_ns, in_ns_set, in_multiname; // Needed so we can strip out the index for all CONSTANT_PrivateNamespace entries // since the name for private namespaces is not important private boolean disableDebuggingInfo = false; ConstantPool constantPool; IndexHistory(List<Decoder> decoders) { int size = 0; int preferredSize = 0; int[] poolPartLengths = new int[7]; for (Decoder decoder : decoders) { if (decoder == null) { continue; } final ConstantPool pool = decoder.constantPool; pool.totalSize = size; size += pool.size(); preferredSize += pool.ends[MULTINAME] - pool.ends[STRING]; for (int j = 0; j < 7; j++) { poolPartLengths[j] += pool.positions[j].length; } } map = new int[size]; in_ns = new WritableDataBuffer(preferredSize); in_ns_set = new WritableDataBuffer(preferredSize); in_multiname = new WritableDataBuffer(preferredSize); for (int i = 0; i < 7; i++) { final int poolPartLength = poolPartLengths[i]; if (poolPartLength == 0) { continue; } if (i < NS) { poolParts[i] = new PoolPart(poolPartLength); } else { switch (i) { case NS: poolParts[i] = new NSPool(poolPartLength); break; case NS_SET: poolParts[i] = new NSSPool(poolPartLength); break; case MULTINAME: poolParts[i] = new MultiNamePool(poolPartLength); break; } } } } void disableDebugging() { disableDebuggingInfo = true; } public int[] getRawPartPoolPositions(int kind) { return constantPool.positions[kind]; } TIntObjectHashMap<byte[]> getModifiedMethodBodies() { return constantPool.modifiedMethodBodies == null ? (constantPool.modifiedMethodBodies = new TIntObjectHashMap<>()) : constantPool.modifiedMethodBodies; } public int getNewIndex(int insertionIndex) { return map[insertionIndex]; } public int getIndex(int kind, int index, int insertionIndex, int actualStart) { return map[insertionIndex] = decodeOnDemand(kind, index, actualStart, -1); } public int getIndexWithSpecifiedNsRaw(int index, int actuaNsRaw) { return map[getMapIndex(MULTINAME, index)] = decodeOnDemand(MULTINAME, index, -1, actuaNsRaw); } public int getIndex(int kind, int index) { if (index == 0) { return 0; } else { int mapIndex = getMapIndex(kind, index); int newIndex = getNewIndex(mapIndex); if (newIndex == 0) { return map[mapIndex] = decodeOnDemand(kind, index, -1, -1); } else { return newIndex; } } } public ByteBuffer createBuffer(PoolPart metadataInfo) { int bufferSize = metadataInfo.totalBytes; for (PoolPart poolPart : poolParts) { if (poolPart != null && poolPart.totalBytes > bufferSize) { bufferSize = poolPart.totalBytes; } } return ByteBuffer.wrap(new byte[bufferSize + 5 /*u32*/]).order(ByteOrder.LITTLE_ENDIAN); } public void writeTo(WritableByteChannel channel, ByteBuffer buffer) throws IOException { for (PoolPart poolPart : poolParts) { if (poolPart == null) { PoolPart.writeU32(buffer, 0); } else { poolPart.writeTo(buffer); } buffer.flip(); channel.write(buffer); buffer.clear(); } } public int getMapIndex(final int kind, final int oldIndex) { int index = constantPool.totalSize; for (int i = kind + 1; i < 7; i++) { int length = constantPool.positions[i].length; if (length != 0) { index += length - 1; } } return index + oldIndex - 1; } private int decodeOnDemand(final int kind, final int index, final int actualStart, final int actuaNsRaw) { final PoolPart poolPart = poolParts[kind]; final int[] positions = constantPool.positions[kind]; final int endPos = constantPool.ends[kind]; DataBuffer dataIn = constantPool.in; int start = actualStart == -1 ? positions[index] : actualStart; int end = (index != positions.length - 1) ? positions[index + 1] : endPos; if (kind == NS) { final int originalPos = dataIn.position(); dataIn.seek(positions[index]); start = in_ns.getSize(); switch (in_ns.copyU8(dataIn)) { case CONSTANT_PrivateNamespace: if (disableDebuggingInfo) { in_ns.writeU32(0); // name not important for private namespace break; } // else fall through and treat like a normal namespace case CONSTANT_Namespace: case CONSTANT_PackageNamespace: case CONSTANT_PackageInternalNs: case CONSTANT_ProtectedNamespace: case CONSTANT_ExplicitNamespace: case CONSTANT_StaticProtectedNs: in_ns.writeU32(getIndex(STRING, dataIn.readU32())); break; default: assert false; } dataIn.seek(originalPos); end = in_ns.getSize(); dataIn = in_ns; } else if (kind == NS_SET) { int originalPos = dataIn.position(); dataIn.seek(positions[index]); start = in_ns_set.getSize(); int count = dataIn.readU32(); in_ns_set.writeU32(count); for (int k = 0; k < count; k++) { in_ns_set.writeU32(getIndex(NS, dataIn.readU32())); } dataIn.seek(originalPos); end = in_ns_set.getSize(); dataIn = in_ns_set; } else if (kind == MULTINAME) { final int originalPos = dataIn.position(); dataIn.seek(positions[index]); start = in_multiname.getSize(); int constKind = dataIn.readU8(); if (!(constKind == CONSTANT_TypeName)) { in_multiname.writeU8(constKind); } switch (constKind) { case CONSTANT_Qname: case CONSTANT_QnameA: { final int ns; if (actuaNsRaw != -1) { dataIn.readU32(); ns = actuaNsRaw; } else { ns = dataIn.readU32(); } in_multiname.writeU32(getIndex(NS, ns)); in_multiname.writeU32(getIndex(STRING, dataIn.readU32())); break; } case CONSTANT_Multiname: case CONSTANT_MultinameA: { in_multiname.writeU32(getIndex(STRING, dataIn.readU32())); in_multiname.writeU32(getIndex(NS_SET, dataIn.readU32())); break; } case CONSTANT_RTQname: case CONSTANT_RTQnameA: { in_multiname.writeU32(getIndex(STRING, dataIn.readU32())); break; } case CONSTANT_RTQnameL: case CONSTANT_RTQnameLA: break; case CONSTANT_MultinameL: case CONSTANT_MultinameLA: { in_multiname.writeU32(getIndex(NS_SET, dataIn.readU32())); break; } case CONSTANT_TypeName: { int newNameIndex = getIndex(MULTINAME, dataIn.readU32()); final int count = dataIn.readU32(); final int[] newParams = new int[count]; for (int i = 0; i < count; i++) { newParams[i] = getIndex(MULTINAME, dataIn.readU32()); } start = in_multiname.getSize(); in_multiname.writeU8(constKind); in_multiname.writeU32(newNameIndex); in_multiname.writeU32(count); for (int i = 0; i < count; i++) { in_multiname.writeU32(newParams[i]); } break; } default: assert false; } dataIn.seek(originalPos); end = in_multiname.getSize(); dataIn = in_multiname; } int newIndex = poolPart.contains(dataIn, start, end); if (newIndex == -1) { newIndex = poolPart.store(dataIn, start, end); } return newIndex; } }