/******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *****************************************************************************/
/*
 * Created on Aug 22, 2005
 */
package com.ibm.wala.cast.java.loader;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cast.ir.ssa.AssignInstruction;
import com.ibm.wala.cast.ir.ssa.AstAssertInstruction;
import com.ibm.wala.cast.ir.ssa.AstEchoInstruction;
import com.ibm.wala.cast.ir.ssa.AstGlobalRead;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cast.ir.ssa.AstLexicalWrite;
import com.ibm.wala.cast.ir.ssa.AstPropertyRead;
import com.ibm.wala.cast.ir.ssa.AstPropertyWrite;
import com.ibm.wala.cast.ir.ssa.AstYieldInstruction;
import com.ibm.wala.cast.ir.translator.AstTranslator;
import com.ibm.wala.cast.ir.translator.AstTranslator.AstLexicalInformation;
import com.ibm.wala.cast.java.ssa.AstJavaInstructionFactory;
import com.ibm.wala.cast.java.ssa.AstJavaInvokeInstruction;
import com.ibm.wala.cast.java.ssa.AstJavaNewEnclosingInstruction;
import com.ibm.wala.cast.java.ssa.EnclosingObjectReference;
import com.ibm.wala.cast.java.translator.SourceModuleTranslator;
import com.ibm.wala.cast.loader.AstClass;
import com.ibm.wala.cast.loader.AstField;
import com.ibm.wala.cast.loader.AstMethod;
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
import com.ibm.wala.cast.tree.CAstAnnotation;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstQualifier;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.CAstType;
import com.ibm.wala.cast.tree.CAstType.Function;
import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.ClassLoaderImpl;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.JavaLanguage.JavaInstructionFactory;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.ModuleEntry;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeCT.AnnotationsReader.ConstantElementValue;
import com.ibm.wala.shrikeCT.AnnotationsReader.ElementValue;
import com.ibm.wala.shrikeCT.ClassConstants;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.strings.Atom;

/**
 * A {@link ClassLoaderImpl} that processes source file entities in the
 * compile-time classpath.
 */
public abstract class JavaSourceLoaderImpl extends ClassLoaderImpl {
  public Map<CAstEntity, IClass> fTypeMap = HashMapFactory.make();
/** BEGIN Custom change: Common superclass is optional */
  private final boolean existsCommonSuperclass;   // extension to deal with X10 that has no common superclass
/** END Custom change: Common superclass is optional */

  /**
   * WALA representation of a Java class residing in a source file
   *
   * @author rfuhrer
   */
  public class JavaClass extends AstClass {
    protected final IClass enclosingClass;

    protected final Collection<TypeName> superTypeNames;

    private final Collection<Annotation> annotations;

    public JavaClass(String typeName, Collection<TypeName> superTypeNames, CAstSourcePositionMap.Position position, Collection<CAstQualifier> qualifiers,
        JavaSourceLoaderImpl loader, IClass enclosingClass, Collection<Annotation> annotations) {
      super(position, TypeName.string2TypeName(typeName), loader, (short) mapToInt(qualifiers), new HashMap<Atom, IField>(), new HashMap<Selector, IMethod>());
      this.superTypeNames = superTypeNames;
      this.enclosingClass = enclosingClass;
      this.annotations = annotations;
    }

    @Override
    public Collection<Annotation> getAnnotations() {
      return annotations;
    }

    @Override
    public IClassHierarchy getClassHierarchy() {
      return cha;
    }

