Package macromedia.abc

Source Code of macromedia.abc.Encoder$BytecodeBuffer3

/*
*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*
*/

package macromedia.abc;

import static macromedia.asc.embedding.avmplus.ActionBlockConstants.*;
import macromedia.asc.util.IntegerPool;
import macromedia.asc.util.IntList;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

/**
* abc encoder. If the encoder is provided with multiple constant pools, it will use do merging.
*
* @author Clement Wong
*/
public class Encoder implements Visitor
{
  public Encoder(int majorVersion, int minorVersion)
  {
    this.majorVersion = majorVersion;
    this.minorVersion = minorVersion;

    poolIndex = 0;
    peepHole = false;
    disableDebugging = false;
    removeMetadata = false;

    }

  private ConstantPool pool;
  private int majorVersion, minorVersion;
  private int poolIndex, opcodePass, exPass;
  private boolean disableDebugging, removeMetadata, peepHole;

    private HashSet<String> keep_metadata = new HashSet<String>();

  private BytecodeBuffer2 methodInfo;
  private ByteArrayPool2 metadataInfo;
  private BytecodeBuffer2 classInfo;
  private BytecodeBuffer2 scriptInfo;
  private BytecodeBuffer2 methodBodies;
  private BytecodeBuffer3 opcodes;
  private BytecodeBuffer exceptions;

  private BytecodeBuffer currentBuffer;

    private ConstantPool[] pools;

  public void enablePeepHole()
  {
    peepHole = true;
  }

  public void disableDebugging()
  {
    disableDebugging = true;
        pool.history.disableDebugging();
  }

  public void removeMetadata()
  {
    removeMetadata = true;
  }

    public void addMetadataToKeep(String meta_name)
    {
        keep_metadata.add(meta_name);
    }

  public void addConstantPools(ConstantPool[] pools)
  {
        this.pools = pools;
    pool = ConstantPool.merge(pools);
        if( disableDebugging )
            pool.history.disableDebugging();
  }
 
  public void test()
  {
    // C: testing only...
    for (int i = 0, size = pools.length; i < size; i++)
    {
      /*
      for (int j = 1, count = pools[i].intpositions.length; j < count; j++)
      {
        int original = pools[i].getInt(j);
        int real = pool.getInt(pool.history.getIndex(i, 0, j));

        if (original != real)
        {
          throw new DecoderException("Error (int) in constant pooling merging...");
        }
      }

      for (int j = 1, count = pools[i].uintpositions.length; j < count; j++)
      {
        long original = pools[i].getLong(j);
        long real = pool.getLong(pool.history.getIndex(i, 1, j));

        if (original != real)
        {
          throw new DecoderException("Error (uint) in constant pooling merging...");
        }
      }

      for (int j = 1, count = pools[i].doublepositions.length; j < count; j++)
      {
        double original = pools[i].getDouble(j);
        double real = pool.getDouble(pool.history.getIndex(i, 2, j));

        if (original != real)
        {
          throw new DecoderException("Error (double) in constant pooling merging...");
        }
      }

      for (int j = 1, count = pools[i].strpositions.length; j < count; j++)
      {
        String original = pools[i].getString(j);
        System.out.println("(" + i + "," + j + ")-->(0," + pool.history.getIndex(i, 3, j) + "): " + original);
      }

      for (int j = 1, count = pools[i].nspositions.length; j < count; j++)
      {
        String original = pools[i].getNamespaceName(j);
        String real = pool.getNamespaceName(pool.history.getIndex(i, 4, j));

        if (!original.equals(real))
        {
          throw new DecoderException("Error (namespace) in constant pooling merging...");
        }
      }

      for (int j = 1, count = pools[i].nsspositions.length; j < count; j++)
      {
        String[] original = pools[i].getNamespaceSet(j);
        String[] real = pool.getNamespaceSet(pool.history.getIndex(i, 5, j));

        if (original.length != real.length)
        {
          throw new DecoderException("Error (namespace set) in constant pooling merging...");
        }
        else
        {
          for (int k = 0, len = original.length; k < len; k++)
          {
            if (!original[k].equals(real[k]))
            {
              throw new DecoderException("Error (namespace set) in constant pooling merging...");
            }
          }
        }
      }

      for (int j = 1, count = pools[i].mnpositions.length; j < count; j++)
      {
        Object original = pools[i].getGeneralMultiname(j);
        Object real = pool.getGeneralMultiname(pool.history.getIndex(i, 6, j));

        if (original.getClass() != real.getClass())
        {
          throw new DecoderException("Error (multiname) in constant pooling merging...");
        }

        if (original instanceof QName && !((QName) original).equals(real))
        {
          throw new DecoderException("Error (multiname) in constant pooling merging...");
        }

        if (original instanceof MultiName && !((MultiName) original).equals(real))
        {
          throw new DecoderException("Error (multiname) in constant pooling merging...");
        }
      }
      */
    }
  }

