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