    @Override
    public IClass getSuperclass() {
      boolean excludedSupertype=false;
      for (TypeName name : superTypeNames) {
        IClass domoType = lookupClass(name);
        if (domoType != null && !domoType.isInterface()) {
          return domoType;
        }
        if (domoType == null && getClassHierarchy().getScope().getExclusions().contains(name.toString().substring(1))){
          excludedSupertype = true;
        }
      }

      // The following test allows the root class to reside in source; without
      // it, the assertion requires all classes represented by a JavaClass to
      // have a superclass.
/** BEGIN Custom change: Common superclass is optional */
      // Is no longer true in new X10 - no common object super class
      if (existsCommonSuperclass && !getName().equals(JavaSourceLoaderImpl.this.getLanguage().getRootType().getName()) && !excludedSupertype) {
/** END Custom change: Common superclass is optional */
        Assertions.UNREACHABLE("Cannot find super class for " + this + " in " + superTypeNames);
      }

      if (excludedSupertype){
        System.err.println("Not tracking calls through excluded superclass of " + getName() + " extends " + superTypeNames);
      }

      return null;
    }

    @Override
    public Collection<IClass> getDirectInterfaces() {
      List<IClass> result = new ArrayList<>();
      for (TypeName name : superTypeNames) {
        IClass domoType = lookupClass(name);
        if (domoType != null && domoType.isInterface()) {
            result.add(domoType);
        }
        if (domoType == null && !getClassHierarchy().getScope().getExclusions().contains(name.toString().substring(1))){
          assert false : "Failed to find non-excluded interface: " + name;
        }
      }

      return result;
    }

    protected void addMethod(CAstEntity methodEntity, IClass owner, AbstractCFG<?, ?> cfg, SymbolTable symtab, boolean hasCatchBlock,
        Map<IBasicBlock<SSAInstruction>, TypeReference[]> caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
      declaredMethods.put(Util.methodEntityToSelector(methodEntity), new ConcreteJavaMethod(methodEntity, owner, cfg, symtab,
          hasCatchBlock, caughtTypes, hasMonitorOp, lexicalInfo, debugInfo));
    }

    protected void addMethod(CAstEntity methodEntity, IClass owner) {
      declaredMethods.put(Util.methodEntityToSelector(methodEntity), new AbstractJavaMethod(methodEntity, owner));
    }

    protected void addField(CAstEntity fieldEntity) {
      declaredFields.put(Util.fieldEntityToAtom(fieldEntity), new JavaField(fieldEntity, JavaSourceLoaderImpl.this, this, JavaSourceLoaderImpl.this.getAnnotations(fieldEntity)));
    }

    public IClass getEnclosingClass() {
      return enclosingClass;
    }

    @Override
    public String toString() {
      StringBuffer sb = new StringBuffer("<src-class: " );
      sb.append(getName().toString());
      if (enclosingClass != null) {
        sb.append(" (within " + enclosingClass.getName() + ")");
      }
      if (annotations != null && !annotations.isEmpty()) {
        for(Annotation a : annotations) {
          sb.append("[" + a.getType().getName().getClassName() + "]");
        }
      }
      return sb.toString();
    }
  }

  protected Collection<Annotation> getAnnotations(CAstEntity e) {
    Collection<CAstAnnotation> annotations = e.getAnnotations();
    if (annotations == null || annotations.isEmpty()) {
      return null;
    } else {
      Collection<Annotation> result = HashSetFactory.make();
      for(CAstAnnotation ca : annotations) {
        TypeName walaTypeName = toWALATypeName(ca.getType());
        TypeReference ref = TypeReference.findOrCreate(getReference(), walaTypeName);
        if (ca.getArguments() == null || ca.getArguments().isEmpty()) {
          result.add(Annotation.make(ref));
        } else {
          Map<String,ElementValue> args = HashMapFactory.make();
          for(Map.Entry<String, Object> a : ca.getArguments().entrySet()) {
            args.put(a.getKey(), new ConstantElementValue(a.getValue()));
          }
          result.add(Annotation.makeWithNamed(ref, args));
        }
      }
      return result;
    }
  }

  /**
   * WALA representation of a field on a Java type that resides in a source file
   *
   * @author rfuhrer
   */
  protected class JavaField extends AstField {
    protected JavaField(CAstEntity fieldEntity, IClassLoader loader, IClass declaringClass, Collection<Annotation> annotations) {
      super(FieldReference.findOrCreate(declaringClass.getReference(), Atom.findOrCreateUnicodeAtom(fieldEntity.getName()),
          TypeReference.findOrCreate(loader.getReference(), TypeName.string2TypeName(fieldEntity.getType().getName()))),
          fieldEntity.getQualifiers(), declaringClass, declaringClass.getClassHierarchy(), annotations);
    }
  }

