package com.intellij.flex.uiDesigner.abc; import com.intellij.flex.uiDesigner.abc.Decoder.Opcodes; import com.intellij.flex.uiDesigner.io.AbstractByteArrayOutputStream; import com.intellij.util.containers.IntIntHashMap; import gnu.trove.TIntArrayList; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.List; import static com.intellij.flex.uiDesigner.abc.ActionBlockConstants.*; import static com.intellij.flex.uiDesigner.abc.Decoder.MethodCodeDecoding; @SuppressWarnings({"deprecation"}) public class Encoder { private final static byte[] EMPTY_METHOD_BODY = {0x01, 0x02, 0x04, 0x05, 0x03, (byte)0xd0, 0x30, 0x47, 0x00, 0x00}; private final static byte[] MODIFY_INIT_METHOD_BODY_MARKER = {}; private final TIntArrayList tempMetadataList = new TIntArrayList(8); final Opcodes opcodeDecoder = new Opcodes(); protected IndexHistory history; private int majorVersion, minorVersion; protected int decoderIndex; protected int opcodePass; private int exPass; private boolean disableDebugging, peepHole; protected DataBuffer2 methodInfo; private MetadataInfoByteArray metadataInfo; private DataBuffer2 instanceInfo; private DataBuffer2 classInfo; private DataBuffer2 scriptInfo; protected DataBuffer2 methodBodies; protected DataBuffer3 opcodes; private WritableDataBuffer exceptions; protected WritableDataBuffer currentBuffer; private AbcModifier abcModifier; private boolean abcModifierApplicable; public Encoder() { this(46, 16); } private Encoder(int majorVersion, int minorVersion) { this.majorVersion = majorVersion; this.minorVersion = minorVersion; peepHole = false; disableDebugging = false; } public void enablePeepHole() { peepHole = true; } @SuppressWarnings({"UnusedDeclaration"}) public void disableDebugging() { disableDebugging = true; history.disableDebugging(); } public void configure(List<Decoder> decoders, @Nullable AbcTranscoder.TransientString excludedName) { int estimatedSize = 0, total = 0; final int n = decoders.size(); for (int i = 0; i < n; i++) { Decoder decoder = decoders.get(i); if (excludedName != null && decoder.name != null && excludedName.same(decoder.name)) { decoders.set(i, null); continue; } estimatedSize += decoder.methodInfo.estimatedSize; total += decoder.methodInfo.size(); } methodInfo = new DataBuffer2(estimatedSize); methodInfo.writeU32(total); total = 0; for (Decoder decoder : decoders) { if (decoder == null) { continue; } total += decoder.metadataInfo.size(); } metadataInfo = new MetadataInfoByteArray(total); estimatedSize = 0; int classEstimatedSize = 0; total = 0; for (Decoder decoder : decoders) { if (decoder == null) { continue; } estimatedSize += decoder.classInfo.instanceEstimatedSize; classEstimatedSize += decoder.classInfo.classEstimatedSize; total += decoder.classInfo.size(); } instanceInfo = new DataBuffer2(estimatedSize); classInfo = new DataBuffer2(classEstimatedSize); instanceInfo.writeU32(total); estimatedSize = 0; total = 0; for (Decoder decoder : decoders) { if (decoder == null) { continue; } estimatedSize += decoder.scriptInfo.estimatedSize; total += decoder.scriptInfo.size(); } scriptInfo = new DataBuffer2(estimatedSize); scriptInfo.writeU32(total); estimatedSize = 0; total = 0; for (Decoder decoder : decoders) { if (decoder == null) { continue; } estimatedSize += decoder.methodBodies.estimatedSize; total += decoder.methodBodies.size(); } methodBodies = new DataBuffer2(estimatedSize); methodBodies.writeU32(total); opcodes = new DataBuffer3(decoders, 4096); exceptions = new WritableDataBuffer(4096); history = new IndexHistory(decoders); if (disableDebugging) { history.disableDebugging(); } } public void useDecoder(int index, Decoder decoder) { decoderIndex = index; history.constantPool = decoder.constantPool; abcModifier = decoder.abcModifier; abcModifierApplicable = abcModifier != null; } public void endDecoder(Decoder decoder) { methodInfo.processedSize += decoder.methodInfo.size(); metadataInfo.processedSize += decoder.metadataInfo.size(); instanceInfo.processedSize += decoder.classInfo.size(); classInfo.processedSize += decoder.classInfo.size(); scriptInfo.processedSize += decoder.scriptInfo.size(); methodBodies.processedSize += decoder.methodBodies.size(); } public ByteBuffer writeDoAbc(final AbstractByteArrayOutputStream channel) throws IOException { final ByteBuffer buffer = history.createBuffer(metadataInfo); final int filePositionBefore = channel.size(); buffer.putShort((short)((TagTypes.DoABC2 << 6) | 63)); buffer.position(6); buffer.putInt(1); buffer.put((byte)'_'); buffer.put((byte)0); buffer.putShort((short)minorVersion); buffer.putShort((short)majorVersion); buffer.flip(); channel.write(buffer); buffer.clear(); history.writeTo(channel, buffer); methodInfo.writeTo(channel); metadataInfo.writeTo(buffer); buffer.flip(); channel.write(buffer); buffer.clear(); instanceInfo.writeTo(channel); classInfo.writeTo(channel); scriptInfo.writeTo(channel); methodBodies.writeTo(channel); final int size = channel.size() - filePositionBefore - 6; buffer.putInt(size); buffer.flip(); channel.write(buffer, filePositionBefore + 2); return buffer; } public void writeDoAbc(final FileChannel channel, final boolean asTag) throws IOException { final ByteBuffer buffer = history.createBuffer(metadataInfo); final int filePositionBefore; if (asTag) { filePositionBefore = (int)channel.position(); buffer.putShort((short)((TagTypes.DoABC2 << 6) | 63)); buffer.position(6); buffer.putInt(1); buffer.put((byte)0); } else { filePositionBefore = -1; } buffer.putShort((short)minorVersion); buffer.putShort((short)majorVersion); buffer.flip(); channel.write(buffer); buffer.clear(); history.writeTo(channel, buffer); methodInfo.writeTo(channel); metadataInfo.writeTo(buffer); buffer.flip(); channel.write(buffer); buffer.clear(); instanceInfo.writeTo(channel); classInfo.writeTo(channel); scriptInfo.writeTo(channel); methodBodies.writeTo(channel); if (asTag) { final int size = (int)channel.position() - filePositionBefore - 6; buffer.putInt(size); buffer.flip(); channel.write(buffer, filePositionBefore + 2); } } public void methodInfo(DataBuffer in) throws DecoderException { int paramCount = in.readU32(); methodInfo.writeU32(paramCount); int returnType = in.readU32(); methodInfo.writeU32(history.getIndex(IndexHistory.MULTINAME, returnType)); for (int i = 0; i < paramCount; i++) { methodInfo.writeU32(history.getIndex(IndexHistory.MULTINAME, in.readU32())); } int nativeName = in.readU32(); methodInfo.writeU32(disableDebugging ? 0 : history.getIndex(IndexHistory.STRING, nativeName)); int flags = in.readU8(); if (disableDebugging) { // Nuke the param names if we're getting rid of debugging info, don't want them showing // up in release code flags &= ~METHOD_HasParamNames; } methodInfo.writeU8(flags); if ((flags & METHOD_HasOptional) != 0) { int optionalCount = in.readU32(); methodInfo.writeU32(optionalCount); for (int i = 0; i < optionalCount; i++) { int value = in.readU32(); int kind = -1; int rawKind = in.readU8(); switch (rawKind) { case CONSTANT_Utf8: kind = IndexHistory.STRING; break; case CONSTANT_Integer: kind = IndexHistory.INT; break; case CONSTANT_UInteger: kind = IndexHistory.UINT; break; case CONSTANT_Double: kind = IndexHistory.DOUBLE; break; case CONSTANT_Namespace: case CONSTANT_PrivateNamespace: case CONSTANT_PackageNamespace: case CONSTANT_PackageInternalNs: case CONSTANT_ProtectedNamespace: case CONSTANT_ExplicitNamespace: case CONSTANT_StaticProtectedNs: kind = IndexHistory.NS; break; case CONSTANT_Qname: case CONSTANT_QnameA: case CONSTANT_Multiname: case CONSTANT_MultinameA: case CONSTANT_TypeName: kind = IndexHistory.MULTINAME; break; case CONSTANT_Namespace_Set: kind = IndexHistory.NS_SET; break; } int newIndex; switch (rawKind) { case 0: case CONSTANT_False: case CONSTANT_True: case CONSTANT_Null: // The index doesn't matter, as long as its non 0 // there are no boolean values in any cpool, instead the value will be determined by the kind byte newIndex = value; break; default: { if (kind == -1) { throw new DecoderException("Unknown constant type " + rawKind + " for " + value); } newIndex = history.getIndex(kind, value); } } methodInfo.writeU32(newIndex); methodInfo.writeU8(rawKind); } } if ((flags & METHOD_HasParamNames) != 0 && paramCount != 0) { for (int j = 0; j < paramCount; ++j) { methodInfo.writeU32(history.getIndex(IndexHistory.STRING, in.readU32())); } } } public void metadataInfo(int index, int name, int itemCount, DataBuffer data) throws DecoderException { WritableDataBuffer buffer = new WritableDataBuffer(6); buffer.writeU32(history.getIndex(IndexHistory.STRING, name)); buffer.writeU32(itemCount); if (itemCount != 0) { for (int j = 0; j < itemCount; j++) { buffer.writeU32(history.getIndex(IndexHistory.STRING, data.readU32())); } for (int j = 0; j < itemCount; j++) { buffer.writeU32(history.getIndex(IndexHistory.STRING, data.readU32())); } } metadataInfo.addData(index, buffer); } public void startInstance(DataBuffer in) { final int nameIndex = in.readU32(); instanceInfo.writeU32(history.getIndex(IndexHistory.MULTINAME, nameIndex)); instanceInfo.writeU32(history.getIndex(IndexHistory.MULTINAME, in.readU32())); abcModifierApplicable = abcModifier != null && compareLocalName(in, nameIndex, abcModifier.getClassLocalName()); int flags = in.readU8(); instanceInfo.writeU8(flags); if ((flags & CLASS_FLAG_protected) != 0) { instanceInfo.writeU32(history.getIndex(IndexHistory.NS, in.readU32())); } final int interfaceCount = in.readU32(); instanceInfo.writeU32(interfaceCount); if (interfaceCount > 0) { for (int j = 0; j < interfaceCount; j++) { instanceInfo.writeU32(history.getIndex(IndexHistory.MULTINAME, in.readU32())); } } final int oldIInit = in.readU32(); instanceInfo.writeU32(methodInfo.getIndex(oldIInit)); if (abcModifierApplicable && abcModifier.isModifyConstructor()) { history.getModifiedMethodBodies().put(oldIInit, MODIFY_INIT_METHOD_BODY_MARKER); } currentBuffer = instanceInfo; } public void endInstance() { currentBuffer = null; if (abcModifierApplicable) { abcModifier.assertOnInstanceEnd(); } } public void startClass(int cinit) { classInfo.writeU32(methodInfo.getIndex(cinit)); currentBuffer = classInfo; } public void endClass() { currentBuffer = null; } public void startScript(int initID) { scriptInfo.writeU32(methodInfo.getIndex(initID)); currentBuffer = scriptInfo; } public void endScript() { currentBuffer = null; } public int startMethodBody(int methodInfoIndex, int maxStack, int maxRegs, int scopeDepth, int maxScope) { TIntObjectHashMap<byte[]> modifiedMethodBodies = history.constantPool.modifiedMethodBodies == null ? null : history.getModifiedMethodBodies(); byte[] bytes = modifiedMethodBodies == null ? null : modifiedMethodBodies.get(methodInfoIndex); final int result; if (bytes != null) { if (bytes == EMPTY_METHOD_BODY) { methodBodies.writeU32(methodInfo.getIndex(methodInfoIndex)); methodBodies.writeTo(bytes); return MethodCodeDecoding.STOP; } else { // we cannot change iinit to empty body, because iinit may contains some default value for complex instance properties, // so, we allow all code before constructsuper opcode. result = MethodCodeDecoding.STOP_AFTER_CONSTRUCT_SUPER; } } else { result = MethodCodeDecoding.CONTINUE; } methodBodies.writeU32(methodInfo.getIndex(methodInfoIndex)); methodBodies.writeU32(maxStack); methodBodies.writeU32(maxRegs); methodBodies.writeU32(scopeDepth); methodBodies.writeU32(maxScope); currentBuffer = methodBodies; opcodePass = 1; exPass = 1; return result; } public void endMethodBody() { currentBuffer = null; opcodes.clear(); exceptions.clear(); } public void endOpcodes() { if (opcodePass == 1) { opcodePass = 2; } else if (opcodePass == 2) { methodBodies.writeU32(opcodes.getSize()); methodBodies.writeTo(opcodes); } } public void exception(int start, int end, int target, int type, int name) { if (exPass == 2) { exceptions.writeU32(opcodes.getOffset(start)); exceptions.writeU32(opcodes.getOffset(end)); exceptions.writeU32(opcodes.getOffset(target)); exceptions.writeU32(history.getIndex(IndexHistory.MULTINAME, type)); exceptions.writeU32(history.getIndex(IndexHistory.MULTINAME, name)); } } public void startExceptions(int exceptionCount) { if (exPass == 2) { exceptions.writeU32(exceptionCount); } } public void endExceptions() { if (exPass == 1) { exPass++; } else if (exPass == 2) { methodBodies.writeTo(exceptions); } } public void traitCount(int traitCount) { if (abcModifierApplicable && currentBuffer == instanceInfo) { traitCount += abcModifier.instanceMethodTraitDelta(); assert traitCount >= 0; } currentBuffer.writeU32(traitCount); } private void encodeMetaData(TIntArrayList metadata) { if (metadata != null) { currentBuffer.writeU32(metadata.size()); for (int i = 0, n = metadata.size(); i < n; i++) { currentBuffer.writeU32(metadata.getQuick(i)); } } } private TIntArrayList trimMetadata(int[] metadata) { if (metadata == null) { return null; } tempMetadataList.resetQuick(); for (int aMetadata : metadata) { int newIndex = metadataInfo.getIndex(aMetadata); if (newIndex != -1) { tempMetadataList.add(newIndex); } } return tempMetadataList.isEmpty() ? null : tempMetadataList; } public void slotTrait(int traitKind, int name, int slotId, int type, int value, int valueKind, int[] metadata, DataBuffer in) throws DecoderException { if (!abcModifierApplicable || !abcModifier.slotTraitName(name, traitKind, in, this)) { currentBuffer.writeU32(history.getIndex(IndexHistory.MULTINAME, name)); } TIntArrayList newMetadata = trimMetadata(metadata); if (((traitKind >> 4) & TRAIT_FLAG_metadata) != 0 && newMetadata == null) { traitKind &= ~(TRAIT_FLAG_metadata << 4); } currentBuffer.writeU8(traitKind); currentBuffer.writeU32(slotId); currentBuffer.writeU32(history.getIndex(IndexHistory.MULTINAME, type)); int kind = -1; switch (valueKind) { case CONSTANT_Utf8: kind = IndexHistory.STRING; break; case CONSTANT_Integer: kind = IndexHistory.INT; break; case CONSTANT_UInteger: kind = IndexHistory.UINT; break; case CONSTANT_Double: kind = IndexHistory.DOUBLE; break; case CONSTANT_Namespace: case CONSTANT_PrivateNamespace: case CONSTANT_PackageNamespace: case CONSTANT_PackageInternalNs: case CONSTANT_ProtectedNamespace: case CONSTANT_ExplicitNamespace: case CONSTANT_StaticProtectedNs: kind = IndexHistory.NS; break; case CONSTANT_Qname: case CONSTANT_QnameA: case CONSTANT_Multiname: case CONSTANT_MultinameA: case CONSTANT_TypeName: kind = IndexHistory.MULTINAME; break; case CONSTANT_Namespace_Set: kind = IndexHistory.NS_SET; break; } int newIndex; switch (valueKind) { case 0: case CONSTANT_False: case CONSTANT_True: case CONSTANT_Null: newIndex = value; break; default: { if (kind == -1) { throw new DecoderException("writing slotTrait: don't know what constant type it is... " + valueKind + "," + value); } newIndex = history.getIndex(kind, value); } } currentBuffer.writeU32(newIndex); if (value != 0) { currentBuffer.writeU8(valueKind); } encodeMetaData(newMetadata); } public boolean changeAccessModifier(String fieldName, int name, DataBuffer in) { final int originalPosition = in.position(); in.seek(history.getRawPartPoolPositions(IndexHistory.MULTINAME)[name]); int constKind = in.readU8(); assert constKind == CONSTANT_Qname || constKind == CONSTANT_QnameA; int ns = in.readU32(); int localName = in.readU32(); in.seek(history.getRawPartPoolPositions(IndexHistory.NS)[ns]); int nsKind = in.readU8(); if (nsKind == CONSTANT_PrivateNamespace) { in.seek(history.getRawPartPoolPositions(IndexHistory.STRING)[localName]); if (compare(in, fieldName)) { currentBuffer.writeU32(history.getIndexWithSpecifiedNsRaw(name, findPublicNamespace(in))); in.seek(originalPosition); return true; } } in.seek(originalPosition); return false; } public static final class SkipMethodKey { public final String name; // CONSTANT_PackageNamespace or CONSTANT_ProtectedNamespace public final byte packageNamespace; public final boolean clear; public SkipMethodKey(String name, boolean isPublic) { this(name, isPublic, false); } public SkipMethodKey(String name, boolean isPublic, boolean clear) { this.name = name; this.clear = clear; packageNamespace = clear ? CONSTANT_PrivateNamespace : isPublic ? CONSTANT_PackageNamespace : CONSTANT_ProtectedNamespace; } } public int skipMethod(final List<SkipMethodKey> keys, int nameIndex, DataBuffer in, int methodInfo) { final int originalPosition = in.position(); in.seek(history.getRawPartPoolPositions(IndexHistory.MULTINAME)[nameIndex]); int constKind = in.readU8(); assert constKind == CONSTANT_Qname || constKind == CONSTANT_QnameA; int ns = in.readU32(); int localName = in.readU32(); in.seek(history.getRawPartPoolPositions(IndexHistory.NS)[ns]); final int nsKind = in.readU8(); if (nsKind == CONSTANT_PackageNamespace || nsKind == CONSTANT_ProtectedNamespace || nsKind == CONSTANT_PrivateNamespace) { in.seek(history.getRawPartPoolPositions(IndexHistory.STRING)[localName]); int stringLength = in.readU32(); for (int i = 0, size = keys.size(); i < size; i++) { SkipMethodKey key = keys.get(i); if (key.packageNamespace == nsKind && compare(in, stringLength, key.name)) { if (key.clear) { history.getModifiedMethodBodies().put(methodInfo, EMPTY_METHOD_BODY); in.seek(originalPosition); return -2; } in.seek(originalPosition); return i; } } } in.seek(originalPosition); return -1; } private boolean compareLocalName(DataBuffer in, int nameIndex, @Nullable String s) { if (s == null) { return true; } final int originalPosition = in.position(); in.seek(history.getRawPartPoolPositions(IndexHistory.MULTINAME)[nameIndex]); int constKind = in.readU8(); assert constKind == CONSTANT_Qname || constKind == CONSTANT_QnameA; int ns = in.readU32(); int localName = in.readU32(); boolean result = false; in.seek(history.getRawPartPoolPositions(IndexHistory.NS)[ns]); int nsKind = in.readU8(); if (nsKind == CONSTANT_PackageNamespace) { in.seek(history.getRawPartPoolPositions(IndexHistory.STRING)[localName]); if (compare(in, s)) { result = true; } } //final String s = in.readString(in.readU32()); in.seek(originalPosition); return result; } public boolean clearMethodBody(String name, int nameIndex, DataBuffer in, int methodInfo) { final int originalPosition = in.position(); in.seek(history.getRawPartPoolPositions(IndexHistory.MULTINAME)[nameIndex]); int constKind = in.readU8(); assert constKind == CONSTANT_Qname || constKind == CONSTANT_QnameA; int ns = in.readU32(); int localName = in.readU32(); in.seek(history.getRawPartPoolPositions(IndexHistory.NS)[ns]); int nsKind = in.readU8(); if (nsKind == CONSTANT_PackageNamespace) { in.seek(history.getRawPartPoolPositions(IndexHistory.STRING)[localName]); if (compare(in, name)) { history.getModifiedMethodBodies().put(methodInfo, EMPTY_METHOD_BODY); in.seek(originalPosition); return true; } } in.seek(originalPosition); return false; } private int findPublicNamespace(DataBuffer in) { final int originalPosition = in.position(); try { int[] positions = history.getRawPartPoolPositions(IndexHistory.NS); for (int i = 0, positionsLength = positions.length; i < positionsLength; i++) { in.seek(positions[i]); if (in.readU8() == CONSTANT_PackageNamespace) { in.seek(history.getRawPartPoolPositions(IndexHistory.STRING)[in.readU32()]); // magic, I don't know, cannot find info in AVM spec // but ns with kind CONSTANT_PackageNamespace is public and ns with empty name is current public in current class if (in.readU32() == 0) { return i; } } } throw new IllegalArgumentException(); } finally { in.seek(originalPosition); } } private static boolean compare(final DataBuffer in, final String s) { return compare(in, in.readU32(), s); } private static boolean compare(final DataBuffer in, final int stringLength, final String s) { if (stringLength != s.length()) { return false; } final int offset = in.position + in.offset; final byte[] data = in.data; for (int j = stringLength - 1; j > -1; j--) { if (data[offset + j] != s.charAt(j)) { return false; } } return true; } public void methodTrait(int traitKind, int name, int dispId, int methodInfoIndex, int[] metadata, DataBuffer in) { if (abcModifierApplicable && abcModifier.methodTrait(traitKind, name, in, methodInfoIndex, this)) { return; } if (!abcModifierApplicable || !abcModifier.methodTraitName(name, traitKind, in, this)) { currentBuffer.writeU32(history.getIndex(IndexHistory.MULTINAME, name)); } TIntArrayList newMetadata = trimMetadata(metadata); if (((traitKind >> 4) & TRAIT_FLAG_metadata) != 0 && newMetadata == null) { traitKind &= ~(TRAIT_FLAG_metadata << 4); } currentBuffer.writeU8(traitKind); currentBuffer.writeU32(dispId); currentBuffer.writeU32(methodInfo.getIndex(methodInfoIndex)); encodeMetaData(newMetadata); } public void classTrait(int kind, int name, int slotId, int classInfoIndex, int[] metadata) { currentBuffer.writeU32(history.getIndex(IndexHistory.MULTINAME, name)); TIntArrayList newMetadata = trimMetadata(metadata); if (((kind >> 4) & TRAIT_FLAG_metadata) != 0 && newMetadata == null) { kind &= ~(TRAIT_FLAG_metadata << 4); } currentBuffer.writeU8(kind); currentBuffer.writeU32(slotId); currentBuffer.writeU32(classInfo.getIndex(classInfoIndex)); encodeMetaData(newMetadata); } public void functionTrait(int kind, int name, int slotId, int methodInfo, int[] metadata) { currentBuffer.writeU32(history.getIndex(IndexHistory.MULTINAME, name)); TIntArrayList newMetadata = trimMetadata(metadata); if (((kind >> 4) & TRAIT_FLAG_metadata) != 0 && newMetadata == null) { kind &= ~(TRAIT_FLAG_metadata << 4); } currentBuffer.writeU8(kind); currentBuffer.writeU32(slotId); currentBuffer.writeU32(this.methodInfo.getIndex(methodInfo)); encodeMetaData(newMetadata); } static final int W = 8; int[] window = new int[W]; int windowSize = 0; int head = 0; void clearWindow() { for (int i = 0; i < W; i++) { window[i] = 0; } windowSize = 0; } void beginop(int opcode) { window[head] = opcodes.getSize(); head = (head + 1) & (W - 1); if (windowSize < 8) { ++windowSize; } opcodes.writeU8(opcode); } private int opat(int i) { if (peepHole && i <= windowSize) { int ip = window[(head - i) & (W - 1)]; if (ip < opcodes.getSize()) { return opcodes.readU8(ip); } } return 0; } void setOpcodeAt(int i, int opcode) { assert peepHole; if (i <= windowSize) { int ip = window[(head - i) & (W - 1)]; if (ip < opcodes.getSize()) { opcodes.writeU8(ip, opcode); } } } int readByteAt(int i) { return i <= windowSize ? (byte)opcodes.readU8(1 + window[(head - i) & (W - 1)]) : 0; } int readIntAt(int i) { return i <= windowSize ? opcodes.readU32(1 + window[(head - i) & (W - 1)]) : 0; } void rewind(int i) { int to = (head - i) & (W - 1); opcodes.delete(opcodes.getSize() - window[to]); head = to; windowSize -= i; } public void target(int oldPos) { if (opcodePass == 1) { opcodes.mapOffsets(oldPos); clearWindow(); } } public void OP_returnvalue() { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } if (opat(1) == OP_pushundefined) { rewind(1); if (opcodePass == 1) { beginop(OP_returnvoid); } return; } beginop(OP_returnvalue); } } public void OP_debugline(int linenum) { if (opcodePass == 1) { if (!disableDebugging) { beginop(OP_debugline); opcodes.writeU32(linenum); } } } public void OP_debug(int di_local, int index, int slot, int linenum) { if (opcodePass == 1 && !disableDebugging) { beginop(OP_debug); opcodes.writeU8(di_local); opcodes.writeU32(history.getIndex(IndexHistory.STRING, index)); opcodes.writeU8(slot); opcodes.writeU32(linenum); } } public void OP_debugfile(DataBuffer in) { int index = in.readU32(); if (opcodePass == 1 && !disableDebugging) { beginop(OP_debugfile); if (index == 0) { opcodes.writeU32(0); } else { writeDebugFile(in, index); } } } private static final byte[] debugBasepath = {'$'}; protected void writeDebugFile(DataBuffer in, int oldIndex) { //opcodes.writeU32(history.getIndex(IndexHistory.STRING, oldIndex)); int insertionIndex = history.getMapIndex(IndexHistory.STRING, oldIndex); int newIndex = history.getNewIndex(insertionIndex); if (newIndex == 0) { // E:\dev\hero_private\frameworks\projects\framework\src => _ // but for included file (include "someFile.as") another format - just 'debugfile "C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\formats\TextLayoutFormatInc.as' - we don't support it yet final int originalPosition = in.position(); final int start = history.getRawPartPoolPositions(IndexHistory.STRING)[oldIndex]; in.seek(start); int stringLength = in.readU32(); //char[] s = new char[n]; //for (int j = 0; j < n; j++) { // s[j] = (char)in.data[in.position + in.offset + j]; //} //String file = new String(s); byte[] data = in.data; int c; int actualStart = -1; for (int i = 0; i < stringLength; i++) { c = data[in.position + in.offset + i]; if (c > 127) { break; // supports only ASCII } if (c == ';') { if (i < debugBasepath.length) { // may be, our injected classes todo is it actual? break; } actualStart = in.position + i - debugBasepath.length; final int p = in.offset + actualStart; data[p] = '$'; //System.arraycopy(debugBasepath, 0, data, p, debugBasepath.length); stringLength = stringLength - i + debugBasepath.length; if (stringLength < 128) { actualStart--; data[p - 1] = (byte)stringLength; } else { actualStart -= 2; data[p - 2] = (byte)((stringLength & 0x7F) | 0x80); data[p - 1] = (byte)((stringLength >> 7) & 0x7F); } break; } } in.seek(originalPosition); newIndex = history.getIndex(IndexHistory.STRING, oldIndex, insertionIndex, actualStart); } opcodes.writeU32(newIndex); } public void OP_jump(int offset, int pos) { if (opcodePass == 1) { beginop(OP_jump); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_pushstring(int index) { if (opcodePass == 1) { beginop(OP_pushstring); opcodes.writeU32(history.getIndex(IndexHistory.STRING, index)); } } public void OP_pushnamespace(int index) { if (opcodePass == 1) { beginop(OP_pushnamespace); opcodes.writeU32(history.getIndex(IndexHistory.NS, index)); } } public void OP_pushint(int index) { if (opcodePass == 1) { beginop(OP_pushint); opcodes.writeU32(history.getIndex(IndexHistory.INT, index)); } } public void OP_pushuint(int index) { if (opcodePass == 1) { beginop(OP_pushuint); opcodes.writeU32(history.getIndex(IndexHistory.UINT, index)); } } public void OP_pushdouble(int index) { if (opcodePass == 1) { beginop(OP_pushdouble); opcodes.writeU32(history.getIndex(IndexHistory.DOUBLE, index)); } } public void OP_getlocal(int index) { if (opcodePass == 1) { if (opat(1) == OP_setlocal && readIntAt(1) == index) { rewind(1); if (opcodePass == 1) { beginop(OP_dup); } OP_setlocal(index); return; } beginop(OP_getlocal); opcodes.writeU32(index); } } public void OP_pop() { if (opcodePass == 1) { switch (opat(1)) { case OP_callproperty: setOpcodeAt(1, OP_callpropvoid); return; case OP_callsuper: setOpcodeAt(1, OP_callsupervoid); return; } beginop(OP_pop); } } public void OP_convert_s() { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } switch (opat(1)) { case OP_coerce_s: case OP_convert_s: case OP_pushstring: case OP_typeof: // result is already string return; } if (opat(2) == OP_pushstring && opat(1) == OP_add) { // result must be string, so dont coerce after return; } beginop(OP_convert_s); } } public void OP_convert_m_p(int param) { if (opcodePass == 1) { beginop(OP_convert_m); opcodes.writeU32(param); } } public void OP_convert_b() { if (opcodePass == 1) { switch (opat(1)) { case OP_equals: case OP_strictequals: case OP_not: case OP_greaterthan: case OP_lessthan: case OP_greaterequals: case OP_lessequals: case OP_istype: case OP_istypelate: case OP_instanceof: case OP_deleteproperty: case OP_in: case OP_convert_b: case OP_pushtrue: case OP_pushfalse: // dont need convert return; } beginop(OP_convert_b); } } public void OP_negate_p(int param) { if (opcodePass == 1) { beginop(OP_negate_p); opcodes.writeU32(param); } } public void OP_increment_p(int param) { if (opcodePass == 1) { beginop(OP_increment_p); opcodes.writeU32(param); } } public void OP_inclocal(int index) { if (opcodePass == 1) { beginop(OP_inclocal); opcodes.writeU32(index); } } public void OP_inclocal_p(int param, int index) { if (opcodePass == 1) { beginop(OP_inclocal_p); opcodes.writeU32(param); opcodes.writeU32(index); } } public void OP_kill(int index) { if (opcodePass == 1) { switch (opat(1)) { case OP_returnvalue: case OP_returnvoid: // unreachable return; } beginop(OP_kill); opcodes.writeU32(index); } } public void OP_inclocal_i(int index) { if (opcodePass == 1) { beginop(OP_inclocal_i); opcodes.writeU32(index); } } public void OP_decrement() { if (opcodePass == 1) { beginop(OP_decrement); } } public void OP_decrement_p(int param) { if (opcodePass == 1) { beginop(OP_decrement_p); opcodes.writeU32(param); } } public void OP_declocal(int index) { if (opcodePass == 1) { beginop(OP_declocal); opcodes.writeU32(index); } } public void OP_declocal_p(int param, int index) { if (opcodePass == 1) { beginop(OP_declocal_p); opcodes.writeU32(param); opcodes.writeU32(index); } } public void OP_declocal_i(int index) { if (opcodePass == 1) { beginop(OP_declocal_i); opcodes.writeU32(index); } } public void OP_setlocal(int index) { if (opcodePass == 1) { if (opat(2) == OP_getlocal && readIntAt(2) == index && opat(1) == OP_increment_i) { rewind(2); OP_inclocal_i(index); return; } if (opat(2) == OP_getlocal && readIntAt(2) == index && opat(1) == OP_increment) { rewind(2); OP_inclocal(index); return; } beginop(OP_setlocal); opcodes.writeU32(index); } } public void OP_add() { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } beginop(OP_add); } } public void OP_add_p(int param) { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } beginop(OP_add_p); opcodes.writeU32(param); } } public void OP_subtract() { if (opcodePass == 1) { if (opat(1) == OP_pushbyte && readByteAt(1) == 1) { rewind(1); OP_decrement(); return; } beginop(OP_subtract); } } public void OP_subtract_p(int param) { if (opcodePass == 1) { if (opat(1) == OP_pushbyte && readByteAt(1) == 1) { rewind(1); OP_decrement_p(param); return; } beginop(OP_subtract_p); opcodes.writeU32(param); } } public void OP_subtract_i() { if (opcodePass == 1) { if (opat(1) == OP_pushbyte && readIntAt(1) == 1) { rewind(1); if (opcodePass == 1) { beginop(OP_decrement_i); } return; } beginop(OP_subtract_i); } } public void OP_multiply_p(int param) { if (opcodePass == 1) { beginop(OP_multiply_p); opcodes.writeU32(param); } } public void OP_divide_p(int param) { if (opcodePass == 1) { beginop(OP_divide_p); opcodes.writeU32(param); } } public void OP_modulo() { if (opcodePass == 1) { beginop(OP_modulo); } } public void OP_modulo_p(int param) { if (opcodePass == 1) { beginop(OP_modulo_p); opcodes.writeU32(param); } } public void OP_lookupswitch(int defaultPos, int[] casePos, int oldPos, int oldTablePos) { if (opcodePass == 1) { opcodes.mapOffsets(oldPos); beginop(OP_lookupswitch); opcodes.mapOffsets(oldPos + 1); opcodes.writeS24(defaultPos); opcodes.writeU32(casePos.length == 0 ? 0 : casePos.length - 1); for (int i = 0, size = casePos.length; i < size; i++) { opcodes.mapOffsets(oldTablePos + 3 * i); opcodes.writeS24(casePos[i]); } } else if (opcodePass == 2) { opcodes.updateOffset(oldPos + 1, oldPos, oldPos + defaultPos); for (int i = 0, size = casePos.length; i < size; i++) { opcodes.updateOffset(oldTablePos + (3 * i), oldPos, oldPos + casePos[i]); } } } public void OP_iftrue(int offset, int pos) { if (opcodePass == 1) { if (opat(1) == OP_convert_b) { rewind(1); } if (opat(1) == OP_pushtrue) { rewind(1); OP_jump(offset, pos); return; } beginop(OP_iftrue); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_iffalse(int offset, int pos) { if (opcodePass == 1) { if (opat(1) == OP_convert_b) { rewind(1); } if (opat(2) == OP_strictequals && opat(1) == OP_not) { rewind(2); OP_ifstricteq(offset, pos); return; } if (opat(2) == OP_equals && opat(1) == OP_not) { rewind(2); OP_ifeq(offset, pos); return; } if (opat(1) == OP_not) { rewind(1); OP_iftrue(offset, pos); return; } if (opat(1) == OP_pushfalse) { rewind(1); OP_jump(offset, pos); return; } beginop(OP_iffalse); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifeq(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifeq); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifne(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifne); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifstricteq(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifstricteq); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifstrictne(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifstrictne); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_iflt(int offset, int pos) { if (opcodePass == 1) { beginop(OP_iflt); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifle(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifle); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifgt(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifgt); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifge(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifge); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_newobject(int size) { if (opcodePass == 1) { if (opat(1) == OP_coerce_a && size >= 1) { rewind(1); } beginop(OP_newobject); opcodes.writeU32(size); } } public void OP_newarray(int size) { if (opcodePass == 1) { beginop(OP_newarray); opcodes.writeU32(size); } } public void OP_getproperty(int index) { if (opcodePass == 1) { if (opat(1) == OP_findpropstrict && readIntAt(1) == history.getIndex(IndexHistory.MULTINAME, index)) { rewind(1); op32(index, OP_getlex); return; } beginop(OP_getproperty); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void OP_setproperty(int index) { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } beginop(OP_setproperty); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void OP_initproperty(int index) { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } beginop(OP_initproperty); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void op32(int index, int opcode) { if (opcodePass == 1) { beginop(opcode); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void OP_hasnext2(int objectRegister, int indexRegister) { if (opcodePass == 1) { beginop(OP_hasnext2); opcodes.writeU32(objectRegister); opcodes.writeU32(indexRegister); } } public void OP_setslot(int index) { if (opcodePass == 1) { beginop(OP_setslot); opcodes.writeU32(index); } } public void OP_getslot(int index) { if (opcodePass == 1) { beginop(OP_getslot); opcodes.writeU32(index); } } @SuppressWarnings({"deprecation"}) public void OP_setglobalslot(int index) { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } beginop(OP_setglobalslot); opcodes.writeU32(index); } } @SuppressWarnings({"deprecation"}) public void OP_getglobalslot(int index) { if (opcodePass == 1) { beginop(OP_getglobalslot); opcodes.writeU32(index); } } public void OP_call(int size) { if (opcodePass == 1) { beginop(OP_call); opcodes.writeU32(size); } } public void OP_construct(int size) { if (opcodePass == 1) { beginop(OP_construct); opcodes.writeU32(size); } } public void OP_applytype(int size) { if (opcodePass == 1) { beginop(OP_applytype); opcodes.writeU32(size); } } public void OP_newfunction(int id) { if (opcodePass == 1) { beginop(OP_newfunction); opcodes.writeU32(methodInfo.getIndex(id)); } } public void OP_newclass(int id) { if (opcodePass == 1) { beginop(OP_newclass); opcodes.writeU32(classInfo.getIndex(id)); } } public void OP_callstatic(int id, int argc) { if (opcodePass == 1) { beginop(OP_callstatic); opcodes.writeU32(methodInfo.getIndex(id)); opcodes.writeU32(argc); } } public void OP_callmethod(int id, int argc) { if (opcodePass == 1) { beginop(OP_callmethod); opcodes.writeU32(methodInfo.getIndex(id)); opcodes.writeU32(argc); } } public void OP_callproperty(int index, int argc) { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { rewind(1); } beginop(OP_callproperty); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); opcodes.writeU32(argc); } } public void OP_callproplex(int index, int argc) { if (opcodePass == 1) { beginop(OP_callproplex); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); opcodes.writeU32(argc); } } public void OP_constructprop(int index, int argc) { if (opcodePass == 1) { beginop(OP_constructprop); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); opcodes.writeU32(argc); } } public void OP_callsuper(int index, int argc) { if (opcodePass == 1) { beginop(OP_callsuper); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); opcodes.writeU32(argc); } } public void OP_constructsuper(int argc) { if (opcodePass == 1) { beginop(OP_constructsuper); opcodes.writeU32(argc); } } public void OP_pushshort(int n) { if (opcodePass == 1) { if (peepHole && n >= -128 && n <= 127) { OP_pushbyte(n); return; } beginop(OP_pushshort); opcodes.writeU32(n); } } public void OP_astype(int index) { if (opcodePass == 1) { beginop(OP_astype); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void OP_coerce(int index) { if (opcodePass == 1) { if (opat(1) == OP_coerce && readIntAt(1) == history.getIndex(IndexHistory.MULTINAME, index)) { // second coerce to same type is redundant return; } beginop(OP_coerce); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void OP_coerce_a() { if (opcodePass == 1) { if (opat(1) == OP_coerce_a) { return; } beginop(OP_coerce_a); } } public void OP_coerce_i() { if (opcodePass == 1) { switch (opat(1)) { case OP_coerce_i: case OP_convert_i: case OP_increment_i: case OP_decrement_i: case OP_pushbyte: case OP_pushshort: case OP_pushint: case OP_bitand: case OP_bitor: case OP_bitxor: case OP_lshift: case OP_rshift: case OP_add_i: case OP_subtract_i: case OP_multiply_i: case OP_bitnot: // coerce is redundant return; } beginop(OP_coerce_i); } } public void OP_coerce_u() { if (opcodePass == 1) { switch (opat(1)) { case OP_coerce_u: case OP_convert_u: case OP_urshift: // redundant coerce return; } beginop(OP_coerce_u); } } public void OP_coerce_d() { if (opcodePass == 1) { switch (opat(1)) { case OP_subtract: case OP_multiply: case OP_divide: case OP_modulo: case OP_increment: case OP_decrement: case OP_inclocal: case OP_declocal: case OP_coerce_d: case OP_convert_d: // coerce is redundant return; } beginop(OP_coerce_d); } } public void OP_coerce_s() { if (opcodePass == 1) { switch (opat(1)) { case OP_coerce_s: case OP_convert_s: case OP_pushstring: case OP_typeof: // result is already string return; } if (opat(2) == OP_pushstring && opat(1) == OP_add) { // result must be string, so dont coerce after return; } beginop(OP_coerce_s); } } public void OP_istype(int index) { if (opcodePass == 1) { beginop(OP_istype); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); } } public void OP_istypelate() { if (opcodePass == 1) { beginop(OP_istypelate); } } public void OP_pushbyte(int n) { if (opcodePass == 1) { if (opat(1) == OP_pushbyte && readByteAt(1) == n || opat(1) == OP_dup && opat(2) == OP_pushbyte && readByteAt(2) == n) { if (opcodePass == 1) { beginop(OP_dup); } return; } beginop(OP_pushbyte); opcodes.writeU8(n); } } public void OP_getscopeobject(int index) { if (opcodePass == 1) { beginop(OP_getscopeobject); opcodes.writeU8(index); } } public void OP_convert_i() { if (opcodePass == 1) { switch (opat(1)) { case OP_convert_i: case OP_coerce_i: case OP_bitand: case OP_bitor: case OP_bitxor: case OP_lshift: case OP_rshift: case OP_add_i: case OP_subtract_i: case OP_increment_i: case OP_decrement_i: case OP_multiply_i: case OP_pushbyte: case OP_pushshort: case OP_pushint: return; } beginop(OP_convert_i); } } public void OP_dxns(int index) { if (opcodePass == 1) { beginop(OP_dxns); opcodes.writeU32(history.getIndex(IndexHistory.STRING, index)); } } public void OP_ifnlt(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifnlt); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifnle(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifnle); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifngt(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifngt); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_ifnge(int offset, int pos) { if (opcodePass == 1) { beginop(OP_ifnge); opcodes.writeS24(offset); opcodes.mapOffsets(pos); } else if (opcodePass == 2) { opcodes.updateOffset(pos + offset); } } public void OP_newcatch(int index) { if (opcodePass == 1) { beginop(OP_newcatch); opcodes.writeU32(index); } } public void OP_setlocal1() { if (opcodePass == 1) { if (opat(2) == OP_getlocal1 && opat(1) == OP_increment_i) { rewind(2); OP_inclocal_i(1); return; } if (opat(2) == OP_getlocal1 && opat(1) == OP_increment) { rewind(2); OP_inclocal(1); return; } beginop(OP_setlocal1); } } public void OP_setlocal2() { if (opcodePass == 1) { if (opat(2) == OP_getlocal2 && opat(1) == OP_increment_i) { rewind(2); OP_inclocal_i(2); return; } if (opat(2) == OP_getlocal2 && opat(1) == OP_increment) { rewind(2); OP_inclocal(2); return; } beginop(OP_setlocal2); } } public void OP_setlocal3() { if (opcodePass == 1) { if (opat(2) == OP_getlocal3 && opat(1) == OP_increment_i) { rewind(2); OP_inclocal_i(3); return; } if (opat(2) == OP_getlocal3 && opat(1) == OP_increment) { rewind(2); OP_inclocal(3); return; } beginop(OP_setlocal3); } } public void OP_label() { if (opcodePass == 1) { beginop(OP_label); } } public void OP_pushconstant(int id) { if (opcodePass == 1) { beginop(OP_pushuninitialized); opcodes.writeU32(id); } } public void OP_callsupervoid(int index, int argc) { if (opcodePass == 1) { beginop(OP_callsupervoid); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); opcodes.writeU32(argc); } } public void OP_callpropvoid(int index, int argc) { if (opcodePass == 1) { beginop(OP_callpropvoid); opcodes.writeU32(history.getIndex(IndexHistory.MULTINAME, index)); opcodes.writeU32(argc); } } private static class MetadataInfoByteArray extends PoolPart { private int size = 0; private int processedSize; private final IntIntHashMap indexes; MetadataInfoByteArray(int total) { super(total + 1); indexes = new IntIntHashMap(total); } int addData(int oldIndex, WritableDataBuffer data) { int index = contains(data, 0, data.getSize()); if (index == -1) { index = store(data, 0, data.getSize()); size += data.getSize(); } // ByteArrayPool is 1 based, we want zero based for metadataInfos indexes.put(calcIndex(oldIndex), index - 1); return index; } private int calcIndex(int oldIndex) { return processedSize + oldIndex; } int getIndex(int oldIndex) { return indexes.get(calcIndex(oldIndex)); } int size() { return size; } void writeTo(ByteBuffer buffer) { writeTo(buffer, 0); } } protected static class DataBuffer2 extends WritableDataBuffer { private int processedSize; DataBuffer2(int estimatedSize) { super(estimatedSize); } int getIndex(int oldIndex) { return processedSize + oldIndex; } } protected class DataBuffer3 extends WritableDataBuffer { final IntIntHashMap offsets; final List<Decoder> decoders; DataBuffer3(List<Decoder> decoders, int estimatedSize) { super(estimatedSize); offsets = new IntIntHashMap(); this.decoders = decoders; } void mapOffsets(int offset) { offsets.put(offset, getSize()); } int getOffset(int offset) { int i = offsets.get(offset); if (i != -1) { return i; } else { throw new IllegalArgumentException("getOffset: can't match " + offset + " with a new offset"); } } void updateOffset(int offset) { int i = offsets.get(offset); int p = offsets.get(decoders.get(decoderIndex).position()); if (i != -1 && p != -1) { writeS24(p - 3, i - p); } } void updateOffset(int oldOffsetPos, int oldPos, int oldTarget) { int i = offsets.get(oldTarget); int p = offsets.get(oldPos); int s = offsets.get(oldOffsetPos); if (i != -1 && p != -1 && s != -1) { writeS24(s, i - p); } else { if (i == -1) { System.out.println("updateOffset2: can't match i " + oldTarget + " with a new offset"); } if (p == -1) { System.out.println("updateOffset2: can't match p " + oldPos + " with a new offset"); } if (s == -1) { System.out.println("updateOffset2: can't match s " + oldOffsetPos + " with a new offset"); } System.out.println(offsets); } } public void clear() { super.clear(); offsets.clear(); } } }