  public void configure(Decoder[] decoders)
  {
    int estimatedSize = 0, total = 0;
    int[] sizes = new int[decoders.length];
    for (int i = 0, size = sizes.length; i < size; i++)
    {
      estimatedSize += decoders[i].methodInfo.estimatedSize;
      sizes[i] = decoders[i].methodInfo.size();
      total += sizes[i];
    }
    methodInfo = new BytecodeBuffer2(estimatedSize, sizes);
    methodInfo.writeU32(total);


    estimatedSize = 0; total = 0;
    sizes = new int[decoders.length];
    for (int i = 0, size = sizes.length; i < size; i++)
    {
      estimatedSize += decoders[i].metadataInfo.estimatedSize;
      sizes[i] = decoders[i].metadataInfo.size();
      total += sizes[i];
    }
    metadataInfo = new ByteArrayPool2(sizes);


    estimatedSize = 0; total = 0;
    sizes = new int[decoders.length];
    for (int i = 0, size = sizes.length; i < size; i++)
    {
      estimatedSize += decoders[i].classInfo.estimatedSize;
      sizes[i] = decoders[i].classInfo.size();
      total += sizes[i];
    }
    classInfo = new BytecodeBuffer2(estimatedSize, sizes);
    classInfo.writeU32(total);


    estimatedSize = 0; total = 0;
    sizes = new int[decoders.length];
    for (int i = 0, size = sizes.length; i < size; i++)
    {
      estimatedSize += decoders[i].scriptInfo.estimatedSize;
      sizes[i] = decoders[i].scriptInfo.size();
      total += sizes[i];
    }
    scriptInfo = new BytecodeBuffer2(estimatedSize, sizes);
    scriptInfo.writeU32(total);


    estimatedSize = 0; total = 0;
    sizes = new int[decoders.length];
    for (int i = 0, size = sizes.length; i < size; i++)
    {
      estimatedSize += decoders[i].methodBodies.estimatedSize;
      sizes[i] = decoders[i].methodBodies.size();
      total += sizes[i];
    }
    methodBodies = new BytecodeBuffer2(estimatedSize, sizes);
    methodBodies.writeU32(total);

    opcodes = new BytecodeBuffer3(decoders, 4096);
    exceptions = new BytecodeBuffer(4096);
  }

  public void useConstantPool(int index)
  {
    poolIndex = index;
  }

  public byte[] toABC()
  {
    /*
    System.out.println();
    System.out.println("--Constant Pool--");
    System.out.println("total: " + pool.history.total + " duplicate: " + pool.history.duplicate);
    System.out.println("totalBytes: " + pool.history.totalBytes + " duplicateBytes: " + pool.history.duplicateBytes);
    System.out.println("--Method Info--");
    System.out.println("before: " + methodInfo.estimatedSize + " after: " + methodInfo.size());
    System.out.println("--Metadata Info--");
    System.out.println("before: " + metadataInfo.estimatedSize + " after: " + metadataInfo.size());
    System.out.println("--Class Info--");
    System.out.println("before: " + classInfo.estimatedSize + " after: " + classInfo.size());
    System.out.println("--Script Info--");
    System.out.println("before: " + scriptInfo.estimatedSize + " after: " + scriptInfo.size());
    System.out.println("--Method Bodies--");
    System.out.println("before: " + methodBodies.estimatedSize + " after: " + methodBodies.size());
    System.out.println();
    */
   
    int size = pool.in.size() + methodInfo.size() + metadataInfo.size() + classInfo.size() + scriptInfo.size() + methodBodies.size();
    ByteArrayOutputStream baos = new ByteArrayOutputStream(size);

    try
    {
      baos.write((byte) minorVersion);
      baos.write((byte) (minorVersion >> 8));
      baos.write((byte) majorVersion);
      baos.write((byte) (majorVersion >> 8));

      pool.writeTo(baos);
      methodInfo.writeTo(baos);
      metadataInfo.writeTo(baos);
      classInfo.writeTo(baos);
      scriptInfo.writeTo(baos);
      methodBodies.writeTo(baos);
    }
    catch (IOException ex)
    {
      return null;
    }

    return baos.toByteArray();
  }