  /**
   * Generic DOMO representation of a method on a Java type that resides in a
   * source file
   *
   * @author rfuhrer
   */
  protected abstract class JavaEntityMethod extends AstMethod {
    private final TypeReference[] parameterTypes;

    private final TypeReference[] exceptionTypes;

    public JavaEntityMethod(CAstEntity methodEntity, IClass owner, AbstractCFG<?, ?> cfg, SymbolTable symtab, boolean hasCatchBlock,
        Map<IBasicBlock<SSAInstruction>, TypeReference[]> caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
      super(owner, methodEntity.getQualifiers(), cfg, symtab, MethodReference.findOrCreate(owner.getReference(), Util
          .methodEntityToSelector(methodEntity)), hasCatchBlock, caughtTypes, hasMonitorOp, lexicalInfo, debugInfo, JavaSourceLoaderImpl.this.getAnnotations(methodEntity));
      this.parameterTypes = computeParameterTypes(methodEntity);
      this.exceptionTypes = computeExceptionTypes(methodEntity);
    }

    public JavaEntityMethod(CAstEntity methodEntity, IClass owner) {
      super(owner, methodEntity.getQualifiers(), MethodReference.findOrCreate(owner.getReference(), Util
          .methodEntityToSelector(methodEntity)), JavaSourceLoaderImpl.this.getAnnotations(methodEntity));
      this.parameterTypes = computeParameterTypes(methodEntity);
      this.exceptionTypes = computeExceptionTypes(methodEntity);
    }

    public int getMaxLocals() {
      Assertions.UNREACHABLE("AbstractJavaMethod.getMaxLocals() called");
      return 0;
    }

    public int getMaxStackHeight() {
      Assertions.UNREACHABLE("AbstractJavaMethod.getMaxStackHeight() called");
      return 0;
    }

    @Override
    public TypeReference getParameterType(int i) {
      return parameterTypes[i];
    }

    protected TypeReference[] computeParameterTypes(CAstEntity methodEntity) {
      TypeReference[] types;
      CAstType.Function type = (Function) methodEntity.getType();
      int argCount = type.getArgumentTypes().size();
      if (isStatic()) {
        types = new TypeReference[argCount];
        for (int i = 0; i < argCount; i++) {
          types[i] = TypeReference.findOrCreate(JavaSourceLoaderImpl.this.getReference(), type.getArgumentTypes()
              .get(i).getName());
        }
      } else {
        types = new TypeReference[argCount + 1];
        types[0] = cls.getReference();
        for (int i = 0; i < argCount; i++) {
          types[i + 1] = TypeReference.findOrCreate(JavaSourceLoaderImpl.this.getReference(), type.getArgumentTypes()
              .get(i).getName());
        }
      }

      return types;
    }

    @Override
    public TypeReference[] getDeclaredExceptions() {
      return exceptionTypes;
    }

    protected TypeReference[] computeExceptionTypes(CAstEntity methodEntity) {
      CAstType.Function fType = (Function) methodEntity.getType();
      Collection<CAstType> exceptionTypes = fType.getExceptionTypes();

      TypeReference[] result = new TypeReference[exceptionTypes.size()];
      int i = 0;
      for (CAstType type : exceptionTypes) {
        result[i] = TypeReference.findOrCreate(JavaSourceLoaderImpl.this.getReference(), type.getName());
        ++i;
      }

      return result;
    }

    @Override
    public String toString() {
      return "<src-method: " + this.getReference() + ">";
    }
  }

  /**
   * DOMO representation of an abstract (body-less) method on a Java type that
   * resides in a source file
   *
   * @author rfuhrer
   */
  protected class AbstractJavaMethod extends JavaEntityMethod {
    public AbstractJavaMethod(CAstEntity methodEntity, IClass owner) {
      super(methodEntity, owner);
    }

