Package com.mobixess.jodb.core.query

Source Code of com.mobixess.jodb.core.query.NQLoader$StackItem

/*
Copyright (C) 2007  Mobixess Inc. http://www.java-objects-database.com

This file is part of the JODB (Java Objects Database) open source project.

JODB is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation.

JODB is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package com.mobixess.jodb.core.query;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;

import com.mobixess.jodb.core.JodbIOException;
import com.mobixess.jodb.core.io.JODBOperationContext;
import com.mobixess.jodb.core.transaction.JODBSession;
import com.mobixess.jodb.core.transaction.JODBSession.ClassDescriptor;
import com.mobixess.jodb.query.api.Predicate;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil.PRIMITIVES_ENUMERATION;

@SuppressWarnings("unchecked")
public class NQLoader extends ClassLoader implements INqLoader {

    private URL _predicateClassURL;

    private boolean _predicateOptimized;

    private boolean _comparatorOptimized;
   
    private PredicateSubjectAnalysisDataContainer _predicateAnalysisDataContainer;

   
   

    // private Logger _logger = Utils.getLogger(getClass().getName());

    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#registerPredicate(com.mobixess.jodb.core.io.JODBOperationContext, com.mobixess.jodb.query.api.Predicate, java.util.Comparator)
     */
    public void registerPredicate(JODBOperationContext context,
            Predicate predicate, Comparator comparator)
            throws IOException
    {
        Class predicateClass = predicate.getClass();
        _predicateClassURL = getClassLocation(predicateClass);
        if (_predicateClassURL == null) {
            // TODO add logging
            return;
        }

        _predicateAnalysisDataContainer = new PredicateSubjectAnalysisDataContainer();
        _predicateAnalysisDataContainer._session = context.getSession();
        _predicateAnalysisDataContainer._predicateClass = predicate.getClass();
       
        Method[] methods = predicateClass.getMethods();
        Method predicateMethod = null;
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals("match")) {
                Class[] types = methods[i].getParameterTypes();
                if (types.length != 1) {
                    continue;
                }
                if (!types[0].getName().equals("java.lang.Object")) {
                    if(predicateMethod != null){
                        throw new JodbIOException("Illegal predicate type");
                    }
                    _predicateAnalysisDataContainer._predicateSubjectClass = types[0];
                    predicateMethod = methods[i];
                    _predicateAnalysisDataContainer._predicateSubjectBytecode = getHierachyLocationsForClass(_predicateAnalysisDataContainer._predicateSubjectClass);
                    //break;
                }
            }
        }

        if (_predicateAnalysisDataContainer._predicateSubjectBytecode == null) {
            // TODO add logging
            return;
        }
       
        extractAllFieldsForClass(_predicateAnalysisDataContainer._predicateSubjectClass, _predicateAnalysisDataContainer._cumulativeFieldsNames);

        byte[] predicateBinary;
        try {
            predicateBinary = getBinaryForClass(_predicateClassURL);
            if (predicateBinary == null) {
                return;
            }
            _predicateAnalysisDataContainer._predicateSubjectClassDescriptor = context
                    .getSession()
                    .getDescriptorForClass(
                            _predicateAnalysisDataContainer._predicateSubjectClass);
            _predicateOptimized = accessAnalize(predicateBinary,
                    predicateMethod, _predicateAnalysisDataContainer);
            //defineClass(predicate.getClass().getName(),predicateBinary ,0, predicateBinary.length );
        } catch (IOException e) {
            // TODO add logging
            e.printStackTrace();
            return;
        }
       
        byte[] comparatorBinary = null;
        Method comparatorMethod = null;
        if (_predicateOptimized && comparator != null) {
            Class comparatorClass = comparator.getClass();
            URL comparatorUrl = getClassLocation(comparatorClass);
            if (comparatorUrl == null) {
                return;
            }
            try {
                comparatorBinary = getBinaryForClass(comparatorUrl);
                if (comparatorBinary != null) {
                    comparatorMethod = comparatorClass.getMethod("compare",
                            _predicateAnalysisDataContainer._predicateSubjectClass,
                            _predicateAnalysisDataContainer._predicateSubjectClass);
                    _comparatorOptimized = accessAnalize(comparatorBinary,
                            comparatorMethod, _predicateAnalysisDataContainer);
                }else{
                    _comparatorOptimized = false;
                }
               
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }else{
            _comparatorOptimized = comparator == null;
        }

        if (_predicateOptimized) {
            resolveSubjectClasses(_predicateAnalysisDataContainer);
            resolvePredicate(predicateBinary, predicateClass.getName(), predicateMethod, _predicateAnalysisDataContainer);
            if(_comparatorOptimized && comparator != null){
                resolvePredicate(comparatorBinary, comparator.getClass().getName(), comparatorMethod, _predicateAnalysisDataContainer);
            }
        }
    }

    private HashMap<String, byte[]> getHierachyLocationsForClass(Class clazz)
            throws IOException
    {
        HashMap<String, byte[]> result = new HashMap<String, byte[]>();
        do {
            URL url = getClassLocation(clazz);
            if (url == null) {
                return null;
            }
            byte[] binary = getBinaryForClass(url);
            if (binary == null) {
                return null;
            }
            result.put(clazz.getName(), binary);
        } while ((clazz = clazz.getSuperclass()) != null);
        return result;
    }

    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#getSyntheticProxySetMethod()
     */
    public synchronized Field getSyntheticProxySetMethod() throws IOException {
        if(_predicateAnalysisDataContainer._syntheticProxyField == null){
            try {
                Class transformedClass = loadClass(_predicateAnalysisDataContainer._predicateSubjectClass.getName());
                _predicateAnalysisDataContainer._syntheticProxyField = transformedClass.getField(_predicateAnalysisDataContainer._syntheticProxyFieldName);
            } catch (Exception e) {
                throw new JodbIOException(e);
            }
        }
        return _predicateAnalysisDataContainer._syntheticProxyField;
    }

    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#getPredicateSubjectClass()
     */
    public Class getPredicateSubjectClass() {
        return _predicateAnalysisDataContainer._predicateSubjectClass;
    }

    private void resolveSubjectClasses(
            PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
    {
        String syntheticProxyFieldName;
        String syntheticProxyFieldNameBase = syntheticProxyFieldName= "_$$$synthProxy";
        int modifCounter = 0;
        while(predicateAnalysisDataContainer._cumulativeFieldsNames.get(syntheticProxyFieldName)!=null){
            syntheticProxyFieldName=syntheticProxyFieldNameBase+modifCounter;
            modifCounter++;
        }
        predicateAnalysisDataContainer._syntheticProxyFieldName = syntheticProxyFieldName;
        String[] types = predicateAnalysisDataContainer._predicateSubjectClassDescriptor.getTypes();
        if (predicateAnalysisDataContainer._lowestClassInHieratchyToModify == Integer.MAX_VALUE){
            predicateAnalysisDataContainer._lowestClassInHieratchyToModify  = 0;
        }
        String typeForProxyEmbeding = types[predicateAnalysisDataContainer._lowestClassInHieratchyToModify];
        //Iterator<String> classCodeNamesIterator = predicateAnalysisDataContainer._predicateSubjectBytecode.keySet().iterator();
        //while (classCodeNamesIterator.hasNext()) {
        predicateAnalysisDataContainer._transformedQueryBytecode._subjectBytecode = new HashMap<String, byte[]>();
        for (int i = predicateAnalysisDataContainer._lowestClassInHieratchyToModify; i >= 0 ; --i) {
            String className = types[i];
            byte[] transformedSubjectClass = transformSubjectClass(className, className.equals(typeForProxyEmbeding), predicateAnalysisDataContainer);
            defineClass(className,transformedSubjectClass, 0, transformedSubjectClass.length);
            predicateAnalysisDataContainer._transformedQueryBytecode._subjectBytecode.put(className, transformedSubjectClass);
        }
       
    }
   
    private byte[] transformSubjectClass(String className, boolean embedProxy, PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
    {
        ClassReader cr = new ClassReader(predicateAnalysisDataContainer._predicateSubjectBytecode.get(className));
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS
                | ClassWriter.COMPUTE_FRAMES);
        PredicateSubjectTransformer cv = new PredicateSubjectTransformer(cw,className,predicateAnalysisDataContainer, embedProxy);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        return cw.toByteArray();
    }

    private boolean accessAnalize(byte[] predicateBytecode,
            Method predicateMethod,
            PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
    {
        ClassReader cr = new ClassReader(predicateBytecode);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS
                | ClassWriter.COMPUTE_FRAMES);
        PredicateAnalizer cv = predicateAnalysisDataContainer._predicateAnalizer = new PredicateAnalizer(cw,
                predicateMethod, predicateAnalysisDataContainer);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        if (cv.isNotEligible()) {
            return false;
        }
        return analizePredicateSubjectAccessFields(predicateAnalysisDataContainer);
    }
   
    private void resolvePredicate(byte[] predicateBytecode, String predicateClassName,
            Method predicateMethod,
            PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
    {
        ClassReader cr = new ClassReader(predicateBytecode);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS
                | ClassWriter.COMPUTE_FRAMES);
        PredicateAnalizer cv = new PredicateAnalizer(cw,
                predicateMethod, predicateAnalysisDataContainer);//predicateAnalysisDataContainer._predicateAnalizer;
        cv.enableTransformation(true);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        byte[] transformedPredicateBytecode = cw.toByteArray();
        defineClass(predicateClassName , transformedPredicateBytecode, 0, transformedPredicateBytecode.length);
        predicateAnalysisDataContainer._transformedQueryBytecode._predicateBytecode = transformedPredicateBytecode;
    }

    private boolean analizePredicateSubjectAccessFields(
            PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
    {
        int totalMethodsVerified = 0;
        String[] predicateHierarchy = predicateAnalysisDataContainer._predicateSubjectClassDescriptor.getTypes();
        for (int i = 0; i < predicateHierarchy.length && totalMethodsVerified < predicateAnalysisDataContainer._referringMethods.size(); i++)
        {
            String nextClass = predicateHierarchy[i];
            byte[] bytecode = predicateAnalysisDataContainer._predicateSubjectBytecode
                    .get(nextClass);
            ClassReader cr = new ClassReader(bytecode);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS
                    | ClassWriter.COMPUTE_FRAMES);
            SubjectFieldAccessAnalizer predicateSubjectFieldAccessAnalizer = new SubjectFieldAccessAnalizer(cw, nextClass, predicateAnalysisDataContainer);
            cr.accept(predicateSubjectFieldAccessAnalizer,
                    ClassReader.EXPAND_FRAMES);
            if(predicateSubjectFieldAccessAnalizer._totalMethodsAnalized > 0){
                int currentHierarchyIndex = predicateAnalysisDataContainer._predicateSubjectClassDescriptor.getHierarchyTypeIndex(nextClass);
                predicateAnalysisDataContainer._lowestClassInHieratchyToModify = Math.min(predicateAnalysisDataContainer._lowestClassInHieratchyToModify, currentHierarchyIndex);
                predicateAnalysisDataContainer._classesForTransformation.put(nextClass, null);
            }
            if (predicateAnalysisDataContainer._complexMethods.size() > 0)
            {
                // TODO add log or error
                return false;
            }
            totalMethodsVerified = predicateSubjectFieldAccessAnalizer._totalMethodsAnalized;
        }

        return true;
    }

    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#isComparatorOptimized()
     */
    public boolean isComparatorOptimized() {
        return _comparatorOptimized;
    }

    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#isPredicateOptimized()
     */
    public boolean isPredicateOptimized() {
        return _predicateOptimized;
    }
   
    private void extractAllFieldsForClass(Class clazz, HashMap<String, String> result){
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            result.put(fields[i].getName(), null);
        }
        Class[] interfaces = clazz.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            extractAllFieldsForClass(interfaces[i], result);
        }
        Class superClass = clazz.getSuperclass();
        if(superClass == null){
            return;
        }
        extractAllFieldsForClass(superClass, result);
    }

    /*
     * protected Class<?> findClass(String name, boolean isPredicateType)
     * throws ClassNotFoundException { if(!isPredicateType){ byte[] b; try { b =
     * getBinaryForClass(_predicateClassURL); } catch (IOException e) { //TODO
     * add logging? throw new ClassNotFoundException("",e); } return
     * defineClass(name, b, 0, b.length); } // TODO Auto-generated method stub
     * throw new RuntimeException("Not Implemented"); //return
     * super.findClass(name); }
     */
    private byte[] getBinaryForClass(URL url) throws IOException {
        InputStream inputStream = url.openStream();
        byte[] cache = new byte[1024 * 2];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        int read;
        while ((read = inputStream.read(cache)) >= 0 ) {
            byteArrayOutputStream.write(cache, 0, read);
        }
        return byteArrayOutputStream.toByteArray();
    }
   
    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#getAccessedPredicateSubjectFields()
     */
    public Iterator<Integer> getAccessedPredicateSubjectFields(){
        return _predicateAnalysisDataContainer._referringMethods.values().iterator();
    }
   
    private static String composeReferringMethodId(String name, String desc){
        return name+'\u0000'+desc;
    }
   
    private static String composeFieldId(String owner,String name, String desc){
        return owner+'\u0000'+name+'\u0000'+desc;
    }

    public static URL getClassLocation(final Class clazz) {
        URL result = null;
        final String clsAsResource = clazz.getName().replace('.', '/')
                + ".class";
   
        final ProtectionDomain pd = clazz.getProtectionDomain();
        if (pd != null) {
            final CodeSource cs = pd.getCodeSource();
            // 'cs' can be null depending on the classloader
            if (cs != null) {
                result = cs.getLocation();
            }
   
            if (result != null) {
                if ("file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar")
                                || result.toExternalForm().endsWith(".zip"))
                        {
                            result = new URL("jar:" + result.toExternalForm()
                                    + "!/" + clsAsResource);
                        } else if (new File(result.toURI()).isDirectory()) {// FIXED: result.getFile()-->result.toURI() by Francois Rioux
                            result = new URL(result, clsAsResource);
                        }
                    } catch (Exception e) {
                        // TODO add logging
                        e.printStackTrace();
                    }
                }
            }
        }
   
        if (result == null) {
            // Try to find 'clazz' definition as a resource
            ClassLoader clsLoader = clazz.getClassLoader();
   
            result = clsLoader != null ? clsLoader.getResource(clsAsResource)
                    : ClassLoader.getSystemResource(clsAsResource);
        }
   
        return result;
    }
   
    /* (non-Javadoc)
     * @see com.mobixess.jodb.core.query.INqLoader#getTransformedQueryBytecode()
     */
    public QueryBytecode getTransformedQueryBytecode(){
        return _predicateAnalysisDataContainer._transformedQueryBytecode;
    }
   

    private static class PredicateSubjectAnalysisDataContainer {
       
        JODBSession _session;
       
        Vector<String> _complexMethods = new Vector<String>();
       
        HashMap<String, Integer> _referringMethods = new HashMap<String, Integer>();
       
        HashMap<String, org.objectweb.asm.commons.Method> _syntheticReferringMethods = new HashMap<String, org.objectweb.asm.commons.Method>();
   
        HashMap<String, Type> _directFieldsAccess = new HashMap<String, Type>();
   
        //HashMap<String, String> _fieldByMethodAccessMap = new HashMap<String, String>();
   
        HashMap<String, byte[]> _predicateSubjectBytecode;
       
        HashMap<String, String> _classesForTransformation = new HashMap<String, String>();
   
        Class _predicateSubjectClass;
   
        ClassDescriptor _predicateSubjectClassDescriptor;
       
        Class _predicateClass;
       
        int _lowestClassInHieratchyToModify = Integer.MAX_VALUE;
       
        HashMap<String, String> _cumulativeFieldsNames = new HashMap<String, String>();//
       
        Class _transformedPredicate;
       
        Field _syntheticProxyField;
       
        String _syntheticProxyFieldName;
       
        PredicateAnalizer _predicateAnalizer;
       
        QueryBytecode _transformedQueryBytecode = new QueryBytecode();
    }

    private static class PredicateSubjectTransformer extends ClassAdapter implements Opcodes {
       
        PredicateSubjectAnalysisDataContainer _subjectAnalysisDataContainer;
        String _internalClassName;
        boolean _addSyntheticProxy;

        public PredicateSubjectTransformer(ClassVisitor cv, String className, PredicateSubjectAnalysisDataContainer predicateSubjectAnalysisDataContainer, boolean addSyntheticProxy) {
            super(cv);
            _subjectAnalysisDataContainer = predicateSubjectAnalysisDataContainer;
            _addSyntheticProxy = addSyntheticProxy;
            _internalClassName = className.replace('.', '/');
        }
       
        @Override
        public FieldVisitor visitField(int access, String name, String desc,
                String signature, Object value)
        {
            if(_addSyntheticProxy){
                super.visitField(Opcodes.ACC_PUBLIC, _subjectAnalysisDataContainer._syntheticProxyFieldName, Type.getDescriptor(NQueryDataObjectProxy.class), null, null).visitEnd();
                _addSyntheticProxy = false;
            }
            return super.visitField(access, name, desc, signature, value);
        }
       
        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                String signature, String[] exceptions)
        {
            MethodVisitor result = super.visitMethod(access, name, desc, signature, exceptions);
            Integer fieldId = _subjectAnalysisDataContainer._referringMethods.get(composeReferringMethodId(name, desc));
            if(fieldId != null){
                result = new ReferringMethodTransformer(result,access, name, desc, fieldId);
            }
            return result;
        }
       
        @Override
        public void visitEnd() {
            try {
                addVarAccessDelegators();
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            super.visitEnd();
        }
       
        private void writeVarInitCondition(MethodVisitor mv, int referringFieldId , String fieldName, Class typeClass){

            //Label l0 = new Label();
            //mv.visitLabel(l0);
            //mv.visitLineNumber(32, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, _internalClassName, _subjectAnalysisDataContainer._syntheticProxyFieldName, "Lcom/mobixess/jodb/core/query/NQueryDataObjectProxy;");
            //mv.visitFieldInsn(GETFIELD, "com/mobixess/jodb/tests/transformations/TestTrans", "_dataObjectProxy", "Lcom/mobixess/jodb/core/query/NQueryDataObjectProxy;");
            mv.visitLdcInsn(referringFieldId);
            //mv.visitIntInsn(SIPUSH, 32000);
            mv.visitMethodInsn(INVOKEVIRTUAL, "com/mobixess/jodb/core/query/NQueryDataObjectProxy", "isFieldRequested", "(I)Z");
            Label l1 = new Label();
            mv.visitJumpInsn(IFNE, l1);
            Label l2 = new Label();
            mv.visitLabel(l2);
            //mv.visitLineNumber(33, l2);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, _internalClassName, _subjectAnalysisDataContainer._syntheticProxyFieldName, "Lcom/mobixess/jodb/core/query/NQueryDataObjectProxy;");
            //mv.visitFieldInsn(GETFIELD, "com/mobixess/jodb/tests/transformations/TestTrans", "_dataObjectProxy", "Lcom/mobixess/jodb/core/query/NQueryDataObjectProxy;");
            mv.visitLdcInsn(referringFieldId);
            //mv.visitIntInsn(BIPUSH, 10);
            Type type = Type.getType(typeClass);
            String getMethodSuffix = typeClass.getName();
            int lastDotIndx = getMethodSuffix.lastIndexOf('.');
            if(lastDotIndx>=0){
                getMethodSuffix = getMethodSuffix.substring(lastDotIndx+1);
            }
            mv.visitMethodInsn(INVOKEVIRTUAL, "com/mobixess/jodb/core/query/NQueryDataObjectProxy", "getValue_"+getMethodSuffix, "(I)"+type.getDescriptor());
            //mv.visitMethodInsn(INVOKEVIRTUAL, "com/mobixess/jodb/core/query/NQueryDataObjectProxy", "getIntValue", "(I)I");
            mv.visitFieldInsn(PUTFIELD, _internalClassName, fieldName, type.getDescriptor());
            //mv.visitFieldInsn(PUTFIELD, "com/mobixess/jodb/tests/transformations/TestTrans", "_localVar", "I");
            mv.visitLabel(l1);
        }
       
        private void writeReturn(MethodVisitor mv, Field field){
            Label l4 = new Label();
            mv.visitLabel(l4);
            Type declaringClassType = Type.getType(field.getDeclaringClass());
            Class fieldTypeClass = field.getType();
           
            int returnOpcode = -1;
            if(!fieldTypeClass.isPrimitive()){
                returnOpcode = Opcodes.ARETURN;
            }else{
                try {
                    PRIMITIVES_ENUMERATION primitives_enumeration = PrimitiveJavaTypesUtil.getEnumeratedType(fieldTypeClass.getName());
                    switch (primitives_enumeration) {
                        case _long:
                            returnOpcode = Opcodes.LRETURN;
                            break;
                        case _double:
                            returnOpcode = Opcodes.DRETURN;
                            break;
                        case _float:
                            returnOpcode = Opcodes.FRETURN;
                            break;
                        default:
                            returnOpcode = Opcodes.IRETURN;
                            break;
                    }
                } catch (JodbIOException e) {
                    //should never happen
                    e.printStackTrace();
                }
            }
            Type type = Type.getType(field.getType());
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, declaringClassType.getInternalName(), field.getName(), type.getDescriptor());
            mv.visitInsn(returnOpcode);
            mv.visitEnd();
            Label l3 = new Label();
            mv.visitLabel(l3);
            //mv.visitLocalVariable("this", "Lcom/mobixess/jodb/tests/transformations/TestTrans;", null, l0, l3, 0);
            mv.visitLocalVariable("this", declaringClassType.getDescriptor(), null, l4, l3, 0);
            mv.visitMaxs(3, 1);
            mv.visitEnd();
        }

        private void addVarAccessDelegators() throws ClassNotFoundException{
            Iterator<String> keys = _subjectAnalysisDataContainer._directFieldsAccess.keySet().iterator();
            ClassDescriptor subjectClassDescriptor = _subjectAnalysisDataContainer._predicateSubjectClassDescriptor;
            while (keys.hasNext()) {
                String nextVar = keys.next();
                Type owner = _subjectAnalysisDataContainer._directFieldsAccess.get(nextVar);
                ClassDescriptor ownerClassDescriptor = _subjectAnalysisDataContainer._session.getDescriptorForClass(owner.getClassName());
                if(!owner.getInternalName().equals(_internalClassName)){
                    continue;
                }
                String methodNameBase = "_synthAcc$$$"+nextVar;
                String methodName = methodNameBase;
                int counter = 0;
                while(subjectClassDescriptor.getMethodForName(methodName)!=null){
                    methodName = methodNameBase+counter;
                    counter++;
                }
               
                Field field = ownerClassDescriptor.getFieldForName(nextVar);
                Type returnType = Type.getType(field.getType());
                org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(methodName,returnType,new Type[0]);
                MethodVisitor mv = super.visitMethod(ACC_PUBLIC, methodName, method.getDescriptor(), null, null);
                int fieldId = ownerClassDescriptor.getFieldIDForName(nextVar);
                writeVarInitCondition(mv, fieldId, nextVar, field.getType());
                writeReturn(mv, field);
                _subjectAnalysisDataContainer._referringMethods.put(composeReferringMethodId(methodName, method.getDescriptor()), fieldId);
                _subjectAnalysisDataContainer._syntheticReferringMethods.put(nextVar, method);
            }
        }
       
       
        class ReferringMethodTransformer extends AdviceAdapter{
           
            String _methodId;
            String _fieldName;

            public ReferringMethodTransformer(MethodVisitor mv, int access,
                    String name, String desc, int accessedFieldId)
            {
                super(mv, access, name, desc);
                _methodId = composeReferringMethodId(name, desc);
                Field field =_subjectAnalysisDataContainer._predicateSubjectClassDescriptor.getFieldForID(accessedFieldId, null);
                _fieldName = field.getName();
            }

            @Override
            protected void onMethodEnter() {
                int referringFieldId = _subjectAnalysisDataContainer._referringMethods.get(_methodId);
                Field field = _subjectAnalysisDataContainer._predicateSubjectClassDescriptor.getFieldForID(referringFieldId, null);
                Class typeClass = field.getType();
                writeVarInitCondition(mv, referringFieldId, _fieldName, typeClass);
            }
           
            @Override
            protected void onMethodExit(int opcode) {
            }
           
        }
    }

    private static class PredicateAnalizer extends ClassAdapter {

        //private final static String MATCH_METHOD_NAME = "match";

        // private final static String MATCH_METHOD_SIGNATURE = null;
        // private final static String MATCH_METHOD_EXCEPTIONS =
        // Type.getInternalName(IOException.class);
        private String _matchMethodDesc;
       
        private String _methodName;
       
        private Method _predicateMethod;

        private Type _predicateClass;

        private OuterClassAccessVerifier _predicateMethodAdapter;
       
        private String _outerClassRefFieldName;

        private PredicateSubjectAnalysisDataContainer _predicateAnalysisDataContainer;
       
        boolean _notEligible = false;

        private boolean _transform;

        public PredicateAnalizer(ClassVisitor cv,
                Method predicateMethod,
                PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
        {
            super(cv);
            _predicateMethod = predicateMethod;
            _matchMethodDesc = Type.getMethodDescriptor(predicateMethod);
            _methodName = predicateMethod.getName();
            Class predicateClass = predicateMethod.getDeclaringClass();
            _predicateClass = Type.getType(predicateClass);
            ClassDescriptor predicateClassDescriptor = predicateAnalysisDataContainer._session.getDescriptorForClass(predicateClass);
            Field field = predicateClassDescriptor.getOuterRefField();
            if(field!=null){
                _outerClassRefFieldName = field.getName();
            }
            _predicateAnalysisDataContainer = predicateAnalysisDataContainer;
        }
       
        public void enableTransformation(boolean enable){
            _transform = enable;
        }

        public MethodVisitor visitMethod(int acc, String name, String desc,
                String signature, String[] exceptions)
        {
            MethodVisitor mv = cv.visitMethod(acc, name, desc, signature,
                    exceptions);
            if (_methodName.equals(name) && _matchMethodDesc.equals(desc))
            {
                if (!_transform) {
                    mv = _predicateMethodAdapter = new ReferringMethodsExtractor(_predicateClass.getInternalName(), acc, name,
                            desc, mv);
                }else{
                    _predicateMethodAdapter = null;
                    mv = new ReferringVarsTransformer(_predicateClass.getInternalName(), acc, name,
                            desc, mv);
                }
               
            }else{
                if (!_transform) {
                    mv = _predicateMethodAdapter = new OuterClassAccessVerifier(_predicateClass.getInternalName(), acc, name,
                            desc, mv);
                }
            }
            return mv;
        }

        public boolean isNotEligible() {
            return _notEligible;
        }
       
        private class OuterClassAccessVerifier extends CodeAnalyzerAdapter {

            public OuterClassAccessVerifier(String owner, int access,
                    String name, String descriptor, MethodVisitor mv)
            {
                super(owner, access, name, descriptor, mv);
            }
           
            @Override
            public void visitFieldInsn(int opcode, String owner, String name,
                    String desc)
            {
                switch (opcode) {
                    case Opcodes.GETFIELD:
                        if(_outerClassRefFieldName!=null && _outerClassRefFieldName.equals(name) && owner.equals(_predicateClass.getInternalName())){
                            _notEligible = true;
                        }
                        break;
                }
                super.visitFieldInsn(opcode, owner, name, desc);
            }
           
        }

        private class ReferringMethodsExtractor extends OuterClassAccessVerifier {

            // HashMap<String, String> _callsToPredicateSubject = new
            // HashMap<String, String>();

            // HashMap<String, String> _fieldAccessToPredicateSubject = new
            // HashMap<String, String>();

            BitSet _localPredicateSubjectVariables = new BitSet();

            // HashMap<Integer, String> _localPredicateSubjectVariables = new
            // HashMap<Integer, String>();

            public ReferringMethodsExtractor(String owner, int access,
                    String name, String desc, MethodVisitor mv)
            {
                super(owner, access, name, desc, mv);
                int startLocalVarIndex = 1;
                int totalStartVars = _predicateMethod.getParameterTypes().length;
                _localPredicateSubjectVariables.set(startLocalVarIndex, startLocalVarIndex+totalStartVars);
            }

            @Override
            public void visitMethodInsn(int opcode, String owner, String name,
                    String desc)
            {
                switch (opcode) {
                    case Opcodes.INVOKEINTERFACE:
                    case Opcodes.INVOKESPECIAL:
                    case Opcodes.INVOKEVIRTUAL:
                        handleMethodInsn(opcode, owner, name, desc);
                }
                super.visitMethodInsn(opcode, owner, name, desc);
            }

            public void handleMethodInsn(int opcode, String owner, String name,
                    String desc)
            {
                for (int i = 1; !_notEligible && i < _stack.size(); i++) {
                    StackItem item = _stack.get(i);
//                    if(item._localVarId < 0){
//                        _notEligible = true;
//                        break;
//                    }
                    if (item._localVarId >=0 && _localPredicateSubjectVariables.get(item._localVarId)) {
                        _notEligible = true;
                        break;
                    }
                }
                if (!_notEligible && _stack.size() > 0) {
                    StackItem invocationItem = _stack.get(0);
                    if (invocationItem._localVarId != -1 && _localPredicateSubjectVariables
                            .get(invocationItem._localVarId))
                    {
                        String methodId = composeReferringMethodId(name, desc);
                        _predicateAnalysisDataContainer._referringMethods.put(methodId,null);
                    }
                }
            }

            @Override
            public void visitFieldInsn(int opcode, String owner, String name,
                    String desc)
            {
                switch (opcode) {
                    case Opcodes.PUTFIELD:
                    case Opcodes.PUTSTATIC:
                        StackItem stackItem = _stack.get(0);
                        if (_localPredicateSubjectVariables
                                .get(stackItem._localVarId))
                        {
                            _notEligible = true;
                        }
                        break;
                    case Opcodes.GETFIELD:
                        stackItem = _stack.get(_stack.size()-1);
                        if (_localPredicateSubjectVariables
                                .get(stackItem._localVarId))
                        {
                            Type type = Type.getObjectType(owner);
                            ClassDescriptor ownerDescriptor;
                            try {
                                ownerDescriptor = _predicateAnalysisDataContainer._session.getDescriptorForClass(type.getClassName());
                            } catch (ClassNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                            Type ownerType = Type.getType(ownerDescriptor.getType());
                            Field field = ownerDescriptor.getFieldForName(name);
                            if(field==null){
                                _notEligible = true;
                                break;
                            }
                            Class fieldType = field.getType();
                            if(fieldType.isPrimitive() || fieldType == String.class){
                                _predicateAnalysisDataContainer._directFieldsAccess.put(name,ownerType);
                                int typeIndex = _predicateAnalysisDataContainer._predicateSubjectClassDescriptor.getHierarchyTypeIndex(ownerType.getClassName());
                                _predicateAnalysisDataContainer._lowestClassInHieratchyToModify = Math.min(typeIndex, _predicateAnalysisDataContainer._lowestClassInHieratchyToModify);
                            }else{
                                _notEligible = true;
                            }
                        }
                        break;
                }
                super.visitFieldInsn(opcode, owner, name, desc);
            }

            @Override
            public void visitVarInsn(int opcode, int var) {
                switch (opcode) {
                    case Opcodes.ASTORE:
                        StackItem stackItem = _stack.get(0);
                        super.visitVarInsn(opcode, var);
                        if (stackItem._localVarId == -1 || _localPredicateSubjectVariables
                                .get(stackItem._localVarId))
                        {
                            Object varDesc = _locals.get(var);
                            if (!(varDesc instanceof String)) {
                                _notEligible = true;
                                break;
                            }
                            String type = (String) varDesc;
                            type = type.replace('/', '.');
                            if (!_predicateAnalysisDataContainer._predicateSubjectClassDescriptor
                                    .isHierarchyType(type))
                            {
                                _notEligible = true;
                            }
                            _localPredicateSubjectVariables.set(var);
                        }
                        break;
                    default:
                        super.visitVarInsn(opcode, var);
                }

            }
        }
       
        private class ReferringVarsTransformer extends CodeAnalyzerAdapter {

            public ReferringVarsTransformer(String owner, int access,
                    String name, String desc, MethodVisitor mv)
            {
                super(owner, access, name, desc, mv);
            }



            @Override
            public void visitFieldInsn(int opcode, String owner, String name,
                    String desc)
            {
                switch (opcode) {
                    case Opcodes.GETFIELD:
                        org.objectweb.asm.commons.Method method =_predicateAnalysisDataContainer._syntheticReferringMethods.get(name);
                        if(method!=null){
                            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, method.getName(), method.getDescriptor());
                            return;
                        }
                }
                super.visitFieldInsn(opcode, owner, name, desc);
            }

          
        }

    }

    private static class SubjectFieldAccessAnalizer extends ClassAdapter {

        String _predicateSubjectClassName;

        int _totalMethodsAnalized;

        PredicateSubjectAnalysisDataContainer _predicateAnalysisDataContainer;

        public SubjectFieldAccessAnalizer(ClassVisitor cv,
                String predicateSubjectClassName, PredicateSubjectAnalysisDataContainer predicateAnalysisDataContainer)
        {
            super(cv);
            _predicateSubjectClassName = predicateSubjectClassName;
            _predicateAnalysisDataContainer = predicateAnalysisDataContainer;
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                String signature, String[] exceptions)
        {
            MethodVisitor result = super.visitMethod(access, name, desc,
                    signature, exceptions);
            String nameId = composeReferringMethodId(name, desc);
            if (_predicateAnalysisDataContainer._referringMethods.containsKey(nameId))
            {
                result = new PredicateSubjectFieldAccessMethodAdapter(_predicateSubjectClassName, access, name, desc, result);
                _totalMethodsAnalized++;
            }
            return result;
        }

        private class PredicateSubjectFieldAccessMethodAdapter extends
                CodeAnalyzerAdapter
        {

            // ALOAD 0
            // GETFIELD
            // com/mobixess/jodb/tests/transformations/TestTrans._localVar : I
            // IRETURN
            boolean _complexMethod;

            public PredicateSubjectFieldAccessMethodAdapter(String owner,
                    int access, String name, String descriptor, MethodVisitor mv)
            {
                super(owner, access, name, descriptor, mv);
            }

            @Override
            void handleOpcode(int opcode, int iarg, String sarg) {
                if (!_complexMethod) {
                    switch (opcode) {
                        case Opcodes.ALOAD:
                            if (iarg != 0) {
                                markAsComplex();
                            }
                            break;
                        case Opcodes.GETFIELD:
                        case Opcodes.IRETURN:
                        case Opcodes.ARETURN:
                            break;
                        default:
                            markAsComplex();
                            break;
                    }
                }
                super.handleOpcode(opcode, iarg, sarg);
            }

            @Override
            public void visitFieldInsn(int opcode, String owner, String name,
                    String desc)
            {
                if (opcode == Opcodes.GETFIELD) {
                    if (_stack.size() != 1 || _stack.get(0)._localVarId != 0) {
                        markAsComplex();
                    } else {
                        //_predicateAnalysisDataContainer._fieldByMethodAccessMap.put(name, _methodDecriptor);
                        String methodId = composeReferringMethodId(_methodName, _methodDecriptor);
                        Integer accessField = _predicateAnalysisDataContainer._referringMethods.get(methodId);
                        if(accessField == null){
                            Type type = Type.getObjectType(owner);
                            ClassDescriptor ownerDescriptor;
                            try {
                                ownerDescriptor = _predicateAnalysisDataContainer._session.getDescriptorForClass(type.getClassName());
                            } catch (ClassNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                            int id = ownerDescriptor.getFieldIDForName(name);
//                            Class realOwner = ownerDescriptor.getTopmostFieldOwner(name);
//                            String fieldId = composeFieldId(realOwner.getName(), name, desc);
                            _predicateAnalysisDataContainer._referringMethods.put(methodId, id);
                        }
                    }
                }
                super.visitFieldInsn(opcode, owner, name, desc);
            }

            private void markAsComplex() {
                if (_complexMethod) return;
                _complexMethod = true;
                _predicateAnalysisDataContainer._complexMethods.add(_methodDecriptor);
            }

        }
    }

    private static class CodeAnalyzerAdapter extends OpcodeAnalyzerAdapter {

        String _methodDecriptor;
       
        String _methodName;

        List<StackItem> _locals;

        List<StackItem> _stack;

        int _maxStack;

        int _maxLocals;

        List<Label> _labels;

        Map<Label, String> _uninitializedTypes;

        public CodeAnalyzerAdapter(String owner, int access, String name,
                String descriptor, MethodVisitor mv)
        {
            super(mv);
            _methodDecriptor = descriptor;
            _methodName = name;
            initLocalsAndStack();
            _uninitializedTypes = new HashMap<Label, String>();

            if ((access & Opcodes.ACC_STATIC) == 0) {
                if (name.equals("<init>")) {
                    addLocal(Opcodes.UNINITIALIZED_THIS);
                } else {
                    addLocal(owner);
                }
            }
            Type[] types = Type.getArgumentTypes(descriptor);
            for (int i = 0; i < types.length; ++i) {
                Type type = types[i];
                switch (type.getSort()) {
                    case Type.BOOLEAN:
                    case Type.CHAR:
                    case Type.BYTE:
                    case Type.SHORT:
                    case Type.INT:
                        addLocal(Opcodes.INTEGER);
                        break;
                    case Type.LONG:
                        addLocal(Opcodes.LONG);
                        addLocal(Opcodes.TOP);
                        break;
                    case Type.DOUBLE:
                        addLocal(Opcodes.DOUBLE);
                        addLocal(Opcodes.TOP);
                        break;
                    case Type.FLOAT:
                        addLocal(Opcodes.FLOAT);
                        break;
                    case Type.ARRAY:
                        addLocal(types[i].getDescriptor());
                        break;
                    default:
                        addLocal(types[i].getInternalName());
                }
            }
        }
       
        private void initLocalsAndStack(){
            _locals = new ArrayList<StackItem>();
            _stack = new ArrayList<StackItem>();
        }
       
//        @Override
//        public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
//            addLocal(desc);
//            super.visitLocalVariable(name, desc, signature, start, end, index);
//        }

        private void addLocal(Object obj) {
            _locals.add(new StackItem(obj, _locals.size()));
        }

        void handleOpcode(int opcode, int iarg, String sarg) {
            if (_locals == null) {
                return;
            }
            Object t1, t2, t3, t4;
            switch (opcode) {
                case Opcodes.I2B:
                case Opcodes.I2C:
                case Opcodes.I2S:
                case Opcodes.GOTO:
                case Opcodes.RETURN:
                case Opcodes.FNEG:
                case Opcodes.DNEG:
                case Opcodes.LNEG:
                case Opcodes.NOP:
                case Opcodes.INEG:
                    break;
                case Opcodes.ACONST_NULL:
                    stackPush(Opcodes.NULL);
                    break;
                case Opcodes.BIPUSH:
                case Opcodes.SIPUSH:
                case Opcodes.ICONST_M1:
                case Opcodes.ICONST_0:
                case Opcodes.ICONST_1:
                case Opcodes.ICONST_2:
                case Opcodes.ICONST_3:
                case Opcodes.ICONST_4:
                case Opcodes.ICONST_5:
                    stackPush(Opcodes.INTEGER);
                    break;
                case Opcodes.LCONST_0:
                case Opcodes.LCONST_1:
                    stackPush(Opcodes.LONG);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.FCONST_0:
                case Opcodes.FCONST_1:
                case Opcodes.FCONST_2:
                    stackPush(Opcodes.FLOAT);
                    break;
                case Opcodes.DCONST_0:
                case Opcodes.DCONST_1:
                    stackPush(Opcodes.DOUBLE);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.LLOAD:
                case Opcodes.DLOAD:
                    stackPushItem(getLocalForId(iarg));
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.ILOAD:
                case Opcodes.FLOAD:
                case Opcodes.ALOAD:
                    stackPushItem(getLocalForId(iarg));
                    break;
                case Opcodes.LALOAD:
                case Opcodes.D2L:
                    stackMultiPop(2);
                    stackPush(Opcodes.LONG);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.IALOAD:
                case Opcodes.BALOAD:
                case Opcodes.CALOAD:
                case Opcodes.SALOAD:
                    stackMultiPop(2);
                    stackPush(Opcodes.INTEGER);
                    break;
                case Opcodes.FALOAD:
                    stackMultiPop(2);
                    stackPush(Opcodes.FLOAT);
                    break;
                case Opcodes.DALOAD:
                case Opcodes.L2D:
                    stackMultiPop(2);
                    stackPush(Opcodes.DOUBLE);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.AALOAD:
                    stackPop();
                    StackItem nextOnStack = stackPop();
                    t1 = nextOnStack != null ? nextOnStack._object : null;
                    //t1 = stackPop();// was
                    pushDescriptor(((String) t1).substring(1));
                    break;
                case Opcodes.LSTORE:
                case Opcodes.DSTORE:
                    stackPop();
                    nextOnStack = stackPop();
                    t1 = nextOnStack != null ? nextOnStack._object : null;
                    //t1 = stackPop();//was
                    set(iarg, t1);
                    set(iarg + 1, Opcodes.TOP);
                    if (iarg > 0) {
                        t2 = getLocalForId(iarg - 1);
                        if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
                            set(iarg - 1, Opcodes.TOP);
                        }
                    }
                    break;
                case Opcodes.IASTORE:
                case Opcodes.BASTORE:
                case Opcodes.CASTORE:
                case Opcodes.SASTORE:
                case Opcodes.FASTORE:
                case Opcodes.AASTORE:
                    stackMultiPop(3);
                    break;
                case Opcodes.LASTORE:
                case Opcodes.DASTORE:
                    stackMultiPop(4);
                    break;
                case Opcodes.ISTORE:
                case Opcodes.FSTORE:
                case Opcodes.ASTORE:
                    nextOnStack = stackPop();
                    t1 = nextOnStack != null ? nextOnStack._object : null;
                    set(iarg, t1);
                    if (iarg > 0) {
                        t2 = getLocalForId(iarg - 1);
                        if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
                            set(iarg - 1, Opcodes.TOP);
                        }
                    }
                    break;
                case Opcodes.POP:
                case Opcodes.IFEQ:
                case Opcodes.IFNE:
                case Opcodes.IFLT:
                case Opcodes.IFGE:
                case Opcodes.IFGT:
                case Opcodes.IFLE:
                case Opcodes.IRETURN:
                case Opcodes.FRETURN:
                case Opcodes.ARETURN:
                case Opcodes.TABLESWITCH:
                case Opcodes.LOOKUPSWITCH:
                case Opcodes.ATHROW:
                case Opcodes.MONITORENTER:
                case Opcodes.MONITOREXIT:
                case Opcodes.IFNULL:
                case Opcodes.IFNONNULL:
                    stackPop();
                    break;
                case Opcodes.POP2:
                case Opcodes.IF_ICMPEQ:
                case Opcodes.IF_ICMPNE:
                case Opcodes.IF_ICMPLT:
                case Opcodes.IF_ICMPGE:
                case Opcodes.IF_ICMPGT:
                case Opcodes.IF_ICMPLE:
                case Opcodes.IF_ACMPEQ:
                case Opcodes.IF_ACMPNE:
                case Opcodes.LRETURN:
                case Opcodes.DRETURN:
                    stackMultiPop(2);
                    break;
                case Opcodes.SWAP:
                    StackItem st1 = stackPop();
                    StackItem st2 = stackPop();
                    stackPush(st1);
                    stackPush(st2);
                    break;
                case Opcodes.IADD:
                case Opcodes.ISUB:
                case Opcodes.IMUL:
                case Opcodes.IDIV:
                case Opcodes.IREM:
                case Opcodes.IAND:
                case Opcodes.IOR:
                case Opcodes.IXOR:
                case Opcodes.ISHL:
                case Opcodes.ISHR:
                case Opcodes.IUSHR:
                case Opcodes.L2I:
                case Opcodes.D2I:
                case Opcodes.FCMPL:
                case Opcodes.FCMPG:
                    stackMultiPop(2);
                    stackPush(Opcodes.INTEGER);
                    break;
                case Opcodes.LADD:
                case Opcodes.LSUB:
                case Opcodes.LMUL:
                case Opcodes.LDIV:
                case Opcodes.LREM:
                case Opcodes.LAND:
                case Opcodes.LOR:
                case Opcodes.LXOR:
                    stackMultiPop(4);
                    stackPush(Opcodes.LONG);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.FADD:
                case Opcodes.FSUB:
                case Opcodes.FMUL:
                case Opcodes.FDIV:
                case Opcodes.FREM:
                case Opcodes.L2F:
                case Opcodes.D2F:
                    stackMultiPop(2);
                    stackPush(Opcodes.FLOAT);
                    break;
                case Opcodes.DADD:
                case Opcodes.DSUB:
                case Opcodes.DMUL:
                case Opcodes.DDIV:
                case Opcodes.DREM:
                    stackMultiPop(4);
                    stackPush(Opcodes.DOUBLE);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.DUP:
                    st1 = stackPop();
                    stackPush(st1);
                    stackPush(st1);
                    break;
                case Opcodes.DUP_X1:
                    st1 = stackPop();
                    st2 = stackPop();
                    stackPush(st1);
                    stackPush(st2);
                    stackPush(st1);
                    break;
                case Opcodes.DUP_X2:
                    st1 = stackPop();
                    st2 = stackPop();
                    StackItem st3 = stackPop();
                    stackPush(st1);
                    stackPush(st3);
                    stackPush(st2);
                    stackPush(st1);
                    break;
                case Opcodes.DUP2:
                    st1 = stackPop();
                    st2 = stackPop();
                    stackPush(st2);
                    stackPush(st1);
                    stackPush(st2);
                    stackPush(st1);
                    break;
                case Opcodes.DUP2_X1:
                    st1 = stackPop();
                    st2 = stackPop();
                    st3 = stackPop();
                    stackPush(st2);
                    stackPush(st1);
                    stackPush(st3);
                    stackPush(st2);
                    stackPush(st1);
                    break;
                case Opcodes.DUP2_X2:
                    st1 = stackPop();
                    st2 = stackPop();
                    st3 = stackPop();
                    StackItem st4 = stackPop();
                    stackPush(st2);
                    stackPush(st1);
                    stackPush(st4);
                    stackPush(st3);
                    stackPush(st2);
                    stackPush(st1);
                    break;
                case Opcodes.LSHL:
                case Opcodes.LSHR:
                case Opcodes.LUSHR:
                    stackMultiPop(3);
                    stackPush(Opcodes.LONG);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.IINC:
                    set(iarg, Opcodes.INTEGER);
                    break;
                case Opcodes.I2L:
                case Opcodes.F2L:
                    stackPop();
                    stackPush(Opcodes.LONG);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.I2F:
                    stackPop();
                    stackPush(Opcodes.FLOAT);
                    break;
                case Opcodes.I2D:
                case Opcodes.F2D:
                    stackPop();
                    stackPush(Opcodes.DOUBLE);
                    stackPush(Opcodes.TOP);
                    break;
                case Opcodes.F2I:
                case Opcodes.ARRAYLENGTH:
                case Opcodes.INSTANCEOF:
                    stackPop();
                    stackPush(Opcodes.INTEGER);
                    break;
                case Opcodes.NEW:
                    stackPush(_labels.get(0));
                    break;
                case Opcodes.NEWARRAY:
                    stackPop();
                    switch (iarg) {
                        case Opcodes.T_BOOLEAN:
                            pushDescriptor("[Z");
                            break;
                        case Opcodes.T_CHAR:
                            pushDescriptor("[C");
                            break;
                        case Opcodes.T_BYTE:
                            pushDescriptor("[B");
                            break;
                        case Opcodes.T_SHORT:
                            pushDescriptor("[S");
                            break;
                        case Opcodes.T_INT:
                            pushDescriptor("[I");
                            break;
                        case Opcodes.T_FLOAT:
                            pushDescriptor("[F");
                            break;
                        case Opcodes.T_DOUBLE:
                            pushDescriptor("[D");
                            break;
                        default:
                            pushDescriptor("[J");
                            break;
                    }
                    break;
                case Opcodes.ANEWARRAY:
                    stackPop();
                    if (sarg.charAt(0) == '[') {
                        pushDescriptor("[" + sarg);
                    } else {
                        pushDescriptor("[L" + sarg + ";");
                    }
                    break;
                case Opcodes.CHECKCAST:
                    StackItem currentItem = stackPop();
                    if (sarg.charAt(0) == '[') {
                        pushDescriptor(sarg);
                    } else {
                        currentItem._object = sarg;
                        stackPushItem(currentItem);
                    }
                    break;
                case Opcodes.LCMP:
                case Opcodes.DCMPL:
                case Opcodes.DCMPG:
                    stackMultiPop(4);
                    stackPush(Opcodes.INTEGER);
                    break;
                case Opcodes.JSR:
                case Opcodes.RET:
                    throw new RuntimeException("JSR/RET are not supported");
                case Opcodes.GETSTATIC:
                    pushDescriptor(sarg);
                    break;
                case Opcodes.PUTSTATIC:
                    stackPop(sarg);
                    break;
                case Opcodes.GETFIELD:
                    stackPop();
                    pushDescriptor(sarg);
                    break;
                case Opcodes.PUTFIELD:
                    stackPop(sarg);
                    stackPop();
                    break;
                default:
                    stackMultiPop(iarg);
                    pushDescriptor(sarg);
                    break;
            }
            _labels = null;
        }

        private StackItem getLocalForId(int localId) {
            _maxLocals = Math.max(_maxLocals, localId);
            return localId < _locals.size() ? _locals.get(localId)
                    : new StackItem(Opcodes.TOP);
        }

        private void stackPush(Object type) {
            stackPushItem(new StackItem(type));
        }

        private void stackPushItem(StackItem item) {
            _stack.add(item);
            _maxStack = Math.max(_maxStack, _stack.size());
        }

        private void pushDescriptor(String descriptor) {
            int index = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1
                    : 0;
            switch (descriptor.charAt(index)) {
                case 'V':
                    return;
                case 'Z':
                case 'C':
                case 'B':
                case 'S':
                case 'I':
                    stackPush(Opcodes.INTEGER);
                    return;
                case 'F':
                    stackPush(Opcodes.FLOAT);
                    return;
                case 'J':
                    stackPush(Opcodes.LONG);
                    stackPush(Opcodes.TOP);
                    return;
                case 'D':
                    stackPush(Opcodes.DOUBLE);
                    stackPush(Opcodes.TOP);
                    return;
                case '[':
                    if (index == 0) {
                        stackPush(descriptor);
                    } else {
                        stackPush(descriptor.substring(index, descriptor
                                .length()));
                    }
                    break;
                // case 'L':
                default:
                    if (index == 0) {
                        stackPush(descriptor.substring(1,
                                descriptor.length() - 1));
                    } else {
                        stackPush(descriptor.substring(index + 1, descriptor
                                .length() - 1));
                    }
                    return;
            }
        }

        private void set(int local, Object type) {
            _maxLocals = Math.max(_maxLocals, local);
            while (local >= _locals.size()) {
                addLocal(Opcodes.TOP);
            }
            _locals.set(local, new StackItem(type, local));
        }

        @Override
        public void visitFrame(int type, int nLocal, Object[] local,
                int nStack, Object[] stack)
        {
            super.visitFrame(type, nLocal, local, nStack, stack);

            if (_locals == null) {
                _locals = new ArrayList<StackItem>();
                _stack = new ArrayList<StackItem>();
            } else {
                _locals.clear();
                _stack.clear();
            }
            visitFrameTypes(nLocal, local, _locals);
            visitFrameTypes(nStack, stack, _stack);
            _maxStack = Math.max(_maxStack, _stack.size());
        }

        private void visitFrameTypes(int n, Object[] types, List result) {
            for (int i = 0; i < n; ++i) {
                Object type = types[i];
                result.add(type);
                if (type == Opcodes.LONG || type == Opcodes.DOUBLE) {
                    result.add(Opcodes.TOP);
                }
            }
        }

        @Override
        public void visitIincInsn(int var, int increment) {
            if (mv != null) {
                mv.visitIincInsn(var, increment);
            }
            handleOpcode(Opcodes.IINC, var, null);
        }

        @Override
        public void visitInsn(int opcode) {
            super.visitInsn(opcode);
            if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN
                    || opcode == Opcodes.ATHROW)
            {
                _locals.clear();
                _stack.clear();
            }
        }

        @Override
        public void visitTableSwitchInsn(int min, int max, Label dflt,
                Label labels[])
        {
            super.visitTableSwitchInsn(min, max, dflt, labels);
            _stack.clear();
        }

        @Override
        public void visitJumpInsn(int opcode, Label label) {
            super.visitJumpInsn(opcode, label);
            if (opcode == Opcodes.GOTO) {
                _stack.clear();
            }
        }

        @Override
        public void visitLabel(Label label) {
            super.visitLabel(label);
            if (_labels == null) {
                _labels = new ArrayList<Label>(5);
            }
            _labels.add(label);
        }

        @Override
        public void visitLdcInsn(Object cst) {
            super.visitLdcInsn(cst);
            if (cst instanceof Integer) {
                stackPush(Opcodes.INTEGER);
            } else if (cst instanceof Long) {
                stackPush(Opcodes.LONG);
                stackPush(Opcodes.TOP);
            } else if (cst instanceof Float) {
                stackPush(Opcodes.FLOAT);
            } else if (cst instanceof Double) {
                stackPush(Opcodes.DOUBLE);
                stackPush(Opcodes.TOP);
            } else if (cst instanceof String) {
                stackPush("java/lang/String");
            } else if (cst instanceof Type) {
                stackPush("java/lang/Class");
            } else {
                throw new IllegalArgumentException();
            }
            _labels = null;
        }

        private void stackMultiPop(int n) {
            int size = _stack.size();
            int end = size - n;
            for (int i = size - 1; i >= end; --i) {
                _stack.remove(i);
            }
        }

        private StackItem stackPop() {
            return _stack.remove(_stack.size() - 1);
        }

        private void stackPop(String desc) {
            char c = desc.charAt(0);
            if (c == '(') {
                int n = 0;
                Type[] types = Type.getArgumentTypes(desc);
                for (int i = 0; i < types.length; ++i) {
                    n += types[i].getSize();
                }
                stackMultiPop(n);
            } else if (c == 'D' || c == 'J') {
                stackMultiPop(2);
            } else {
                stackPop();
            }
        }

        @Override
        public void visitLookupSwitchInsn(Label dflt, int keys[],
                Label labels[])
        {
            super.visitLookupSwitchInsn(dflt, keys, labels);
            _stack.clear();
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            if (mv != null) {
                _maxStack = Math.max(_maxStack, maxStack);
                _maxLocals = Math.max(_maxLocals, maxLocals);
                mv.visitMaxs(_maxStack, _maxLocals);
            }
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name,
                String desc)
        {
            super.visitMethodInsn(opcode, owner, name, desc);
            stackPop(desc);
            if (opcode != Opcodes.INVOKESTATIC) {
                StackItem nextOnStack = stackPop();
                Object t = nextOnStack != null ? nextOnStack._object : null;
                if (opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') {
                    Object u;
                    if (t == Opcodes.UNINITIALIZED_THIS) {
                        u = owner;
                    } else {
                        u = _uninitializedTypes.get(t);
                    }
                    for (int i = 0; i < _locals.size(); ++i) {
                        if (_locals.get(i) == t) {
                            _locals.set(i, new StackItem(u));
                        }
                    }
                    for (int i = 0; i < _stack.size(); ++i) {
                        if (_stack.get(i) == t) {
                            _stack.set(i, new StackItem(u));
                        }
                    }
                }
            }
            pushDescriptor(desc);
            _labels = null;
        }

        @Override
        public void visitTypeInsn(int opcode, String desc) {
            if (opcode == Opcodes.NEW) {
                if (_labels == null) {
                    Label l = new Label();
                    _labels = new ArrayList<Label>(5);
                    _labels.add(l);
                    if (mv != null) {
                        mv.visitLabel(l);
                    }
                }
                for (int i = 0; i < _labels.size(); ++i) {
                    _uninitializedTypes.put(_labels.get(i), desc);
                }
            }
            super.visitTypeInsn(opcode, desc);
        }
    }

    static class StackItem {
        int _localVarId;

        Object _object;

        public StackItem(Object object) {
            this(object, -1);
        }

        public StackItem(Object object, int varId) {
            _localVarId = varId;
            _object = object;
        }
    }

    abstract static class OpcodeAnalyzerAdapter extends MethodAdapter {

        public OpcodeAnalyzerAdapter(MethodVisitor mv) {
            super(mv);
        }

        abstract void handleOpcode(int opcode, int iarg, String sarg);

        @Override
        public void visitFieldInsn(int opcode, String owner, String name,
                String desc)
        {
            if (mv != null) {
                mv.visitFieldInsn(opcode, owner, name, desc);
            }
            handleOpcode(opcode, 0, desc);
        }

        @Override
        public void visitFrame(int type, int nLocal, Object[] local,
                int nStack, Object[] stack)
        {
            if (type != Opcodes.F_NEW) {
                throw new IllegalStateException();
            }

            if (mv != null) {
                mv.visitFrame(type, nLocal, local, nStack, stack);
            }
        }

        @Override
        public void visitIincInsn(int var, int increment) {
            if (mv != null) {
                mv.visitIincInsn(var, increment);
            }
            handleOpcode(Opcodes.IINC, var, null);
        }

        @Override
        public void visitInsn(int opcode) {
            if (mv != null) {
                mv.visitInsn(opcode);
            }
            handleOpcode(opcode, 0, null);
        }

        @Override
        public void visitTableSwitchInsn(int min, int max, Label dflt,
                Label labels[])
        {
            if (mv != null) {
                mv.visitTableSwitchInsn(min, max, dflt, labels);
            }
            handleOpcode(Opcodes.TABLESWITCH, 0, null);
        }

        @Override
        public void visitIntInsn(int opcode, int operand) {
            if (mv != null) {
                mv.visitIntInsn(opcode, operand);
            }
            handleOpcode(opcode, operand, null);
        }

        @Override
        public void visitJumpInsn(int opcode, Label label) {
            if (mv != null) {
                mv.visitJumpInsn(opcode, label);
            }
            handleOpcode(opcode, 0, null);
        }

        @Override
        public void visitLabel(Label label) {
            if (mv != null) {
                mv.visitLabel(label);
            }
        }

        // ------------------------------------------------------------------------

        @Override
        public void visitMultiANewArrayInsn(String desc, int dims) {
            if (mv != null) {
                mv.visitMultiANewArrayInsn(desc, dims);
            }
            handleOpcode(Opcodes.MULTIANEWARRAY, dims, desc);
        }

        @Override
        public void visitLdcInsn(Object cst) {
            if (mv != null) {
                mv.visitLdcInsn(cst);
            }
        }

        @Override
        public void visitLookupSwitchInsn(Label dflt, int keys[],
                Label labels[])
        {
            if (mv != null) {
                mv.visitLookupSwitchInsn(dflt, keys, labels);
            }
            handleOpcode(Opcodes.LOOKUPSWITCH, 0, null);
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            if (mv != null) {
                mv.visitMaxs(maxStack, maxLocals);
            }
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name,
                String desc)
        {
            if (mv != null) {
                mv.visitMethodInsn(opcode, owner, name, desc);
            }
        }

        @Override
        public void visitTypeInsn(int opcode, String desc) {
            if (mv != null) {
                mv.visitTypeInsn(opcode, desc);
            }
            handleOpcode(opcode, 0, desc);
        }

        @Override
        public void visitVarInsn(int opcode, int var) {
            if (mv != null) {
                mv.visitVarInsn(opcode, var);
            }
            handleOpcode(opcode, var, null);
        }
    }

}
TOP

Related Classes of com.mobixess.jodb.core.query.NQLoader$StackItem

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.