  public void methodInfo(int returnType, int[] paramTypes, int nativeName, int flags, int[] values, int[] value_kinds, int[] param_names)
  {
    if (paramTypes == null)
    {
      methodInfo.writeU32(0);
    }
    else
    {
      methodInfo.writeU32(paramTypes.length);
    }

    methodInfo.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, returnType));

    for (int i = 0, paramCount = (paramTypes == null) ? 0 : paramTypes.length; i < paramCount; i++)
    {
      methodInfo.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, paramTypes[i]));
    }

    methodInfo.writeU32((disableDebugging) ? 0 : pool.history.getIndex(poolIndex, IndexHistory.cp_string, nativeName));

        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)
    {
      if (values == null)
      {
        methodInfo.writeU32(0);
      }
      else
      {
        methodInfo.writeU32(values.length);
      }
    }

    for (int i = 0, optionalCount = (values == null) ? 0 : values.length; i < optionalCount; i++)
    {
      int kind = -1;

      switch (value_kinds[i])
      {
        case CONSTANT_Utf8:
        kind = IndexHistory.cp_string;
        break;
        case CONSTANT_Integer:
        kind = IndexHistory.cp_int;
        break;
        case CONSTANT_UInteger:
        kind = IndexHistory.cp_uint;
        break;
        case CONSTANT_Double:
        kind = IndexHistory.cp_double;
        break;
        case CONSTANT_Decimal:
        kind = IndexHistory.cp_decimal;
        break;
        case CONSTANT_Namespace:
        case CONSTANT_PrivateNamespace:
            case CONSTANT_PackageNamespace:
            case CONSTANT_PackageInternalNs:
            case CONSTANT_ProtectedNamespace:
            case CONSTANT_ExplicitNamespace:
            case CONSTANT_StaticProtectedNs:
        kind = IndexHistory.cp_ns;
        break;
      case CONSTANT_Qname:
      case CONSTANT_QnameA:
        case CONSTANT_Multiname:
        case CONSTANT_MultinameA:
            case CONSTANT_TypeName:
                kind = IndexHistory.cp_mn;
        break;
        case CONSTANT_Namespace_Set:
        kind = IndexHistory.cp_nsset;
        break;
      }

      int newIndex = 0;

      switch(value_kinds[i])
      {
      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 = values[i];
        break;
      default:
      {
        if (kind == -1)
        {
          System.out.println("writing MethodInfo: don't know what constant type it is... " + value_kinds[i] + "," + values[i]);
        }
        newIndex = pool.history.getIndex(poolIndex, kind, values[i]);
      }
      }

      methodInfo.writeU32(newIndex);
      methodInfo.writeU8(value_kinds[i]);
    }
        // Nuke the param names if we're not keeping debugging around
        if( (flags & METHOD_HasParamNames) != 0 && param_names != null)
        {
            for( int i = 0 ; i < param_names.length; ++i )
            {
                methodInfo.writeU32( pool.history.getIndex(poolIndex, IndexHistory.cp_string, returnType) );
            }
        }
  }

  public void metadataInfo(int index, int name, int[] keys, int[] values)
  {


        try
        {
            String s = pools[poolIndex].getString(name);
            if (removeMetadata && !keep_metadata.contains(s) )
                return;

            BytecodeBuffer b = new BytecodeBuffer(6);
            b.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, name));
            if (keys == null)
            {
                b.writeU32(0);
            }
            else
            {
                b.writeU32(keys.length);
            }

            for (int i = 0, keyCount = (keys == null) ? 0 : keys.length; i < keyCount; i++)
            {
                b.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, keys[i]));
            }

            for (int i = 0, valueCount = (values == null) ? 0 : values.length; i < valueCount; i++)
            {
                b.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, values[i]));
            }

            metadataInfo.addByteArray(poolIndex, index, b);
        }
        catch( DecoderException ex)
        {
            // this should never happen
            // ex.printStackTrace();
        }
  }

  public void startInstance(int name, int superName, boolean isDynamic, boolean isFinal, boolean isInterface, int[] interfaces, int iinit, int protectedNamespace)
  {
    classInfo.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, name));
    classInfo.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, superName));

    int flags = 0;
    flags = (isFinal) ? (flags | CLASS_FLAG_final) : flags;
    flags = (!isDynamic) ? (flags | CLASS_FLAG_sealed) : flags;
    flags = (isInterface) ? (flags | CLASS_FLAG_interface) : flags;
    flags = (protectedNamespace != 0) ? (flags | CLASS_FLAG_protected) : flags;
    classInfo.writeU8(flags);

    if (protectedNamespace != 0)
    {
      classInfo.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_ns, protectedNamespace));
    }
   
    if (interfaces == null)
    {
      classInfo.writeU32(0);
    }
    else
    {
      classInfo.writeU32(interfaces.length);
    }

    for (int i = 0, interfaceCount = interfaces == null ? 0 : interfaces.length; i < interfaceCount; i++)
    {
      classInfo.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, interfaces[i]));
    }

    classInfo.writeU32(methodInfo.getIndex(poolIndex, iinit));

    currentBuffer = classInfo;
  }

  public void endInstance()
  {
    currentBuffer = null;
  }

  public void startClass(int name, int cinit)
  {
    classInfo.writeU32(methodInfo.getIndex(poolIndex, cinit));

    currentBuffer = classInfo;
  }

  public void endClass()
  {
    currentBuffer = null;
  }

  public void startScript(int initID)
  {
    scriptInfo.writeU32(methodInfo.getIndex(poolIndex, initID));

    currentBuffer = scriptInfo;
  }

  public void endScript()
  {
    currentBuffer = null;
  }

  public void startMethodBody(int methodInfo, int maxStack, int maxRegs, int scopeDepth, int maxScope, int codeStart, long codeLength)
  {
    methodBodies.writeU32(this.methodInfo.getIndex(poolIndex, methodInfo));
    methodBodies.writeU32(maxStack);
    methodBodies.writeU32(maxRegs);
    methodBodies.writeU32(scopeDepth);
    methodBodies.writeU32(maxScope);

    currentBuffer = methodBodies;
    opcodePass = 1;
    exPass = 1;
  }

  public void endMethodBody()
  {
    currentBuffer = null;
    opcodes.clear();
    exceptions.clear();
  }

  public void startOpcodes(int methodInfo)
  {
  }

  public void endOpcodes()
  {
    if (opcodePass == 1)
    {
      opcodePass = 2;
    }
    else if (opcodePass == 2)
    {
      methodBodies.writeU32(opcodes.size());
      methodBodies.writeBytes(opcodes, 0, opcodes.size());
    }
  }

  public void exception(long start, long end, long 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(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, type));
      if (minorVersion != 15)
      {
        exceptions.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, name));
      }
    }
  }

  public void startExceptions(int exceptionCount)
  {
    if (exPass == 2)
    {
      exceptions.writeU32(exceptionCount);
    }
  }

  public void endExceptions()
  {
    if (exPass == 1)
    {
      exPass++;
    }
    else if (exPass == 2)
    {
      methodBodies.writeBytes(exceptions, 0, exceptions.size());
    }
  }

  public void traitCount(int traitCount)
  {
    currentBuffer.writeU32(traitCount);
  }