    @Override
    public String getLocalVariableName(int bcIndex, int localNumber) {
      Assertions.UNREACHABLE("AbstractJavaMethod.getLocalVariableName() called");
      return null;
    }

    @Override
    public boolean hasLocalVariableTable() {
      Assertions.UNREACHABLE("AbstractJavaMethod.hasLocalVariableTable() called");
      return false;
    }

    @Override
    public LexicalParent[] getParents() {
      return new LexicalParent[0];
    }

    @Override
    public IClassHierarchy getClassHierarchy() {
      return cha;
    }
  }

  /**
   * DOMO representation of a concrete method (which has a body) on a Java type
   * that resides in a source file
   *
   * @author rfuhrer
   */
  public class ConcreteJavaMethod extends JavaEntityMethod {
    public ConcreteJavaMethod(CAstEntity methodEntity, IClass owner, AbstractCFG<?, ?> cfg, SymbolTable symtab, boolean hasCatchBlock,
        Map<IBasicBlock<SSAInstruction>, TypeReference[]> caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
      super(methodEntity, owner, cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, lexicalInfo, debugInfo);
    }

    @Override
    public IClassHierarchy getClassHierarchy() {
      return cha;
    }

    @Override
    public String getLocalVariableName(int bcIndex, int localNumber) {
      return null;
    }

    @Override
    public boolean hasLocalVariableTable() {
      return false;
    }

    @Override
    public LexicalParent[] getParents() {
      if (AstTranslator.DEBUG_LEXICAL) {
        System.err.println(("resolving parents of " + this));
      }

      if (lexicalInfo() == null) {
        if (AstTranslator.DEBUG_LEXICAL)
          System.err.println("no info");
        return new LexicalParent[0];
      }

      final String[] parents = lexicalInfo().getScopingParents();

      if (parents == null) {
        if (AstTranslator.DEBUG_LEXICAL)
          System.err.println("no parents");
        return new LexicalParent[0];
      }

      LexicalParent result[] = new LexicalParent[parents.length];

      for (int i = 0; i < parents.length; i++) {
        int lastLeftParen = parents[i].lastIndexOf('(');
        int lastQ = parents[i].lastIndexOf('/', lastLeftParen);
        String typeName = parents[i].substring(0, lastQ);
        final IClass cls = lookupClass(TypeName.string2TypeName(typeName));

        String sig = parents[i].substring(lastQ);
        int nameEnd = sig.indexOf('(');
        String nameStr = sig.substring(1, nameEnd);
        Atom name = Atom.findOrCreateUnicodeAtom(nameStr);

        String descStr = sig.substring(nameEnd);
        Descriptor desc = Descriptor.findOrCreateUTF8(Language.JAVA, descStr);

        final Selector sel = new Selector(name, desc);

        if (AstTranslator.DEBUG_LEXICAL)
          System.err.println(("get " + typeName + ", " + nameStr + ", " + descStr));

        final int hack = i;
        result[i] = new LexicalParent() {
          @Override
          public String getName() {
            return parents[hack];
          }

          @Override
          public AstMethod getMethod() {
            return (AstMethod) cls.getMethod(sel);
          }
        };

        if (AstTranslator.DEBUG_LEXICAL)
          System.err.println(("parent " + result[i].getName() + " is " + result[i].getMethod()));
      }

      return result;
    }
  }

  public static int mapToInt(Collection<CAstQualifier> qualifiers) {
    int result = 0;
    for (CAstQualifier q : qualifiers) {
      if (q == CAstQualifier.PUBLIC)
        result |= ClassConstants.ACC_PUBLIC;
      if (q == CAstQualifier.PROTECTED)
        result |= ClassConstants.ACC_PROTECTED;
      if (q == CAstQualifier.PRIVATE)
        result |= ClassConstants.ACC_PRIVATE;
      if (q == CAstQualifier.STATIC)
        result |= ClassConstants.ACC_STATIC;
      if (q == CAstQualifier.FINAL)
        result |= ClassConstants.ACC_FINAL;
      if (q == CAstQualifier.SYNCHRONIZED)
        result |= ClassConstants.ACC_SYNCHRONIZED;
      if (q == CAstQualifier.TRANSIENT)
        result |= ClassConstants.ACC_TRANSIENT;
      if (q == CAstQualifier.NATIVE)
        result |= ClassConstants.ACC_NATIVE;
      if (q == CAstQualifier.INTERFACE)
        result |= ClassConstants.ACC_INTERFACE;
      if (q == CAstQualifier.ABSTRACT)
        result |= ClassConstants.ACC_ABSTRACT;
      if (q == CAstQualifier.VOLATILE)
        result |= ClassConstants.ACC_VOLATILE;
      if (q == CAstQualifier.STRICTFP)
        result |= ClassConstants.ACC_STRICT;
    }
    return result;
  }

/** BEGIN Custom change: Common superclass is optional */
  public JavaSourceLoaderImpl(boolean existsCommonSuperClass, ClassLoaderReference loaderRef, IClassLoader parent,
      IClassHierarchy cha) {
    super(loaderRef, cha.getScope().getArrayClassLoader(), parent, cha.getScope().getExclusions(), cha);
    this.existsCommonSuperclass = existsCommonSuperClass;
  }

  public JavaSourceLoaderImpl(ClassLoaderReference loaderRef, IClassLoader parent, IClassHierarchy cha) {
    // standard case: we have a common super class
    this(true, loaderRef, parent, cha);
  }
/** END Custom change: Common superclass is optional */

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  @Override
  protected void loadAllSources(Set<ModuleEntry> modules) {
    getTranslator().loadAllSources(modules);
  }

  protected abstract SourceModuleTranslator getTranslator();
/** BEGIN Custom change: Optional deletion of fTypeMap */
  public static volatile boolean deleteTypeMapAfterInit = true;
/** END Custom change: Optional deletion of fTypeMap */


  @Override
  public void init(List<Module> modules) throws IOException {
    super.init(modules);
/** BEGIN Custom change: Optional deletion of fTypeMap */
    if (deleteTypeMapAfterInit) {
      fTypeMap = null;
    }
/** END Custom change: Optional deletion of fTypeMap */
  }

  public void defineFunction(CAstEntity n, IClass owner, AbstractCFG<?, ?> cfg, SymbolTable symtab, boolean hasCatchBlock,
      Map<IBasicBlock<SSAInstruction>, TypeReference[]> caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
    ((JavaClass) owner).addMethod(n, owner, cfg, symtab, hasCatchBlock, caughtTypes, hasMonitorOp, lexicalInfo, debugInfo);
  }

  public void defineAbstractFunction(CAstEntity n, IClass owner) {
    ((JavaClass) owner).addMethod(n, owner);
  }

  public void defineField(CAstEntity n, IClass owner) {
    ((JavaClass) owner).addField(n);
  }

  protected static TypeName toWALATypeName(CAstType type) {
    return TypeName.string2TypeName(type.getName());
  }

  public IClass defineType(CAstEntity type, String typeName, CAstEntity owner) {
    Collection<TypeName> superTypeNames = new ArrayList<>();
    for (CAstType superType : type.getType().getSupertypes()) {
      superTypeNames.add(toWALATypeName(superType));
    }

    JavaClass javaClass = new JavaClass(typeName, superTypeNames, type.getPosition(), type.getQualifiers(), this,
        (owner != null) ? (JavaClass) fTypeMap.get(owner) : (JavaClass) null, getAnnotations(type));

    if (getParent().lookupClass(javaClass.getName()) != null) {
      return null;
    }

    fTypeMap.put(type, javaClass);
    loadedClasses.put(javaClass.getName(), javaClass);
    return javaClass;
  }

  @Override
  public String toString() {
    return "Java Source Loader (classes " + loadedClasses.values() + ")";
  }

  public static class InstructionFactory extends JavaInstructionFactory implements AstJavaInstructionFactory {