//  private void encodeMetaData(int kind, int[] metadata)
//  {
//    if (((kind >> 4) & TRAIT_FLAG_metadata) != 0)
//    {
//      if (metadata == null)
//      {
//        currentBuffer.writeU32(0);
//      }
//      else
//      {
//        currentBuffer.writeU32(metadata.length);
//      }
//
//      for (int i = 0, length = metadata == null ? 0 : metadata.length; i < length; i++)
//      {
//        currentBuffer.writeU32(metadataInfo.getIndex(poolIndex, metadata[i]));
//      }
//    }
//  }

    private void encodeMetaData(int kind, IntList metadata)
    {
        if (((kind >> 4) & TRAIT_FLAG_metadata) != 0)
        {
            if (metadata == null)
            {
                currentBuffer.writeU32(0);
            }
            else
            {
                currentBuffer.writeU32(metadata.size());
            }

            for (int i = 0, length = metadata == null ? 0 : metadata.size(); i < length; i++)
            {
                currentBuffer.writeU32(metadata.get(i));
            }
        }
    }

    private IntList trimMetadata(int[] metadata)
    {
        IntList newMetadata = new IntList();
        int length = metadata != null ? metadata.length : 0;
        for( int i = 0; i < length; ++i)
        {
            int new_index = metadataInfo.getIndex(poolIndex, metadata[i]) ;
            if( new_index != -1 )
            {
                newMetadata.add(new_index);
            }
        }
        return newMetadata;
    }

  public void slotTrait(int trait_kind, int name, int slotId, int type, int value, int value_kind, int[] metadata)
  {
    currentBuffer.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, name));
        IntList new_metadata = trimMetadata(metadata);
    if ( ((trait_kind >> 4) & TRAIT_FLAG_metadata) != 0 && new_metadata.size()==0 )
    {
      trait_kind = trait_kind & ~(TRAIT_FLAG_metadata << 4);
    }
    currentBuffer.writeU8(trait_kind);

    currentBuffer.writeU32(slotId);
    currentBuffer.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, type));

    int kind = -1;

    switch(value_kind)
    {
    case CONSTANT_Utf8:
      kind = IndexHistory.cp_string;
      break;
    case CONSTANT_Integer:
      kind = IndexHistory.cp_int;
      break;
    case CONSTANT_UInteger:
      kind = IndexHistory.cp_uint;
      break;
    case CONSTANT_Double:
      kind = IndexHistory.cp_double;
      break;
    case CONSTANT_Decimal:
      kind = IndexHistory.cp_decimal;
      break;
    case CONSTANT_Namespace:
    case CONSTANT_PrivateNamespace:
        case CONSTANT_PackageNamespace:
        case CONSTANT_PackageInternalNs:
        case CONSTANT_ProtectedNamespace:
        case CONSTANT_ExplicitNamespace:
        case CONSTANT_StaticProtectedNs:     
      kind = IndexHistory.cp_ns;
      break;
    case CONSTANT_Qname:
    case CONSTANT_QnameA:
    case CONSTANT_Multiname:
    case CONSTANT_MultinameA:
        case CONSTANT_TypeName:
            kind = IndexHistory.cp_mn;
      break;
    case CONSTANT_Namespace_Set:
      kind = IndexHistory.cp_nsset;
      break;
    }

    int newIndex = 0;
    switch(value_kind)
    {
    case 0:
    case CONSTANT_False:
    case CONSTANT_True:
    case CONSTANT_Null:
      newIndex = value;
      break;
    default:
    {
      if (kind == -1)
      {
        System.out.println("writing slotTrait: don't know what constant type it is... " + value_kind + "," + value);
      }
      newIndex = pool.history.getIndex(poolIndex, kind, value);
    }
    }

    currentBuffer.writeU32(newIndex);
    if (value != 0)
    {
      currentBuffer.writeU8(value_kind);
    }

    encodeMetaData(trait_kind, new_metadata);
  }

  public void methodTrait(int trait_kind, int name, int dispId, int methodInfo, int[] metadata)
  {
    currentBuffer.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, name));
        IntList new_metadata = trimMetadata(metadata);
    if ( ((trait_kind >> 4) & TRAIT_FLAG_metadata) != 0 && new_metadata.size()==0 )
    {
      trait_kind = trait_kind & ~(TRAIT_FLAG_metadata << 4);
    }
    currentBuffer.writeU8(trait_kind);

    //currentBuffer.writeU32(0);
    currentBuffer.writeU32(dispId);
    currentBuffer.writeU32(this.methodInfo.getIndex(poolIndex, methodInfo));

    encodeMetaData(trait_kind, new_metadata);
  }

  public void classTrait(int kind, int name, int slotId, int classIndex, int[] metadata)
  {
    currentBuffer.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, name));
        IntList new_metadata = trimMetadata(metadata);
    if ( ((kind >> 4) & TRAIT_FLAG_metadata) != 0 && new_metadata.size()==0 )
    {
      kind = kind & ~(TRAIT_FLAG_metadata << 4);
    }
    currentBuffer.writeU8(kind);

    currentBuffer.writeU32(slotId);
    currentBuffer.writeU32(classInfo.getIndex(poolIndex, classIndex));

    encodeMetaData(kind, new_metadata);
  }

  public void functionTrait(int kind, int name, int slotId, int methodInfo, int[] metadata)
  {
    currentBuffer.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, name));
        IntList new_metadata = trimMetadata(metadata);
    if ( ((kind >> 4) & TRAIT_FLAG_metadata) != 0 && new_metadata.size()==0 )
    {
      kind = kind & ~(TRAIT_FLAG_metadata << 4);
    }
    currentBuffer.writeU8(kind);

    currentBuffer.writeU32(slotId);
    currentBuffer.writeU32(this.methodInfo.getIndex(poolIndex, methodInfo));

    encodeMetaData(kind, new_metadata);
  }

  static final int W = 8;
  int[] window = new int[W];
    int window_size = 0;
  int head = 0;
  boolean reachable = true;
 
  void clearWindow()
  {
    for (int i=0; i < W; i++)
      window[i] = 0;
        window_size = 0;
  }
 
  void beginop(int opcode)
  {
    window[head] = opcodes.size();
    head = (head+1) & (W-1);
        if( window_size < 8 )
            ++window_size;
    opcodes.writeU8(opcode);
  }
 
  int opat(int i)
  {
    if (peepHole)
    {
            if( i <= window_size )
            {
                int ip = window[(head-i) & (W-1)];
                if (ip < opcodes.size())
                    return opcodes.readU8(ip);
            }
    }
    return 0;
  }
 
  void setOpcodeAt(int i, int opcode)
  {
    assert peepHole;

        if( i <= window_size )
        {
            int ip = window[(head-i) & (W-1)];
            if (ip < opcodes.size())
                opcodes.writeU8(ip, opcode);
        }
  }
 
  int readByteAt(int i)
  {
        if( i <= window_size )
        {
            int ip = 1+window[(head-i) & (W-1)];
            return (byte) opcodes.readU8(ip);
        }
        return 0;
  }
 
  int readIntAt(int i)
  {
        if( i <= window_size )
        {
            int ip = 1+window[(head-i) & (W-1)];
            return (int) opcodes.readU32(ip);
        }
        return 0;
  }
 
  void rewind(int i)
  {
    int to = (head-i) & (W-1);
    int ip = window[to];
    int end = opcodes.size();
    opcodes.delete(end-ip);
    head = to;
        window_size -= i;
  }
 
  public void target(int oldPos)
  {
    if (opcodePass == 1)
    {
      opcodes.mapOffsets(oldPos);
      clearWindow();
    }
  }

  public void OP_returnvoid()
  {
    if (opcodePass == 1)
    {
      /*
      if (opat(2) == OP_getlocal0 && opat(1) == OP_pushscope)
        rewind(2);
     
      if (opat(1) == OP_returnvalue)
        return;
      */
      beginop(OP_returnvoid);
    }
  }

  public void OP_returnvalue()
  {
    if (opcodePass == 1)
    {
     
      if (opat(1) == OP_coerce_a)
      {
        rewind(1);
      }
     
      if (opat(1) == OP_pushundefined)
      {
        rewind(1);           
        OP_returnvoid();
        return;
      }
     
      // eliminate dead code... maybe do this higher up?
      /*
      for (int i=1; i < W; i++)
      {
        if (opat(i) == OP_returnvalue)
        {
          rewind(i);
          break;
        }
      }
     
      if (  opat(4) == OP_pushundefined &&
          opat(3) == OP_coerce_a &&
          opat(2) == OP_setlocal1 &&
          opat(1) == OP_getlocal1)
      {
        rewind(4);
        OP_returnvoid();
        return;
      }
      */
     
      beginop(OP_returnvalue);
    }
  }

  public void OP_nop()
  {
    if (opcodePass == 1)
    {
      beginop(OP_nop);
    }
  }

  public void OP_bkpt()
  {
    if (opcodePass == 1)
    {
      beginop(OP_bkpt);
    }
  }

  public void OP_timestamp()
  {
    if (opcodePass == 1)
    {
      beginop(OP_timestamp);
    }
  }

  public void OP_debugline(int linenum)
  {
    if (opcodePass == 1)
    {
      if (!disableDebugging)
      {
        beginop(OP_debugline);
        opcodes.writeU32(linenum);
      }
    }
  }

  public void OP_bkptline()
  {
    if (opcodePass == 1)
    {
      beginop(OP_bkptline);
    }
  }

  public void OP_debug(int di_local, int index, int slot, int linenum)
  {
    if (opcodePass == 1)
    {
      if (!disableDebugging)
      {
        beginop(OP_debug);
        opcodes.writeU8(di_local);
        // FIX: is this a constant pool index? if so, we need to know the constant type...
        // opcodes.writeU32(index);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, index));
        opcodes.writeU8(slot);
        opcodes.writeU32(linenum);
      }
    }
  }

  public void OP_debugfile(int index)
  {
    if (opcodePass == 1)
    {
      if (!disableDebugging)
      {
        beginop(OP_debugfile);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, index));
      }
    }
  }

  public void OP_jump(int offset, int pos)
  {
    if (opcodePass == 1)
        {
      /*
      if (opat(1) == OP_jump)
      {
        // unreachable
        return;
      }
      */

      beginop(OP_jump);
      opcodes.writeS24(offset);
      opcodes.mapOffsets(pos);
    }
    else if (opcodePass == 2)
    {
      opcodes.updateOffset(pos + offset);
    }
  }

  public void OP_pushnull()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushnull);
    }
  }

  public void OP_pushundefined()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushundefined);
    }
  }

    public void OP_pushstring(int index)
    {
      if (opcodePass == 1)
      {
        beginop(OP_pushstring);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, index));
      }
    }

    public void OP_pushnamespace(int index)
    {
      if (opcodePass == 1)
      {
        beginop(OP_pushnamespace);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_ns, index));
      }
    }

    public void OP_pushint(int index)
    {
      if (opcodePass == 1)
      {
        beginop(OP_pushint);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_int, index));
      }
    }

    public void OP_pushuint(int index)
    {
      if (opcodePass == 1)
      {
        beginop(OP_pushuint);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_uint, index));
      }
    }

    public void OP_pushdouble(int index)
    {
      if (opcodePass == 1)
      {
        beginop(OP_pushdouble);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_double, index));
      }
    }

    public void OP_pushdecimal(int index)
    {
      if (opcodePass == 1)
      {
        beginop(OP_pushdecimal);
        opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_decimal, index));
      }
    }

  public void OP_getlocal(int index)
  {
    if (opcodePass == 1)
    {
      if (opat(1) == OP_setlocal && readIntAt(1) == index)
      {
        rewind(1);
        OP_dup();
        OP_setlocal(index);
        return;
      }

      beginop(OP_getlocal);
      opcodes.writeU32(index);
    }
  }

  public void OP_pushtrue()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushtrue);
    }
  }

  public void OP_pushfalse()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushfalse);
    }
  }

  public void OP_pushnan()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushnan);
    }
  }

  public void OP_pushdnan()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushdnan);
    }
  }

  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_dup()
  {
    if (opcodePass == 1)
    {
      beginop(OP_dup);
    }
  }

  public void OP_swap()
  {
    if (opcodePass == 1)
    {
      beginop(OP_swap);
    }
  }

  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_esc_xelem()
  {
    if (opcodePass == 1)
    {
      beginop(OP_esc_xelem);
    }
  }

    public void OP_esc_xattr()
    {
        if (opcodePass == 1)
        {
            beginop(OP_esc_xattr);
        }
    }

    public void OP_checkfilter()
    {
        if (opcodePass == 1)
        {
            beginop(OP_checkfilter);
        }
    }

  public void OP_convert_d()
  {
    if (opcodePass == 1)
    {
      beginop(OP_convert_d);
    }
  }

  public void OP_convert_m()
  {
    if (opcodePass == 1)
    {
      beginop(OP_convert_m);
    }
  }

  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_convert_o()
  {
    if (opcodePass == 1)
    {
      beginop(OP_convert_o);
    }
  }

  public void OP_negate()
  {
    if (opcodePass == 1)
    {
      beginop(OP_negate);
    }
  }

  public void OP_negate_p(int param)
  {
    if (opcodePass == 1)
    {
      beginop(OP_negate_p);
      opcodes.writeU32(param);
    }
  }

  public void OP_negate_i()
  {
    if (opcodePass == 1)
    {
      beginop(OP_negate_i);
    }
  }

  public void OP_increment()
  {
    if (opcodePass == 1)
    {
      beginop(OP_increment);
    }
  }

  public void OP_increment_p(int param)
  {
    if (opcodePass == 1)
    {
      beginop(OP_increment_p);
      opcodes.writeU32(param);
    }
  }

  public void OP_increment_i()
  {
    if (opcodePass == 1)
    {
      beginop(OP_increment_i);
    }
  }

  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_decrement_i()
  {
    if (opcodePass == 1)
    {
      beginop(OP_decrement_i);
    }
  }

  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_typeof()
  {
    if (opcodePass == 1)
    {
      beginop(OP_typeof);
    }
  }

  public void OP_not()
  {
    if (opcodePass == 1)
    {
      beginop(OP_not);
    }
  }

  public void OP_bitnot()
  {
    if (opcodePass == 1)
    {
      beginop(OP_bitnot);
    }
  }

  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_add_i()
  {
    if (opcodePass == 1)
    {
      beginop(OP_add_i);
    }
  }

  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);
        OP_decrement_i();
        return;
      }
      beginop(OP_subtract_i);
    }
  }

  public void OP_multiply()
  {
    if (opcodePass == 1)
    {
      beginop(OP_multiply);
    }
  }

  public void OP_multiply_p(int param)
  {
    if (opcodePass == 1)
    {
      beginop(OP_multiply_p);
      opcodes.writeU32(param);
    }
  }

  public void OP_multiply_i()
  {
    if (opcodePass == 1)
    {
      beginop(OP_multiply_i);
    }
  }

  public void OP_divide()
  {
    if (opcodePass == 1)
    {
      beginop(OP_divide);
    }
  }

  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_lshift()
  {
    if (opcodePass == 1)
    {
      beginop(OP_lshift);
    }
  }

  public void OP_rshift()
  {
    if (opcodePass == 1)
    {
      beginop(OP_rshift);
    }
  }

  public void OP_urshift()
  {
    if (opcodePass == 1)
    {
      beginop(OP_urshift);
    }
  }

  public void OP_bitand()
  {
    if (opcodePass == 1)
    {
      beginop(OP_bitand);
    }
  }

  public void OP_bitor()
  {
    if (opcodePass == 1)
    {
      beginop(OP_bitor);
    }
  }

  public void OP_bitxor()
  {
    if (opcodePass == 1)
    {
      beginop(OP_bitxor);
    }
  }

  public void OP_equals()
  {
    if (opcodePass == 1)
    {
      beginop(OP_equals);
    }
  }

  public void OP_strictequals()
  {
    if (opcodePass == 1)
    {
      beginop(OP_strictequals);
    }
  }

  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 == null || 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_lessthan()
  {
    if (opcodePass == 1)
    {
      beginop(OP_lessthan);
    }
  }

  public void OP_lessequals()
  {
    if (opcodePass == 1)
    {
      beginop(OP_lessequals);
    }
  }

  public void OP_greaterthan()
  {
    if (opcodePass == 1)
    {
      beginop(OP_greaterthan);
    }
  }

  public void OP_greaterequals()
  {
    if (opcodePass == 1)
    {
      beginop(OP_greaterequals);
    }
  }

  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) == pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index))
      {
        rewind(1);
        OP_getlex(index);
        return;
      }

      beginop(OP_getproperty);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

    public void OP_setproperty(int index)
    {
        if (opcodePass == 1)
        {
            if (opat(1) == OP_coerce_a)
                rewind(1);

            beginop(OP_setproperty);
            opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
        }
    }

    public void OP_initproperty(int index)
    {
        if (opcodePass == 1)
        {
            if (opat(1) == OP_coerce_a)
                rewind(1);

            beginop(OP_initproperty);
            opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
        }
    }

  public void OP_getdescendants(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_getdescendants);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_findpropstrict(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_findpropstrict);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_findproperty(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_findproperty);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_finddef(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_finddef);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }
 
  public void OP_getlex(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_getlex);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_nextname()
  {
    if (opcodePass == 1)
    {
      beginop(OP_nextname);
    }
  }

  public void OP_nextvalue()
  {
    if (opcodePass == 1)
    {
      beginop(OP_nextvalue);
    }
  }

  public void OP_hasnext()
  {
    if (opcodePass == 1)
    {
      beginop(OP_hasnext);
    }
  }

  public void OP_hasnext2(int objectRegister, int indexRegister)
  {
    if (opcodePass == 1)
    {
      beginop(OP_hasnext2);
      opcodes.writeU32(objectRegister);
      opcodes.writeU32(indexRegister);     
    }
  }
 
  public void OP_deleteproperty(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_deleteproperty);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  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);
    }
  }

  public void OP_setglobalslot(int index)
  {
    if (opcodePass == 1)
    {
      if (opat(1) == OP_coerce_a)
        rewind(1);

      beginop(OP_setglobalslot);
      opcodes.writeU32(index);
    }
  }

  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(poolIndex, id));
    }
  }

  public void OP_newclass(int id)
  {
    if (opcodePass == 1)
    {
      beginop(OP_newclass);
      opcodes.writeU32(classInfo.getIndex(poolIndex, id));
    }
  }

  public void OP_callstatic(int id, int argc)
  {
    if (opcodePass == 1)
    {
      beginop(OP_callstatic);
      opcodes.writeU32(methodInfo.getIndex(poolIndex, id));
      opcodes.writeU32(argc);
    }
  }

  public void OP_callmethod(int id, int argc)
  {
    if (opcodePass == 1)
    {
      beginop(OP_callmethod);
      opcodes.writeU32(methodInfo.getIndex(poolIndex, 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(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
      opcodes.writeU32(argc);
    }
  }

  public void OP_callproplex(int index, int argc)
  {
    if (opcodePass == 1)
    {
      beginop(OP_callproplex);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
      opcodes.writeU32(argc);
    }
  }

  public void OP_constructprop(int index, int argc)
  {
    if (opcodePass == 1)
    {
      beginop(OP_constructprop);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
      opcodes.writeU32(argc);
    }
  }

  public void OP_callsuper(int index, int argc)
  {
    if (opcodePass == 1)
    {
      beginop(OP_callsuper);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
      opcodes.writeU32(argc);
    }
  }

  public void OP_getsuper(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_getsuper);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_setsuper(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_setsuper);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  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(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_astypelate()
  {
    if (opcodePass == 1)
    {
      beginop(OP_astypelate);
    }
  }

  public void OP_coerce(int index)
  {
    if (opcodePass == 1)
    {
      if (opat(1) == OP_coerce && readIntAt(1) == pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index))
      {
        // second coerce to same type is redundant
        return;
      }

      beginop(OP_coerce);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
    }
  }

  public void OP_coerce_b()
  {
    if (opcodePass == 1)
    {
      beginop(OP_coerce_b);
    }
  }

  public void OP_coerce_o()
  {
    if (opcodePass == 1)
    {
      beginop(OP_coerce_o);
    }
  }

  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(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, 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 )
      {
        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_pushscope()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushscope);
    }
  }

  public void OP_popscope()
  {
    if (opcodePass == 1)
    {
      beginop(OP_popscope);
    }
  }

  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_convert_u()
  {
    if (opcodePass == 1)
    {
      beginop(OP_convert_u);
    }
  }

  public void OP_throw()
  {
    if (opcodePass == 1)
    {
      beginop(OP_throw);
    }
  }

  public void OP_instanceof()
  {
    if (opcodePass == 1)
    {
      beginop(OP_instanceof);
    }
  }

  public void OP_in()
  {
    if (opcodePass == 1)
    {
      beginop(OP_in);
    }
  }

  public void OP_dxns(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_dxns);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_string, index));
    }
  }

  public void OP_dxnslate()
  {
    if (opcodePass == 1)
    {
      beginop(OP_dxnslate);
    }
  }

  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_pushwith()
  {
    if (opcodePass == 1)
    {
      beginop(OP_pushwith);
    }
  }

  public void OP_newactivation()
  {
    if (opcodePass == 1)
    {
      beginop(OP_newactivation);
    }
  }

  public void OP_newcatch(int index)
  {
    if (opcodePass == 1)
    {
      beginop(OP_newcatch);
      opcodes.writeU32(index);     
    }
  }

  public void OP_deldescendants()
  {
    if (opcodePass == 1)
    {
      beginop(OP_deldescendants);
    }
  }

  public void OP_getglobalscope()
  {
    if (opcodePass == 1)
    {
      beginop(OP_getglobalscope);
    }
  }

  public void OP_getlocal0()
  {
    if (opcodePass == 1)
    {
      beginop(OP_getlocal0);
    }
  }

  public void OP_getlocal1()
  {
    if (opcodePass == 1)
    {
      beginop(OP_getlocal1);
    }
  }

  public void OP_getlocal2()
  {
    if (opcodePass == 1)
    {
      beginop(OP_getlocal2);
    }
  }

  public void OP_getlocal3()
  {
    if (opcodePass == 1)
    {
      beginop(OP_getlocal3);
    }
  }

  public void OP_setlocal0()
  {
    if (opcodePass == 1)
    {
      beginop(OP_setlocal0);
    }
  }

  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(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
      opcodes.writeU32(argc);
    }
  }

  public void OP_callpropvoid(int index, int argc)
  {
    if (opcodePass == 1)
    {
      beginop(OP_callpropvoid);
      opcodes.writeU32(pool.history.getIndex(poolIndex, IndexHistory.cp_mn, index));
      opcodes.writeU32(argc);
    }
  }

    public void OP_li8()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_li8);
        }
    }

    public void OP_li16()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_li16);
        }
    }

    public void OP_li32()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_li32);
        }
    }

    public void OP_lf32()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_lf32);
        }
    }

    public void OP_lf64()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_lf64);
        }
    }

    public void OP_si8()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_si8);
        }
    }

    public void OP_si16()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_si16);
        }
    }

    public void OP_si32()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_si32);
        }
    }

    public void OP_sf32()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_sf32);
        }
    }

    public void OP_sf64()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_sf64);
        }
    }

    public void OP_sxi1()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_sxi1);
        }
    }

    public void OP_sxi8()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_sxi8);
        }
    }

    public void OP_sxi16()
    {
        if( opcodePass == 1 )
        {
            beginop(OP_sxi16);
        }
    }

    class ByteArrayPool2 extends ByteArrayPool
    {
        ByteArrayPool2(int[] sizes)
        {
            this.sizes = sizes;
            indexes = new HashMap<Integer, Integer>();
        }

        int size = 0;

        private int[] sizes;

        private Map<Integer, Integer> indexes;

        int addByteArray(int poolIndex, int oldIndex, BytecodeBuffer ba)
        {
            int index = this.contains(ba, 0, ba.size());
            if( index == -1 )
            {
                index = this.store(ba, 0, ba.size());
                size += ba.size();
            }
            // ByteArrayPool is 1 based, we want zero based for metadataInfos
            indexes.put(IntegerPool.getNumber(calcIndex(poolIndex, oldIndex)), IntegerPool.getNumber(index-1));
            return index;
        }

        private int calcIndex(int poolIndex, int oldIndex)
        {
            int newIndex = 0;
            for (int i = 0; i < poolIndex; i++)
            {
                newIndex += sizes[i];
            }
            newIndex += oldIndex;

            return newIndex;
        }

        int getIndex(int poolIndex, int oldIndex)
        {
            int newIndex = calcIndex(poolIndex, oldIndex);
            Integer i = indexes.get(IntegerPool.getNumber(newIndex));
            return i != null ? i.intValue() : -1;
        }

        int size()
        {
            return size;
        }

        void writeTo(BytecodeBuffer b)
        {
            Map sortedMap = new TreeMap();

            for (Iterator i = map.keySet().iterator(); i.hasNext();)
            {
                Object key = i.next(); // ByteArray
                Object value = map.get(key); // Integer
                sortedMap.put(value, key);
            }

            b.writeU32((sortedMap.size() == 0) ? 0 : sortedMap.size() );

            for (Iterator i = sortedMap.keySet().iterator(); i.hasNext();)
            {
                Integer index = (Integer) i.next();
                ByteArray a = (ByteArray) sortedMap.get(index);
                b.writeBytes(a.b, a.start, a.end);
            }
        }

        void writeTo(OutputStream os) throws java.io.IOException
        {
            BytecodeBuffer b = new BytecodeBuffer(size());
            writeTo(b);
            b.writeTo(os);
        }
    }

  class BytecodeBuffer2 extends BytecodeBuffer
  {
    BytecodeBuffer2(int estimatedSize, int[] sizes)
    {
      super(estimatedSize);
      this.sizes = sizes;
      this.estimatedSize = estimatedSize;
    }

    private int[] sizes;
    int estimatedSize;

    int getIndex(int poolIndex, int oldIndex)
    {
      int newIndex = 0;
      for (int i = 0; i < poolIndex; i++)
      {
        newIndex += sizes[i];
      }
      newIndex += oldIndex;

      return newIndex;
    }
  }

  class BytecodeBuffer3 extends BytecodeBuffer
  {
    BytecodeBuffer3(Decoder[] decoders, int estimatedSize)
    {
      super(estimatedSize);
      offsets = new HashMap<Integer, Integer>();
      this.decoders = decoders;
    }

    Map<Integer, Integer> offsets;
    Decoder[] decoders;

    void mapOffsets(long offset)
    {
      Integer oldPos = IntegerPool.getNumber((int) offset);
      Integer newPos = IntegerPool.getNumber(size());

      offsets.put(IntegerPool.getNumber(oldPos), IntegerPool.getNumber(newPos));
    }

    long getOffset(long offset)
    {
      Integer i = offsets.get(IntegerPool.getNumber((int) offset));
      if (i != null)
      {
        return i.intValue();
      }
      else
      {
        System.out.println("getOffset: can't match " + offset + " with a new offset ");
        System.out.println(offsets);
        return 0;
      }
    }

    void updateOffset(long offset)
    {
      Integer i = offsets.get(IntegerPool.getNumber((int) offset));
      Integer p = offsets.get(IntegerPool.getNumber(decoders[poolIndex].pos()));

      if (i != null && p != null)
      {
        writeS24(p-3, i-p);
      }
      else
      {
        /* cn: temporarily disable this warning.  The warnings that are being generated are
         *     for jumps which are part of unreachable code, but I haven't found where those
         *     unecessary jumps are being genreated within codeGenerator yet.  Disabling to redeuce
         *     annoyance in the short term,
         *     bug #149141 remains open until the root cause is found and removed
        if (i == null)
          System.out.println("updateOffset1: can't match i " + offset + " with a new offset");
        if (p == null)
          System.out.println("updateOffset1: can't match p " + decoders[poolIndex].pos() + " with a new offset");
        System.out.println(offsets);
        */
      }
    }

    void updateOffset(long oldOffsetPos, long oldPos, long oldTarget)
    {
      Integer i = offsets.get(IntegerPool.getNumber((int) oldTarget));
      Integer p = offsets.get(IntegerPool.getNumber((int) oldPos));
      Integer s = offsets.get(IntegerPool.getNumber((int) oldOffsetPos));

      if (i != null && p != null && s != null)
      {
        writeS24(s, i-p);
      }
      else
      {
        if (i == null)
          System.out.println("updateOffset2: can't match i " + oldTarget + " with a new offset");
        if (p == null)
          System.out.println("updateOffset2: can't match p " + oldPos + " with a new offset");
        if (s == null)
          System.out.println("updateOffset2: can't match s " + oldOffsetPos + " with a new offset");
        System.out.println(offsets);
      }
    }

    public void clear()
    {
      super.clear();
      offsets.clear();
    }
  }
}

TOP

Related Classes of macromedia.abc.Encoder$BytecodeBuffer3

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.