    @Override
    public com.ibm.wala.cast.java.ssa.EnclosingObjectReference EnclosingObjectReference(int iindex, int lval, TypeReference type) {
      return new EnclosingObjectReference(iindex, lval, type);
    }

    @Override
    public AstJavaNewEnclosingInstruction JavaNewEnclosingInstruction(int iindex, int result, NewSiteReference site, int enclosing) {
      return new AstJavaNewEnclosingInstruction(iindex, result, site, enclosing);
    }

    @Override
    public AstJavaInvokeInstruction JavaInvokeInstruction(int iindex, int result[], int[] params, int exception, CallSiteReference site) {
      return result == null ? new AstJavaInvokeInstruction(iindex, params, exception, site) : new AstJavaInvokeInstruction(iindex, result[0],
          params, exception, site);
    }

    @Override
    public AstAssertInstruction AssertInstruction(int iindex, int value, boolean fromSpecification) {
      return new AstAssertInstruction(iindex, value, fromSpecification);
    }

    @Override
    public com.ibm.wala.cast.ir.ssa.AssignInstruction AssignInstruction(int iindex, int result, int val) {
       return new AssignInstruction(iindex, result, val);
    }

    @Override
    public com.ibm.wala.cast.ir.ssa.EachElementGetInstruction EachElementGetInstruction(int iindex, int value, int objectRef, int propRef) {
      throw new UnsupportedOperationException();
    }

    @Override
    public com.ibm.wala.cast.ir.ssa.EachElementHasNextInstruction EachElementHasNextInstruction(int iindex, int value, int objectRef, int propRef) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstEchoInstruction EchoInstruction(int iindex, int[] rvals) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstGlobalRead GlobalRead(int iindex, int lhs, FieldReference global) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstGlobalWrite GlobalWrite(int iindex, FieldReference global, int rhs) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstIsDefinedInstruction IsDefinedInstruction(int iindex, int lval, int rval, int fieldVal, FieldReference fieldRef) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstIsDefinedInstruction IsDefinedInstruction(int iindex, int lval, int rval, FieldReference fieldRef) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstIsDefinedInstruction IsDefinedInstruction(int iindex, int lval, int rval, int fieldVal) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstIsDefinedInstruction IsDefinedInstruction(int iindex, int lval, int rval) {
      throw new UnsupportedOperationException();
    }

    @Override
    public AstLexicalRead LexicalRead(int iindex, Access[] accesses) {
      return new AstLexicalRead(iindex, accesses);
    }

    @Override
    public AstLexicalRead LexicalRead(int iindex, Access access) {
       return new AstLexicalRead(iindex, access);
    }

    @Override
    public AstLexicalRead LexicalRead(int iindex, int lhs, String definer, String globalName, TypeReference type) {
      return new AstLexicalRead(iindex, lhs, definer, globalName, type);
    }

    @Override
    public AstLexicalWrite LexicalWrite(int iindex, Access[] accesses) {
      return new AstLexicalWrite(iindex, accesses);
    }

    @Override
    public AstLexicalWrite LexicalWrite(int iindex, Access access) {
      return new AstLexicalWrite(iindex, access);
    }

    @Override
    public AstLexicalWrite LexicalWrite(int iindex, String definer, String globalName, TypeReference type, int rhs) {
       return new AstLexicalWrite(iindex, definer, globalName, type, rhs);
    }

    @Override
    public AstYieldInstruction YieldInstruction(int iindex, int[] rvals) {
      return new AstYieldInstruction(iindex, rvals);
    }
    
    @Override
    public AstPropertyRead PropertyRead(int iindex, int result, int objectRef, int memberRef) {
      assert false;
      return null;
    }

    @Override
    public AstPropertyWrite PropertyWrite(int iindex, int objectRef, int memberRef, int value) {
      assert false;
      return null;
    }
  }

  protected static final InstructionFactory insts = new InstructionFactory();

  @Override
  public InstructionFactory getInstructionFactory() {
    return insts;
  }

}
