diff --git a/build.third_step6.gradle b/build.third_step6.gradle index 0bc156155..d87976633 100644 --- a/build.third_step6.gradle +++ b/build.third_step6.gradle @@ -28,6 +28,8 @@ sourceSets{ "${srcDir}/fine-guava/src", "${srcDir}/fine-lucene/src", "${srcDir}/fine-lucene/resources", + "${srcDir}/fine-cglib/src", + "${srcDir}/fine-cglib/resources", ] } } @@ -76,6 +78,8 @@ task copyFiles(type:Copy,dependsOn:'compileJava'){ with dataContent.call("${srcDir}/fine-guava/src") with dataContent.call("${srcDir}/fine-lucene/src") with dataContent.call("${srcDir}/fine-lucene/resources") + with dataContent.call("${srcDir}/fine-cglib/src") + with dataContent.call("${srcDir}/fine-cglib/resources") into "${classesDir}" } } diff --git a/fine-cglib/resources/com/fr/third/net/sf/cglib/util/words.txt b/fine-cglib/resources/com/fr/third/net/sf/cglib/util/words.txt new file mode 100644 index 000000000..263a4396d --- /dev/null +++ b/fine-cglib/resources/com/fr/third/net/sf/cglib/util/words.txt @@ -0,0 +1,50 @@ +Casuarinaceae +hylomorphic +granitize +biddably +repulsive +amphimictical +trio +toxodont +nonreigning +dragbar +Moronidae +unlanguishing +metabolizable +Osmerus +goran +spiritfulness +tetrachloromethane +baobab +caroline +radioconductor +imband +crinoline +circummundane +incontractile +forerank +modernization +meal +fishman +underbuy +pertain +equiped +cockal +unshrined +Harb +heterotaxis +commensurableness +baggy +sarcophilous +tankard +acervuline +unverifiably +premidnight +strangles +vitellus +Socratean +flock +scourage +feverlike +citharist +harn diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanCopier.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanCopier.java new file mode 100644 index 000000000..393ab2fc3 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanCopier.java @@ -0,0 +1,176 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.*; +import java.security.ProtectionDomain; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; +import java.util.*; + +/** + * @author Chris Nokleberg + */ +abstract public class BeanCopier +{ + private static final BeanCopierKey KEY_FACTORY = + (BeanCopierKey)KeyFactory.create(BeanCopierKey.class); + private static final Type CONVERTER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.core.Converter"); + private static final Type BEAN_COPIER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BeanCopier"); + private static final Signature COPY = + new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER }); + private static final Signature CONVERT = + TypeUtils.parseSignature("Object convert(Object, Class, Object)"); + + interface BeanCopierKey { + public Object newInstance(String source, String target, boolean useConverter); + } + + public static BeanCopier create(Class source, Class target, boolean useConverter) { + Generator gen = new Generator(); + gen.setSource(source); + gen.setTarget(target); + gen.setUseConverter(useConverter); + return gen.create(); + } + + abstract public void copy(Object from, Object to, Converter converter); + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(BeanCopier.class.getName()); + private Class source; + private Class target; + private boolean useConverter; + + public Generator() { + super(SOURCE); + } + + public void setSource(Class source) { + if(!Modifier.isPublic(source.getModifiers())){ + setNamePrefix(source.getName()); + } + this.source = source; + } + + public void setTarget(Class target) { + if(!Modifier.isPublic(target.getModifiers())){ + setNamePrefix(target.getName()); + } + + this.target = target; + } + + public void setUseConverter(boolean useConverter) { + this.useConverter = useConverter; + } + + protected ClassLoader getDefaultClassLoader() { + return source.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(source); + } + + public BeanCopier create() { + Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter); + return (BeanCopier)super.create(key); + } + + public void generateClass(ClassVisitor v) { + Type sourceType = Type.getType(source); + Type targetType = Type.getType(target); + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + BEAN_COPIER, + null, + Constants.SOURCE_FILE); + + EmitUtils.null_constructor(ce); + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null); + PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source); + PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target); + + Map names = new HashMap(); + for (int i = 0; i < getters.length; i++) { + names.put(getters[i].getName(), getters[i]); + } + Local targetLocal = e.make_local(); + Local sourceLocal = e.make_local(); + if (useConverter) { + e.load_arg(1); + e.checkcast(targetType); + e.store_local(targetLocal); + e.load_arg(0); + e.checkcast(sourceType); + e.store_local(sourceLocal); + } else { + e.load_arg(1); + e.checkcast(targetType); + e.load_arg(0); + e.checkcast(sourceType); + } + for (int i = 0; i < setters.length; i++) { + PropertyDescriptor setter = setters[i]; + PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName()); + if (getter != null) { + MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod()); + MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); + if (useConverter) { + Type setterType = write.getSignature().getArgumentTypes()[0]; + e.load_local(targetLocal); + e.load_arg(2); + e.load_local(sourceLocal); + e.invoke(read); + e.box(read.getSignature().getReturnType()); + EmitUtils.load_class(e, setterType); + e.push(write.getSignature().getName()); + e.invoke_interface(CONVERTER, CONVERT); + e.unbox_or_zero(setterType); + e.invoke(write); + } else if (compatible(getter, setter)) { + e.dup2(); + e.invoke(read); + e.invoke(write); + } + } + } + e.return_value(); + e.end_method(); + ce.end_class(); + } + + private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) { + // TODO: allow automatic widening conversions? + return setter.getPropertyType().isAssignableFrom(getter.getPropertyType()); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanGenerator.java new file mode 100644 index 000000000..51ef40ffe --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanGenerator.java @@ -0,0 +1,149 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.security.ProtectionDomain; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class BeanGenerator extends AbstractClassGenerator +{ + private static final Source SOURCE = new Source(BeanGenerator.class.getName()); + private static final BeanGeneratorKey KEY_FACTORY = + (BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class); + + interface BeanGeneratorKey { + public Object newInstance(String superclass, Map props); + } + + private Class superclass; + private Map props = new HashMap(); + private boolean classOnly; + + public BeanGenerator() { + super(SOURCE); + } + + /** + * Set the class which the generated class will extend. The class + * must not be declared as final, and must have a non-private + * no-argument constructor. + * @param superclass class to extend, or null to extend Object + */ + public void setSuperclass(Class superclass) { + if (superclass != null && superclass.equals(Object.class)) { + superclass = null; + } + this.superclass = superclass; + } + + public void addProperty(String name, Class type) { + if (props.containsKey(name)) { + throw new IllegalArgumentException("Duplicate property name \"" + name + "\""); + } + props.put(name, Type.getType(type)); + } + + protected ClassLoader getDefaultClassLoader() { + if (superclass != null) { + return superclass.getClassLoader(); + } else { + return null; + } + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(superclass); + } + + public Object create() { + classOnly = false; + return createHelper(); + } + + public Object createClass() { + classOnly = true; + return createHelper(); + } + + private Object createHelper() { + if (superclass != null) { + setNamePrefix(superclass.getName()); + } + String superName = (superclass != null) ? superclass.getName() : "java.lang.Object"; + Object key = KEY_FACTORY.newInstance(superName, props); + return super.create(key); + } + + public void generateClass(ClassVisitor v) throws Exception { + int size = props.size(); + String[] names = (String[])props.keySet().toArray(new String[size]); + Type[] types = new Type[size]; + for (int i = 0; i < size; i++) { + types[i] = (Type)props.get(names[i]); + } + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT, + null, + null); + EmitUtils.null_constructor(ce); + EmitUtils.add_properties(ce, names, types); + ce.end_class(); + } + + protected Object firstInstance(Class type) { + if (classOnly) { + return type; + } else { + return ReflectUtils.newInstance(type); + } + } + + protected Object nextInstance(Object instance) { + Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass(); + if (classOnly) { + return protoclass; + } else { + return ReflectUtils.newInstance(protoclass); + } + } + + public static void addProperties(BeanGenerator gen, Map props) { + for (Iterator it = props.keySet().iterator(); it.hasNext();) { + String name = (String)it.next(); + gen.addProperty(name, (Class)props.get(name)); + } + } + + public static void addProperties(BeanGenerator gen, Class type) { + addProperties(gen, ReflectUtils.getBeanProperties(type)); + } + + public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) { + for (int i = 0; i < descriptors.length; i++) { + gen.addProperty(descriptors[i].getName(), descriptors[i].getPropertyType()); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMap.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMap.java new file mode 100644 index 000000000..9dddac1b4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMap.java @@ -0,0 +1,320 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.security.ProtectionDomain; +import java.beans.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +/** + * A Map-based view of a JavaBean. The default set of keys is the + * union of all property names (getters or setters). An attempt to set + * a read-only property will be ignored, and write-only properties will + * be returned as null. Removal of objects is not a + * supported (the key set is fixed). + * @author Chris Nokleberg + */ +abstract public class BeanMap implements Map { + /** + * Limit the properties reflected in the key set of the map + * to readable properties. + * @see BeanMap.Generator#setRequire + */ + public static final int REQUIRE_GETTER = 1; + + /** + * Limit the properties reflected in the key set of the map + * to writable properties. + * @see BeanMap.Generator#setRequire + */ + public static final int REQUIRE_SETTER = 2; + + /** + * Helper method to create a new BeanMap. For finer + * control over the generated instance, use a new instance of + * BeanMap.Generator instead of this static method. + * @param bean the JavaBean underlying the map + * @return a new BeanMap instance + */ + public static BeanMap create(Object bean) { + Generator gen = new Generator(); + gen.setBean(bean); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(BeanMap.class.getName()); + + private static final BeanMapKey KEY_FACTORY = + (BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME); + + interface BeanMapKey { + public Object newInstance(Class type, int require); + } + + private Object bean; + private Class beanClass; + private int require; + + public Generator() { + super(SOURCE); + } + + /** + * Set the bean that the generated map should reflect. The bean may be swapped + * out for another bean of the same type using {@link #setBean}. + * Calling this method overrides any value previously set using {@link #setBeanClass}. + * You must call either this method or {@link #setBeanClass} before {@link #create}. + * @param bean the initial bean + */ + public void setBean(Object bean) { + this.bean = bean; + if (bean != null) + beanClass = bean.getClass(); + } + + /** + * Set the class of the bean that the generated map should support. + * You must call either this method or {@link #setBeanClass} before {@link #create}. + * @param beanClass the class of the bean + */ + public void setBeanClass(Class beanClass) { + this.beanClass = beanClass; + } + + /** + * Limit the properties reflected by the generated map. + * @param require any combination of {@link #REQUIRE_GETTER} and + * {@link #REQUIRE_SETTER}; default is zero (any property allowed) + */ + public void setRequire(int require) { + this.require = require; + } + + protected ClassLoader getDefaultClassLoader() { + return beanClass.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(beanClass); + } + + /** + * Create a new instance of the BeanMap. An existing + * generated class will be reused if possible. + */ + public BeanMap create() { + if (beanClass == null) + throw new IllegalArgumentException("Class of bean unknown"); + setNamePrefix(beanClass.getName()); + return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require)); + } + + public void generateClass(ClassVisitor v) throws Exception { + new BeanMapEmitter(v, getClassName(), beanClass, require); + } + + protected Object firstInstance(Class type) { + return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean); + } + + protected Object nextInstance(Object instance) { + return ((BeanMap)instance).newInstance(bean); + } + } + + /** + * Create a new BeanMap instance using the specified bean. + * This is faster than using the {@link #create} static method. + * @param bean the JavaBean underlying the map + * @return a new BeanMap instance + */ + abstract public BeanMap newInstance(Object bean); + + /** + * Get the type of a property. + * @param name the name of the JavaBean property + * @return the type of the property, or null if the property does not exist + */ + abstract public Class getPropertyType(String name); + + protected Object bean; + + protected BeanMap() { + } + + protected BeanMap(Object bean) { + setBean(bean); + } + + public Object get(Object key) { + return get(bean, key); + } + + public Object put(Object key, Object value) { + return put(bean, key, value); + } + + /** + * Get the property of a bean. This allows a BeanMap + * to be used statically for multiple beans--the bean instance tied to the + * map is ignored and the bean passed to this method is used instead. + * @param bean the bean to query; must be compatible with the type of + * this BeanMap + * @param key must be a String + * @return the current value, or null if there is no matching property + */ + abstract public Object get(Object bean, Object key); + + /** + * Set the property of a bean. This allows a BeanMap + * to be used statically for multiple beans--the bean instance tied to the + * map is ignored and the bean passed to this method is used instead. + * @param key must be a String + * @return the old value, if there was one, or null + */ + abstract public Object put(Object bean, Object key, Object value); + + /** + * Change the underlying bean this map should use. + * @param bean the new JavaBean + * @see #getBean + */ + public void setBean(Object bean) { + this.bean = bean; + } + + /** + * Return the bean currently in use by this map. + * @return the current JavaBean + * @see #setBean + */ + public Object getBean() { + return bean; + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean containsKey(Object key) { + return keySet().contains(key); + } + + public boolean containsValue(Object value) { + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object v = get(it.next()); + if (((value == null) && (v == null)) || (value != null && value.equals(v))) + return true; + } + return false; + } + + public int size() { + return keySet().size(); + } + + public boolean isEmpty() { + return size() == 0; + } + + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + for (Iterator it = t.keySet().iterator(); it.hasNext();) { + Object key = it.next(); + put(key, t.get(key)); + } + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof Map)) { + return false; + } + Map other = (Map)o; + if (size() != other.size()) { + return false; + } + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + if (!other.containsKey(key)) { + return false; + } + Object v1 = get(key); + Object v2 = other.get(key); + if (!((v1 == null) ? v2 == null : v1.equals(v2))) { + return false; + } + } + return true; + } + + public int hashCode() { + int code = 0; + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + Object value = get(key); + code += ((key == null) ? 0 : key.hashCode()) ^ + ((value == null) ? 0 : value.hashCode()); + } + return code; + } + + // TODO: optimize + public Set entrySet() { + HashMap copy = new HashMap(); + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + copy.put(key, get(key)); + } + return Collections.unmodifiableMap(copy).entrySet(); + } + + public Collection values() { + Set keys = keySet(); + List values = new ArrayList(keys.size()); + for (Iterator it = keys.iterator(); it.hasNext();) { + values.add(get(it.next())); + } + return Collections.unmodifiableCollection(values); + } + + /* + * @see java.util.AbstractMap#toString + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append('{'); + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + sb.append(key); + sb.append('='); + sb.append(get(key)); + if (it.hasNext()) { + sb.append(", "); + } + } + sb.append('}'); + return sb.toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMapEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMapEmitter.java new file mode 100644 index 000000000..1284f33ba --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMapEmitter.java @@ -0,0 +1,192 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.beans.*; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +class BeanMapEmitter extends ClassEmitter { + private static final Type BEAN_MAP = + TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BeanMap"); + private static final Type FIXED_KEY_SET = + TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.FixedKeySet"); + private static final Signature CSTRUCT_OBJECT = + TypeUtils.parseConstructor("Object"); + private static final Signature CSTRUCT_STRING_ARRAY = + TypeUtils.parseConstructor("String[]"); + private static final Signature BEAN_MAP_GET = + TypeUtils.parseSignature("Object get(Object, Object)"); + private static final Signature BEAN_MAP_PUT = + TypeUtils.parseSignature("Object put(Object, Object, Object)"); + private static final Signature KEY_SET = + TypeUtils.parseSignature("java.util.Set keySet()"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", BEAN_MAP, new Type[]{ Constants.TYPE_OBJECT }); + private static final Signature GET_PROPERTY_TYPE = + TypeUtils.parseSignature("Class getPropertyType(String)"); + + public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) { + super(v); + + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + EmitUtils.factory_method(this, NEW_INSTANCE); + generateConstructor(); + + Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type)); + Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type)); + Map allProps = new HashMap(); + allProps.putAll(getters); + allProps.putAll(setters); + + if (require != 0) { + for (Iterator it = allProps.keySet().iterator(); it.hasNext();) { + String name = (String)it.next(); + if ((((require & BeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) || + (((require & BeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) { + it.remove(); + getters.remove(name); + setters.remove(name); + } + } + } + generateGet(type, getters); + generatePut(type, setters); + + String[] allNames = getNames(allProps); + generateKeySet(allNames); + generateGetPropertyType(allProps, allNames); + end_class(); + } + + private Map makePropertyMap(PropertyDescriptor[] props) { + Map names = new HashMap(); + for (int i = 0; i < props.length; i++) { + names.put(((PropertyDescriptor)props[i]).getName(), props[i]); + } + return names; + } + + private String[] getNames(Map propertyMap) { + return (String[])propertyMap.keySet().toArray(new String[propertyMap.size()]); + } + + private void generateConstructor() { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null); + e.load_this(); + e.load_arg(0); + e.super_invoke_constructor(CSTRUCT_OBJECT); + e.return_value(); + e.end_method(); + } + + private void generateGet(Class type, final Map getters) { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null); + e.load_arg(0); + e.checkcast(Type.getType(type)); + e.load_arg(1); + e.checkcast(Constants.TYPE_STRING); + EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + PropertyDescriptor pd = (PropertyDescriptor)getters.get(key); + MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod()); + e.invoke(method); + e.box(method.getSignature().getReturnType()); + e.return_value(); + } + public void processDefault() { + e.aconst_null(); + e.return_value(); + } + }); + e.end_method(); + } + + private void generatePut(Class type, final Map setters) { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null); + e.load_arg(0); + e.checkcast(Type.getType(type)); + e.load_arg(1); + e.checkcast(Constants.TYPE_STRING); + EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + PropertyDescriptor pd = (PropertyDescriptor)setters.get(key); + if (pd.getReadMethod() == null) { + e.aconst_null(); + } else { + MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod()); + e.dup(); + e.invoke(read); + e.box(read.getSignature().getReturnType()); + } + e.swap(); // move old value behind bean + e.load_arg(2); // new value + MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod()); + e.unbox(write.getSignature().getArgumentTypes()[0]); + e.invoke(write); + e.return_value(); + } + public void processDefault() { + // fall-through + } + }); + e.aconst_null(); + e.return_value(); + e.end_method(); + } + + private void generateKeySet(String[] allNames) { + // static initializer + declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null); + + CodeEmitter e = begin_static(); + e.new_instance(FIXED_KEY_SET); + e.dup(); + EmitUtils.push_array(e, allNames); + e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY); + e.putfield("keys"); + e.return_value(); + e.end_method(); + + // keySet + e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null); + e.load_this(); + e.getfield("keys"); + e.return_value(); + e.end_method(); + } + + private void generateGetPropertyType(final Map allProps, String[] allNames) { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null); + e.load_arg(0); + EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key); + EmitUtils.load_class(e, Type.getType(pd.getPropertyType())); + e.return_value(); + } + public void processDefault() { + e.aconst_null(); + e.return_value(); + } + }); + e.end_method(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBean.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBean.java new file mode 100644 index 000000000..9d6946ba5 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBean.java @@ -0,0 +1,142 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +/** + * @author Juozas Baliuka + */ +abstract public class BulkBean +{ + private static final BulkBeanKey KEY_FACTORY = + (BulkBeanKey)KeyFactory.create(BulkBeanKey.class); + + interface BulkBeanKey { + public Object newInstance(String target, String[] getters, String[] setters, String[] types); + } + + protected Class target; + protected String[] getters, setters; + protected Class[] types; + + protected BulkBean() { } + + abstract public void getPropertyValues(Object bean, Object[] values); + abstract public void setPropertyValues(Object bean, Object[] values); + + public Object[] getPropertyValues(Object bean) { + Object[] values = new Object[getters.length]; + getPropertyValues(bean, values); + return values; + } + + public Class[] getPropertyTypes() { + return (Class[])types.clone(); + } + + public String[] getGetters() { + return (String[])getters.clone(); + } + + public String[] getSetters() { + return (String[])setters.clone(); + } + + public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) { + Generator gen = new Generator(); + gen.setTarget(target); + gen.setGetters(getters); + gen.setSetters(setters); + gen.setTypes(types); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(BulkBean.class.getName()); + private Class target; + private String[] getters; + private String[] setters; + private Class[] types; + + public Generator() { + super(SOURCE); + } + + public void setTarget(Class target) { + this.target = target; + } + + public void setGetters(String[] getters) { + this.getters = getters; + } + + public void setSetters(String[] setters) { + this.setters = setters; + } + + public void setTypes(Class[] types) { + this.types = types; + } + + protected ClassLoader getDefaultClassLoader() { + return target.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(target); + } + + public BulkBean create() { + setNamePrefix(target.getName()); + String targetClassName = target.getName(); + String[] typeClassNames = ReflectUtils.getNames(types); + Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames); + return (BulkBean)super.create(key); + } + + public void generateClass(ClassVisitor v) throws Exception { + new BulkBeanEmitter(v, getClassName(), target, getters, setters, types); + } + + protected Object firstInstance(Class type) { + BulkBean instance = (BulkBean)ReflectUtils.newInstance(type); + instance.target = target; + + int length = getters.length; + instance.getters = new String[length]; + System.arraycopy(getters, 0, instance.getters, 0, length); + + instance.setters = new String[length]; + System.arraycopy(setters, 0, instance.setters, 0, length); + + instance.types = new Class[types.length]; + System.arraycopy(types, 0, instance.types, 0, types.length); + + return instance; + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanEmitter.java new file mode 100644 index 000000000..9aaf2b985 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanEmitter.java @@ -0,0 +1,156 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +class BulkBeanEmitter extends ClassEmitter { + private static final Signature GET_PROPERTY_VALUES = + TypeUtils.parseSignature("void getPropertyValues(Object, Object[])"); + private static final Signature SET_PROPERTY_VALUES = + TypeUtils.parseSignature("void setPropertyValues(Object, Object[])"); + private static final Signature CSTRUCT_EXCEPTION = + TypeUtils.parseConstructor("Throwable, int"); + private static final Type BULK_BEAN = + TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BulkBean"); + private static final Type BULK_BEAN_EXCEPTION = + TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BulkBeanException"); + + public BulkBeanEmitter(ClassVisitor v, + String className, + Class target, + String[] getterNames, + String[] setterNames, + Class[] types) { + super(v); + + Method[] getters = new Method[getterNames.length]; + Method[] setters = new Method[setterNames.length]; + validate(target, getterNames, setterNames, types, getters, setters); + + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BULK_BEAN, null, Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + generateGet(target, getters); + generateSet(target, setters); + end_class(); + } + + private void generateGet(final Class target, final Method[] getters) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_VALUES, null); + if (getters.length >= 0) { + e.load_arg(0); + e.checkcast(Type.getType(target)); + Local bean = e.make_local(); + e.store_local(bean); + for (int i = 0; i < getters.length; i++) { + if (getters[i] != null) { + MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]); + e.load_arg(1); + e.push(i); + e.load_local(bean); + e.invoke(getter); + e.box(getter.getSignature().getReturnType()); + e.aastore(); + } + } + } + e.return_value(); + e.end_method(); + } + + private void generateSet(final Class target, final Method[] setters) { + // setPropertyValues + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_PROPERTY_VALUES, null); + if (setters.length > 0) { + Local index = e.make_local(Type.INT_TYPE); + e.push(0); + e.store_local(index); + e.load_arg(0); + e.checkcast(Type.getType(target)); + e.load_arg(1); + Block handler = e.begin_block(); + int lastIndex = 0; + for (int i = 0; i < setters.length; i++) { + if (setters[i] != null) { + MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]); + int diff = i - lastIndex; + if (diff > 0) { + e.iinc(index, diff); + lastIndex = i; + } + e.dup2(); + e.aaload(i); + e.unbox(setter.getSignature().getArgumentTypes()[0]); + e.invoke(setter); + } + } + handler.end(); + e.return_value(); + e.catch_exception(handler, Constants.TYPE_THROWABLE); + e.new_instance(BULK_BEAN_EXCEPTION); + e.dup_x1(); + e.swap(); + e.load_local(index); + e.invoke_constructor(BULK_BEAN_EXCEPTION, CSTRUCT_EXCEPTION); + e.athrow(); + } else { + e.return_value(); + } + e.end_method(); + } + + private static void validate(Class target, + String[] getters, + String[] setters, + Class[] types, + Method[] getters_out, + Method[] setters_out) { + int i = -1; + if (setters.length != types.length || getters.length != types.length) { + throw new BulkBeanException("accessor array length must be equal type array length", i); + } + try { + for (i = 0; i < types.length; i++) { + if (getters[i] != null) { + Method method = ReflectUtils.findDeclaredMethod(target, getters[i], null); + if (method.getReturnType() != types[i]) { + throw new BulkBeanException("Specified type " + types[i] + + " does not match declared type " + method.getReturnType(), i); + } + if (Modifier.isPrivate(method.getModifiers())) { + throw new BulkBeanException("Property is private", i); + } + getters_out[i] = method; + } + if (setters[i] != null) { + Method method = ReflectUtils.findDeclaredMethod(target, setters[i], new Class[]{ types[i] }); + if (Modifier.isPrivate(method.getModifiers()) ){ + throw new BulkBeanException("Property is private", i); + } + setters_out[i] = method; + } + } + } catch (NoSuchMethodException e) { + throw new BulkBeanException("Cannot find specified property", i); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanException.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanException.java new file mode 100644 index 000000000..34c8983b2 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanException.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import com.fr.third.net.sf.cglib.core.CodeGenerationException; + +public class BulkBeanException extends RuntimeException +{ + private int index; + private Throwable cause; + + public BulkBeanException(String message, int index) { + super(message); + this.index = index; + } + + public BulkBeanException(Throwable cause, int index) { + super(cause.getMessage()); + this.index = index; + this.cause = cause; + } + + public int getIndex() { + return index; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/FixedKeySet.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/FixedKeySet.java new file mode 100644 index 000000000..1fac58f5d --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/FixedKeySet.java @@ -0,0 +1,36 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.util.*; + +public /* need it for class loading */ class FixedKeySet extends AbstractSet { + private Set set; + private int size; + + public FixedKeySet(String[] keys) { + size = keys.length; + set = Collections.unmodifiableSet(new HashSet(Arrays.asList(keys))); + } + + public Iterator iterator() { + return set.iterator(); + } + + public int size() { + return size; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/ImmutableBean.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/ImmutableBean.java new file mode 100644 index 000000000..1424a1e28 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/ImmutableBean.java @@ -0,0 +1,128 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; +/** + * @author Chris Nokleberg + */ +public class ImmutableBean +{ + private static final Type ILLEGAL_STATE_EXCEPTION = + TypeUtils.parseType("IllegalStateException"); + private static final Signature CSTRUCT_OBJECT = + TypeUtils.parseConstructor("Object"); + private static final Class[] OBJECT_CLASSES = { Object.class }; + private static final String FIELD_NAME = "CGLIB$RWBean"; + + private ImmutableBean() { + } + + public static Object create(Object bean) { + Generator gen = new Generator(); + gen.setBean(bean); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(ImmutableBean.class.getName()); + private Object bean; + private Class target; + + public Generator() { + super(SOURCE); + } + + public void setBean(Object bean) { + this.bean = bean; + target = bean.getClass(); + } + + protected ClassLoader getDefaultClassLoader() { + return target.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(target); + } + + public Object create() { + String name = target.getName(); + setNamePrefix(name); + return super.create(name); + } + + public void generateClass(ClassVisitor v) { + Type targetType = Type.getType(target); + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + targetType, + null, + Constants.SOURCE_FILE); + + ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null); + + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + e.load_arg(0); + e.checkcast(targetType); + e.putfield(FIELD_NAME); + e.return_value(); + e.end_method(); + + PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target); + Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false); + Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true); + + for (int i = 0; i < getters.length; i++) { + MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]); + e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC); + e.load_this(); + e.getfield(FIELD_NAME); + e.invoke(getter); + e.return_value(); + e.end_method(); + } + + for (int i = 0; i < setters.length; i++) { + MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]); + e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC); + e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable"); + e.end_method(); + } + + ce.end_class(); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean }); + } + + // TODO: optimize + protected Object nextInstance(Object instance) { + return firstInstance(instance.getClass()); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/AbstractClassGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/AbstractClassGenerator.java new file mode 100644 index 000000000..928004a9a --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/AbstractClassGenerator.java @@ -0,0 +1,353 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.net.sf.cglib.core.internal.Function; +import com.fr.third.net.sf.cglib.core.internal.LoadingCache; +import com.fr.third.org.objectweb.asm.ClassReader; + +import java.lang.ref.WeakReference; +import java.security.ProtectionDomain; +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.WeakHashMap; + +/** + * Abstract class for all code-generating CGLIB utilities. + * In addition to caching generated classes for performance, it provides hooks for + * customizing the ClassLoader, name of the generated class, and transformations + * applied before generation. + */ +abstract public class AbstractClassGenerator +implements ClassGenerator +{ + private static final ThreadLocal CURRENT = new ThreadLocal(); + + private static volatile Map CACHE = new WeakHashMap(); + + private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE; + private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE; + private Source source; + private ClassLoader classLoader; + private String namePrefix; + private Object key; + private boolean useCache = true; + private String className; + private boolean attemptLoad; + + protected static class ClassLoaderData { + private final Set reservedClassNames = new HashSet(); + + /** + * {@link AbstractClassGenerator} here holds "cache key" (e.g. {@link com.fr.third.net.sf.cglib.proxy.Enhancer} + * configuration), and the value is the generated class plus some additional values + * (see {@link #unwrapCachedValue(Object)}. + *

The generated classes can be reused as long as their classloader is reachable.

+ *

Note: the only way to access a class is to find it through generatedClasses cache, thus + * the key should not expire as long as the class itself is alive (its classloader is alive).

+ */ + private final LoadingCache generatedClasses; + + /** + * Note: ClassLoaderData object is stored as a value of {@code WeakHashMap} thus + * this classLoader reference should be weak otherwise it would make classLoader strongly reachable + * and alive forever. + * Reference queue is not required since the cleanup is handled by {@link WeakHashMap}. + */ + private final WeakReference classLoader; + + private final Predicate uniqueNamePredicate = new Predicate() { + public boolean evaluate(Object name) { + return reservedClassNames.contains(name); + } + }; + + private static final Function GET_KEY = new Function() { + public Object apply(AbstractClassGenerator gen) { + return gen.key; + } + }; + + public ClassLoaderData(ClassLoader classLoader) { + if (classLoader == null) { + throw new IllegalArgumentException("classLoader == null is not yet supported"); + } + this.classLoader = new WeakReference(classLoader); + Function load = + new Function() { + public Object apply(AbstractClassGenerator gen) { + Class klass = gen.generate(ClassLoaderData.this); + return gen.wrapCachedClass(klass); + } + }; + generatedClasses = new LoadingCache(GET_KEY, load); + } + + public ClassLoader getClassLoader() { + return classLoader.get(); + } + + public void reserveName(String name) { + reservedClassNames.add(name); + } + + public Predicate getUniqueNamePredicate() { + return uniqueNamePredicate; + } + + public Object get(AbstractClassGenerator gen, boolean useCache) { + if (!useCache) { + return gen.generate(ClassLoaderData.this); + } else { + Object cachedValue = generatedClasses.get(gen); + return gen.unwrapCachedValue(cachedValue); + } + } + } + + protected T wrapCachedClass(Class klass) { + return (T) new WeakReference(klass); + } + + protected Object unwrapCachedValue(T cached) { + return ((WeakReference) cached).get(); + } + + protected static class Source { + String name; + public Source(String name) { + this.name = name; + } + } + + protected AbstractClassGenerator(Source source) { + this.source = source; + } + + protected void setNamePrefix(String namePrefix) { + this.namePrefix = namePrefix; + } + + final protected String getClassName() { + return className; + } + + private void setClassName(String className) { + this.className = className; + } + + private String generateClassName(Predicate nameTestPredicate) { + return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate); + } + + /** + * Set the ClassLoader in which the class will be generated. + * Concrete subclasses of AbstractClassGenerator (such as Enhancer) + * will try to choose an appropriate default if this is unset. + *

+ * Classes are cached per-ClassLoader using a WeakHashMap, to allow + * the generated classes to be removed when the associated loader is garbage collected. + * @param classLoader the loader to generate the new class with, or null to use the default + */ + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + /** + * Override the default naming policy. + * @see DefaultNamingPolicy + * @param namingPolicy the custom policy, or null to use the default + */ + public void setNamingPolicy(NamingPolicy namingPolicy) { + if (namingPolicy == null) + namingPolicy = DefaultNamingPolicy.INSTANCE; + this.namingPolicy = namingPolicy; + } + + /** + * @see #setNamingPolicy + */ + public NamingPolicy getNamingPolicy() { + return namingPolicy; + } + + /** + * Whether use and update the static cache of generated classes + * for a class with the same properties. Default is true. + */ + public void setUseCache(boolean useCache) { + this.useCache = useCache; + } + + /** + * @see #setUseCache + */ + public boolean getUseCache() { + return useCache; + } + + /** + * If set, CGLIB will attempt to load classes from the specified + * ClassLoader before generating them. Because generated + * class names are not guaranteed to be unique, the default is false. + */ + public void setAttemptLoad(boolean attemptLoad) { + this.attemptLoad = attemptLoad; + } + + public boolean getAttemptLoad() { + return attemptLoad; + } + + /** + * Set the strategy to use to create the bytecode from this generator. + * By default an instance of {@see DefaultGeneratorStrategy} is used. + */ + public void setStrategy(GeneratorStrategy strategy) { + if (strategy == null) + strategy = DefaultGeneratorStrategy.INSTANCE; + this.strategy = strategy; + } + + /** + * @see #setStrategy + */ + public GeneratorStrategy getStrategy() { + return strategy; + } + + /** + * Used internally by CGLIB. Returns the AbstractClassGenerator + * that is being used to generate a class in the current thread. + */ + public static AbstractClassGenerator getCurrent() { + return (AbstractClassGenerator)CURRENT.get(); + } + + public ClassLoader getClassLoader() { + ClassLoader t = classLoader; + if (t == null) { + t = getDefaultClassLoader(); + } + if (t == null) { + t = getClass().getClassLoader(); + } + if (t == null) { + t = Thread.currentThread().getContextClassLoader(); + } + if (t == null) { + throw new IllegalStateException("Cannot determine classloader"); + } + return t; + } + + abstract protected ClassLoader getDefaultClassLoader(); + + /** + * Returns the protection domain to use when defining the class. + *

+ * Default implementation returns null for using a default protection domain. Sub-classes may + * override to use a more specific protection domain. + *

+ * + * @return the protection domain (null for using a default) + */ + protected ProtectionDomain getProtectionDomain() { + return null; + } + + protected Object create(Object key) { + try { + ClassLoader loader = getClassLoader(); + Map cache = CACHE; + ClassLoaderData data = cache.get(loader); + if (data == null) { + synchronized (AbstractClassGenerator.class) { + cache = CACHE; + data = cache.get(loader); + if (data == null) { + Map newCache = new WeakHashMap(cache); + data = new ClassLoaderData(loader); + newCache.put(loader, data); + CACHE = newCache; + } + } + } + this.key = key; + Object obj = data.get(this, getUseCache()); + if (obj instanceof Class) { + return firstInstance((Class) obj); + } + return nextInstance(obj); + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + + protected Class generate(ClassLoaderData data) { + Class gen; + Object save = CURRENT.get(); + CURRENT.set(this); + try { + ClassLoader classLoader = data.getClassLoader(); + if (classLoader == null) { + throw new IllegalStateException("ClassLoader is null while trying to define class " + + getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " + + "Please file an issue at cglib's issue tracker."); + } + synchronized (classLoader) { + String name = generateClassName(data.getUniqueNamePredicate()); + data.reserveName(name); + this.setClassName(name); + } + if (attemptLoad) { + try { + gen = classLoader.loadClass(getClassName()); + return gen; + } catch (ClassNotFoundException e) { + // ignore + } + } + byte[] b = strategy.generate(this); + String className = ClassNameReader.getClassName(new ClassReader(b)); + ProtectionDomain protectionDomain = getProtectionDomain(); + synchronized (classLoader) { // just in case + if (protectionDomain == null) { + gen = ReflectUtils.defineClass(className, b, classLoader); + } else { + gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain); + } + } + return gen; + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } finally { + CURRENT.set(save); + } + } + + abstract protected Object firstInstance(Class type) throws Exception; + abstract protected Object nextInstance(Object instance) throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Block.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Block.java new file mode 100644 index 000000000..44b8ea62b --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Block.java @@ -0,0 +1,49 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Label; + +public class Block +{ + private CodeEmitter e; + private Label start; + private Label end; + + public Block(CodeEmitter e) { + this.e = e; + start = e.mark(); + } + + public CodeEmitter getCodeEmitter() { + return e; + } + + public void end() { + if (end != null) { + throw new IllegalStateException("end of label already set"); + } + end = e.mark(); + } + + public Label getStart() { + return start; + } + + public Label getEnd() { + return end; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassEmitter.java new file mode 100644 index 000000000..fd156dfd5 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassEmitter.java @@ -0,0 +1,282 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.net.sf.cglib.transform.ClassTransformer; + +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.FieldVisitor; +import com.fr.third.org.objectweb.asm.MethodVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; +import com.fr.third.org.objectweb.asm.Type; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class ClassEmitter extends ClassTransformer { + private ClassInfo classInfo; + private Map fieldInfo; + + private static int hookCounter; + private MethodVisitor rawStaticInit; + private CodeEmitter staticInit; + private CodeEmitter staticHook; + private Signature staticHookSig; + + public ClassEmitter(ClassVisitor cv) { + setTarget(cv); + } + + public ClassEmitter() { + super(Opcodes.ASM6); + } + + public void setTarget(ClassVisitor cv) { + this.cv = cv; + fieldInfo = new HashMap(); + + // just to be safe + staticInit = staticHook = null; + staticHookSig = null; + } + + synchronized private static int getNextHook() { + return ++hookCounter; + } + + public ClassInfo getClassInfo() { + return classInfo; + } + + public void begin_class(int version, final int access, String className, final Type superType, final Type[] interfaces, String source) { + final Type classType = Type.getType("L" + className.replace('.', '/') + ";"); + classInfo = new ClassInfo() { + public Type getType() { + return classType; + } + public Type getSuperType() { + return (superType != null) ? superType : Constants.TYPE_OBJECT; + } + public Type[] getInterfaces() { + return interfaces; + } + public int getModifiers() { + return access; + } + }; + cv.visit(version, + access, + classInfo.getType().getInternalName(), + null, + classInfo.getSuperType().getInternalName(), + TypeUtils.toInternalNames(interfaces)); + if (source != null) + cv.visitSource(source, null); + init(); + } + + public CodeEmitter getStaticHook() { + if (TypeUtils.isInterface(getAccess())) { + throw new IllegalStateException("static hook is invalid for this class"); + } + if (staticHook == null) { + staticHookSig = new Signature("CGLIB$STATICHOOK" + getNextHook(), "()V"); + staticHook = begin_method(Constants.ACC_STATIC, + staticHookSig, + null); + if (staticInit != null) { + staticInit.invoke_static_this(staticHookSig); + } + } + return staticHook; + } + + protected void init() { + } + + public int getAccess() { + return classInfo.getModifiers(); + } + + public Type getClassType() { + return classInfo.getType(); + } + + public Type getSuperType() { + return classInfo.getSuperType(); + } + + public void end_class() { + if (staticHook != null && staticInit == null) { + // force creation of static init + begin_static(); + } + if (staticInit != null) { + staticHook.return_value(); + staticHook.end_method(); + rawStaticInit.visitInsn(Constants.RETURN); + rawStaticInit.visitMaxs(0, 0); + staticInit = staticHook = null; + staticHookSig = null; + } + cv.visitEnd(); + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + if (classInfo == null) + throw new IllegalStateException("classInfo is null! " + this); + MethodVisitor v = cv.visitMethod(access, + sig.getName(), + sig.getDescriptor(), + null, + TypeUtils.toInternalNames(exceptions)); + if (sig.equals(Constants.SIG_STATIC) && !TypeUtils.isInterface(getAccess())) { + rawStaticInit = v; + MethodVisitor wrapped = new MethodVisitor(Opcodes.ASM6, v) { + public void visitMaxs(int maxStack, int maxLocals) { + // ignore + } + public void visitInsn(int insn) { + if (insn != Constants.RETURN) { + super.visitInsn(insn); + } + } + }; + staticInit = new CodeEmitter(this, wrapped, access, sig, exceptions); + if (staticHook == null) { + // force static hook creation + getStaticHook(); + } else { + staticInit.invoke_static_this(staticHookSig); + } + return staticInit; + } else if (sig.equals(staticHookSig)) { + return new CodeEmitter(this, v, access, sig, exceptions) { + public boolean isStaticHook() { + return true; + } + }; + } else { + return new CodeEmitter(this, v, access, sig, exceptions); + } + } + + public CodeEmitter begin_static() { + return begin_method(Constants.ACC_STATIC, Constants.SIG_STATIC, null); + } + + public void declare_field(int access, String name, Type type, Object value) { + FieldInfo existing = (FieldInfo)fieldInfo.get(name); + FieldInfo info = new FieldInfo(access, name, type, value); + if (existing != null) { + if (!info.equals(existing)) { + throw new IllegalArgumentException("Field \"" + name + "\" has been declared differently"); + } + } else { + fieldInfo.put(name, info); + cv.visitField(access, name, type.getDescriptor(), null, value); + } + } + + // TODO: make public? + boolean isFieldDeclared(String name) { + return fieldInfo.get(name) != null; + } + + FieldInfo getFieldInfo(String name) { + FieldInfo field = (FieldInfo)fieldInfo.get(name); + if (field == null) { + throw new IllegalArgumentException("Field " + name + " is not declared in " + getClassType().getClassName()); + } + return field; + } + + static class FieldInfo { + int access; + String name; + Type type; + Object value; + + public FieldInfo(int access, String name, Type type, Object value) { + this.access = access; + this.name = name; + this.type = type; + this.value = value; + } + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof FieldInfo)) + return false; + FieldInfo other = (FieldInfo)o; + if (access != other.access || + !name.equals(other.name) || + !type.equals(other.type)) { + return false; + } + if ((value == null) ^ (other.value == null)) + return false; + if (value != null && !value.equals(other.value)) + return false; + return true; + } + + public int hashCode() { + return access ^ name.hashCode() ^ type.hashCode() ^ ((value == null) ? 0 : value.hashCode()); + } + } + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + begin_class(version, + access, + name.replace('/', '.'), + TypeUtils.fromInternalName(superName), + TypeUtils.fromInternalNames(interfaces), + null); // TODO + } + + public void visitEnd() { + end_class(); + } + + public FieldVisitor visitField(int access, + String name, + String desc, + String signature, + Object value) { + declare_field(access, name, Type.getType(desc), value); + return null; // TODO + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return begin_method(access, + new Signature(name, desc), + TypeUtils.fromInternalNames(exceptions)); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassGenerator.java new file mode 100644 index 000000000..e191dda61 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassGenerator.java @@ -0,0 +1,22 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.ClassVisitor; + +public interface ClassGenerator { + void generateClass(ClassVisitor v) throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassInfo.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassInfo.java new file mode 100644 index 000000000..5f71d22fe --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassInfo.java @@ -0,0 +1,47 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; + +abstract public class ClassInfo { + + protected ClassInfo() { + } + + abstract public Type getType(); + abstract public Type getSuperType(); + abstract public Type[] getInterfaces(); + abstract public int getModifiers(); + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof ClassInfo)) + return false; + return getType().equals(((ClassInfo)o).getType()); + } + + public int hashCode() { + return getType().hashCode(); + } + + public String toString() { + // TODO: include modifiers, superType, interfaces + return getType().getClassName(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassNameReader.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassNameReader.java new file mode 100644 index 000000000..66fb320fa --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassNameReader.java @@ -0,0 +1,63 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.ClassReader; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +import java.util.*; + +// TODO: optimize (ClassReader buffers entire class before accept) +public class ClassNameReader { + private ClassNameReader() { + } + + private static final EarlyExitException EARLY_EXIT = new EarlyExitException(); + private static class EarlyExitException extends RuntimeException { } + + public static String getClassName(ClassReader r) { + + return getClassInfo(r)[0]; + + } + + public static String[] getClassInfo(ClassReader r) { + final List array = new ArrayList(); + try { + r.accept(new ClassVisitor(Opcodes.ASM6, null) { + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + array.add( name.replace('/', '.') ); + if(superName != null){ + array.add( superName.replace('/', '.') ); + } + for(int i = 0; i < interfaces.length; i++ ){ + array.add( interfaces[i].replace('/', '.') ); + } + + throw EARLY_EXIT; + } + }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } catch (EarlyExitException e) { } + + return (String[])array.toArray( new String[]{} ); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassesKey.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassesKey.java new file mode 100644 index 000000000..5edaa58ae --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassesKey.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +public class ClassesKey { + private static final Key FACTORY = (Key)KeyFactory.create(Key.class); + + interface Key { + Object newInstance(Object[] array); + } + + private ClassesKey() { + } + + public static Object create(Object[] array) { + return FACTORY.newInstance(classNames(array)); + } + + private static String[] classNames(Object[] objects) { + if (objects == null) { + return null; + } + String[] classNames = new String[objects.length]; + for (int i = 0; i < objects.length; i++) { + Object object = objects[i]; + if (object != null) { + Class aClass = object.getClass(); + classNames[i] = aClass == null ? null : aClass.getName(); + } + } + return classNames; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeEmitter.java new file mode 100644 index 000000000..5ac119198 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeEmitter.java @@ -0,0 +1,864 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.io.*; +import java.util.*; +import com.fr.third.org.objectweb.asm.*; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class CodeEmitter extends LocalVariablesSorter { + private static final Signature BOOLEAN_VALUE = + TypeUtils.parseSignature("boolean booleanValue()"); + private static final Signature CHAR_VALUE = + TypeUtils.parseSignature("char charValue()"); + private static final Signature LONG_VALUE = + TypeUtils.parseSignature("long longValue()"); + private static final Signature DOUBLE_VALUE = + TypeUtils.parseSignature("double doubleValue()"); + private static final Signature FLOAT_VALUE = + TypeUtils.parseSignature("float floatValue()"); + private static final Signature INT_VALUE = + TypeUtils.parseSignature("int intValue()"); + private static final Signature CSTRUCT_NULL = + TypeUtils.parseConstructor(""); + private static final Signature CSTRUCT_STRING = + TypeUtils.parseConstructor("String"); + + public static final int ADD = Constants.IADD; + public static final int MUL = Constants.IMUL; + public static final int XOR = Constants.IXOR; + public static final int USHR = Constants.IUSHR; + public static final int SUB = Constants.ISUB; + public static final int DIV = Constants.IDIV; + public static final int NEG = Constants.INEG; + public static final int REM = Constants.IREM; + public static final int AND = Constants.IAND; + public static final int OR = Constants.IOR; + + public static final int GT = Constants.IFGT; + public static final int LT = Constants.IFLT; + public static final int GE = Constants.IFGE; + public static final int LE = Constants.IFLE; + public static final int NE = Constants.IFNE; + public static final int EQ = Constants.IFEQ; + + private ClassEmitter ce; + private State state; + + private static class State + extends MethodInfo + { + ClassInfo classInfo; + int access; + Signature sig; + Type[] argumentTypes; + int localOffset; + Type[] exceptionTypes; + + State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes) { + this.classInfo = classInfo; + this.access = access; + this.sig = sig; + this.exceptionTypes = exceptionTypes; + localOffset = TypeUtils.isStatic(access) ? 0 : 1; + argumentTypes = sig.getArgumentTypes(); + } + + public ClassInfo getClassInfo() { + return classInfo; + } + + public int getModifiers() { + return access; + } + + public Signature getSignature() { + return sig; + } + + public Type[] getExceptionTypes() { + return exceptionTypes; + } + + public Attribute getAttribute() { + // TODO + return null; + } + } + + CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes) { + super(access, sig.getDescriptor(), mv); + this.ce = ce; + state = new State(ce.getClassInfo(), access, sig, exceptionTypes); + } + + public CodeEmitter(CodeEmitter wrap) { + super(wrap); + this.ce = wrap.ce; + this.state = wrap.state; + } + + public boolean isStaticHook() { + return false; + } + + public Signature getSignature() { + return state.sig; + } + + public Type getReturnType() { + return state.sig.getReturnType(); + } + + public MethodInfo getMethodInfo() { + return state; + } + + public ClassEmitter getClassEmitter() { + return ce; + } + + public void end_method() { + visitMaxs(0, 0); + } + + public Block begin_block() { + return new Block(this); + } + + public void catch_exception(Block block, Type exception) { + if (block.getEnd() == null) { + throw new IllegalStateException("end of block is unset"); + } + mv.visitTryCatchBlock(block.getStart(), + block.getEnd(), + mark(), + exception.getInternalName()); + } + + public void goTo(Label label) { mv.visitJumpInsn(Constants.GOTO, label); } + public void ifnull(Label label) { mv.visitJumpInsn(Constants.IFNULL, label); } + public void ifnonnull(Label label) { mv.visitJumpInsn(Constants.IFNONNULL, label); } + + public void if_jump(int mode, Label label) { + mv.visitJumpInsn(mode, label); + } + + public void if_icmp(int mode, Label label) { + if_cmp(Type.INT_TYPE, mode, label); + } + + public void if_cmp(Type type, int mode, Label label) { + int intOp = -1; + int jumpmode = mode; + switch (mode) { + case GE: jumpmode = LT; break; + case LE: jumpmode = GT; break; + } + switch (type.getSort()) { + case Type.LONG: + mv.visitInsn(Constants.LCMP); + break; + case Type.DOUBLE: + mv.visitInsn(Constants.DCMPG); + break; + case Type.FLOAT: + mv.visitInsn(Constants.FCMPG); + break; + case Type.ARRAY: + case Type.OBJECT: + switch (mode) { + case EQ: + mv.visitJumpInsn(Constants.IF_ACMPEQ, label); + return; + case NE: + mv.visitJumpInsn(Constants.IF_ACMPNE, label); + return; + } + throw new IllegalArgumentException("Bad comparison for type " + type); + default: + switch (mode) { + case EQ: intOp = Constants.IF_ICMPEQ; break; + case NE: intOp = Constants.IF_ICMPNE; break; + case GE: swap(); /* fall through */ + case LT: intOp = Constants.IF_ICMPLT; break; + case LE: swap(); /* fall through */ + case GT: intOp = Constants.IF_ICMPGT; break; + } + mv.visitJumpInsn(intOp, label); + return; + } + if_jump(jumpmode, label); + } + + public void pop() { mv.visitInsn(Constants.POP); } + public void pop2() { mv.visitInsn(Constants.POP2); } + public void dup() { mv.visitInsn(Constants.DUP); } + public void dup2() { mv.visitInsn(Constants.DUP2); } + public void dup_x1() { mv.visitInsn(Constants.DUP_X1); } + public void dup_x2() { mv.visitInsn(Constants.DUP_X2); } + public void dup2_x1() { mv.visitInsn(Constants.DUP2_X1); } + public void dup2_x2() { mv.visitInsn(Constants.DUP2_X2); } + public void swap() { mv.visitInsn(Constants.SWAP); } + public void aconst_null() { mv.visitInsn(Constants.ACONST_NULL); } + + public void swap(Type prev, Type type) { + if (type.getSize() == 1) { + if (prev.getSize() == 1) { + swap(); // same as dup_x1(), pop(); + } else { + dup_x2(); + pop(); + } + } else { + if (prev.getSize() == 1) { + dup2_x1(); + pop2(); + } else { + dup2_x2(); + pop2(); + } + } + } + + public void monitorenter() { mv.visitInsn(Constants.MONITORENTER); } + public void monitorexit() { mv.visitInsn(Constants.MONITOREXIT); } + + public void math(int op, Type type) { mv.visitInsn(type.getOpcode(op)); } + + public void array_load(Type type) { mv.visitInsn(type.getOpcode(Constants.IALOAD)); } + public void array_store(Type type) { mv.visitInsn(type.getOpcode(Constants.IASTORE)); } + + /** + * Casts from one primitive numeric type to another + */ + public void cast_numeric(Type from, Type to) { + if (from != to) { + if (from == Type.DOUBLE_TYPE) { + if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Constants.D2F); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Constants.D2L); + } else { + mv.visitInsn(Constants.D2I); + cast_numeric(Type.INT_TYPE, to); + } + } else if (from == Type.FLOAT_TYPE) { + if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Constants.F2D); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Constants.F2L); + } else { + mv.visitInsn(Constants.F2I); + cast_numeric(Type.INT_TYPE, to); + } + } else if (from == Type.LONG_TYPE) { + if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Constants.L2D); + } else if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Constants.L2F); + } else { + mv.visitInsn(Constants.L2I); + cast_numeric(Type.INT_TYPE, to); + } + } else { + if (to == Type.BYTE_TYPE) { + mv.visitInsn(Constants.I2B); + } else if (to == Type.CHAR_TYPE) { + mv.visitInsn(Constants.I2C); + } else if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Constants.I2D); + } else if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Constants.I2F); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Constants.I2L); + } else if (to == Type.SHORT_TYPE) { + mv.visitInsn(Constants.I2S); + } + } + } + } + + public void push(int i) { + if (i < -1) { + mv.visitLdcInsn(new Integer(i)); + } else if (i <= 5) { + mv.visitInsn(TypeUtils.ICONST(i)); + } else if (i <= Byte.MAX_VALUE) { + mv.visitIntInsn(Constants.BIPUSH, i); + } else if (i <= Short.MAX_VALUE) { + mv.visitIntInsn(Constants.SIPUSH, i); + } else { + mv.visitLdcInsn(new Integer(i)); + } + } + + public void push(long value) { + if (value == 0L || value == 1L) { + mv.visitInsn(TypeUtils.LCONST(value)); + } else { + mv.visitLdcInsn(new Long(value)); + } + } + + public void push(float value) { + if (value == 0f || value == 1f || value == 2f) { + mv.visitInsn(TypeUtils.FCONST(value)); + } else { + mv.visitLdcInsn(new Float(value)); + } + } + public void push(double value) { + if (value == 0d || value == 1d) { + mv.visitInsn(TypeUtils.DCONST(value)); + } else { + mv.visitLdcInsn(new Double(value)); + } + } + + public void push(String value) { + mv.visitLdcInsn(value); + } + + public void newarray() { + newarray(Constants.TYPE_OBJECT); + } + + public void newarray(Type type) { + if (TypeUtils.isPrimitive(type)) { + mv.visitIntInsn(Constants.NEWARRAY, TypeUtils.NEWARRAY(type)); + } else { + emit_type(Constants.ANEWARRAY, type); + } + } + + public void arraylength() { + mv.visitInsn(Constants.ARRAYLENGTH); + } + + public void load_this() { + if (TypeUtils.isStatic(state.access)) { + throw new IllegalStateException("no 'this' pointer within static method"); + } + mv.visitVarInsn(Constants.ALOAD, 0); + } + + /** + * Pushes all of the arguments of the current method onto the stack. + */ + public void load_args() { + load_args(0, state.argumentTypes.length); + } + + /** + * Pushes the specified argument of the current method onto the stack. + * @param index the zero-based index into the argument list + */ + public void load_arg(int index) { + load_local(state.argumentTypes[index], + state.localOffset + skipArgs(index)); + } + + // zero-based (see load_this) + public void load_args(int fromArg, int count) { + int pos = state.localOffset + skipArgs(fromArg); + for (int i = 0; i < count; i++) { + Type t = state.argumentTypes[fromArg + i]; + load_local(t, pos); + pos += t.getSize(); + } + } + + private int skipArgs(int numArgs) { + int amount = 0; + for (int i = 0; i < numArgs; i++) { + amount += state.argumentTypes[i].getSize(); + } + return amount; + } + + private void load_local(Type t, int pos) { + // TODO: make t == null ok? + mv.visitVarInsn(t.getOpcode(Constants.ILOAD), pos); + } + + private void store_local(Type t, int pos) { + // TODO: make t == null ok? + mv.visitVarInsn(t.getOpcode(Constants.ISTORE), pos); + } + + public void iinc(Local local, int amount) { + mv.visitIincInsn(local.getIndex(), amount); + } + + public void store_local(Local local) { + store_local(local.getType(), local.getIndex()); + } + + public void load_local(Local local) { + load_local(local.getType(), local.getIndex()); + } + + public void return_value() { + mv.visitInsn(state.sig.getReturnType().getOpcode(Constants.IRETURN)); + } + + public void getfield(String name) { + ClassEmitter.FieldInfo info = ce.getFieldInfo(name); + int opcode = TypeUtils.isStatic(info.access) ? Constants.GETSTATIC : Constants.GETFIELD; + emit_field(opcode, ce.getClassType(), name, info.type); + } + + public void putfield(String name) { + ClassEmitter.FieldInfo info = ce.getFieldInfo(name); + int opcode = TypeUtils.isStatic(info.access) ? Constants.PUTSTATIC : Constants.PUTFIELD; + emit_field(opcode, ce.getClassType(), name, info.type); + } + + public void super_getfield(String name, Type type) { + emit_field(Constants.GETFIELD, ce.getSuperType(), name, type); + } + + public void super_putfield(String name, Type type) { + emit_field(Constants.PUTFIELD, ce.getSuperType(), name, type); + } + + public void super_getstatic(String name, Type type) { + emit_field(Constants.GETSTATIC, ce.getSuperType(), name, type); + } + + public void super_putstatic(String name, Type type) { + emit_field(Constants.PUTSTATIC, ce.getSuperType(), name, type); + } + + public void getfield(Type owner, String name, Type type) { + emit_field(Constants.GETFIELD, owner, name, type); + } + + public void putfield(Type owner, String name, Type type) { + emit_field(Constants.PUTFIELD, owner, name, type); + } + + public void getstatic(Type owner, String name, Type type) { + emit_field(Constants.GETSTATIC, owner, name, type); + } + + public void putstatic(Type owner, String name, Type type) { + emit_field(Constants.PUTSTATIC, owner, name, type); + } + + // package-protected for EmitUtils, try to fix + void emit_field(int opcode, Type ctype, String name, Type ftype) { + mv.visitFieldInsn(opcode, + ctype.getInternalName(), + name, + ftype.getDescriptor()); + } + + public void super_invoke() { + super_invoke(state.sig); + } + + public void super_invoke(Signature sig) { + emit_invoke(Constants.INVOKESPECIAL, ce.getSuperType(), sig); + } + + public void invoke_constructor(Type type) { + invoke_constructor(type, CSTRUCT_NULL); + } + + public void super_invoke_constructor() { + invoke_constructor(ce.getSuperType()); + } + + public void invoke_constructor_this() { + invoke_constructor(ce.getClassType()); + } + + private void emit_invoke(int opcode, Type type, Signature sig) { + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME) && + ((opcode == Constants.INVOKEVIRTUAL) || + (opcode == Constants.INVOKESTATIC))) { + // TODO: error + } + mv.visitMethodInsn(opcode, + type.getInternalName(), + sig.getName(), + sig.getDescriptor(), + opcode == Opcodes.INVOKEINTERFACE); + } + + public void invoke_interface(Type owner, Signature sig) { + emit_invoke(Constants.INVOKEINTERFACE, owner, sig); + } + + public void invoke_virtual(Type owner, Signature sig) { + emit_invoke(Constants.INVOKEVIRTUAL, owner, sig); + } + + public void invoke_static(Type owner, Signature sig) { + emit_invoke(Constants.INVOKESTATIC, owner, sig); + } + + public void invoke_virtual_this(Signature sig) { + invoke_virtual(ce.getClassType(), sig); + } + + public void invoke_static_this(Signature sig) { + invoke_static(ce.getClassType(), sig); + } + + public void invoke_constructor(Type type, Signature sig) { + emit_invoke(Constants.INVOKESPECIAL, type, sig); + } + + public void invoke_constructor_this(Signature sig) { + invoke_constructor(ce.getClassType(), sig); + } + + public void super_invoke_constructor(Signature sig) { + invoke_constructor(ce.getSuperType(), sig); + } + + public void new_instance_this() { + new_instance(ce.getClassType()); + } + + public void new_instance(Type type) { + emit_type(Constants.NEW, type); + } + + private void emit_type(int opcode, Type type) { + String desc; + if (TypeUtils.isArray(type)) { + desc = type.getDescriptor(); + } else { + desc = type.getInternalName(); + } + mv.visitTypeInsn(opcode, desc); + } + + public void aaload(int index) { + push(index); + aaload(); + } + + public void aaload() { mv.visitInsn(Constants.AALOAD); } + public void aastore() { mv.visitInsn(Constants.AASTORE); } + public void athrow() { mv.visitInsn(Constants.ATHROW); } + + public Label make_label() { + return new Label(); + } + + public Local make_local() { + return make_local(Constants.TYPE_OBJECT); + } + + public Local make_local(Type type) { + return new Local(newLocal(type.getSize()), type); + } + + public void checkcast_this() { + checkcast(ce.getClassType()); + } + + public void checkcast(Type type) { + if (!type.equals(Constants.TYPE_OBJECT)) { + emit_type(Constants.CHECKCAST, type); + } + } + + public void instance_of(Type type) { + emit_type(Constants.INSTANCEOF, type); + } + + public void instance_of_this() { + instance_of(ce.getClassType()); + } + + public void process_switch(int[] keys, ProcessSwitchCallback callback) { + float density; + if (keys.length == 0) { + density = 0; + } else { + density = (float)keys.length / (keys[keys.length - 1] - keys[0] + 1); + } + process_switch(keys, callback, density >= 0.5f); + } + + public void process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable) { + if (!isSorted(keys)) + throw new IllegalArgumentException("keys to switch must be sorted ascending"); + Label def = make_label(); + Label end = make_label(); + + try { + if (keys.length > 0) { + int len = keys.length; + int min = keys[0]; + int max = keys[len - 1]; + int range = max - min + 1; + + if (useTable) { + Label[] labels = new Label[range]; + Arrays.fill(labels, def); + for (int i = 0; i < len; i++) { + labels[keys[i] - min] = make_label(); + } + mv.visitTableSwitchInsn(min, max, def, labels); + for (int i = 0; i < range; i++) { + Label label = labels[i]; + if (label != def) { + mark(label); + callback.processCase(i + min, end); + } + } + } else { + Label[] labels = new Label[len]; + for (int i = 0; i < len; i++) { + labels[i] = make_label(); + } + mv.visitLookupSwitchInsn(def, keys, labels); + for (int i = 0; i < len; i++) { + mark(labels[i]); + callback.processCase(keys[i], end); + } + } + } + + mark(def); + callback.processDefault(); + mark(end); + + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + + private static boolean isSorted(int[] keys) { + for (int i = 1; i < keys.length; i++) { + if (keys[i] < keys[i - 1]) + return false; + } + return true; + } + + public void mark(Label label) { + mv.visitLabel(label); + } + + Label mark() { + Label label = make_label(); + mv.visitLabel(label); + return label; + } + + public void push(boolean value) { + push(value ? 1 : 0); + } + + /** + * Toggles the integer on the top of the stack from 1 to 0 or vice versa + */ + public void not() { + push(1); + math(XOR, Type.INT_TYPE); + } + + public void throw_exception(Type type, String msg) { + new_instance(type); + dup(); + push(msg); + invoke_constructor(type, CSTRUCT_STRING); + athrow(); + } + + /** + * If the argument is a primitive class, replaces the primitive value + * on the top of the stack with the wrapped (Object) equivalent. For + * example, char -> Character. + * If the class is Void, a null is pushed onto the stack instead. + * @param type the class indicating the current type of the top stack value + */ + public void box(Type type) { + if (TypeUtils.isPrimitive(type)) { + if (type == Type.VOID_TYPE) { + aconst_null(); + } else { + Type boxed = TypeUtils.getBoxedType(type); + new_instance(boxed); + if (type.getSize() == 2) { + // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o + dup_x2(); + dup_x2(); + pop(); + } else { + // p -> po -> opo -> oop -> o + dup_x1(); + swap(); + } + invoke_constructor(boxed, new Signature(Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{ type })); + } + } + } + + /** + * If the argument is a primitive class, replaces the object + * on the top of the stack with the unwrapped (primitive) + * equivalent. For example, Character -> char. + * @param type the class indicating the desired type of the top stack value + * @return true if the value was unboxed + */ + public void unbox(Type type) { + Type t = Constants.TYPE_NUMBER; + Signature sig = null; + switch (type.getSort()) { + case Type.VOID: + return; + case Type.CHAR: + t = Constants.TYPE_CHARACTER; + sig = CHAR_VALUE; + break; + case Type.BOOLEAN: + t = Constants.TYPE_BOOLEAN; + sig = BOOLEAN_VALUE; + break; + case Type.DOUBLE: + sig = DOUBLE_VALUE; + break; + case Type.FLOAT: + sig = FLOAT_VALUE; + break; + case Type.LONG: + sig = LONG_VALUE; + break; + case Type.INT: + case Type.SHORT: + case Type.BYTE: + sig = INT_VALUE; + } + + if (sig == null) { + checkcast(type); + } else { + checkcast(t); + invoke_virtual(t, sig); + } + } + + /** + * Allocates and fills an Object[] array with the arguments to the + * current method. Primitive values are inserted as their boxed + * (Object) equivalents. + */ + public void create_arg_array() { + /* generates: + Object[] args = new Object[]{ arg1, new Integer(arg2) }; + */ + + push(state.argumentTypes.length); + newarray(); + for (int i = 0; i < state.argumentTypes.length; i++) { + dup(); + push(i); + load_arg(i); + box(state.argumentTypes[i]); + aastore(); + } + } + + + /** + * Pushes a zero onto the stack if the argument is a primitive class, or a null otherwise. + */ + public void zero_or_null(Type type) { + if (TypeUtils.isPrimitive(type)) { + switch (type.getSort()) { + case Type.DOUBLE: + push(0d); + break; + case Type.LONG: + push(0L); + break; + case Type.FLOAT: + push(0f); + break; + case Type.VOID: + aconst_null(); + default: + push(0); + } + } else { + aconst_null(); + } + } + + /** + * Unboxes the object on the top of the stack. If the object is null, the + * unboxed primitive value becomes zero. + */ + public void unbox_or_zero(Type type) { + if (TypeUtils.isPrimitive(type)) { + if (type != Type.VOID_TYPE) { + Label nonNull = make_label(); + Label end = make_label(); + dup(); + ifnonnull(nonNull); + pop(); + zero_or_null(type); + goTo(end); + mark(nonNull); + unbox(type); + mark(end); + } + } else { + checkcast(type); + } + } + + public void visitMaxs(int maxStack, int maxLocals) { + if (!TypeUtils.isAbstract(state.access)) { + mv.visitMaxs(0, 0); + } + } + + public void invoke(MethodInfo method, Type virtualType) { + ClassInfo classInfo = method.getClassInfo(); + Type type = classInfo.getType(); + Signature sig = method.getSignature(); + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { + invoke_constructor(type, sig); + } else if (TypeUtils.isInterface(classInfo.getModifiers())) { + invoke_interface(type, sig); + } else if (TypeUtils.isStatic(method.getModifiers())) { + invoke_static(type, sig); + } else { + invoke_virtual(virtualType, sig); + } + } + + public void invoke(MethodInfo method) { + invoke(method, method.getClassInfo().getType()); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeGenerationException.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeGenerationException.java new file mode 100644 index 000000000..78ec6d3a4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeGenerationException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +/** + * @version $Id: CodeGenerationException.java,v 1.3 2004/06/24 21:15:21 herbyderby Exp $ + */ +public class CodeGenerationException extends RuntimeException { + private Throwable cause; + + public CodeGenerationException(Throwable cause) { + super(cause.getClass().getName() + "-->" + cause.getMessage()); + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/CollectionUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CollectionUtils.java new file mode 100644 index 000000000..d9424f339 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CollectionUtils.java @@ -0,0 +1,76 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.util.*; +import java.lang.reflect.Array; + +/** + * @author Chris Nokleberg + * @version $Id: CollectionUtils.java,v 1.7 2004/06/24 21:15:21 herbyderby Exp $ + */ +public class CollectionUtils { + private CollectionUtils() { } + + public static Map bucket(Collection c, Transformer t) { + Map buckets = new HashMap(); + for (Iterator it = c.iterator(); it.hasNext();) { + Object value = (Object)it.next(); + Object key = t.transform(value); + List bucket = (List)buckets.get(key); + if (bucket == null) { + buckets.put(key, bucket = new LinkedList()); + } + bucket.add(value); + } + return buckets; + } + + public static void reverse(Map source, Map target) { + for (Iterator it = source.keySet().iterator(); it.hasNext();) { + Object key = it.next(); + target.put(source.get(key), key); + } + } + + public static Collection filter(Collection c, Predicate p) { + Iterator it = c.iterator(); + while (it.hasNext()) { + if (!p.evaluate(it.next())) { + it.remove(); + } + } + return c; + } + + public static List transform(Collection c, Transformer t) { + List result = new ArrayList(c.size()); + for (Iterator it = c.iterator(); it.hasNext();) { + result.add(t.transform(it.next())); + } + return result; + } + + public static Map getIndexMap(List list) { + Map indexes = new HashMap(); + int index = 0; + for (Iterator it = list.iterator(); it.hasNext();) { + indexes.put(it.next(), new Integer(index++)); + } + return indexes; + } +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Constants.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Constants.java new file mode 100644 index 000000000..08da2d0cb --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Constants.java @@ -0,0 +1,68 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Juozas Baliuka baliuka@mwm.lt + * @version $Id: Constants.java,v 1.21 2006/03/05 02:43:19 herbyderby Exp $ + */ +public interface Constants extends com.fr.third.org.objectweb.asm.Opcodes { + public static final Class[] EMPTY_CLASS_ARRAY = {}; + public static final Type[] TYPES_EMPTY = {}; + + public static final Signature SIG_STATIC = + TypeUtils.parseSignature("void ()"); + + public static final Type TYPE_OBJECT_ARRAY = TypeUtils.parseType("Object[]"); + public static final Type TYPE_CLASS_ARRAY = TypeUtils.parseType("Class[]"); + public static final Type TYPE_STRING_ARRAY = TypeUtils.parseType("String[]"); + + public static final Type TYPE_OBJECT = TypeUtils.parseType("Object"); + public static final Type TYPE_CLASS = TypeUtils.parseType("Class"); + public static final Type TYPE_CLASS_LOADER = TypeUtils.parseType("ClassLoader"); + public static final Type TYPE_CHARACTER = TypeUtils.parseType("Character"); + public static final Type TYPE_BOOLEAN = TypeUtils.parseType("Boolean"); + public static final Type TYPE_DOUBLE = TypeUtils.parseType("Double"); + public static final Type TYPE_FLOAT = TypeUtils.parseType("Float"); + public static final Type TYPE_LONG = TypeUtils.parseType("Long"); + public static final Type TYPE_INTEGER = TypeUtils.parseType("Integer"); + public static final Type TYPE_SHORT = TypeUtils.parseType("Short"); + public static final Type TYPE_BYTE = TypeUtils.parseType("Byte"); + public static final Type TYPE_NUMBER = TypeUtils.parseType("Number"); + public static final Type TYPE_STRING = TypeUtils.parseType("String"); + public static final Type TYPE_THROWABLE = TypeUtils.parseType("Throwable"); + public static final Type TYPE_BIG_INTEGER = TypeUtils.parseType("java.math.BigInteger"); + public static final Type TYPE_BIG_DECIMAL = TypeUtils.parseType("java.math.BigDecimal"); + public static final Type TYPE_STRING_BUFFER = TypeUtils.parseType("StringBuffer"); + public static final Type TYPE_RUNTIME_EXCEPTION = TypeUtils.parseType("RuntimeException"); + public static final Type TYPE_ERROR = TypeUtils.parseType("Error"); + public static final Type TYPE_SYSTEM = TypeUtils.parseType("System"); + public static final Type TYPE_SIGNATURE = TypeUtils.parseType("com.fr.third.net.sf.cglib.core.Signature"); + public static final Type TYPE_TYPE = Type.getType(Type.class); + + public static final String CONSTRUCTOR_NAME = ""; + public static final String STATIC_NAME = ""; + public static final String SOURCE_FILE = ""; + public static final String SUID_FIELD_NAME = "serialVersionUID"; + + public static final int PRIVATE_FINAL_STATIC = ACC_PRIVATE | ACC_FINAL | ACC_STATIC; + + public static final int SWITCH_STYLE_TRIE = 0; + public static final int SWITCH_STYLE_HASH = 1; + public static final int SWITCH_STYLE_HASHONLY = 2; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Converter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Converter.java new file mode 100644 index 000000000..6b8cbffeb --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Converter.java @@ -0,0 +1,20 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +public interface Converter { + Object convert(Object value, Class target, Object context); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Customizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Customizer.java new file mode 100644 index 000000000..e4e7df7d4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Customizer.java @@ -0,0 +1,28 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +/** + * Customizes key types for {@link KeyFactory} when building equals, hashCode, and toString. + * For customization of field types, use {@link FieldTypeCustomizer} + * + * @see KeyFactory#CLASS_BY_NAME + */ +public interface Customizer extends KeyFactoryCustomizer { + void customize(CodeEmitter e, Type type); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DebuggingClassWriter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DebuggingClassWriter.java new file mode 100644 index 000000000..a6706c4b6 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DebuggingClassWriter.java @@ -0,0 +1,114 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.ClassWriter; +import com.fr.third.org.objectweb.asm.ClassReader; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +import java.io.*; +import java.lang.reflect.Constructor; + +public class DebuggingClassWriter extends ClassVisitor { + + public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation"; + + private static String debugLocation; + private static Constructor traceCtor; + + private String className; + private String superName; + + static { + debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY); + if (debugLocation != null) { + System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'"); + try { + Class clazz = Class.forName("com.fr.third.org.objectweb.asm.util.TraceClassVisitor"); + traceCtor = clazz.getConstructor(new Class[]{ClassVisitor.class, PrintWriter.class}); + } catch (Throwable ignore) { + } + } + } + + public DebuggingClassWriter(int flags) { + super(Opcodes.ASM6, new ClassWriter(flags)); + } + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + className = name.replace('/', '.'); + this.superName = superName.replace('/', '.'); + super.visit(version, access, name, signature, superName, interfaces); + } + + public String getClassName() { + return className; + } + + public String getSuperName() { + return superName; + } + + public byte[] toByteArray() { + + return (byte[]) java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + + + byte[] b = ((ClassWriter) DebuggingClassWriter.super.cv).toByteArray(); + if (debugLocation != null) { + String dirs = className.replace('.', File.separatorChar); + try { + new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs(); + + File file = new File(new File(debugLocation), dirs + ".class"); + OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); + try { + out.write(b); + } finally { + out.close(); + } + + if (traceCtor != null) { + file = new File(new File(debugLocation), dirs + ".asm"); + out = new BufferedOutputStream(new FileOutputStream(file)); + try { + ClassReader cr = new ClassReader(b); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(out)); + ClassVisitor tcv = (ClassVisitor)traceCtor.newInstance(new Object[]{null, pw}); + cr.accept(tcv, 0); + pw.flush(); + } finally { + out.close(); + } + } + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + return b; + } + }); + + } + } diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultGeneratorStrategy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultGeneratorStrategy.java new file mode 100644 index 000000000..2ec6b3016 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultGeneratorStrategy.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.ClassWriter; + +public class DefaultGeneratorStrategy implements GeneratorStrategy { + public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy(); + + public byte[] generate(ClassGenerator cg) throws Exception { + DebuggingClassWriter cw = getClassVisitor(); + transform(cg).generateClass(cw); + return transform(cw.toByteArray()); + } + + protected DebuggingClassWriter getClassVisitor() throws Exception { + return new DebuggingClassWriter(ClassWriter.COMPUTE_FRAMES); + } + + protected final ClassWriter getClassWriter() { + // Cause compile / runtime errors for people who implemented the old + // interface without using @Override + throw new UnsupportedOperationException("You are calling " + + "getClassWriter, which no longer exists in this cglib version."); + } + + protected byte[] transform(byte[] b) throws Exception { + return b; + } + + protected ClassGenerator transform(ClassGenerator cg) throws Exception { + return cg; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultNamingPolicy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultNamingPolicy.java new file mode 100644 index 000000000..6609ef9fd --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultNamingPolicy.java @@ -0,0 +1,71 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.util.Set; + +/** + * The default policy used by {@link AbstractClassGenerator}. + * Generates names such as + *

com.fr.third.net.sf.cglib.Foo$$EnhancerByCGLIB$$38272841

+ * This is composed of a prefix based on the name of the superclass, a fixed + * string incorporating the CGLIB class responsible for generation, and a + * hashcode derived from the parameters used to create the object. If the same + * name has been previously been used in the same ClassLoader, a + * suffix is added to ensure uniqueness. + */ +public class DefaultNamingPolicy implements NamingPolicy { + public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy(); + + /** + * This allows to test collisions of {@code key.hashCode()}. + */ + private final static boolean STRESS_HASH_CODE = Boolean.getBoolean("com.fr.third.net.sf.cglib.test.stressHashCodes"); + + public String getClassName(String prefix, String source, Object key, Predicate names) { + if (prefix == null) { + prefix = "com.fr.third.net.sf.cglib.empty.Object"; + } else if (prefix.startsWith("java")) { + prefix = "$" + prefix; + } + String base = + prefix + "$$" + + source.substring(source.lastIndexOf('.') + 1) + + getTag() + "$$" + + Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode()); + String attempt = base; + int index = 2; + while (names.evaluate(attempt)) + attempt = base + "_" + index++; + return attempt; + } + + /** + * Returns a string which is incorporated into every generated class name. + * By default returns "ByCGLIB" + */ + protected String getTag() { + return "ByCGLIB"; + } + + public int hashCode() { + return getTag().hashCode(); + } + + public boolean equals(Object o) { + return (o instanceof DefaultNamingPolicy) && ((DefaultNamingPolicy) o).getTag().equals(getTag()); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DuplicatesPredicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DuplicatesPredicate.java new file mode 100644 index 000000000..67c2204ec --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DuplicatesPredicate.java @@ -0,0 +1,27 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.lang.reflect.Method; +import java.util.*; + +public class DuplicatesPredicate implements Predicate { + private Set unique = new HashSet(); + + public boolean evaluate(Object arg) { + return unique.add(MethodWrapper.create((Method)arg)); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/EmitUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/EmitUtils.java new file mode 100644 index 000000000..ac1812b6d --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/EmitUtils.java @@ -0,0 +1,960 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; + +import com.fr.third.net.sf.cglib.core.internal.CustomizerRegistry; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +public class EmitUtils { + private static final Signature CSTRUCT_NULL = + TypeUtils.parseConstructor(""); + private static final Signature CSTRUCT_THROWABLE = + TypeUtils.parseConstructor("Throwable"); + + private static final Signature GET_NAME = + TypeUtils.parseSignature("String getName()"); + private static final Signature HASH_CODE = + TypeUtils.parseSignature("int hashCode()"); + private static final Signature EQUALS = + TypeUtils.parseSignature("boolean equals(Object)"); + private static final Signature STRING_LENGTH = + TypeUtils.parseSignature("int length()"); + private static final Signature STRING_CHAR_AT = + TypeUtils.parseSignature("char charAt(int)"); + private static final Signature FOR_NAME = + TypeUtils.parseSignature("Class forName(String)"); + private static final Signature DOUBLE_TO_LONG_BITS = + TypeUtils.parseSignature("long doubleToLongBits(double)"); + private static final Signature FLOAT_TO_INT_BITS = + TypeUtils.parseSignature("int floatToIntBits(float)"); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Signature APPEND_STRING = + TypeUtils.parseSignature("StringBuffer append(String)"); + private static final Signature APPEND_INT = + TypeUtils.parseSignature("StringBuffer append(int)"); + private static final Signature APPEND_DOUBLE = + TypeUtils.parseSignature("StringBuffer append(double)"); + private static final Signature APPEND_FLOAT = + TypeUtils.parseSignature("StringBuffer append(float)"); + private static final Signature APPEND_CHAR = + TypeUtils.parseSignature("StringBuffer append(char)"); + private static final Signature APPEND_LONG = + TypeUtils.parseSignature("StringBuffer append(long)"); + private static final Signature APPEND_BOOLEAN = + TypeUtils.parseSignature("StringBuffer append(boolean)"); + private static final Signature LENGTH = + TypeUtils.parseSignature("int length()"); + private static final Signature SET_LENGTH = + TypeUtils.parseSignature("void setLength(int)"); + private static final Signature GET_DECLARED_METHOD = + TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])"); + + + + public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}"); + + private EmitUtils() { + } + + public static void factory_method(ClassEmitter ce, Signature sig) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null); + e.new_instance_this(); + e.dup(); + e.load_args(); + e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes())); + e.return_value(); + e.end_method(); + } + + public static void null_constructor(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null); + e.load_this(); + e.super_invoke_constructor(); + e.return_value(); + e.end_method(); + } + + /** + * Process an array on the stack. Assumes the top item on the stack + * is an array of the specified type. For each element in the array, + * puts the element on the stack and triggers the callback. + * @param type the type of the array (type.isArray() must be true) + * @param callback the callback triggered for each element + */ + public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) { + Type componentType = TypeUtils.getComponentType(type); + Local array = e.make_local(); + Local loopvar = e.make_local(Type.INT_TYPE); + Label loopbody = e.make_label(); + Label checkloop = e.make_label(); + e.store_local(array); + e.push(0); + e.store_local(loopvar); + e.goTo(checkloop); + + e.mark(loopbody); + e.load_local(array); + e.load_local(loopvar); + e.array_load(componentType); + callback.processElement(componentType); + e.iinc(loopvar, 1); + + e.mark(checkloop); + e.load_local(loopvar); + e.load_local(array); + e.arraylength(); + e.if_icmp(e.LT, loopbody); + } + + /** + * Process two arrays on the stack in parallel. Assumes the top two items on the stack + * are arrays of the specified class. The arrays must be the same length. For each pair + * of elements in the arrays, puts the pair on the stack and triggers the callback. + * @param type the type of the arrays (type.isArray() must be true) + * @param callback the callback triggered for each pair of elements + */ + public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) { + Type componentType = TypeUtils.getComponentType(type); + Local array1 = e.make_local(); + Local array2 = e.make_local(); + Local loopvar = e.make_local(Type.INT_TYPE); + Label loopbody = e.make_label(); + Label checkloop = e.make_label(); + e.store_local(array1); + e.store_local(array2); + e.push(0); + e.store_local(loopvar); + e.goTo(checkloop); + + e.mark(loopbody); + e.load_local(array1); + e.load_local(loopvar); + e.array_load(componentType); + e.load_local(array2); + e.load_local(loopvar); + e.array_load(componentType); + callback.processElement(componentType); + e.iinc(loopvar, 1); + + e.mark(checkloop); + e.load_local(loopvar); + e.load_local(array1); + e.arraylength(); + e.if_icmp(e.LT, loopbody); + } + + public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) { + try { + switch (switchStyle) { + case Constants.SWITCH_STYLE_TRIE: + string_switch_trie(e, strings, callback); + break; + case Constants.SWITCH_STYLE_HASH: + string_switch_hash(e, strings, callback, false); + break; + case Constants.SWITCH_STYLE_HASHONLY: + string_switch_hash(e, strings, callback, true); + break; + default: + throw new IllegalArgumentException("unknown switch style " + switchStyle); + } + } catch (RuntimeException ex) { + throw ex; + } catch (Error ex) { + throw ex; + } catch (Exception ex) { + throw new CodeGenerationException(ex); + } + } + + private static void string_switch_trie(final CodeEmitter e, + String[] strings, + final ObjectSwitchCallback callback) throws Exception { + final Label def = e.make_label(); + final Label end = e.make_label(); + final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() { + public Object transform(Object value) { + return new Integer(((String)value).length()); + } + }); + e.dup(); + e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH); + e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label ignore_end) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + stringSwitchHelper(e, bucket, callback, def, end, 0); + } + public void processDefault() { + e.goTo(def); + } + }); + e.mark(def); + e.pop(); + callback.processDefault(); + e.mark(end); + } + + private static void stringSwitchHelper(final CodeEmitter e, + List strings, + final ObjectSwitchCallback callback, + final Label def, + final Label end, + final int index) throws Exception { + final int len = ((String)strings.get(0)).length(); + final Map buckets = CollectionUtils.bucket(strings, new Transformer() { + public Object transform(Object value) { + return new Integer(((String)value).charAt(index)); + } + }); + e.dup(); + e.push(index); + e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT); + e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label ignore_end) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + if (index + 1 == len) { + e.pop(); + callback.processCase(bucket.get(0), end); + } else { + stringSwitchHelper(e, bucket, callback, def, end, index + 1); + } + } + public void processDefault() { + e.goTo(def); + } + }); + } + + static int[] getSwitchKeys(Map buckets) { + int[] keys = new int[buckets.size()]; + int index = 0; + for (Iterator it = buckets.keySet().iterator(); it.hasNext();) { + keys[index++] = ((Integer)it.next()).intValue(); + } + Arrays.sort(keys); + return keys; + } + + private static void string_switch_hash(final CodeEmitter e, + final String[] strings, + final ObjectSwitchCallback callback, + final boolean skipEquals) throws Exception { + final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() { + public Object transform(Object value) { + return new Integer(value.hashCode()); + } + }); + final Label def = e.make_label(); + final Label end = e.make_label(); + e.dup(); + e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE); + e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label ignore_end) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + Label next = null; + if (skipEquals && bucket.size() == 1) { + if (skipEquals) + e.pop(); + callback.processCase((String)bucket.get(0), end); + } else { + for (Iterator it = bucket.iterator(); it.hasNext();) { + String string = (String)it.next(); + if (next != null) { + e.mark(next); + } + if (it.hasNext()) { + e.dup(); + } + e.push(string); + e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); + if (it.hasNext()) { + e.if_jump(e.EQ, next = e.make_label()); + e.pop(); + } else { + e.if_jump(e.EQ, def); + } + callback.processCase(string, end); + } + } + } + public void processDefault() { + e.pop(); + } + }); + e.mark(def); + callback.processDefault(); + e.mark(end); + } + + public static void load_class_this(CodeEmitter e) { + load_class_helper(e, e.getClassEmitter().getClassType()); + } + + public static void load_class(CodeEmitter e, Type type) { + if (TypeUtils.isPrimitive(type)) { + if (type == Type.VOID_TYPE) { + throw new IllegalArgumentException("cannot load void type"); + } + e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS); + } else { + load_class_helper(e, type); + } + } + + private static void load_class_helper(CodeEmitter e, final Type type) { + if (e.isStaticHook()) { + // have to fall back on non-optimized load + e.push(TypeUtils.emulateClassGetName(type)); + e.invoke_static(Constants.TYPE_CLASS, FOR_NAME); + } else { + ClassEmitter ce = e.getClassEmitter(); + String typeName = TypeUtils.emulateClassGetName(type); + + // TODO: can end up with duplicated field names when using chained transformers; incorporate static hook # somehow + String fieldName = "CGLIB$load_class$" + TypeUtils.escapeType(typeName); + if (!ce.isFieldDeclared(fieldName)) { + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null); + CodeEmitter hook = ce.getStaticHook(); + hook.push(typeName); + hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME); + hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS); + } + e.getfield(fieldName); + } + } + + public static void push_array(CodeEmitter e, Object[] array) { + e.push(array.length); + e.newarray(Type.getType(remapComponentType(array.getClass().getComponentType()))); + for (int i = 0; i < array.length; i++) { + e.dup(); + e.push(i); + push_object(e, array[i]); + e.aastore(); + } + } + + private static Class remapComponentType(Class componentType) { + if (componentType.equals(Type.class)) + return Class.class; + return componentType; + } + + public static void push_object(CodeEmitter e, Object obj) { + if (obj == null) { + e.aconst_null(); + } else { + Class type = obj.getClass(); + if (type.isArray()) { + push_array(e, (Object[])obj); + } else if (obj instanceof String) { + e.push((String)obj); + } else if (obj instanceof Type) { + load_class(e, (Type)obj); + } else if (obj instanceof Class) { + load_class(e, Type.getType((Class)obj)); + } else if (obj instanceof BigInteger) { + e.new_instance(Constants.TYPE_BIG_INTEGER); + e.dup(); + e.push(obj.toString()); + e.invoke_constructor(Constants.TYPE_BIG_INTEGER); + } else if (obj instanceof BigDecimal) { + e.new_instance(Constants.TYPE_BIG_DECIMAL); + e.dup(); + e.push(obj.toString()); + e.invoke_constructor(Constants.TYPE_BIG_DECIMAL); + } else { + throw new IllegalArgumentException("unknown type: " + obj.getClass()); + } + } + } + + /** + * @deprecated use {@link #hash_code(CodeEmitter, Type, int, CustomizerRegistry)} instead + */ + @Deprecated + public static void hash_code(CodeEmitter e, Type type, int multiplier, final Customizer customizer) { + hash_code(e, type, multiplier, CustomizerRegistry.singleton(customizer)); + } + + public static void hash_code(CodeEmitter e, Type type, int multiplier, final CustomizerRegistry registry) { + if (TypeUtils.isArray(type)) { + hash_array(e, type, multiplier, registry); + } else { + e.swap(Type.INT_TYPE, type); + e.push(multiplier); + e.math(e.MUL, Type.INT_TYPE); + e.swap(type, Type.INT_TYPE); + if (TypeUtils.isPrimitive(type)) { + hash_primitive(e, type); + } else { + hash_object(e, type, registry); + } + e.math(e.ADD, Type.INT_TYPE); + } + } + + private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final CustomizerRegistry registry) { + Label skip = e.make_label(); + Label end = e.make_label(); + e.dup(); + e.ifnull(skip); + EmitUtils.process_array(e, type, new ProcessArrayCallback() { + public void processElement(Type type) { + hash_code(e, type, multiplier, registry); + } + }); + e.goTo(end); + e.mark(skip); + e.pop(); + e.mark(end); + } + + private static void hash_object(CodeEmitter e, Type type, CustomizerRegistry registry) { + // (f == null) ? 0 : f.hashCode(); + Label skip = e.make_label(); + Label end = e.make_label(); + e.dup(); + e.ifnull(skip); + boolean customHashCode = false; + for (HashCodeCustomizer customizer : registry.get(HashCodeCustomizer.class)) { + if (customizer.customize(e, type)) { + customHashCode = true; + break; + } + } + if (!customHashCode) { + for (Customizer customizer : registry.get(Customizer.class)) { + customizer.customize(e, type); + } + e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE); + } + e.goTo(end); + e.mark(skip); + e.pop(); + e.push(0); + e.mark(end); + } + + private static void hash_primitive(CodeEmitter e, Type type) { + switch (type.getSort()) { + case Type.BOOLEAN: + // f ? 0 : 1 + e.push(1); + e.math(e.XOR, Type.INT_TYPE); + break; + case Type.FLOAT: + // Float.floatToIntBits(f) + e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS); + break; + case Type.DOUBLE: + // Double.doubleToLongBits(f), hash_code(Long.TYPE) + e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS); + // fall through + case Type.LONG: + hash_long(e); + } + } + + private static void hash_long(CodeEmitter e) { + // (int)(f ^ (f >>> 32)) + e.dup2(); + e.push(32); + e.math(e.USHR, Type.LONG_TYPE); + e.math(e.XOR, Type.LONG_TYPE); + e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE); + } + +// public static void not_equals(CodeEmitter e, Type type, Label notEquals) { +// not_equals(e, type, notEquals, null); +// } + + /** + * @deprecated use {@link #not_equals(CodeEmitter, Type, Label, CustomizerRegistry)} instead + */ + @Deprecated + public static void not_equals(CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) { + not_equals(e, type, notEquals, CustomizerRegistry.singleton(customizer)); + } + + /** + * Branches to the specified label if the top two items on the stack + * are not equal. The items must both be of the specified + * class. Equality is determined by comparing primitive values + * directly and by invoking the equals method for + * Objects. Arrays are recursively processed in the same manner. + */ + public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final CustomizerRegistry registry) { + (new ProcessArrayCallback() { + public void processElement(Type type) { + not_equals_helper(e, type, notEquals, registry, this); + } + }).processElement(type); + } + + private static void not_equals_helper(CodeEmitter e, + Type type, + Label notEquals, + CustomizerRegistry registry, + ProcessArrayCallback callback) { + if (TypeUtils.isPrimitive(type)) { + e.if_cmp(type, e.NE, notEquals); + } else { + Label end = e.make_label(); + nullcmp(e, notEquals, end); + if (TypeUtils.isArray(type)) { + Label checkContents = e.make_label(); + e.dup2(); + e.arraylength(); + e.swap(); + e.arraylength(); + e.if_icmp(e.EQ, checkContents); + e.pop2(); + e.goTo(notEquals); + e.mark(checkContents); + EmitUtils.process_arrays(e, type, callback); + } else { + List customizers = registry.get(Customizer.class); + if (!customizers.isEmpty()) { + for (Customizer customizer : customizers) { + customizer.customize(e, type); + } + e.swap(); + for (Customizer customizer : customizers) { + customizer.customize(e, type); + } + } + e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); + e.if_jump(e.EQ, notEquals); + } + e.mark(end); + } + } + + /** + * If both objects on the top of the stack are non-null, does nothing. + * If one is null, or both are null, both are popped off and execution + * branches to the respective label. + * @param oneNull label to branch to if only one of the objects is null + * @param bothNull label to branch to if both of the objects are null + */ + private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) { + e.dup2(); + Label nonNull = e.make_label(); + Label oneNullHelper = e.make_label(); + Label end = e.make_label(); + e.ifnonnull(nonNull); + e.ifnonnull(oneNullHelper); + e.pop2(); + e.goTo(bothNull); + + e.mark(nonNull); + e.ifnull(oneNullHelper); + e.goTo(end); + + e.mark(oneNullHelper); + e.pop2(); + e.goTo(oneNull); + + e.mark(end); + } + + /* + public static void to_string(CodeEmitter e, + Type type, + ArrayDelimiters delims, + CustomizerRegistry registry) { + e.new_instance(Constants.TYPE_STRING_BUFFER); + e.dup(); + e.invoke_constructor(Constants.TYPE_STRING_BUFFER); + e.swap(); + append_string(e, type, delims, registry); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING); + } + */ + + /** + * @deprecated use {@link #append_string(CodeEmitter, Type, ArrayDelimiters, CustomizerRegistry)} instead + */ + @Deprecated + public static void append_string(final CodeEmitter e, + Type type, + final ArrayDelimiters delims, + final Customizer customizer) { + append_string(e, type, delims, CustomizerRegistry.singleton(customizer)); + } + + public static void append_string(final CodeEmitter e, + Type type, + final ArrayDelimiters delims, + final CustomizerRegistry registry) { + final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS; + ProcessArrayCallback callback = new ProcessArrayCallback() { + public void processElement(Type type) { + append_string_helper(e, type, d, registry, this); + e.push(d.inside); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + }; + append_string_helper(e, type, d, registry, callback); + } + + private static void append_string_helper(CodeEmitter e, + Type type, + ArrayDelimiters delims, + CustomizerRegistry registry, + ProcessArrayCallback callback) { + Label skip = e.make_label(); + Label end = e.make_label(); + if (TypeUtils.isPrimitive(type)) { + switch (type.getSort()) { + case Type.INT: + case Type.SHORT: + case Type.BYTE: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT); + break; + case Type.DOUBLE: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE); + break; + case Type.FLOAT: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT); + break; + case Type.LONG: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG); + break; + case Type.BOOLEAN: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN); + break; + case Type.CHAR: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR); + break; + } + } else if (TypeUtils.isArray(type)) { + e.dup(); + e.ifnull(skip); + e.swap(); + if (delims != null && delims.before != null && !"".equals(delims.before)) { + e.push(delims.before); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + e.swap(); + } + EmitUtils.process_array(e, type, callback); + shrinkStringBuffer(e, 2); + if (delims != null && delims.after != null && !"".equals(delims.after)) { + e.push(delims.after); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + } else { + e.dup(); + e.ifnull(skip); + for (Customizer customizer : registry.get(Customizer.class)) { + customizer.customize(e, type); + } + e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + e.goTo(end); + e.mark(skip); + e.pop(); + e.push("null"); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + e.mark(end); + } + + private static void shrinkStringBuffer(CodeEmitter e, int amt) { + e.dup(); + e.dup(); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH); + e.push(amt); + e.math(e.SUB, Type.INT_TYPE); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH); + } + + public static class ArrayDelimiters { + private String before; + private String inside; + private String after; + + public ArrayDelimiters(String before, String inside, String after) { + this.before = before; + this.inside = inside; + this.after = after; + } + } + + public static void load_method(CodeEmitter e, MethodInfo method) { + load_class(e, method.getClassInfo().getType()); + e.push(method.getSignature().getName()); + push_object(e, method.getSignature().getArgumentTypes()); + e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD); + } + + private interface ParameterTyper { + Type[] getParameterTypes(MethodInfo member); + } + + public static void method_switch(CodeEmitter e, + List methods, + ObjectSwitchCallback callback) { + member_switch_helper(e, methods, callback, true); + } + + public static void constructor_switch(CodeEmitter e, + List constructors, + ObjectSwitchCallback callback) { + member_switch_helper(e, constructors, callback, false); + } + + private static void member_switch_helper(final CodeEmitter e, + List members, + final ObjectSwitchCallback callback, + boolean useName) { + try { + final Map cache = new HashMap(); + final ParameterTyper cached = new ParameterTyper() { + public Type[] getParameterTypes(MethodInfo member) { + Type[] types = (Type[])cache.get(member); + if (types == null) { + cache.put(member, types = member.getSignature().getArgumentTypes()); + } + return types; + } + }; + final Label def = e.make_label(); + final Label end = e.make_label(); + if (useName) { + e.swap(); + final Map buckets = CollectionUtils.bucket(members, new Transformer() { + public Object transform(Object value) { + return ((MethodInfo)value).getSignature().getName(); + } + }); + String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label dontUseEnd) throws Exception { + member_helper_size(e, (List)buckets.get(key), callback, cached, def, end); + } + public void processDefault() throws Exception { + e.goTo(def); + } + }); + } else { + member_helper_size(e, members, callback, cached, def, end); + } + e.mark(def); + e.pop(); + callback.processDefault(); + e.mark(end); + } catch (RuntimeException ex) { + throw ex; + } catch (Error ex) { + throw ex; + } catch (Exception ex) { + throw new CodeGenerationException(ex); + } + } + + private static void member_helper_size(final CodeEmitter e, + List members, + final ObjectSwitchCallback callback, + final ParameterTyper typer, + final Label def, + final Label end) throws Exception { + final Map buckets = CollectionUtils.bucket(members, new Transformer() { + public Object transform(Object value) { + return new Integer(typer.getParameterTypes((MethodInfo)value).length); + } + }); + e.dup(); + e.arraylength(); + e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label dontUseEnd) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + member_helper_type(e, bucket, callback, typer, def, end, new BitSet()); + } + public void processDefault() throws Exception { + e.goTo(def); + } + }); + } + + private static void member_helper_type(final CodeEmitter e, + List members, + final ObjectSwitchCallback callback, + final ParameterTyper typer, + final Label def, + final Label end, + final BitSet checked) throws Exception { + if (members.size() == 1) { + MethodInfo member = (MethodInfo)members.get(0); + Type[] types = typer.getParameterTypes(member); + // need to check classes that have not already been checked via switches + for (int i = 0; i < types.length; i++) { + if (checked == null || !checked.get(i)) { + e.dup(); + e.aaload(i); + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + e.push(TypeUtils.emulateClassGetName(types[i])); + e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); + e.if_jump(e.EQ, def); + } + } + e.pop(); + callback.processCase(member, end); + } else { + // choose the index that has the best chance of uniquely identifying member + Type[] example = typer.getParameterTypes((MethodInfo)members.get(0)); + Map buckets = null; + int index = -1; + for (int i = 0; i < example.length; i++) { + final int j = i; + Map test = CollectionUtils.bucket(members, new Transformer() { + public Object transform(Object value) { + return TypeUtils.emulateClassGetName(typer.getParameterTypes((MethodInfo)value)[j]); + } + }); + if (buckets == null || test.size() > buckets.size()) { + buckets = test; + index = i; + } + } + if (buckets == null || buckets.size() == 1) { + // TODO: switch by returnType + // must have two methods with same name, types, and different return types + e.goTo(def); + } else { + checked.set(index); + + e.dup(); + e.aaload(index); + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + + final Map fbuckets = buckets; + String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label dontUseEnd) throws Exception { + member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked); + } + public void processDefault() throws Exception { + e.goTo(def); + } + }); + } + } + } + + public static void wrap_throwable(Block block, Type wrapper) { + CodeEmitter e = block.getCodeEmitter(); + e.catch_exception(block, Constants.TYPE_THROWABLE); + e.new_instance(wrapper); + e.dup_x1(); + e.swap(); + e.invoke_constructor(wrapper, CSTRUCT_THROWABLE); + e.athrow(); + } + + public static void add_properties(ClassEmitter ce, String[] names, Type[] types) { + for (int i = 0; i < names.length; i++) { + String fieldName = "$cglib_prop_" + names[i]; + ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null); + EmitUtils.add_property(ce, names[i], types[i], fieldName); + } + } + + public static void add_property(ClassEmitter ce, String name, Type type, String fieldName) { + String property = TypeUtils.upperFirst(name); + CodeEmitter e; + e = ce.begin_method(Constants.ACC_PUBLIC, + new Signature("get" + property, + type, + Constants.TYPES_EMPTY), + null); + e.load_this(); + e.getfield(fieldName); + e.return_value(); + e.end_method(); + + e = ce.begin_method(Constants.ACC_PUBLIC, + new Signature("set" + property, + Type.VOID_TYPE, + new Type[]{ type }), + null); + e.load_this(); + e.load_arg(0); + e.putfield(fieldName); + e.return_value(); + e.end_method(); + } + + /* generates: + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch ( e) { + throw e; + } catch (Throwable e) { + throw new (e); + } + */ + public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) { + Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions)); + + if (set.contains(Constants.TYPE_THROWABLE)) + return; + + boolean needThrow = exceptions != null; + if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) { + e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION); + needThrow = true; + } + if (!set.contains(Constants.TYPE_ERROR)) { + e.catch_exception(handler, Constants.TYPE_ERROR); + needThrow = true; + } + if (exceptions != null) { + for (int i = 0; i < exceptions.length; i++) { + e.catch_exception(handler, exceptions[i]); + } + } + if (needThrow) { + e.athrow(); + } + // e -> eo -> oeo -> ooe -> o + e.catch_exception(handler, Constants.TYPE_THROWABLE); + e.new_instance(wrapper); + e.dup_x1(); + e.swap(); + e.invoke_constructor(wrapper, CSTRUCT_THROWABLE); + e.athrow(); + } + + public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) { + return begin_method(e, method, method.getModifiers()); + } + + public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) { + return e.begin_method(access, + method.getSignature(), + method.getExceptionTypes()); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/FieldTypeCustomizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/FieldTypeCustomizer.java new file mode 100644 index 000000000..36fa9ce3d --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/FieldTypeCustomizer.java @@ -0,0 +1,23 @@ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +/** + * Customizes key types for {@link KeyFactory} right in constructor. + */ +public interface FieldTypeCustomizer extends KeyFactoryCustomizer { + /** + * Customizes {@code this.FIELD_0 = ?} assignment in key constructor + * @param e code emitter + * @param index parameter index + * @param type parameter type + */ + void customize(CodeEmitter e, int index, Type type); + + /** + * Computes type of field for storing given parameter + * @param index parameter index + * @param type parameter type + */ + Type getOutType(int index, Type type); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/GeneratorStrategy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/GeneratorStrategy.java new file mode 100644 index 000000000..f4dca84e0 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/GeneratorStrategy.java @@ -0,0 +1,44 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +/** + * The GeneratorStrategyClass. By providing your + * own strategy you may examine or modify the generated class before + * it is loaded. Typically this will be accomplished by subclassing + * {@link DefaultGeneratorStrategy} and overriding the appropriate + * protected method. + * @see AbstractClassGenerator#setStrategy + */ +public interface GeneratorStrategy { + /** + * Generate the class. + * @param cg a class generator on which you can call {@link ClassGenerator#generateClass} + * @return a byte array containing the bits of a valid Class + */ + byte[] generate(ClassGenerator cg) throws Exception; + + /** + * The GeneratorStrategy in use does not currently, but may + * in the future, affect the caching of classes generated by {@link + * AbstractClassGenerator}, so this is a reminder that you should + * correctly implement equals and hashCode + * to avoid generating too many classes. + */ + boolean equals(Object o); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/HashCodeCustomizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/HashCodeCustomizer.java new file mode 100644 index 000000000..cbfcb93cf --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/HashCodeCustomizer.java @@ -0,0 +1,12 @@ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +public interface HashCodeCustomizer extends KeyFactoryCustomizer { + /** + * Customizes calculation of hashcode + * @param e code emitter + * @param type parameter type + */ + boolean customize(CodeEmitter e, Type type); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactory.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactory.java new file mode 100644 index 000000000..f1d5cea1e --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactory.java @@ -0,0 +1,346 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.net.sf.cglib.core.internal.CustomizerRegistry; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.List; + +/** + * Generates classes to handle multi-valued keys, for use in things such as Maps and Sets. + * Code for equals and hashCode methods follow the + * the rules laid out in Effective Java by Joshua Bloch. + *

+ * To generate a KeyFactory, you need to supply an interface which + * describes the structure of the key. The interface should have a + * single method named newInstance, which returns an + * Object. The arguments array can be + * anything--Objects, primitive values, or single or + * multi-dimension arrays of either. For example: + *

+ *     private interface IntStringKey {
+ *         public Object newInstance(int i, String s);
+ *     }
+ * 

+ * Once you have made a KeyFactory, you generate a new key by calling + * the newInstance method defined by your interface. + *

+ *     IntStringKey factory = (IntStringKey)KeyFactory.create(IntStringKey.class);
+ *     Object key1 = factory.newInstance(4, "Hello");
+ *     Object key2 = factory.newInstance(4, "World");
+ * 

+ * Note: + * hashCode equality between two keys key1 and key2 is only guaranteed if + * key1.equals(key2) and the keys were produced by the same factory. + * + * @version $Id: KeyFactory.java,v 1.26 2006/03/05 02:43:19 herbyderby Exp $ + */ +abstract public class KeyFactory { + private static final Signature GET_NAME = + TypeUtils.parseSignature("String getName()"); + private static final Signature GET_CLASS = + TypeUtils.parseSignature("Class getClass()"); + private static final Signature HASH_CODE = + TypeUtils.parseSignature("int hashCode()"); + private static final Signature EQUALS = + TypeUtils.parseSignature("boolean equals(Object)"); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Signature APPEND_STRING = + TypeUtils.parseSignature("StringBuffer append(String)"); + private static final Type KEY_FACTORY = + TypeUtils.parseType("com.fr.third.net.sf.cglib.core.KeyFactory"); + private static final Signature GET_SORT = + TypeUtils.parseSignature("int getSort()"); + + //generated numbers: + private final static int PRIMES[] = { + 11, 73, 179, 331, + 521, 787, 1213, 1823, + 2609, 3691, 5189, 7247, + 10037, 13931, 19289, 26627, + 36683, 50441, 69403, 95401, + 131129, 180179, 247501, 340057, + 467063, 641371, 880603, 1209107, + 1660097, 2279161, 3129011, 4295723, + 5897291, 8095873, 11114263, 15257791, + 20946017, 28754629, 39474179, 54189869, + 74391461, 102123817, 140194277, 192456917, + 264202273, 362693231, 497900099, 683510293, + 938313161, 1288102441, 1768288259 }; + + + public static final Customizer CLASS_BY_NAME = new Customizer() { + public void customize(CodeEmitter e, Type type) { + if (type.equals(Constants.TYPE_CLASS)) { + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + } + } + }; + + public static final FieldTypeCustomizer STORE_CLASS_AS_STRING = new FieldTypeCustomizer() { + public void customize(CodeEmitter e, int index, Type type) { + if (type.equals(Constants.TYPE_CLASS)) { + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + } + } + + public Type getOutType(int index, Type type) { + if (type.equals(Constants.TYPE_CLASS)) { + return Constants.TYPE_STRING; + } + return type; + } + }; + + /** + * {@link Type#hashCode()} is very expensive as it traverses full descriptor to calculate hash code. + * This customizer uses {@link Type#getSort()} as a hash code. + */ + public static final HashCodeCustomizer HASH_ASM_TYPE = new HashCodeCustomizer() { + public boolean customize(CodeEmitter e, Type type) { + if (Constants.TYPE_TYPE.equals(type)) { + e.invoke_virtual(type, GET_SORT); + return true; + } + return false; + } + }; + + /** + * @deprecated this customizer might result in unexpected class leak since key object still holds a strong reference to the Object and class. + * It is recommended to have pre-processing method that would strip Objects and represent Classes as Strings + */ + @Deprecated + public static final Customizer OBJECT_BY_CLASS = new Customizer() { + public void customize(CodeEmitter e, Type type) { + e.invoke_virtual(Constants.TYPE_OBJECT, GET_CLASS); + } + }; + + protected KeyFactory() { + } + + public static KeyFactory create(Class keyInterface) { + return create(keyInterface, null); + } + + public static KeyFactory create(Class keyInterface, Customizer customizer) { + return create(keyInterface.getClassLoader(), keyInterface, customizer); + } + + public static KeyFactory create(Class keyInterface, KeyFactoryCustomizer first, List next) { + return create(keyInterface.getClassLoader(), keyInterface, first, next); + } + + public static KeyFactory create(ClassLoader loader, Class keyInterface, Customizer customizer) { + return create(loader, keyInterface, customizer, Collections.emptyList()); + } + + public static KeyFactory create(ClassLoader loader, Class keyInterface, KeyFactoryCustomizer customizer, + List next) { + Generator gen = new Generator(); + gen.setInterface(keyInterface); + + if (customizer != null) { + gen.addCustomizer(customizer); + } + if (next != null && !next.isEmpty()) { + for (KeyFactoryCustomizer keyFactoryCustomizer : next) { + gen.addCustomizer(keyFactoryCustomizer); + } + } + gen.setClassLoader(loader); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(KeyFactory.class.getName()); + private static final Class[] KNOWN_CUSTOMIZER_TYPES = new Class[]{Customizer.class, FieldTypeCustomizer.class}; + + private Class keyInterface; + // TODO: Make me final when deprecated methods are removed + private CustomizerRegistry customizers = new CustomizerRegistry(KNOWN_CUSTOMIZER_TYPES); + private int constant; + private int multiplier; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return keyInterface.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(keyInterface); + } + + /** + * @deprecated Use {@link #addCustomizer(KeyFactoryCustomizer)} instead. + */ + @Deprecated + public void setCustomizer(Customizer customizer) { + customizers = CustomizerRegistry.singleton(customizer); + } + + public void addCustomizer(KeyFactoryCustomizer customizer) { + customizers.add(customizer); + } + + public List getCustomizers(Class klass) { + return customizers.get(klass); + } + + public void setInterface(Class keyInterface) { + this.keyInterface = keyInterface; + } + + public KeyFactory create() { + setNamePrefix(keyInterface.getName()); + return (KeyFactory)super.create(keyInterface.getName()); + } + + public void setHashConstant(int constant) { + this.constant = constant; + } + + public void setHashMultiplier(int multiplier) { + this.multiplier = multiplier; + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + + public void generateClass(ClassVisitor v) { + ClassEmitter ce = new ClassEmitter(v); + + Method newInstance = ReflectUtils.findNewInstance(keyInterface); + if (!newInstance.getReturnType().equals(Object.class)) { + throw new IllegalArgumentException("newInstance method must return Object"); + } + + Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes()); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + KEY_FACTORY, + new Type[]{ Type.getType(keyInterface) }, + Constants.SOURCE_FILE); + EmitUtils.null_constructor(ce); + EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance)); + + int seed = 0; + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, + TypeUtils.parseConstructor(parameterTypes), + null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + List fieldTypeCustomizers = getCustomizers(FieldTypeCustomizer.class); + for (int i = 0; i < parameterTypes.length; i++) { + Type parameterType = parameterTypes[i]; + Type fieldType = parameterType; + for (FieldTypeCustomizer customizer : fieldTypeCustomizers) { + fieldType = customizer.getOutType(i, fieldType); + } + seed += fieldType.hashCode(); + ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL, + getFieldName(i), + fieldType, + null); + e.dup(); + e.load_arg(i); + for (FieldTypeCustomizer customizer : fieldTypeCustomizers) { + customizer.customize(e, i, parameterType); + } + e.putfield(getFieldName(i)); + } + e.return_value(); + e.end_method(); + + // hash code + e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null); + int hc = (constant != 0) ? constant : PRIMES[(int)(Math.abs(seed) % PRIMES.length)]; + int hm = (multiplier != 0) ? multiplier : PRIMES[(int)(Math.abs(seed * 13) % PRIMES.length)]; + e.push(hc); + for (int i = 0; i < parameterTypes.length; i++) { + e.load_this(); + e.getfield(getFieldName(i)); + EmitUtils.hash_code(e, parameterTypes[i], hm, customizers); + } + e.return_value(); + e.end_method(); + + // equals + e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null); + Label fail = e.make_label(); + e.load_arg(0); + e.instance_of_this(); + e.if_jump(e.EQ, fail); + for (int i = 0; i < parameterTypes.length; i++) { + e.load_this(); + e.getfield(getFieldName(i)); + e.load_arg(0); + e.checkcast_this(); + e.getfield(getFieldName(i)); + EmitUtils.not_equals(e, parameterTypes[i], fail, customizers); + } + e.push(1); + e.return_value(); + e.mark(fail); + e.push(0); + e.return_value(); + e.end_method(); + + // toString + e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null); + e.new_instance(Constants.TYPE_STRING_BUFFER); + e.dup(); + e.invoke_constructor(Constants.TYPE_STRING_BUFFER); + for (int i = 0; i < parameterTypes.length; i++) { + if (i > 0) { + e.push(", "); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + e.load_this(); + e.getfield(getFieldName(i)); + EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizers); + } + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING); + e.return_value(); + e.end_method(); + + ce.end_class(); + } + + private String getFieldName(int arg) { + return "FIELD_" + arg; + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactoryCustomizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactoryCustomizer.java new file mode 100644 index 000000000..1339e145d --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactoryCustomizer.java @@ -0,0 +1,7 @@ +package com.fr.third.net.sf.cglib.core; + +/** + * Marker interface for customizers of {@link KeyFactory} + */ +public interface KeyFactoryCustomizer { +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Local.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Local.java new file mode 100644 index 000000000..d8959284e --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Local.java @@ -0,0 +1,37 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +public class Local +{ + private Type type; + private int index; + + public Local(int index, Type type) { + this.type = type; + this.index = index; + } + + public int getIndex() { + return index; + } + + public Type getType() { + return type; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/LocalVariablesSorter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/LocalVariablesSorter.java new file mode 100644 index 000000000..f80ce99ca --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/LocalVariablesSorter.java @@ -0,0 +1,158 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.MethodVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; +import com.fr.third.org.objectweb.asm.Type; + +/** + * A {@link MethodVisitor} that renumbers local variables in their order of + * appearance. This adapter allows one to easily add new local variables to a + * method. + * + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class LocalVariablesSorter extends MethodVisitor { + + /** + * Mapping from old to new local variable indexes. A local variable at index + * i of size 1 is remapped to 'mapping[2*i]', while a local variable at + * index i of size 2 is remapped to 'mapping[2*i+1]'. + */ + private static class State + { + int[] mapping = new int[40]; + int nextLocal; + } + + protected final int firstLocal; + private final State state; + + public LocalVariablesSorter( + final int access, + final String desc, + final MethodVisitor mv) + { + super(Opcodes.ASM6, mv); + state = new State(); + Type[] args = Type.getArgumentTypes(desc); + state.nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1; + for (int i = 0; i < args.length; i++) { + state.nextLocal += args[i].getSize(); + } + firstLocal = state.nextLocal; + } + + public LocalVariablesSorter(LocalVariablesSorter lvs) { + super(Opcodes.ASM6, lvs.mv); + state = lvs.state; + firstLocal = lvs.firstLocal; + } + + public void visitVarInsn(final int opcode, final int var) { + int size; + switch (opcode) { + case Opcodes.LLOAD: + case Opcodes.LSTORE: + case Opcodes.DLOAD: + case Opcodes.DSTORE: + size = 2; + break; + default: + size = 1; + } + mv.visitVarInsn(opcode, remap(var, size)); + } + + public void visitIincInsn(final int var, final int increment) { + mv.visitIincInsn(remap(var, 1), increment); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + mv.visitMaxs(maxStack, state.nextLocal); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + mv.visitLocalVariable(name, desc, signature, start, end, remap(index)); + } + + // ------------- + + protected int newLocal(final int size) { + int var = state.nextLocal; + state.nextLocal += size; + return var; + } + + private int remap(final int var, final int size) { + if (var < firstLocal) { + return var; + } + int key = 2 * var + size - 1; + int length = state.mapping.length; + if (key >= length) { + int[] newMapping = new int[Math.max(2 * length, key + 1)]; + System.arraycopy(state.mapping, 0, newMapping, 0, length); + state.mapping = newMapping; + } + int value = state.mapping[key]; + if (value == 0) { + value = state.nextLocal + 1; + state.mapping[key] = value; + state.nextLocal += size; + } + return value - 1; + } + + private int remap(final int var) { + if (var < firstLocal) { + return var; + } + int key = 2 * var; + int value = key < state.mapping.length ? state.mapping[key] : 0; + if (value == 0) { + value = key + 1 < state.mapping.length ? state.mapping[key + 1] : 0; + } + if (value == 0) { + throw new IllegalStateException("Unknown local variable " + var); + } + return value - 1; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfo.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfo.java new file mode 100644 index 000000000..daee5afa3 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfo.java @@ -0,0 +1,47 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; + +abstract public class MethodInfo { + + protected MethodInfo() { + } + + abstract public ClassInfo getClassInfo(); + abstract public int getModifiers(); + abstract public Signature getSignature(); + abstract public Type[] getExceptionTypes(); + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof MethodInfo)) + return false; + return getSignature().equals(((MethodInfo)o).getSignature()); + } + + public int hashCode() { + return getSignature().hashCode(); + } + + public String toString() { + // TODO: include modifiers, exceptions + return getSignature().toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfoTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfoTransformer.java new file mode 100644 index 000000000..706af2cfe --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfoTransformer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.lang.reflect.*; + +public class MethodInfoTransformer implements Transformer +{ + private static final MethodInfoTransformer INSTANCE = new MethodInfoTransformer(); + + public static MethodInfoTransformer getInstance() { + return INSTANCE; + } + + public Object transform(Object value) { + if (value instanceof Method) { + return ReflectUtils.getMethodInfo((Method)value); + } else if (value instanceof Constructor) { + return ReflectUtils.getMethodInfo((Constructor)value); + } else { + throw new IllegalArgumentException("cannot get method info for " + value); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodWrapper.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodWrapper.java new file mode 100644 index 000000000..fac82d827 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodWrapper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.lang.reflect.Method; +import java.util.*; + +public class MethodWrapper { + private static final MethodWrapperKey KEY_FACTORY = + (MethodWrapperKey)KeyFactory.create(MethodWrapperKey.class); + + /** Internal interface, only public due to ClassLoader issues. */ + public interface MethodWrapperKey { + public Object newInstance(String name, String[] parameterTypes, String returnType); + } + + private MethodWrapper() { + } + + public static Object create(Method method) { + return KEY_FACTORY.newInstance(method.getName(), + ReflectUtils.getNames(method.getParameterTypes()), + method.getReturnType().getName()); + } + + public static Set createSet(Collection methods) { + Set set = new HashSet(); + for (Iterator it = methods.iterator(); it.hasNext();) { + set.add(create((Method)it.next())); + } + return set; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/NamingPolicy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/NamingPolicy.java new file mode 100644 index 000000000..d240cc4e1 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/NamingPolicy.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.util.Set; + +/** + * Customize the generated class name for {@link AbstractClassGenerator}-based utilities. + */ +public interface NamingPolicy { + /** + * Choose a name for a generated class. + * @param prefix a dotted-name chosen by the generating class (possibly to put the generated class in a particular package) + * @param source the fully-qualified class name of the generating class (for example "com.fr.third.net.sf.cglib.Enhancer") + * @param key A key object representing the state of the parameters; for caching to work properly, equal keys should result + * in the same generated class name. The default policy incorporates key.hashCode() into the class name. + * @param names a predicate that returns true if the given classname has already been used in the same ClassLoader. + * @return the fully-qualified class name + */ + String getClassName(String prefix, String source, Object key, Predicate names); + + /** + * The NamingPolicy in use does not currently, but may + * in the future, affect the caching of classes generated by {@link + * AbstractClassGenerator}, so this is a reminder that you should + * correctly implement equals and hashCode + * to avoid generating too many classes. + */ + boolean equals(Object o); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ObjectSwitchCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ObjectSwitchCallback.java new file mode 100644 index 000000000..0af9a429b --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ObjectSwitchCallback.java @@ -0,0 +1,24 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Label; + +public interface ObjectSwitchCallback { + void processCase(Object key, Label end) throws Exception; + void processDefault() throws Exception; +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Predicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Predicate.java new file mode 100644 index 000000000..9fcb746af --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Predicate.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +public interface Predicate { + boolean evaluate(Object arg); +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessArrayCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessArrayCallback.java new file mode 100644 index 000000000..153074815 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessArrayCallback.java @@ -0,0 +1,22 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +public interface ProcessArrayCallback { + void processElement(Type type); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessSwitchCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessSwitchCallback.java new file mode 100644 index 000000000..b3cbf7745 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessSwitchCallback.java @@ -0,0 +1,23 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Label; + +public interface ProcessSwitchCallback { + void processCase(int key, Label end) throws Exception; + void processDefault() throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ReflectUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ReflectUtils.java new file mode 100644 index 000000000..e829b1834 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ReflectUtils.java @@ -0,0 +1,544 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.beans.*; +import java.lang.reflect.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.util.*; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $ + */ +public class ReflectUtils { + private ReflectUtils() { } + + private static final Map primitives = new HashMap(8); + private static final Map transforms = new HashMap(8); + private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader(); + private static Method DEFINE_CLASS, DEFINE_CLASS_UNSAFE; + private static final ProtectionDomain PROTECTION_DOMAIN; + private static final Object UNSAFE; + private static final Throwable THROWABLE; + + private static final List OBJECT_METHODS = new ArrayList(); + + static { + ProtectionDomain protectionDomain; + Method defineClass, defineClassUnsafe; + Object unsafe; + Throwable throwable = null; + try { + protectionDomain = getProtectionDomain(ReflectUtils.class); + try { + defineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this + Method defineClass = loader.getDeclaredMethod("defineClass", + new Class[]{ String.class, + byte[].class, + Integer.TYPE, + Integer.TYPE, + ProtectionDomain.class }); + defineClass.setAccessible(true); + return defineClass; + } + }); + defineClassUnsafe = null; + unsafe = null; + } catch (Throwable t) { + // Fallback on Jigsaw where this method is not available. + throwable = t; + defineClass = null; + unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + Class u = Class.forName("sun.misc.Unsafe"); + Field theUnsafe = u.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return theUnsafe.get(null); + } + }); + Class u = Class.forName("sun.misc.Unsafe"); + defineClassUnsafe = u.getMethod("defineClass", + new Class[]{ String.class, + byte[].class, + Integer.TYPE, + Integer.TYPE, + ClassLoader.class, + ProtectionDomain.class }); + } + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + Method[] methods = Object.class.getDeclaredMethods(); + for (Method method : methods) { + if ("finalize".equals(method.getName()) + || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) { + continue; + } + OBJECT_METHODS.add(method); + } + return null; + } + }); + } catch (Throwable t) { + if (throwable == null) { + throwable = t; + } + protectionDomain = null; + defineClass = null; + defineClassUnsafe = null; + unsafe = null; + } + PROTECTION_DOMAIN = protectionDomain; + DEFINE_CLASS = defineClass; + DEFINE_CLASS_UNSAFE = defineClassUnsafe; + UNSAFE = unsafe; + THROWABLE = throwable; + } + + private static final String[] CGLIB_PACKAGES = { + "java.lang", + }; + + static { + primitives.put("byte", Byte.TYPE); + primitives.put("char", Character.TYPE); + primitives.put("double", Double.TYPE); + primitives.put("float", Float.TYPE); + primitives.put("int", Integer.TYPE); + primitives.put("long", Long.TYPE); + primitives.put("short", Short.TYPE); + primitives.put("boolean", Boolean.TYPE); + + transforms.put("byte", "B"); + transforms.put("char", "C"); + transforms.put("double", "D"); + transforms.put("float", "F"); + transforms.put("int", "I"); + transforms.put("long", "J"); + transforms.put("short", "S"); + transforms.put("boolean", "Z"); + } + + public static ProtectionDomain getProtectionDomain(final Class source) { + if(source == null) { + return null; + } + return (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return source.getProtectionDomain(); + } + }); + } + + public static Type[] getExceptionTypes(Member member) { + if (member instanceof Method) { + return TypeUtils.getTypes(((Method)member).getExceptionTypes()); + } else if (member instanceof Constructor) { + return TypeUtils.getTypes(((Constructor)member).getExceptionTypes()); + } else { + throw new IllegalArgumentException("Cannot get exception types of a field"); + } + } + + public static Signature getSignature(Member member) { + if (member instanceof Method) { + return new Signature(member.getName(), Type.getMethodDescriptor((Method)member)); + } else if (member instanceof Constructor) { + Type[] types = TypeUtils.getTypes(((Constructor)member).getParameterTypes()); + return new Signature(Constants.CONSTRUCTOR_NAME, + Type.getMethodDescriptor(Type.VOID_TYPE, types)); + + } else { + throw new IllegalArgumentException("Cannot get signature of a field"); + } + } + + public static Constructor findConstructor(String desc) { + return findConstructor(desc, defaultLoader); + } + + public static Constructor findConstructor(String desc, ClassLoader loader) { + try { + int lparen = desc.indexOf('('); + String className = desc.substring(0, lparen).trim(); + return getClass(className, loader).getConstructor(parseTypes(desc, loader)); + } catch (ClassNotFoundException e) { + throw new CodeGenerationException(e); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + public static Method findMethod(String desc) { + return findMethod(desc, defaultLoader); + } + + public static Method findMethod(String desc, ClassLoader loader) { + try { + int lparen = desc.indexOf('('); + int dot = desc.lastIndexOf('.', lparen); + String className = desc.substring(0, dot).trim(); + String methodName = desc.substring(dot + 1, lparen).trim(); + return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader)); + } catch (ClassNotFoundException e) { + throw new CodeGenerationException(e); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException { + int lparen = desc.indexOf('('); + int rparen = desc.indexOf(')', lparen); + List params = new ArrayList(); + int start = lparen + 1; + for (;;) { + int comma = desc.indexOf(',', start); + if (comma < 0) { + break; + } + params.add(desc.substring(start, comma).trim()); + start = comma + 1; + } + if (start < rparen) { + params.add(desc.substring(start, rparen).trim()); + } + Class[] types = new Class[params.size()]; + for (int i = 0; i < types.length; i++) { + types[i] = getClass((String)params.get(i), loader); + } + return types; + } + + private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException { + return getClass(className, loader, CGLIB_PACKAGES); + } + + private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException { + String save = className; + int dimensions = 0; + int index = 0; + while ((index = className.indexOf("[]", index) + 1) > 0) { + dimensions++; + } + StringBuffer brackets = new StringBuffer(className.length() - dimensions); + for (int i = 0; i < dimensions; i++) { + brackets.append('['); + } + className = className.substring(0, className.length() - 2 * dimensions); + + String prefix = (dimensions > 0) ? brackets + "L" : ""; + String suffix = (dimensions > 0) ? ";" : ""; + try { + return Class.forName(prefix + className + suffix, false, loader); + } catch (ClassNotFoundException ignore) { } + for (int i = 0; i < packages.length; i++) { + try { + return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader); + } catch (ClassNotFoundException ignore) { } + } + if (dimensions == 0) { + Class c = (Class)primitives.get(className); + if (c != null) { + return c; + } + } else { + String transform = (String)transforms.get(className); + if (transform != null) { + try { + return Class.forName(brackets + transform, false, loader); + } catch (ClassNotFoundException ignore) { } + } + } + throw new ClassNotFoundException(save); + } + + + public static Object newInstance(Class type) { + return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null); + } + + public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) { + return newInstance(getConstructor(type, parameterTypes), args); + } + + public static Object newInstance(final Constructor cstruct, final Object[] args) { + + boolean flag = cstruct.isAccessible(); + try { + if (!flag) { + cstruct.setAccessible(true); + } + Object result = cstruct.newInstance(args); + return result; + } catch (InstantiationException e) { + throw new CodeGenerationException(e); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e.getTargetException()); + } finally { + if (!flag) { + cstruct.setAccessible(flag); + } + } + + } + + public static Constructor getConstructor(Class type, Class[] parameterTypes) { + try { + Constructor constructor = type.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + public static String[] getNames(Class[] classes) + { + if (classes == null) + return null; + String[] names = new String[classes.length]; + for (int i = 0; i < names.length; i++) { + names[i] = classes[i].getName(); + } + return names; + } + + public static Class[] getClasses(Object[] objects) { + Class[] classes = new Class[objects.length]; + for (int i = 0; i < objects.length; i++) { + classes[i] = objects[i].getClass(); + } + return classes; + } + + public static Method findNewInstance(Class iface) { + Method m = findInterfaceMethod(iface); + if (!m.getName().equals("newInstance")) { + throw new IllegalArgumentException(iface + " missing newInstance method"); + } + return m; + } + + public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) { + Set methods = new HashSet(); + for (int i = 0; i < properties.length; i++) { + PropertyDescriptor pd = properties[i]; + if (read) { + methods.add(pd.getReadMethod()); + } + if (write) { + methods.add(pd.getWriteMethod()); + } + } + methods.remove(null); + return (Method[])methods.toArray(new Method[methods.size()]); + } + + public static PropertyDescriptor[] getBeanProperties(Class type) { + return getPropertiesHelper(type, true, true); + } + + public static PropertyDescriptor[] getBeanGetters(Class type) { + return getPropertiesHelper(type, true, false); + } + + public static PropertyDescriptor[] getBeanSetters(Class type) { + return getPropertiesHelper(type, false, true); + } + + private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { + try { + BeanInfo info = Introspector.getBeanInfo(type, Object.class); + PropertyDescriptor[] all = info.getPropertyDescriptors(); + if (read && write) { + return all; + } + List properties = new ArrayList(all.length); + for (int i = 0; i < all.length; i++) { + PropertyDescriptor pd = all[i]; + if ((read && pd.getReadMethod() != null) || + (write && pd.getWriteMethod() != null)) { + properties.add(pd); + } + } + return (PropertyDescriptor[])properties.toArray(new PropertyDescriptor[properties.size()]); + } catch (IntrospectionException e) { + throw new CodeGenerationException(e); + } + } + + + + public static Method findDeclaredMethod(final Class type, + final String methodName, final Class[] parameterTypes) + throws NoSuchMethodException { + + Class cl = type; + while (cl != null) { + try { + return cl.getDeclaredMethod(methodName, parameterTypes); + } catch (NoSuchMethodException e) { + cl = cl.getSuperclass(); + } + } + throw new NoSuchMethodException(methodName); + + } + + public static List addAllMethods(final Class type, final List list) { + + + if (type == Object.class) { + list.addAll(OBJECT_METHODS); + } else + list.addAll(java.util.Arrays.asList(type.getDeclaredMethods())); + + Class superclass = type.getSuperclass(); + if (superclass != null) { + addAllMethods(superclass, list); + } + Class[] interfaces = type.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + addAllMethods(interfaces[i], list); + } + + return list; + } + + public static List addAllInterfaces(Class type, List list) { + Class superclass = type.getSuperclass(); + if (superclass != null) { + list.addAll(Arrays.asList(type.getInterfaces())); + addAllInterfaces(superclass, list); + } + return list; + } + + + public static Method findInterfaceMethod(Class iface) { + if (!iface.isInterface()) { + throw new IllegalArgumentException(iface + " is not an interface"); + } + Method[] methods = iface.getDeclaredMethods(); + if (methods.length != 1) { + throw new IllegalArgumentException("expecting exactly 1 method in " + iface); + } + return methods[0]; + } + + public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception { + return defineClass(className, b, loader, PROTECTION_DOMAIN); + } + + public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain) throws Exception { + Class c; + if (DEFINE_CLASS != null) { + Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), protectionDomain }; + c = (Class)DEFINE_CLASS.invoke(loader, args); + } else if (DEFINE_CLASS_UNSAFE != null) { + Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), loader, protectionDomain }; + c = (Class)DEFINE_CLASS_UNSAFE.invoke(UNSAFE, args); + } else { + throw new CodeGenerationException(THROWABLE); + } + // Force static initializers to run. + Class.forName(className, true, loader); + return c; + } + + public static int findPackageProtected(Class[] classes) { + for (int i = 0; i < classes.length; i++) { + if (!Modifier.isPublic(classes[i].getModifiers())) { + return i; + } + } + return 0; + } + + public static MethodInfo getMethodInfo(final Member member, final int modifiers) { + final Signature sig = getSignature(member); + return new MethodInfo() { + private ClassInfo ci; + public ClassInfo getClassInfo() { + if (ci == null) + ci = ReflectUtils.getClassInfo(member.getDeclaringClass()); + return ci; + } + public int getModifiers() { + return modifiers; + } + public Signature getSignature() { + return sig; + } + public Type[] getExceptionTypes() { + return ReflectUtils.getExceptionTypes(member); + } + public Attribute getAttribute() { + return null; + } + }; + } + + public static MethodInfo getMethodInfo(Member member) { + return getMethodInfo(member, member.getModifiers()); + } + + public static ClassInfo getClassInfo(final Class clazz) { + final Type type = Type.getType(clazz); + final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass()); + return new ClassInfo() { + public Type getType() { + return type; + } + public Type getSuperType() { + return sc; + } + public Type[] getInterfaces() { + return TypeUtils.getTypes(clazz.getInterfaces()); + } + public int getModifiers() { + return clazz.getModifiers(); + } + }; + } + + // used by MethodInterceptorGenerated generated code + public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) + { + Map map = new HashMap(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + map.put(method.getName() + Type.getMethodDescriptor(method), method); + } + Method[] result = new Method[namesAndDescriptors.length / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]); + if (result[i] == null) { + // TODO: error? + } + } + return result; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/RejectModifierPredicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/RejectModifierPredicate.java new file mode 100644 index 000000000..587df62c1 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/RejectModifierPredicate.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.lang.reflect.*; + +public class RejectModifierPredicate implements Predicate { + private int rejectMask; + + public RejectModifierPredicate(int rejectMask) { + this.rejectMask = rejectMask; + } + + public boolean evaluate(Object arg) { + return (((Member)arg).getModifiers() & rejectMask) == 0; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Signature.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Signature.java new file mode 100644 index 000000000..63576bb17 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Signature.java @@ -0,0 +1,73 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import com.fr.third.org.objectweb.asm.Type; + +/** + * A representation of a method signature, containing the method name, + * return type, and parameter types. + */ +public class Signature { + private String name; + private String desc; + + public Signature(String name, String desc) { + // TODO: better error checking + if (name.indexOf('(') >= 0) { + throw new IllegalArgumentException("Name '" + name + "' is invalid"); + } + this.name = name; + this.desc = desc; + } + + public Signature(String name, Type returnType, Type[] argumentTypes) { + this(name, Type.getMethodDescriptor(returnType, argumentTypes)); + } + + public String getName() { + return name; + } + + public String getDescriptor() { + return desc; + } + + public Type getReturnType() { + return Type.getReturnType(desc); + } + + public Type[] getArgumentTypes() { + return Type.getArgumentTypes(desc); + } + + public String toString() { + return name + desc; + } + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof Signature)) + return false; + Signature other = (Signature)o; + return name.equals(other.name) && desc.equals(other.desc); + } + + public int hashCode() { + return name.hashCode() ^ desc.hashCode(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/TinyBitSet.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TinyBitSet.java new file mode 100644 index 000000000..6815bf24f --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TinyBitSet.java @@ -0,0 +1,84 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +@Deprecated +public class TinyBitSet { + private static int[] T = new int[256]; + private int value = 0; + + private static int gcount(int x) { + int c = 0; + while (x != 0) { + c++; + x &= (x - 1); + } + return c; + } + + static { + for (int j = 0; j < 256; j++) { + T[j] = gcount(j); + } + } + + private static int topbit(int i) { + int j; + for (j = 0; i != 0; i ^= j) { + j = i & -i; + } + return j; + } + + private static int log2(int i) { + int j = 0; + for (j = 0; i != 0; i >>= 1) { + j++; + } + return j; + } + + public int length() { + return log2(topbit(value)); + } + + /** + * If bit 31 is set then this method results in an infinite loop. + * + * @return the number of bits set to true in this TinyBitSet. + */ + public int cardinality() { + int w = value; + int c = 0; + while (w != 0) { + c += T[w & 255]; + w >>= 8; + } + return c; + } + + public boolean get(int index) { + return (value & (1 << index)) != 0; + } + + public void set(int index) { + value |= (1 << index); + } + + public void clear(int index) { + value &= ~(1 << index); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Transformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Transformer.java new file mode 100644 index 000000000..d4f605ac1 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Transformer.java @@ -0,0 +1,20 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +public interface Transformer { + Object transform(Object value); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/TypeUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TypeUtils.java new file mode 100644 index 000000000..038e0c6d0 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TypeUtils.java @@ -0,0 +1,421 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.util.*; +import com.fr.third.org.objectweb.asm.Type; + +public class TypeUtils { + private static final Map transforms = new HashMap(); + private static final Map rtransforms = new HashMap(); + + private TypeUtils() { + } + + static { + transforms.put("void", "V"); + transforms.put("byte", "B"); + transforms.put("char", "C"); + transforms.put("double", "D"); + transforms.put("float", "F"); + transforms.put("int", "I"); + transforms.put("long", "J"); + transforms.put("short", "S"); + transforms.put("boolean", "Z"); + + CollectionUtils.reverse(transforms, rtransforms); + } + + public static Type getType(String className) { + return Type.getType("L" + className.replace('.', '/') + ";"); + } + + public static boolean isFinal(int access) { + return (Constants.ACC_FINAL & access) != 0; + } + + public static boolean isStatic(int access) { + return (Constants.ACC_STATIC & access) != 0; + } + + public static boolean isProtected(int access) { + return (Constants.ACC_PROTECTED & access) != 0; + } + + public static boolean isPublic(int access) { + return (Constants.ACC_PUBLIC & access) != 0; + } + + public static boolean isAbstract(int access) { + return (Constants.ACC_ABSTRACT & access) != 0; + } + + public static boolean isInterface(int access) { + return (Constants.ACC_INTERFACE & access) != 0; + } + + public static boolean isPrivate(int access) { + return (Constants.ACC_PRIVATE & access) != 0; + } + + public static boolean isSynthetic(int access) { + return (Constants.ACC_SYNTHETIC & access) != 0; + } + + public static boolean isBridge(int access) { + return (Constants.ACC_BRIDGE & access) != 0; + } + + // getPackage returns null on JDK 1.2 + public static String getPackageName(Type type) { + return getPackageName(getClassName(type)); + } + + public static String getPackageName(String className) { + int idx = className.lastIndexOf('.'); + return (idx < 0) ? "" : className.substring(0, idx); + } + + public static String upperFirst(String s) { + if (s == null || s.length() == 0) { + return s; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + public static String getClassName(Type type) { + if (isPrimitive(type)) { + return (String)rtransforms.get(type.getDescriptor()); + } else if (isArray(type)) { + return getClassName(getComponentType(type)) + "[]"; + } else { + return type.getClassName(); + } + } + + public static Type[] add(Type[] types, Type extra) { + if (types == null) { + return new Type[]{ extra }; + } else { + List list = Arrays.asList(types); + if (list.contains(extra)) { + return types; + } + Type[] copy = new Type[types.length + 1]; + System.arraycopy(types, 0, copy, 0, types.length); + copy[types.length] = extra; + return copy; + } + } + + public static Type[] add(Type[] t1, Type[] t2) { + // TODO: set semantics? + Type[] all = new Type[t1.length + t2.length]; + System.arraycopy(t1, 0, all, 0, t1.length); + System.arraycopy(t2, 0, all, t1.length, t2.length); + return all; + } + + public static Type fromInternalName(String name) { + // TODO; primitives? + return Type.getType("L" + name + ";"); + } + + public static Type[] fromInternalNames(String[] names) { + if (names == null) { + return null; + } + Type[] types = new Type[names.length]; + for (int i = 0; i < names.length; i++) { + types[i] = fromInternalName(names[i]); + } + return types; + } + + public static int getStackSize(Type[] types) { + int size = 0; + for (int i = 0; i < types.length; i++) { + size += types[i].getSize(); + } + return size; + } + + public static String[] toInternalNames(Type[] types) { + if (types == null) { + return null; + } + String[] names = new String[types.length]; + for (int i = 0; i < types.length; i++) { + names[i] = types[i].getInternalName(); + } + return names; + } + + public static Signature parseSignature(String s) { + int space = s.indexOf(' '); + int lparen = s.indexOf('(', space); + int rparen = s.indexOf(')', lparen); + String returnType = s.substring(0, space); + String methodName = s.substring(space + 1, lparen); + StringBuffer sb = new StringBuffer(); + sb.append('('); + for (Iterator it = parseTypes(s, lparen + 1, rparen).iterator(); it.hasNext();) { + sb.append(it.next()); + } + sb.append(')'); + sb.append(map(returnType)); + return new Signature(methodName, sb.toString()); + } + + public static Type parseType(String s) { + return Type.getType(map(s)); + } + + public static Type[] parseTypes(String s) { + List names = parseTypes(s, 0, s.length()); + Type[] types = new Type[names.size()]; + for (int i = 0; i < types.length; i++) { + types[i] = Type.getType((String)names.get(i)); + } + return types; + } + + public static Signature parseConstructor(Type[] types) { + StringBuffer sb = new StringBuffer(); + sb.append("("); + for (int i = 0; i < types.length; i++) { + sb.append(types[i].getDescriptor()); + } + sb.append(")"); + sb.append("V"); + return new Signature(Constants.CONSTRUCTOR_NAME, sb.toString()); + } + + public static Signature parseConstructor(String sig) { + return parseSignature("void (" + sig + ")"); // TODO + } + + private static List parseTypes(String s, int mark, int end) { + List types = new ArrayList(5); + for (;;) { + int next = s.indexOf(',', mark); + if (next < 0) { + break; + } + types.add(map(s.substring(mark, next).trim())); + mark = next + 1; + } + types.add(map(s.substring(mark, end).trim())); + return types; + } + + private static String map(String type) { + if (type.equals("")) { + return type; + } + String t = (String)transforms.get(type); + if (t != null) { + return t; + } else if (type.indexOf('.') < 0) { + return map("java.lang." + type); + } else { + StringBuffer sb = new StringBuffer(); + int index = 0; + while ((index = type.indexOf("[]", index) + 1) > 0) { + sb.append('['); + } + type = type.substring(0, type.length() - sb.length() * 2); + sb.append('L').append(type.replace('.', '/')).append(';'); + return sb.toString(); + } + } + + public static Type getBoxedType(Type type) { + switch (type.getSort()) { + case Type.CHAR: + return Constants.TYPE_CHARACTER; + case Type.BOOLEAN: + return Constants.TYPE_BOOLEAN; + case Type.DOUBLE: + return Constants.TYPE_DOUBLE; + case Type.FLOAT: + return Constants.TYPE_FLOAT; + case Type.LONG: + return Constants.TYPE_LONG; + case Type.INT: + return Constants.TYPE_INTEGER; + case Type.SHORT: + return Constants.TYPE_SHORT; + case Type.BYTE: + return Constants.TYPE_BYTE; + default: + return type; + } + } + + public static Type getUnboxedType(Type type) { + if (Constants.TYPE_INTEGER.equals(type)) { + return Type.INT_TYPE; + } else if (Constants.TYPE_BOOLEAN.equals(type)) { + return Type.BOOLEAN_TYPE; + } else if (Constants.TYPE_DOUBLE.equals(type)) { + return Type.DOUBLE_TYPE; + } else if (Constants.TYPE_LONG.equals(type)) { + return Type.LONG_TYPE; + } else if (Constants.TYPE_CHARACTER.equals(type)) { + return Type.CHAR_TYPE; + } else if (Constants.TYPE_BYTE.equals(type)) { + return Type.BYTE_TYPE; + } else if (Constants.TYPE_FLOAT.equals(type)) { + return Type.FLOAT_TYPE; + } else if (Constants.TYPE_SHORT.equals(type)) { + return Type.SHORT_TYPE; + } else { + return type; + } + } + + public static boolean isArray(Type type) { + return type.getSort() == Type.ARRAY; + } + + public static Type getComponentType(Type type) { + if (!isArray(type)) { + throw new IllegalArgumentException("Type " + type + " is not an array"); + } + return Type.getType(type.getDescriptor().substring(1)); + } + + public static boolean isPrimitive(Type type) { + switch (type.getSort()) { + case Type.ARRAY: + case Type.OBJECT: + return false; + default: + return true; + } + } + + public static String emulateClassGetName(Type type) { + if (isArray(type)) { + return type.getDescriptor().replace('/', '.'); + } else { + return getClassName(type); + } + } + + public static boolean isConstructor(MethodInfo method) { + return method.getSignature().getName().equals(Constants.CONSTRUCTOR_NAME); + } + + public static Type[] getTypes(Class[] classes) { + if (classes == null) { + return null; + } + Type[] types = new Type[classes.length]; + for (int i = 0; i < classes.length; i++) { + types[i] = Type.getType(classes[i]); + } + return types; + } + + public static int ICONST(int value) { + switch (value) { + case -1: return Constants.ICONST_M1; + case 0: return Constants.ICONST_0; + case 1: return Constants.ICONST_1; + case 2: return Constants.ICONST_2; + case 3: return Constants.ICONST_3; + case 4: return Constants.ICONST_4; + case 5: return Constants.ICONST_5; + } + return -1; // error + } + + public static int LCONST(long value) { + if (value == 0L) { + return Constants.LCONST_0; + } else if (value == 1L) { + return Constants.LCONST_1; + } else { + return -1; // error + } + } + + public static int FCONST(float value) { + if (value == 0f) { + return Constants.FCONST_0; + } else if (value == 1f) { + return Constants.FCONST_1; + } else if (value == 2f) { + return Constants.FCONST_2; + } else { + return -1; // error + } + } + + public static int DCONST(double value) { + if (value == 0d) { + return Constants.DCONST_0; + } else if (value == 1d) { + return Constants.DCONST_1; + } else { + return -1; // error + } + } + + public static int NEWARRAY(Type type) { + switch (type.getSort()) { + case Type.BYTE: + return Constants.T_BYTE; + case Type.CHAR: + return Constants.T_CHAR; + case Type.DOUBLE: + return Constants.T_DOUBLE; + case Type.FLOAT: + return Constants.T_FLOAT; + case Type.INT: + return Constants.T_INT; + case Type.LONG: + return Constants.T_LONG; + case Type.SHORT: + return Constants.T_SHORT; + case Type.BOOLEAN: + return Constants.T_BOOLEAN; + default: + return -1; // error + } + } + + public static String escapeType(String s) { + StringBuffer sb = new StringBuffer(); + for (int i = 0, len = s.length(); i < len; i++) { + char c = s.charAt(i); + switch (c) { + case '$': sb.append("$24"); break; + case '.': sb.append("$2E"); break; + case '[': sb.append("$5B"); break; + case ';': sb.append("$3B"); break; + case '(': sb.append("$28"); break; + case ')': sb.append("$29"); break; + case '/': sb.append("$2F"); break; + default: + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/VisibilityPredicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/VisibilityPredicate.java new file mode 100644 index 000000000..031aa6d44 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/VisibilityPredicate.java @@ -0,0 +1,52 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.core; + +import java.lang.reflect.*; +import com.fr.third.org.objectweb.asm.Type; + +public class VisibilityPredicate implements Predicate { + private boolean protectedOk; + private String pkg; + private boolean samePackageOk; + + public VisibilityPredicate(Class source, boolean protectedOk) { + this.protectedOk = protectedOk; + // same package is not ok for the bootstrap loaded classes. In all other cases we are + // generating classes in the same classloader + this.samePackageOk = source.getClassLoader() != null; + pkg = TypeUtils.getPackageName(Type.getType(source)); + } + + public boolean evaluate(Object arg) { + Member member = (Member)arg; + int mod = member.getModifiers(); + if (Modifier.isPrivate(mod)) { + return false; + } else if (Modifier.isPublic(mod)) { + return true; + } else if (Modifier.isProtected(mod) && protectedOk) { + // protected is fine if 'protectedOk' is true (for subclasses) + return true; + } else { + // protected/package private if the member is in the same package as the source class + // and we are generating into the same classloader. + return samePackageOk + && pkg.equals(TypeUtils.getPackageName(Type.getType(member.getDeclaringClass()))); + } + } +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/WeakCacheKey.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/WeakCacheKey.java new file mode 100644 index 000000000..0f0a4666e --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/WeakCacheKey.java @@ -0,0 +1,42 @@ +package com.fr.third.net.sf.cglib.core; + +import java.lang.ref.WeakReference; + +/** + * Allows to check for object equality, yet the class does not keep strong reference to the target. + * {@link #equals(Object)} returns true if and only if the reference is not yet expired and target + * objects are equal in terms of {@link #equals(Object)}. + *

+ * This an internal class, thus it might disappear in future cglib releases. + * + * @param type of the reference + */ +public class WeakCacheKey extends WeakReference { + private final int hash; + + public WeakCacheKey(T referent) { + super(referent); + this.hash = referent.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof WeakCacheKey)) { + return false; + } + Object ours = get(); + Object theirs = ((WeakCacheKey) obj).get(); + return ours != null && theirs != null && ours.equals(theirs); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + T t = get(); + return t == null ? "Clean WeakIdentityKey, hash: " + hash : t.toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/CustomizerRegistry.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/CustomizerRegistry.java new file mode 100644 index 000000000..b8732803c --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/CustomizerRegistry.java @@ -0,0 +1,48 @@ +package com.fr.third.net.sf.cglib.core.internal; + +import com.fr.third.net.sf.cglib.core.Customizer; +import com.fr.third.net.sf.cglib.core.FieldTypeCustomizer; +import com.fr.third.net.sf.cglib.core.KeyFactoryCustomizer; + +import java.util.*; + +public class CustomizerRegistry { + private final Class[] customizerTypes; + private Map> customizers = new HashMap>(); + + public CustomizerRegistry(Class[] customizerTypes) { + this.customizerTypes = customizerTypes; + } + + public void add(KeyFactoryCustomizer customizer) { + Class klass = customizer.getClass(); + for (Class type : customizerTypes) { + if (type.isAssignableFrom(klass)) { + List list = customizers.get(type); + if (list == null) { + customizers.put(type, list = new ArrayList()); + } + list.add(customizer); + } + } + } + + public List get(Class klass) { + List list = customizers.get(klass); + if (list == null) { + return Collections.emptyList(); + } + return (List) list; + } + + /** + * @deprecated Only to keep backward compatibility. + */ + @Deprecated + public static CustomizerRegistry singleton(Customizer customizer) + { + CustomizerRegistry registry = new CustomizerRegistry(new Class[]{Customizer.class}); + registry.add(customizer); + return registry; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/Function.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/Function.java new file mode 100644 index 000000000..bf4f75e07 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/Function.java @@ -0,0 +1,5 @@ +package com.fr.third.net.sf.cglib.core.internal; + +public interface Function { + V apply(K key); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/LoadingCache.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/LoadingCache.java new file mode 100644 index 000000000..4a255fcd6 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/LoadingCache.java @@ -0,0 +1,86 @@ +package com.fr.third.net.sf.cglib.core.internal; + +import java.util.concurrent.*; + +public class LoadingCache { + protected final ConcurrentMap map; + protected final Function loader; + protected final Function keyMapper; + + public static final Function IDENTITY = new Function() { + public Object apply(Object key) { + return key; + } + }; + + public LoadingCache(Function keyMapper, Function loader) { + this.keyMapper = keyMapper; + this.loader = loader; + this.map = new ConcurrentHashMap(); + } + + @SuppressWarnings("unchecked") + public static Function identity() { + return IDENTITY; + } + + public V get(K key) { + final KK cacheKey = keyMapper.apply(key); + Object v = map.get(cacheKey); + if (v != null && !(v instanceof FutureTask)) { + return (V) v; + } + + return createEntry(key, cacheKey, v); + } + + /** + * Loads entry to the cache. + * If entry is missing, put {@link FutureTask} first so other competing thread might wait for the result. + * @param key original key that would be used to load the instance + * @param cacheKey key that would be used to store the entry in internal map + * @param v null or {@link FutureTask} + * @return newly created instance + */ + protected V createEntry(final K key, KK cacheKey, Object v) { + FutureTask task; + boolean creator = false; + if (v != null) { + // Another thread is already loading an instance + task = (FutureTask) v; + } else { + task = new FutureTask(new Callable() { + public V call() throws Exception { + return loader.apply(key); + } + }); + Object prevTask = map.putIfAbsent(cacheKey, task); + if (prevTask == null) { + // creator does the load + creator = true; + task.run(); + } else if (prevTask instanceof FutureTask) { + task = (FutureTask) prevTask; + } else { + return (V) prevTask; + } + } + + V result; + try { + result = task.get(); + } catch (InterruptedException e) { + throw new IllegalStateException("Interrupted while loading cache item", e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw ((RuntimeException) cause); + } + throw new IllegalStateException("Unable to load cache item", cause); + } + if (creator) { + map.put(cacheKey, result); + } + return result; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/BridgeMethodResolver.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/BridgeMethodResolver.java new file mode 100644 index 000000000..7d77d7c79 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/BridgeMethodResolver.java @@ -0,0 +1,125 @@ +/* + * Copyright 2011 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fr.third.net.sf.cglib.proxy; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.fr.third.net.sf.cglib.core.Signature; + +import com.fr.third.org.objectweb.asm.AnnotationVisitor; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.ClassReader; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.FieldVisitor; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.MethodVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +/** + * Uses bytecode reflection to figure out the targets of all bridge methods + * that use invokespecial, so that we can later rewrite them to use invokevirtual. + * + * @author sberlin@gmail.com (Sam Berlin) + */ +class BridgeMethodResolver { + + private final Map/* */declToBridge; + private final ClassLoader classLoader; + + public BridgeMethodResolver(Map declToBridge, ClassLoader classLoader) { + this.declToBridge = declToBridge; + this.classLoader = classLoader; + } + + /** + * Finds all bridge methods that are being called with invokespecial & + * returns them. + */ + public Map/**/resolveAll() { + Map resolved = new HashMap(); + for (Iterator entryIter = declToBridge.entrySet().iterator(); entryIter.hasNext(); ) { + Map.Entry entry = (Map.Entry) entryIter.next(); + Class owner = (Class) entry.getKey(); + Set bridges = (Set) entry.getValue(); + try { + InputStream is = classLoader.getResourceAsStream(owner.getName().replace('.', '/') + ".class"); + if (is == null) { + return resolved; + } + try { + new ClassReader(is) + .accept(new BridgedFinder(bridges, resolved), + ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); + } finally { + is.close(); + } + } catch (IOException ignored) {} + } + return resolved; + } + + private static class BridgedFinder extends ClassVisitor { + private Map/**/ resolved; + private Set/**/ eligibleMethods; + + private Signature currentMethod = null; + + BridgedFinder(Set eligibleMethods, Map resolved) { + super(Opcodes.ASM6); + this.resolved = resolved; + this.eligibleMethods = eligibleMethods; + } + + public void visit(int version, int access, String name, + String signature, String superName, String[] interfaces) { + } + + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + Signature sig = new Signature(name, desc); + if (eligibleMethods.remove(sig)) { + currentMethod = sig; + return new MethodVisitor(Opcodes.ASM6) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (opcode == Opcodes.INVOKESPECIAL && currentMethod != null) { + Signature target = new Signature(name, desc); + // If the target signature is the same as the current, + // we shouldn't change our bridge becaues invokespecial + // is the only way to make progress (otherwise we'll + // get infinite recursion). This would typically + // only happen when a bridge method is created to widen + // the visibility of a superclass' method. + if (!target.equals(currentMethod)) { + resolved.put(currentMethod, target); + } + currentMethod = null; + } + } + }; + } else { + return null; + } + } + } + +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Callback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Callback.java new file mode 100644 index 000000000..ae918cdfd --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Callback.java @@ -0,0 +1,29 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * All callback interfaces used by {@link Enhancer} extend this interface. + * @see MethodInterceptor + * @see NoOp + * @see LazyLoader + * @see Dispatcher + * @see InvocationHandler + * @see FixedValue + */ +public interface Callback +{ +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackFilter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackFilter.java new file mode 100644 index 000000000..c08dea41b --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackFilter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.Method; + +/** + * Map methods of subclasses generated by {@link Enhancer} to a particular + * callback. The type of the callbacks chosen for each method affects + * the bytecode generated for that method in the subclass, and cannot + * change for the life of the class. + *

Note: {@link CallbackFilter} implementations are supposed to be + * lightweight as cglib might keep {@link CallbackFilter} objects + * alive to enable caching of generated classes. Prefer using {@code static} + * classes for implementation of {@link CallbackFilter}.

+ */ +public interface CallbackFilter { + /** + * Map a method to a callback. + * @param method the intercepted method + * @return the index into the array of callbacks (as specified by {@link Enhancer#setCallbacks}) to use for the method, + */ + int accept(Method method); + + /** + * The CallbackFilter in use affects which cached class + * the Enhancer will use, so this is a reminder that + * you should correctly implement equals and + * hashCode for custom CallbackFilter + * implementations in order to improve performance. + */ + boolean equals(Object o); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackGenerator.java new file mode 100644 index 000000000..66c1957d6 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackGenerator.java @@ -0,0 +1,36 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.util.List; +import com.fr.third.net.sf.cglib.core.*; + +interface CallbackGenerator +{ + void generate(ClassEmitter ce, Context context, List methods) throws Exception; + void generateStatic(CodeEmitter e, Context context, List methods) throws Exception; + + interface Context + { + ClassLoader getClassLoader(); + CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method); + int getOriginalModifiers(MethodInfo method); + int getIndex(MethodInfo method); + void emitCallback(CodeEmitter ce, int index); + Signature getImplSignature(MethodInfo method); + void emitLoadArgsAndInvoke(CodeEmitter e, MethodInfo method); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackHelper.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackHelper.java new file mode 100644 index 000000000..81add3184 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackHelper.java @@ -0,0 +1,98 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import com.fr.third.net.sf.cglib.core.ReflectUtils; +import java.lang.reflect.Method; +import java.util.*; + +/** + * @version $Id: CallbackHelper.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $ + */ +abstract public class CallbackHelper +implements CallbackFilter +{ + private Map methodMap = new HashMap(); + private List callbacks = new ArrayList(); + + public CallbackHelper(Class superclass, Class[] interfaces) + { + List methods = new ArrayList(); + Enhancer.getMethods(superclass, interfaces, methods); + Map indexes = new HashMap(); + for (int i = 0, size = methods.size(); i < size; i++) { + Method method = (Method)methods.get(i); + Object callback = getCallback(method); + if (callback == null) + throw new IllegalStateException("getCallback cannot return null"); + boolean isCallback = callback instanceof Callback; + if (!(isCallback || (callback instanceof Class))) + throw new IllegalStateException("getCallback must return a Callback or a Class"); + if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback)) + throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method"); + Integer index = (Integer)indexes.get(callback); + if (index == null) { + index = new Integer(callbacks.size()); + indexes.put(callback, index); + } + methodMap.put(method, index); + callbacks.add(callback); + } + } + + abstract protected Object getCallback(Method method); + + public Callback[] getCallbacks() + { + if (callbacks.size() == 0) + return new Callback[0]; + if (callbacks.get(0) instanceof Callback) { + return (Callback[])callbacks.toArray(new Callback[callbacks.size()]); + } else { + throw new IllegalStateException("getCallback returned classes, not callbacks; call getCallbackTypes instead"); + } + } + + public Class[] getCallbackTypes() + { + if (callbacks.size() == 0) + return new Class[0]; + if (callbacks.get(0) instanceof Callback) { + return ReflectUtils.getClasses(getCallbacks()); + } else { + return (Class[])callbacks.toArray(new Class[callbacks.size()]); + } + } + + public int accept(Method method) + { + return ((Integer)methodMap.get(method)).intValue(); + } + + public int hashCode() + { + return methodMap.hashCode(); + } + + public boolean equals(Object o) + { + if (o == null) + return false; + if (!(o instanceof CallbackHelper)) + return false; + return methodMap.equals(((CallbackHelper)o).methodMap); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackInfo.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackInfo.java new file mode 100644 index 000000000..fdcdfebf4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackInfo.java @@ -0,0 +1,116 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import com.fr.third.org.objectweb.asm.Type; + +class CallbackInfo +{ + public static Type[] determineTypes(Class[] callbackTypes) { + return determineTypes(callbackTypes, true); + } + + public static Type[] determineTypes(Class[] callbackTypes, boolean checkAll) { + Type[] types = new Type[callbackTypes.length]; + for (int i = 0; i < types.length; i++) { + types[i] = determineType(callbackTypes[i], checkAll); + } + return types; + } + + public static Type[] determineTypes(Callback[] callbacks) { + return determineTypes(callbacks, true); + } + + public static Type[] determineTypes(Callback[] callbacks, boolean checkAll) { + Type[] types = new Type[callbacks.length]; + for (int i = 0; i < types.length; i++) { + types[i] = determineType(callbacks[i], checkAll); + } + return types; + } + + public static CallbackGenerator[] getGenerators(Type[] callbackTypes) { + CallbackGenerator[] generators = new CallbackGenerator[callbackTypes.length]; + for (int i = 0; i < generators.length; i++) { + generators[i] = getGenerator(callbackTypes[i]); + } + return generators; + } + + //////////////////// PRIVATE //////////////////// + + private Class cls; + private CallbackGenerator generator; + private Type type; + + private static final CallbackInfo[] CALLBACKS = { + new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE), + new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE), + new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE), + new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE), + new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE), + new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE), + new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE), + }; + + private CallbackInfo(Class cls, CallbackGenerator generator) { + this.cls = cls; + this.generator = generator; + type = Type.getType(cls); + } + + private static Type determineType(Callback callback, boolean checkAll) { + if (callback == null) { + throw new IllegalStateException("Callback is null"); + } + return determineType(callback.getClass(), checkAll); + } + + private static Type determineType(Class callbackType, boolean checkAll) { + Class cur = null; + Type type = null; + for (int i = 0; i < CALLBACKS.length; i++) { + CallbackInfo info = CALLBACKS[i]; + if (info.cls.isAssignableFrom(callbackType)) { + if (cur != null) { + throw new IllegalStateException("Callback implements both " + cur + " and " + info.cls); + } + cur = info.cls; + type = info.type; + if (!checkAll) { + break; + } + } + } + if (cur == null) { + throw new IllegalStateException("Unknown callback type " + callbackType); + } + return type; + } + + private static CallbackGenerator getGenerator(Type callbackType) { + for (int i = 0; i < CALLBACKS.length; i++) { + CallbackInfo info = CALLBACKS[i]; + if (info.type.equals(callbackType)) { + return info.generator; + } + } + throw new IllegalStateException("Unknown callback type " + callbackType); + } +} + + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Dispatcher.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Dispatcher.java new file mode 100644 index 000000000..f940d1d0d --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Dispatcher.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * Dispatching {@link Enhancer} callback. This is identical to the + * {@link LazyLoader} interface but needs to be separate so that Enhancer + * knows which type of code to generate. + */ +public interface Dispatcher extends Callback { + /** + * Return the object which the original method invocation should + * be dispatched. This method is called for every method invocation. + * @return an object that can invoke the method + */ + Object loadObject() throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/DispatcherGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/DispatcherGenerator.java new file mode 100644 index 000000000..8c34d1dd6 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/DispatcherGenerator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Type; + +class DispatcherGenerator implements CallbackGenerator { + public static final DispatcherGenerator INSTANCE = + new DispatcherGenerator(false); + public static final DispatcherGenerator PROXY_REF_INSTANCE = + new DispatcherGenerator(true); + + private static final Type DISPATCHER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Dispatcher"); + private static final Type PROXY_REF_DISPATCHER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.ProxyRefDispatcher"); + private static final Signature LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject()"); + private static final Signature PROXY_REF_LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject(Object)"); + + private boolean proxyRef; + + private DispatcherGenerator(boolean proxyRef) { + this.proxyRef = proxyRef; + } + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + if (!TypeUtils.isProtected(method.getModifiers())) { + CodeEmitter e = context.beginMethod(ce, method); + context.emitCallback(e, context.getIndex(method)); + if (proxyRef) { + e.load_this(); + e.invoke_interface(PROXY_REF_DISPATCHER, PROXY_REF_LOAD_OBJECT); + } else { + e.invoke_interface(DISPATCHER, LOAD_OBJECT); + } + e.checkcast(method.getClassInfo().getType()); + e.load_args(); + e.invoke(method); + e.return_value(); + e.end_method(); + } + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Enhancer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Enhancer.java new file mode 100644 index 000000000..873a88bab --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Enhancer.java @@ -0,0 +1,1319 @@ +/* + * Copyright 2002,2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import java.util.*; + +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; +import com.fr.third.org.objectweb.asm.Label; + +/** + * Generates dynamic subclasses to enable method interception. This + * class started as a substitute for the standard Dynamic Proxy support + * included with JDK 1.3, but one that allowed the proxies to extend a + * concrete base class, in addition to implementing interfaces. The dynamically + * generated subclasses override the non-final methods of the superclass and + * have hooks which callback to user-defined interceptor + * implementations. + *

+ * The original and most general callback type is the {@link MethodInterceptor}, which + * in AOP terms enables "around advice"--that is, you can invoke custom code both before + * and after the invocation of the "super" method. In addition you can modify the + * arguments before calling the super method, or not call it at all. + *

+ * Although MethodInterceptor is generic enough to meet any + * interception need, it is often overkill. For simplicity and performance, additional + * specialized callback types, such as {@link LazyLoader} are also available. + * Often a single callback will be used per enhanced class, but you can control + * which callback is used on a per-method basis with a {@link CallbackFilter}. + *

+ * The most common uses of this class are embodied in the static helper methods. For + * advanced needs, such as customizing the ClassLoader to use, you should create + * a new instance of Enhancer. Other classes within CGLIB follow a similar pattern. + *

+ * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is + * used to explicitly disable this feature. The Factory interface provides an API + * to change the callbacks of an existing object, as well as a faster and easier way to create + * new instances of the same type. + *

+ * For an almost drop-in replacement for + * java.lang.reflect.Proxy, see the {@link Proxy} class. + */ +public class Enhancer extends AbstractClassGenerator +{ + private static final CallbackFilter ALL_ZERO = new CallbackFilter(){ + public int accept(Method method) { + return 0; + } + }; + + private static final Source SOURCE = new Source(Enhancer.class.getName()); + private static final EnhancerKey KEY_FACTORY = + (EnhancerKey)KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null); + + private static final String BOUND_FIELD = "CGLIB$BOUND"; + private static final String FACTORY_DATA_FIELD = "CGLIB$FACTORY_DATA"; + private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS"; + private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS"; + private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS"; + private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS"; + private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED"; + /** + * {@link com.fr.third.net.sf.cglib.core.AbstractClassGenerator.ClassLoaderData#generatedClasses} requires to keep cache key + * in a good shape (the keys should be up and running if the proxy class is alive), and one of the cache keys is + * {@link CallbackFilter}. That is why the generated class contains static field that keeps strong reference to + * the {@link #filter}. + *

This dance achieves two goals: ensures generated class is reusable and available through generatedClasses + * cache, and it enables to unload classloader and the related {@link CallbackFilter} in case user does not need + * that

+ */ + private static final String CALLBACK_FILTER_FIELD = "CGLIB$CALLBACK_FILTER"; + + private static final Type OBJECT_TYPE = + TypeUtils.parseType("Object"); + private static final Type FACTORY = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Factory"); + private static final Type ILLEGAL_STATE_EXCEPTION = + TypeUtils.parseType("IllegalStateException"); + private static final Type ILLEGAL_ARGUMENT_EXCEPTION = + TypeUtils.parseType("IllegalArgumentException"); + private static final Type THREAD_LOCAL = + TypeUtils.parseType("ThreadLocal"); + private static final Type CALLBACK = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Callback"); + private static final Type CALLBACK_ARRAY = + Type.getType(Callback[].class); + private static final Signature CSTRUCT_NULL = + TypeUtils.parseConstructor(""); + private static final Signature SET_THREAD_CALLBACKS = + new Signature(SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY }); + private static final Signature SET_STATIC_CALLBACKS = + new Signature(SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY }); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK_ARRAY }); + private static final Signature MULTIARG_NEW_INSTANCE = + new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ + Constants.TYPE_CLASS_ARRAY, + Constants.TYPE_OBJECT_ARRAY, + CALLBACK_ARRAY, + }); + private static final Signature SINGLE_NEW_INSTANCE = + new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK }); + private static final Signature SET_CALLBACK = + new Signature("setCallback", Type.VOID_TYPE, new Type[]{ Type.INT_TYPE, CALLBACK }); + private static final Signature GET_CALLBACK = + new Signature("getCallback", CALLBACK, new Type[]{ Type.INT_TYPE }); + private static final Signature SET_CALLBACKS = + new Signature("setCallbacks", Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY }); + private static final Signature GET_CALLBACKS = + new Signature("getCallbacks", CALLBACK_ARRAY, new Type[0]); + private static final Signature THREAD_LOCAL_GET = + TypeUtils.parseSignature("Object get()"); + private static final Signature THREAD_LOCAL_SET = + TypeUtils.parseSignature("void set(Object)"); + private static final Signature BIND_CALLBACKS = + TypeUtils.parseSignature("void CGLIB$BIND_CALLBACKS(Object)"); + + private EnhancerFactoryData currentData; + private Object currentKey; + + /** Internal interface, only public due to ClassLoader issues. */ + public interface EnhancerKey { + public Object newInstance(String type, + String[] interfaces, + WeakCacheKey filter, + Type[] callbackTypes, + boolean useFactory, + boolean interceptDuringConstruction, + Long serialVersionUID); + } + + private Class[] interfaces; + private CallbackFilter filter; + private Callback[] callbacks; + private Type[] callbackTypes; + private boolean validateCallbackTypes; + private boolean classOnly; + private Class superclass; + private Class[] argumentTypes; + private Object[] arguments; + private boolean useFactory = true; + private Long serialVersionUID; + private boolean interceptDuringConstruction = true; + + /** + * Create a new Enhancer. A new Enhancer + * object should be used for each generated object, and should not + * be shared across threads. To create additional instances of a + * generated class, use the Factory interface. + * @see Factory + */ + public Enhancer() { + super(SOURCE); + } + + /** + * Set the class which the generated class will extend. As a convenience, + * if the supplied superclass is actually an interface, setInterfaces + * will be called with the appropriate argument instead. + * A non-interface argument must not be declared as final, and must have an + * accessible constructor. + * @param superclass class to extend or interface to implement + * @see #setInterfaces(Class[]) + */ + public void setSuperclass(Class superclass) { + if (superclass != null && superclass.isInterface()) { + setInterfaces(new Class[]{ superclass }); + } else if (superclass != null && superclass.equals(Object.class)) { + // affects choice of ClassLoader + this.superclass = null; + } else { + this.superclass = superclass; + } + } + + /** + * Set the interfaces to implement. The Factory interface will + * always be implemented regardless of what is specified here. + * @param interfaces array of interfaces to implement, or null + * @see Factory + */ + public void setInterfaces(Class[] interfaces) { + this.interfaces = interfaces; + } + + /** + * Set the {@link CallbackFilter} used to map the generated class' methods + * to a particular callback index. + * New object instances will always use the same mapping, but may use different + * actual callback objects. + * @param filter the callback filter to use when generating a new class + * @see #setCallbacks + */ + public void setCallbackFilter(CallbackFilter filter) { + this.filter = filter; + } + + + /** + * Set the single {@link Callback} to use. + * Ignored if you use {@link #createClass}. + * @param callback the callback to use for all methods + * @see #setCallbacks + */ + public void setCallback(final Callback callback) { + setCallbacks(new Callback[]{ callback }); + } + + /** + * Set the array of callbacks to use. + * Ignored if you use {@link #createClass}. + * You must use a {@link CallbackFilter} to specify the index into this + * array for each method in the proxied class. + * @param callbacks the callback array + * @see #setCallbackFilter + * @see #setCallback + */ + public void setCallbacks(Callback[] callbacks) { + if (callbacks != null && callbacks.length == 0) { + throw new IllegalArgumentException("Array cannot be empty"); + } + this.callbacks = callbacks; + } + + /** + * Set whether the enhanced object instances should implement + * the {@link Factory} interface. + * This was added for tools that need for proxies to be more + * indistinguishable from their targets. Also, in some cases it may + * be necessary to disable the Factory interface to + * prevent code from changing the underlying callbacks. + * @param useFactory whether to implement Factory; default is true + */ + public void setUseFactory(boolean useFactory) { + this.useFactory = useFactory; + } + + /** + * Set whether methods called from within the proxy's constructer + * will be intercepted. The default value is true. Unintercepted methods + * will call the method of the proxy's base class, if it exists. + * @param interceptDuringConstruction whether to intercept methods called from the constructor + */ + public void setInterceptDuringConstruction(boolean interceptDuringConstruction) { + this.interceptDuringConstruction = interceptDuringConstruction; + } + + /** + * Set the single type of {@link Callback} to use. + * This may be used instead of {@link #setCallback} when calling + * {@link #createClass}, since it may not be possible to have + * an array of actual callback instances. + * @param callbackType the type of callback to use for all methods + * @see #setCallbackTypes + */ + public void setCallbackType(Class callbackType) { + setCallbackTypes(new Class[]{ callbackType }); + } + + /** + * Set the array of callback types to use. + * This may be used instead of {@link #setCallbacks} when calling + * {@link #createClass}, since it may not be possible to have + * an array of actual callback instances. + * You must use a {@link CallbackFilter} to specify the index into this + * array for each method in the proxied class. + * @param callbackTypes the array of callback types + */ + public void setCallbackTypes(Class[] callbackTypes) { + if (callbackTypes != null && callbackTypes.length == 0) { + throw new IllegalArgumentException("Array cannot be empty"); + } + this.callbackTypes = CallbackInfo.determineTypes(callbackTypes); + } + + /** + * Generate a new class if necessary and uses the specified + * callbacks (if any) to create a new object instance. + * Uses the no-arg constructor of the superclass. + * @return a new instance + */ + public Object create() { + classOnly = false; + argumentTypes = null; + return createHelper(); + } + + /** + * Generate a new class if necessary and uses the specified + * callbacks (if any) to create a new object instance. + * Uses the constructor of the superclass matching the argumentTypes + * parameter, with the given arguments. + * @param argumentTypes constructor signature + * @param arguments compatible wrapped arguments to pass to constructor + * @return a new instance + */ + public Object create(Class[] argumentTypes, Object[] arguments) { + classOnly = false; + if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) { + throw new IllegalArgumentException("Arguments must be non-null and of equal length"); + } + this.argumentTypes = argumentTypes; + this.arguments = arguments; + return createHelper(); + } + + /** + * Generate a new class if necessary and return it without creating a new instance. + * This ignores any callbacks that have been set. + * To create a new instance you will have to use reflection, and methods + * called during the constructor will not be intercepted. To avoid this problem, + * use the multi-arg create method. + * @see #create(Class[], Object[]) + */ + public Class createClass() { + classOnly = true; + return (Class)createHelper(); + } + + /** + * Insert a static serialVersionUID field into the generated class. + * @param sUID the field value, or null to avoid generating field. + */ + public void setSerialVersionUID(Long sUID) { + serialVersionUID = sUID; + } + + private void preValidate() { + if (callbackTypes == null) { + callbackTypes = CallbackInfo.determineTypes(callbacks, false); + validateCallbackTypes = true; + } + if (filter == null) { + if (callbackTypes.length > 1) { + throw new IllegalStateException("Multiple callback types possible but no filter specified"); + } + filter = ALL_ZERO; + } + } + + private void validate() { + if (classOnly ^ (callbacks == null)) { + if (classOnly) { + throw new IllegalStateException("createClass does not accept callbacks"); + } else { + throw new IllegalStateException("Callbacks are required"); + } + } + if (classOnly && (callbackTypes == null)) { + throw new IllegalStateException("Callback types are required"); + } + if (validateCallbackTypes) { + callbackTypes = null; + } + if (callbacks != null && callbackTypes != null) { + if (callbacks.length != callbackTypes.length) { + throw new IllegalStateException("Lengths of callback and callback types array must be the same"); + } + Type[] check = CallbackInfo.determineTypes(callbacks); + for (int i = 0; i < check.length; i++) { + if (!check[i].equals(callbackTypes[i])) { + throw new IllegalStateException("Callback " + check[i] + " is not assignable to " + callbackTypes[i]); + } + } + } else if (callbacks != null) { + callbackTypes = CallbackInfo.determineTypes(callbacks); + } + if (interfaces != null) { + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i] == null) { + throw new IllegalStateException("Interfaces cannot be null"); + } + if (!interfaces[i].isInterface()) { + throw new IllegalStateException(interfaces[i] + " is not an interface"); + } + } + } + } + + /** + * The idea of the class is to cache relevant java.lang.reflect instances so + * proxy-class can be instantiated faster that when using {@link ReflectUtils#newInstance(Class, Class[], Object[])} + * and {@link Enhancer#setThreadCallbacks(Class, Callback[])} + */ + static class EnhancerFactoryData { + public final Class generatedClass; + private final Method setThreadCallbacks; + private final Class[] primaryConstructorArgTypes; + private final Constructor primaryConstructor; + + public EnhancerFactoryData(Class generatedClass, Class[] primaryConstructorArgTypes, boolean classOnly) { + this.generatedClass = generatedClass; + try { + setThreadCallbacks = getCallbacksSetter(generatedClass, SET_THREAD_CALLBACKS_NAME); + if (classOnly) { + this.primaryConstructorArgTypes = null; + this.primaryConstructor = null; + } else { + this.primaryConstructorArgTypes = primaryConstructorArgTypes; + this.primaryConstructor = ReflectUtils.getConstructor(generatedClass, primaryConstructorArgTypes); + } + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + /** + * Creates proxy instance for given argument types, and assigns the callbacks. + * Ideally, for each proxy class, just one set of argument types should be used, + * otherwise it would have to spend time on constructor lookup. + * Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)}, + * with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}" + * + * @see #createUsingReflection(Class) + * @param argumentTypes constructor argument types + * @param arguments constructor arguments + * @param callbacks callbacks to set for the new instance + * @return newly created proxy + */ + public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) { + setThreadCallbacks(callbacks); + try { + // Explicit reference equality is added here just in case Arrays.equals does not have one + if (primaryConstructorArgTypes == argumentTypes || + Arrays.equals(primaryConstructorArgTypes, argumentTypes)) { + // If we have relevant Constructor instance at hand, just call it + // This skips "get constructors" machinery + return ReflectUtils.newInstance(primaryConstructor, arguments); + } + // Take a slow path if observing unexpected argument types + return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments); + } finally { + // clear thread callbacks to allow them to be gc'd + setThreadCallbacks(null); + } + + } + + private void setThreadCallbacks(Callback[] callbacks) { + try { + setThreadCallbacks.invoke(generatedClass, (Object) callbacks); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e.getTargetException()); + } + } + } + + private Object createHelper() { + preValidate(); + Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null, + ReflectUtils.getNames(interfaces), + filter == ALL_ZERO ? null : new WeakCacheKey(filter), + callbackTypes, + useFactory, + interceptDuringConstruction, + serialVersionUID); + this.currentKey = key; + Object result = super.create(key); + return result; + } + + @Override + protected Class generate(ClassLoaderData data) { + validate(); + if (superclass != null) { + setNamePrefix(superclass.getName()); + } else if (interfaces != null) { + setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName()); + } + return super.generate(data); + } + + protected ClassLoader getDefaultClassLoader() { + if (superclass != null) { + return superclass.getClassLoader(); + } else if (interfaces != null) { + return interfaces[0].getClassLoader(); + } else { + return null; + } + } + + protected ProtectionDomain getProtectionDomain() { + if (superclass != null) { + return ReflectUtils.getProtectionDomain(superclass); + } else if (interfaces != null) { + return ReflectUtils.getProtectionDomain(interfaces[0]); + } else { + return null; + } + } + + private Signature rename(Signature sig, int index) { + return new Signature("CGLIB$" + sig.getName() + "$" + index, + sig.getDescriptor()); + } + + /** + * Finds all of the methods that will be extended by an + * Enhancer-generated class using the specified superclass and + * interfaces. This can be useful in building a list of Callback + * objects. The methods are added to the end of the given list. Due + * to the subclassing nature of the classes generated by Enhancer, + * the methods are guaranteed to be non-static, non-final, and + * non-private. Each method signature will only occur once, even if + * it occurs in multiple classes. + * @param superclass the class that will be extended, or null + * @param interfaces the list of interfaces that will be implemented, or null + * @param methods the list into which to copy the applicable methods + */ + public static void getMethods(Class superclass, Class[] interfaces, List methods) + { + getMethods(superclass, interfaces, methods, null, null); + } + + private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic) + { + ReflectUtils.addAllMethods(superclass, methods); + List target = (interfaceMethods != null) ? interfaceMethods : methods; + if (interfaces != null) { + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i] != Factory.class) { + ReflectUtils.addAllMethods(interfaces[i], target); + } + } + } + if (interfaceMethods != null) { + if (forcePublic != null) { + forcePublic.addAll(MethodWrapper.createSet(interfaceMethods)); + } + methods.addAll(interfaceMethods); + } + CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC)); + CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true)); + CollectionUtils.filter(methods, new DuplicatesPredicate()); + CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL)); + } + + public void generateClass(ClassVisitor v) throws Exception { + Class sc = (superclass == null) ? Object.class : superclass; + + if (TypeUtils.isFinal(sc.getModifiers())) + throw new IllegalArgumentException("Cannot subclass final class " + sc.getName()); + List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors())); + filterConstructors(sc, constructors); + + // Order is very important: must add superclass, then + // its superclass chain, then each interface and + // its superinterfaces. + List actualMethods = new ArrayList(); + List interfaceMethods = new ArrayList(); + final Set forcePublic = new HashSet(); + getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic); + + List methods = CollectionUtils.transform(actualMethods, new Transformer() { + public Object transform(Object value) { + Method method = (Method)value; + int modifiers = Constants.ACC_FINAL + | (method.getModifiers() + & ~Constants.ACC_ABSTRACT + & ~Constants.ACC_NATIVE + & ~Constants.ACC_SYNCHRONIZED); + if (forcePublic.contains(MethodWrapper.create(method))) { + modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC; + } + return ReflectUtils.getMethodInfo(method, modifiers); + } + }); + + ClassEmitter e = new ClassEmitter(v); + if (currentData == null) { + e.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + Type.getType(sc), + (useFactory ? + TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) : + TypeUtils.getTypes(interfaces)), + Constants.SOURCE_FILE); + } else { + e.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + null, + new Type[]{FACTORY}, + Constants.SOURCE_FILE); + } + List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance()); + + e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null); + e.declare_field(Constants.ACC_PUBLIC | Constants.ACC_STATIC, FACTORY_DATA_FIELD, OBJECT_TYPE, null); + if (!interceptDuringConstruction) { + e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null); + } + e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null); + e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null); + if (serialVersionUID != null) { + e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID); + } + + for (int i = 0; i < callbackTypes.length; i++) { + e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null); + } + // This is declared private to avoid "public field" pollution + e.declare_field(Constants.ACC_PRIVATE | Constants.ACC_STATIC, CALLBACK_FILTER_FIELD, OBJECT_TYPE, null); + + if (currentData == null) { + emitMethods(e, methods, actualMethods); + emitConstructors(e, constructorInfo); + } else { + emitDefaultConstructor(e); + } + emitSetThreadCallbacks(e); + emitSetStaticCallbacks(e); + emitBindCallbacks(e); + + if (useFactory || currentData != null) { + int[] keys = getCallbackKeys(); + emitNewInstanceCallbacks(e); + emitNewInstanceCallback(e); + emitNewInstanceMultiarg(e, constructorInfo); + emitGetCallback(e, keys); + emitSetCallback(e, keys); + emitGetCallbacks(e); + emitSetCallbacks(e); + } + + e.end_class(); + } + + /** + * Filter the list of constructors from the superclass. The + * constructors which remain will be included in the generated + * class. The default implementation is to filter out all private + * constructors, but subclasses may extend Enhancer to override this + * behavior. + * @param sc the superclass + * @param constructors the list of all declared constructors from the superclass + * @throws IllegalArgumentException if there are no non-private constructors + */ + protected void filterConstructors(Class sc, List constructors) { + CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true)); + if (constructors.size() == 0) + throw new IllegalArgumentException("No visible constructors in " + sc); + } + + /** + * This method should not be called in regular flow. + * Technically speaking {@link #wrapCachedClass(Class)} uses {@link EnhancerFactoryData} as a cache value, + * and the latter enables faster instantiation than plain old reflection lookup and invoke. + * This method is left intact for backward compatibility reasons: just in case it was ever used. + * + * @param type class to instantiate + * @return newly created proxy instance + * @throws Exception if something goes wrong + */ + protected Object firstInstance(Class type) throws Exception { + if (classOnly) { + return type; + } else { + return createUsingReflection(type); + } + } + + protected Object nextInstance(Object instance) { + EnhancerFactoryData data = (EnhancerFactoryData) instance; + + if (classOnly) { + return data.generatedClass; + } + + Class[] argumentTypes = this.argumentTypes; + Object[] arguments = this.arguments; + if (argumentTypes == null) { + argumentTypes = Constants.EMPTY_CLASS_ARRAY; + arguments = null; + } + return data.newInstance(argumentTypes, arguments, callbacks); + } + + @Override + protected Object wrapCachedClass(Class klass) { + Class[] argumentTypes = this.argumentTypes; + if (argumentTypes == null) { + argumentTypes = Constants.EMPTY_CLASS_ARRAY; + } + EnhancerFactoryData factoryData = new EnhancerFactoryData(klass, argumentTypes, classOnly); + Field factoryDataField = null; + try { + // The subsequent dance is performed just once for each class, + // so it does not matter much how fast it goes + factoryDataField = klass.getField(FACTORY_DATA_FIELD); + factoryDataField.set(null, factoryData); + Field callbackFilterField = klass.getDeclaredField(CALLBACK_FILTER_FIELD); + callbackFilterField.setAccessible(true); + callbackFilterField.set(null, this.filter); + } catch (NoSuchFieldException e) { + throw new CodeGenerationException(e); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } + return new WeakReference(factoryData); + } + + @Override + protected Object unwrapCachedValue(Object cached) { + if (currentKey instanceof EnhancerKey) { + EnhancerFactoryData data = ((WeakReference) cached).get(); + return data; + } + return super.unwrapCachedValue(cached); + } + + /** + * Call this method to register the {@link Callback} array to use before + * creating a new instance of the generated class via reflection. If you are using + * an instance of Enhancer or the {@link Factory} interface to create + * new instances, this method is unnecessary. Its primary use is for when you want to + * cache and reuse a generated class yourself, and the generated class does + * not implement the {@link Factory} interface. + *

+ * Note that this method only registers the callbacks on the current thread. + * If you want to register callbacks for instances created by multiple threads, + * use {@link #registerStaticCallbacks}. + *

+ * The registered callbacks are overwritten and subsequently cleared + * when calling any of the create methods (such as + * {@link #create}), or any {@link Factory} newInstance method. + * Otherwise they are not cleared, and you should be careful to set them + * back to null after creating new instances via reflection if + * memory leakage is a concern. + * @param generatedClass a class previously created by {@link Enhancer} + * @param callbacks the array of callbacks to use when instances of the generated + * class are created + * @see #setUseFactory + */ + public static void registerCallbacks(Class generatedClass, Callback[] callbacks) { + setThreadCallbacks(generatedClass, callbacks); + } + + /** + * Similar to {@link #registerCallbacks}, but suitable for use + * when multiple threads will be creating instances of the generated class. + * The thread-level callbacks will always override the static callbacks. + * Static callbacks are never cleared. + * @param generatedClass a class previously created by {@link Enhancer} + * @param callbacks the array of callbacks to use when instances of the generated + * class are created + */ + public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) { + setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME); + } + + /** + * Determine if a class was generated using Enhancer. + * @param type any class + * @return whether the class was generated using Enhancer + */ + public static boolean isEnhanced(Class type) { + try { + getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + private static void setThreadCallbacks(Class type, Callback[] callbacks) { + setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME); + } + + private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) { + // TODO: optimize + try { + Method setter = getCallbacksSetter(type, methodName); + setter.invoke(null, new Object[]{ callbacks }); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(type + " is not an enhanced class"); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e); + } + } + + private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException { + return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class }); + } + + /** + * Instantiates a proxy instance and assigns callback values. + * Implementation detail: java.lang.reflect instances are not cached, so this method should not + * be used on a hot path. + * This method is used when {@link #setUseCache(boolean)} is set to {@code false}. + * + * @param type class to instantiate + * @return newly created instance + */ + private Object createUsingReflection(Class type) { + setThreadCallbacks(type, callbacks); + try{ + + if (argumentTypes != null) { + + return ReflectUtils.newInstance(type, argumentTypes, arguments); + + } else { + + return ReflectUtils.newInstance(type); + + } + }finally{ + // clear thread callbacks to allow them to be gc'd + setThreadCallbacks(type, null); + } + } + + /** + * Helper method to create an intercepted object. + * For finer control over the generated instance, use a new instance of Enhancer + * instead of this static method. + * @param type class to extend or interface to implement + * @param callback the callback to use for all methods + */ + public static Object create(Class type, Callback callback) { + Enhancer e = new Enhancer(); + e.setSuperclass(type); + e.setCallback(callback); + return e.create(); + } + + /** + * Helper method to create an intercepted object. + * For finer control over the generated instance, use a new instance of Enhancer + * instead of this static method. + * @param superclass class to extend or interface to implement + * @param interfaces array of interfaces to implement, or null + * @param callback the callback to use for all methods + */ + public static Object create(Class superclass, Class interfaces[], Callback callback) { + Enhancer e = new Enhancer(); + e.setSuperclass(superclass); + e.setInterfaces(interfaces); + e.setCallback(callback); + return e.create(); + } + + /** + * Helper method to create an intercepted object. + * For finer control over the generated instance, use a new instance of Enhancer + * instead of this static method. + * @param superclass class to extend or interface to implement + * @param interfaces array of interfaces to implement, or null + * @param filter the callback filter to use when generating a new class + * @param callbacks callback implementations to use for the enhanced object + */ + public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) { + Enhancer e = new Enhancer(); + e.setSuperclass(superclass); + e.setInterfaces(interfaces); + e.setCallbackFilter(filter); + e.setCallbacks(callbacks); + return e.create(); + } + + private void emitDefaultConstructor(ClassEmitter ce) { + Constructor declaredConstructor; + try { + declaredConstructor = Object.class.getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + throw new IllegalStateException("Object should have default constructor ", e); + } + MethodInfo constructor = (MethodInfo) MethodInfoTransformer.getInstance().transform(declaredConstructor); + CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC); + e.load_this(); + e.dup(); + Signature sig = constructor.getSignature(); + e.super_invoke_constructor(sig); + e.return_value(); + e.end_method(); + } + + private void emitConstructors(ClassEmitter ce, List constructors) { + boolean seenNull = false; + for (Iterator it = constructors.iterator(); it.hasNext();) { + MethodInfo constructor = (MethodInfo)it.next(); + if (currentData != null && !"()V".equals(constructor.getSignature().getDescriptor())) { + continue; + } + CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC); + e.load_this(); + e.dup(); + e.load_args(); + Signature sig = constructor.getSignature(); + seenNull = seenNull || sig.getDescriptor().equals("()V"); + e.super_invoke_constructor(sig); + if (currentData == null) { + e.invoke_static_this(BIND_CALLBACKS); + if (!interceptDuringConstruction) { + e.load_this(); + e.push(1); + e.putfield(CONSTRUCTED_FIELD); + } + } + e.return_value(); + e.end_method(); + } + if (!classOnly && !seenNull && arguments == null) + throw new IllegalArgumentException("Superclass has no null constructors but no arguments were given"); + } + + private int[] getCallbackKeys() { + int[] keys = new int[callbackTypes.length]; + for (int i = 0; i < callbackTypes.length; i++) { + keys[i] = i; + } + return keys; + } + + private void emitGetCallback(ClassEmitter ce, int[] keys) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null); + e.load_this(); + e.invoke_static_this(BIND_CALLBACKS); + e.load_this(); + e.load_arg(0); + e.process_switch(keys, new ProcessSwitchCallback() { + public void processCase(int key, Label end) { + e.getfield(getCallbackField(key)); + e.goTo(end); + } + public void processDefault() { + e.pop(); // stack height + e.aconst_null(); + } + }); + e.return_value(); + e.end_method(); + } + + private void emitSetCallback(ClassEmitter ce, int[] keys) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null); + e.load_arg(0); + e.process_switch(keys, new ProcessSwitchCallback() { + public void processCase(int key, Label end) { + e.load_this(); + e.load_arg(1); + e.checkcast(callbackTypes[key]); + e.putfield(getCallbackField(key)); + e.goTo(end); + } + public void processDefault() { + // TODO: error? + } + }); + e.return_value(); + e.end_method(); + } + + private void emitSetCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null); + e.load_this(); + e.load_arg(0); + for (int i = 0; i < callbackTypes.length; i++) { + e.dup2(); + e.aaload(i); + e.checkcast(callbackTypes[i]); + e.putfield(getCallbackField(i)); + } + e.return_value(); + e.end_method(); + } + + private void emitGetCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null); + e.load_this(); + e.invoke_static_this(BIND_CALLBACKS); + e.load_this(); + e.push(callbackTypes.length); + e.newarray(CALLBACK); + for (int i = 0; i < callbackTypes.length; i++) { + e.dup(); + e.push(i); + e.load_this(); + e.getfield(getCallbackField(i)); + e.aastore(); + } + e.return_value(); + e.end_method(); + } + + private void emitNewInstanceCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); + Type thisType = getThisType(e); + e.load_arg(0); + e.invoke_static(thisType, SET_THREAD_CALLBACKS); + emitCommonNewInstance(e); + } + + private Type getThisType(CodeEmitter e) { + if (currentData == null) { + return e.getClassEmitter().getClassType(); + } else { + return Type.getType(currentData.generatedClass); + } + } + + private void emitCommonNewInstance(CodeEmitter e) { + Type thisType = getThisType(e); + e.new_instance(thisType); + e.dup(); + e.invoke_constructor(thisType); + e.aconst_null(); + e.invoke_static(thisType, SET_THREAD_CALLBACKS); + e.return_value(); + e.end_method(); + } + + private void emitNewInstanceCallback(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null); + switch (callbackTypes.length) { + case 0: + // TODO: make sure Callback is null + break; + case 1: + // for now just make a new array; TODO: optimize + e.push(1); + e.newarray(CALLBACK); + e.dup(); + e.push(0); + e.load_arg(0); + e.aastore(); + e.invoke_static(getThisType(e), SET_THREAD_CALLBACKS); + break; + default: + e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required"); + } + emitCommonNewInstance(e); + } + + private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null); + final Type thisType = getThisType(e); + e.load_arg(2); + e.invoke_static(thisType, SET_THREAD_CALLBACKS); + e.new_instance(thisType); + e.dup(); + e.load_arg(0); + EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + MethodInfo constructor = (MethodInfo)key; + Type types[] = constructor.getSignature().getArgumentTypes(); + for (int i = 0; i < types.length; i++) { + e.load_arg(1); + e.push(i); + e.aaload(); + e.unbox(types[i]); + } + e.invoke_constructor(thisType, constructor.getSignature()); + e.goTo(end); + } + public void processDefault() { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found"); + } + }); + e.aconst_null(); + e.invoke_static(thisType, SET_THREAD_CALLBACKS); + e.return_value(); + e.end_method(); + } + + private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) { + CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes); + + Map groups = new HashMap(); + final Map indexes = new HashMap(); + final Map originalModifiers = new HashMap(); + final Map positions = CollectionUtils.getIndexMap(methods); + final Map declToBridge = new HashMap(); + + Iterator it1 = methods.iterator(); + Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null; + + while (it1.hasNext()) { + MethodInfo method = (MethodInfo)it1.next(); + Method actualMethod = (it2 != null) ? (Method)it2.next() : null; + int index = filter.accept(actualMethod); + if (index >= callbackTypes.length) { + throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index); + } + originalModifiers.put(method, new Integer((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers())); + indexes.put(method, new Integer(index)); + List group = (List)groups.get(generators[index]); + if (group == null) { + groups.put(generators[index], group = new ArrayList(methods.size())); + } + group.add(method); + + // Optimization: build up a map of Class -> bridge methods in class + // so that we can look up all the bridge methods in one pass for a class. + if (TypeUtils.isBridge(actualMethod.getModifiers())) { + Set bridges = (Set)declToBridge.get(actualMethod.getDeclaringClass()); + if (bridges == null) { + bridges = new HashSet(); + declToBridge.put(actualMethod.getDeclaringClass(), bridges); + } + bridges.add(method.getSignature()); + } + } + + final Map bridgeToTarget = new BridgeMethodResolver(declToBridge, getClassLoader()).resolveAll(); + + Set seenGen = new HashSet(); + CodeEmitter se = ce.getStaticHook(); + se.new_instance(THREAD_LOCAL); + se.dup(); + se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL); + se.putfield(THREAD_CALLBACKS_FIELD); + + final Object[] state = new Object[1]; + CallbackGenerator.Context context = new CallbackGenerator.Context() { + public ClassLoader getClassLoader() { + return Enhancer.this.getClassLoader(); + } + public int getOriginalModifiers(MethodInfo method) { + return ((Integer)originalModifiers.get(method)).intValue(); + } + public int getIndex(MethodInfo method) { + return ((Integer)indexes.get(method)).intValue(); + } + public void emitCallback(CodeEmitter e, int index) { + emitCurrentCallback(e, index); + } + public Signature getImplSignature(MethodInfo method) { + return rename(method.getSignature(), ((Integer)positions.get(method)).intValue()); + } + public void emitLoadArgsAndInvoke(CodeEmitter e, MethodInfo method) { + // If this is a bridge and we know the target was called from invokespecial, + // then we need to invoke_virtual w/ the bridge target instead of doing + // a super, because super may itself be using super, which would bypass + // any proxies on the target. + Signature bridgeTarget = (Signature)bridgeToTarget.get(method.getSignature()); + if (bridgeTarget != null) { + // checkcast each argument against the target's argument types + for (int i = 0; i < bridgeTarget.getArgumentTypes().length; i++) { + e.load_arg(i); + Type target = bridgeTarget.getArgumentTypes()[i]; + if (!target.equals(method.getSignature().getArgumentTypes()[i])) { + e.checkcast(target); + } + } + + e.invoke_virtual_this(bridgeTarget); + + Type retType = method.getSignature().getReturnType(); + // Not necessary to cast if the target & bridge have + // the same return type. + // (This conveniently includes void and primitive types, + // which would fail if casted. It's not possible to + // covariant from boxed to unbox (or vice versa), so no having + // to box/unbox for bridges). + // TODO: It also isn't necessary to checkcast if the return is + // assignable from the target. (This would happen if a subclass + // used covariant returns to narrow the return type within a bridge + // method.) + if (!retType.equals(bridgeTarget.getReturnType())) { + e.checkcast(retType); + } + } else { + e.load_args(); + e.super_invoke(method.getSignature()); + } + } + public CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method) { + CodeEmitter e = EmitUtils.begin_method(ce, method); + if (!interceptDuringConstruction && + !TypeUtils.isAbstract(method.getModifiers())) { + Label constructed = e.make_label(); + e.load_this(); + e.getfield(CONSTRUCTED_FIELD); + e.if_jump(e.NE, constructed); + e.load_this(); + e.load_args(); + e.super_invoke(); + e.return_value(); + e.mark(constructed); + } + return e; + } + }; + for (int i = 0; i < callbackTypes.length; i++) { + CallbackGenerator gen = generators[i]; + if (!seenGen.contains(gen)) { + seenGen.add(gen); + final List fmethods = (List)groups.get(gen); + if (fmethods != null) { + try { + gen.generate(ce, context, fmethods); + gen.generateStatic(se, context, fmethods); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new CodeGenerationException(x); + } + } + } + } + se.return_value(); + se.end_method(); + } + + private void emitSetThreadCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + SET_THREAD_CALLBACKS, + null); + e.getfield(THREAD_CALLBACKS_FIELD); + e.load_arg(0); + e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET); + e.return_value(); + e.end_method(); + } + + private void emitSetStaticCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + SET_STATIC_CALLBACKS, + null); + e.load_arg(0); + e.putfield(STATIC_CALLBACKS_FIELD); + e.return_value(); + e.end_method(); + } + + private void emitCurrentCallback(CodeEmitter e, int index) { + e.load_this(); + e.getfield(getCallbackField(index)); + e.dup(); + Label end = e.make_label(); + e.ifnonnull(end); + e.pop(); // stack height + e.load_this(); + e.invoke_static_this(BIND_CALLBACKS); + e.load_this(); + e.getfield(getCallbackField(index)); + e.mark(end); + } + + private void emitBindCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC, + BIND_CALLBACKS, + null); + Local me = e.make_local(); + e.load_arg(0); + e.checkcast_this(); + e.store_local(me); + + Label end = e.make_label(); + e.load_local(me); + e.getfield(BOUND_FIELD); + e.if_jump(e.NE, end); + e.load_local(me); + e.push(1); + e.putfield(BOUND_FIELD); + + e.getfield(THREAD_CALLBACKS_FIELD); + e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET); + e.dup(); + Label found_callback = e.make_label(); + e.ifnonnull(found_callback); + e.pop(); + + e.getfield(STATIC_CALLBACKS_FIELD); + e.dup(); + e.ifnonnull(found_callback); + e.pop(); + e.goTo(end); + + e.mark(found_callback); + e.checkcast(CALLBACK_ARRAY); + e.load_local(me); + e.swap(); + for (int i = callbackTypes.length - 1; i >= 0; i--) { + if (i != 0) { + e.dup2(); + } + e.aaload(i); + e.checkcast(callbackTypes[i]); + e.putfield(getCallbackField(i)); + } + + e.mark(end); + e.return_value(); + e.end_method(); + } + + private static String getCallbackField(int index) { + return "CGLIB$CALLBACK_" + index; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Factory.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Factory.java new file mode 100644 index 000000000..9cfc61628 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Factory.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002,2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fr.third.net.sf.cglib.proxy; + +/** + * All enhanced instances returned by the {@link Enhancer} class implement this interface. + * Using this interface for new instances is faster than going through the Enhancer + * interface or using reflection. In addition, to intercept methods called during + * object construction you must use these methods instead of reflection. + * @author Juozas Baliuka baliuka@mwm.lt + * @version $Id: Factory.java,v 1.13 2004/06/24 21:15:20 herbyderby Exp $ + */ +public interface Factory { + /** + * Creates new instance of the same type, using the no-arg constructor. + * The class of this object must have been created using a single Callback type. + * If multiple callbacks are required an exception will be thrown. + * @param callback the new interceptor to use + * @return new instance of the same type + */ + Object newInstance(Callback callback); + + /** + * Creates new instance of the same type, using the no-arg constructor. + * @param callbacks the new callbacks(s) to use + * @return new instance of the same type + */ + Object newInstance(Callback[] callbacks); + + /** + * Creates a new instance of the same type, using the constructor + * matching the given signature. + * @param types the constructor argument types + * @param args the constructor arguments + * @param callbacks the new interceptor(s) to use + * @return new instance of the same type + */ + Object newInstance(Class[] types, Object[] args, Callback[] callbacks); + + /** + * Return the Callback implementation at the specified index. + * @param index the callback index + * @return the callback implementation + */ + Callback getCallback(int index); + + /** + * Set the callback for this object for the given type. + * @param index the callback index to replace + * @param callback the new callback + */ + void setCallback(int index, Callback callback); + + /** + * Replace all of the callbacks for this object at once. + * @param callbacks the new callbacks(s) to use + */ + void setCallbacks(Callback[] callbacks); + + /** + * Get the current set of callbacks for ths object. + * @return a new array instance + */ + Callback[] getCallbacks(); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/FixedValue.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/FixedValue.java new file mode 100644 index 000000000..7381e7d09 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/FixedValue.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * {@link Enhancer} callback that simply returns the value to return + * from the proxied method. No information about what method + * is being called is available to the callback, and the type of + * the returned object must be compatible with the return type of + * the proxied method. This makes this callback primarily useful + * for forcing a particular method (through the use of a {@link CallbackFilter} + * to return a fixed value with little overhead. + */ +public interface FixedValue extends Callback { + /** + * Return the object which the original method invocation should + * return. This method is called for every method invocation. + * @return an object matching the type of the return value for every + * method this callback is mapped to + */ + Object loadObject() throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/FixedValueGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/FixedValueGenerator.java new file mode 100644 index 000000000..3bc09044a --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/FixedValueGenerator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Type; + +class FixedValueGenerator implements CallbackGenerator { + public static final FixedValueGenerator INSTANCE = new FixedValueGenerator(); + private static final Type FIXED_VALUE = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.FixedValue"); + private static final Signature LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject()"); + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + CodeEmitter e = context.beginMethod(ce, method); + context.emitCallback(e, context.getIndex(method)); + e.invoke_interface(FIXED_VALUE, LOAD_OBJECT); + e.unbox_or_zero(e.getReturnType()); + e.return_value(); + e.end_method(); + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InterfaceMaker.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InterfaceMaker.java new file mode 100644 index 000000000..41c185dab --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InterfaceMaker.java @@ -0,0 +1,118 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.*; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +/** + * Generates new interfaces at runtime. + * By passing a generated interface to the Enhancer's list of interfaces to + * implement, you can make your enhanced classes handle an arbitrary set + * of method signatures. + * @author Chris Nokleberg + * @version $Id: InterfaceMaker.java,v 1.4 2006/03/05 02:43:19 herbyderby Exp $ + */ +public class InterfaceMaker extends AbstractClassGenerator +{ + private static final Source SOURCE = new Source(InterfaceMaker.class.getName()); + private Map signatures = new HashMap(); + + /** + * Create a new InterfaceMaker. A new InterfaceMaker + * object should be used for each generated interface, and should not + * be shared across threads. + */ + public InterfaceMaker() { + super(SOURCE); + } + + /** + * Add a method signature to the interface. + * @param sig the method signature to add to the interface + * @param exceptions an array of exception types to declare for the method + */ + public void add(Signature sig, Type[] exceptions) { + signatures.put(sig, exceptions); + } + + /** + * Add a method signature to the interface. The method modifiers are ignored, + * since interface methods are by definition abstract and public. + * @param method the method to add to the interface + */ + public void add(Method method) { + add(ReflectUtils.getSignature(method), + ReflectUtils.getExceptionTypes(method)); + } + + /** + * Add all the public methods in the specified class. + * Methods from superclasses are included, except for methods declared in the base + * Object class (e.g. getClass, equals, hashCode). + * @param class the class containing the methods to add to the interface + */ + public void add(Class clazz) { + Method[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (!m.getDeclaringClass().getName().equals("java.lang.Object")) { + add(m); + } + } + } + + /** + * Create an interface using the current set of method signatures. + */ + public Class create() { + setUseCache(false); + return (Class)super.create(this); + } + + protected ClassLoader getDefaultClassLoader() { + return null; + } + + protected Object firstInstance(Class type) { + return type; + } + + protected Object nextInstance(Object instance) { + throw new IllegalStateException("InterfaceMaker does not cache"); + } + + public void generateClass(ClassVisitor v) throws Exception { + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC | Constants.ACC_INTERFACE, + getClassName(), + null, + null, + Constants.SOURCE_FILE); + for (Iterator it = signatures.keySet().iterator(); it.hasNext();) { + Signature sig = (Signature)it.next(); + Type[] exceptions = (Type[])signatures.get(sig); + ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT, + sig, + exceptions).end_method(); + } + ce.end_class(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InvocationHandler.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InvocationHandler.java new file mode 100644 index 000000000..c528c07df --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InvocationHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.Method; + +/** + * {@link java.lang.reflect.InvocationHandler} replacement (unavailable under JDK 1.2). + * This callback type is primarily for use by the {@link Proxy} class but + * may be used with {@link Enhancer} as well. + * @author Neeme Praks neeme@apache.org + * @version $Id: InvocationHandler.java,v 1.3 2004/06/24 21:15:20 herbyderby Exp $ + */ +public interface InvocationHandler +extends Callback +{ + /** + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object) + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; + +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InvocationHandlerGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InvocationHandlerGenerator.java new file mode 100644 index 000000000..7b8b66aa4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/InvocationHandlerGenerator.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import com.fr.third.net.sf.cglib.core.*; +import java.util.*; +import com.fr.third.org.objectweb.asm.Type; + +class InvocationHandlerGenerator +implements CallbackGenerator +{ + public static final InvocationHandlerGenerator INSTANCE = new InvocationHandlerGenerator(); + + private static final Type INVOCATION_HANDLER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.InvocationHandler"); + private static final Type UNDECLARED_THROWABLE_EXCEPTION = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.UndeclaredThrowableException"); + private static final Type METHOD = + TypeUtils.parseType("java.lang.reflect.Method"); + private static final Signature INVOKE = + TypeUtils.parseSignature("Object invoke(Object, java.lang.reflect.Method, Object[])"); + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + Signature impl = context.getImplSignature(method); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, impl.getName(), METHOD, null); + + CodeEmitter e = context.beginMethod(ce, method); + Block handler = e.begin_block(); + context.emitCallback(e, context.getIndex(method)); + e.load_this(); + e.getfield(impl.getName()); + e.create_arg_array(); + e.invoke_interface(INVOCATION_HANDLER, INVOKE); + e.unbox(method.getSignature().getReturnType()); + e.return_value(); + handler.end(); + EmitUtils.wrap_undeclared_throwable(e, handler, method.getExceptionTypes(), UNDECLARED_THROWABLE_EXCEPTION); + e.end_method(); + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + EmitUtils.load_method(e, method); + e.putfield(context.getImplSignature(method).getName()); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/LazyLoader.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/LazyLoader.java new file mode 100644 index 000000000..a8a749f96 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/LazyLoader.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * Lazy-loading {@link Enhancer} callback. + */ +public interface LazyLoader extends Callback { + /** + * Return the object which the original method invocation should be + * dispatched. Called as soon as the first lazily-loaded method in + * the enhanced instance is invoked. The same object is then used + * for every future method call to the proxy instance. + * @return an object that can invoke the method + */ + Object loadObject() throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/LazyLoaderGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/LazyLoaderGenerator.java new file mode 100644 index 000000000..2900ab18b --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/LazyLoaderGenerator.java @@ -0,0 +1,88 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +class LazyLoaderGenerator implements CallbackGenerator { + public static final LazyLoaderGenerator INSTANCE = new LazyLoaderGenerator(); + + private static final Signature LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject()"); + private static final Type LAZY_LOADER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.LazyLoader"); + + public void generate(ClassEmitter ce, Context context, List methods) { + Set indexes = new HashSet(); + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + if (TypeUtils.isProtected(method.getModifiers())) { + // ignore protected methods + } else { + int index = context.getIndex(method); + indexes.add(new Integer(index)); + CodeEmitter e = context.beginMethod(ce, method); + e.load_this(); + e.dup(); + e.invoke_virtual_this(loadMethod(index)); + e.checkcast(method.getClassInfo().getType()); + e.load_args(); + e.invoke(method); + e.return_value(); + e.end_method(); + } + } + + for (Iterator it = indexes.iterator(); it.hasNext();) { + int index = ((Integer)it.next()).intValue(); + + String delegate = "CGLIB$LAZY_LOADER_" + index; + ce.declare_field(Constants.ACC_PRIVATE, delegate, Constants.TYPE_OBJECT, null); + + CodeEmitter e = ce.begin_method(Constants.ACC_PRIVATE | + Constants.ACC_SYNCHRONIZED | + Constants.ACC_FINAL, + loadMethod(index), + null); + e.load_this(); + e.getfield(delegate); + e.dup(); + Label end = e.make_label(); + e.ifnonnull(end); + e.pop(); + e.load_this(); + context.emitCallback(e, index); + e.invoke_interface(LAZY_LOADER, LOAD_OBJECT); + e.dup_x1(); + e.putfield(delegate); + e.mark(end); + e.return_value(); + e.end_method(); + + } + } + + private Signature loadMethod(int index) { + return new Signature("CGLIB$LOAD_PRIVATE_" + index, + Constants.TYPE_OBJECT, + Constants.TYPES_EMPTY); + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodInterceptor.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodInterceptor.java new file mode 100644 index 000000000..78908ea43 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodInterceptor.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002,2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * General-purpose {@link Enhancer} callback which provides for "around advice". + * @author Juozas Baliuka baliuka@mwm.lt + * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $ + */ +public interface MethodInterceptor +extends Callback +{ + /** + * All generated proxied methods call this method instead of the original method. + * The original method may either be invoked by normal reflection using the Method object, + * or by using the MethodProxy (faster). + * @param obj "this", the enhanced object + * @param method intercepted Method + * @param args argument array; primitive types are wrapped + * @param proxy used to invoke super (non-intercepted method); may be called + * as many times as needed + * @throws Throwable any exception may be thrown; if so, super method will not be invoked + * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value. + * @see MethodProxy + */ + public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, + MethodProxy proxy) throws Throwable; + +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodInterceptorGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodInterceptorGenerator.java new file mode 100644 index 000000000..857605112 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodInterceptorGenerator.java @@ -0,0 +1,238 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.Method; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +class MethodInterceptorGenerator +implements CallbackGenerator +{ + public static final MethodInterceptorGenerator INSTANCE = new MethodInterceptorGenerator(); + + static final String EMPTY_ARGS_NAME = "CGLIB$emptyArgs"; + static final String FIND_PROXY_NAME = "CGLIB$findMethodProxy"; + static final Class[] FIND_PROXY_TYPES = { Signature.class }; + + private static final Type ABSTRACT_METHOD_ERROR = + TypeUtils.parseType("AbstractMethodError"); + private static final Type METHOD = + TypeUtils.parseType("java.lang.reflect.Method"); + private static final Type REFLECT_UTILS = + TypeUtils.parseType("com.fr.third.net.sf.cglib.core.ReflectUtils"); + private static final Type METHOD_PROXY = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.MethodProxy"); + private static final Type METHOD_INTERCEPTOR = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.MethodInterceptor"); + private static final Signature GET_DECLARED_METHODS = + TypeUtils.parseSignature("java.lang.reflect.Method[] getDeclaredMethods()"); + private static final Signature GET_DECLARING_CLASS = + TypeUtils.parseSignature("Class getDeclaringClass()"); + private static final Signature FIND_METHODS = + TypeUtils.parseSignature("java.lang.reflect.Method[] findMethods(String[], java.lang.reflect.Method[])"); + private static final Signature MAKE_PROXY = + new Signature("create", METHOD_PROXY, new Type[]{ + Constants.TYPE_CLASS, + Constants.TYPE_CLASS, + Constants.TYPE_STRING, + Constants.TYPE_STRING, + Constants.TYPE_STRING + }); + private static final Signature INTERCEPT = + new Signature("intercept", Constants.TYPE_OBJECT, new Type[]{ + Constants.TYPE_OBJECT, + METHOD, + Constants.TYPE_OBJECT_ARRAY, + METHOD_PROXY + }); + private static final Signature FIND_PROXY = + new Signature(FIND_PROXY_NAME, METHOD_PROXY, new Type[]{ Constants.TYPE_SIGNATURE }); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Transformer METHOD_TO_CLASS = new Transformer(){ + public Object transform(Object value) { + return ((MethodInfo)value).getClassInfo(); + } + }; + private static final Signature CSTRUCT_SIGNATURE = + TypeUtils.parseConstructor("String, String"); + + private String getMethodField(Signature impl) { + return impl.getName() + "$Method"; + } + private String getMethodProxyField(Signature impl) { + return impl.getName() + "$Proxy"; + } + + public void generate(ClassEmitter ce, Context context, List methods) { + Map sigMap = new HashMap(); + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + Signature sig = method.getSignature(); + Signature impl = context.getImplSignature(method); + + String methodField = getMethodField(impl); + String methodProxyField = getMethodProxyField(impl); + + sigMap.put(sig.toString(), methodProxyField); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodField, METHOD, null); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodProxyField, METHOD_PROXY, null); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, EMPTY_ARGS_NAME, Constants.TYPE_OBJECT_ARRAY, null); + CodeEmitter e; + + // access method + e = ce.begin_method(Constants.ACC_FINAL, + impl, + method.getExceptionTypes()); + superHelper(e, method, context); + e.return_value(); + e.end_method(); + + // around method + e = context.beginMethod(ce, method); + Label nullInterceptor = e.make_label(); + context.emitCallback(e, context.getIndex(method)); + e.dup(); + e.ifnull(nullInterceptor); + + e.load_this(); + e.getfield(methodField); + + if (sig.getArgumentTypes().length == 0) { + e.getfield(EMPTY_ARGS_NAME); + } else { + e.create_arg_array(); + } + + e.getfield(methodProxyField); + e.invoke_interface(METHOD_INTERCEPTOR, INTERCEPT); + e.unbox_or_zero(sig.getReturnType()); + e.return_value(); + + e.mark(nullInterceptor); + superHelper(e, method, context); + e.return_value(); + e.end_method(); + } + generateFindProxy(ce, sigMap); + } + + private static void superHelper(CodeEmitter e, MethodInfo method, Context context) + { + if (TypeUtils.isAbstract(method.getModifiers())) { + e.throw_exception(ABSTRACT_METHOD_ERROR, method.toString() + " is abstract" ); + } else { + e.load_this(); + context.emitLoadArgsAndInvoke(e, method); + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) throws Exception { + /* generates: + static { + Class thisClass = Class.forName("NameOfThisClass"); + Class cls = Class.forName("java.lang.Object"); + String[] sigs = new String[]{ "toString", "()Ljava/lang/String;", ... }; + Method[] methods = cls.getDeclaredMethods(); + methods = ReflectUtils.findMethods(sigs, methods); + METHOD_0 = methods[0]; + CGLIB$ACCESS_0 = MethodProxy.create(cls, thisClass, "()Ljava/lang/String;", "toString", "CGLIB$ACCESS_0"); + ... + } + */ + + e.push(0); + e.newarray(); + e.putfield(EMPTY_ARGS_NAME); + + Local thisclass = e.make_local(); + Local declaringclass = e.make_local(); + EmitUtils.load_class_this(e); + e.store_local(thisclass); + + Map methodsByClass = CollectionUtils.bucket(methods, METHOD_TO_CLASS); + for (Iterator i = methodsByClass.keySet().iterator(); i.hasNext();) { + ClassInfo classInfo = (ClassInfo)i.next(); + + List classMethods = (List)methodsByClass.get(classInfo); + e.push(2 * classMethods.size()); + e.newarray(Constants.TYPE_STRING); + for (int index = 0; index < classMethods.size(); index++) { + MethodInfo method = (MethodInfo)classMethods.get(index); + Signature sig = method.getSignature(); + e.dup(); + e.push(2 * index); + e.push(sig.getName()); + e.aastore(); + e.dup(); + e.push(2 * index + 1); + e.push(sig.getDescriptor()); + e.aastore(); + } + + EmitUtils.load_class(e, classInfo.getType()); + e.dup(); + e.store_local(declaringclass); + e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHODS); + e.invoke_static(REFLECT_UTILS, FIND_METHODS); + + for (int index = 0; index < classMethods.size(); index++) { + MethodInfo method = (MethodInfo)classMethods.get(index); + Signature sig = method.getSignature(); + Signature impl = context.getImplSignature(method); + e.dup(); + e.push(index); + e.array_load(METHOD); + e.putfield(getMethodField(impl)); + + e.load_local(declaringclass); + e.load_local(thisclass); + e.push(sig.getDescriptor()); + e.push(sig.getName()); + e.push(impl.getName()); + e.invoke_static(METHOD_PROXY, MAKE_PROXY); + e.putfield(getMethodProxyField(impl)); + } + e.pop(); + } + } + + public void generateFindProxy(ClassEmitter ce, final Map sigMap) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + FIND_PROXY, + null); + e.load_arg(0); + e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); + ObjectSwitchCallback callback = new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + e.getfield((String)sigMap.get(key)); + e.return_value(); + } + public void processDefault() { + e.aconst_null(); + e.return_value(); + } + }; + EmitUtils.string_switch(e, + (String[])sigMap.keySet().toArray(new String[0]), + Constants.SWITCH_STYLE_HASH, + callback); + e.end_method(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodProxy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodProxy.java new file mode 100644 index 000000000..002f2e654 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MethodProxy.java @@ -0,0 +1,233 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.fr.third.net.sf.cglib.core.AbstractClassGenerator; +import com.fr.third.net.sf.cglib.core.CodeGenerationException; +import com.fr.third.net.sf.cglib.core.GeneratorStrategy; +import com.fr.third.net.sf.cglib.core.NamingPolicy; +import com.fr.third.net.sf.cglib.core.Signature; +import com.fr.third.net.sf.cglib.reflect.FastClass; + +/** + * Classes generated by {@link Enhancer} pass this object to the + * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can + * be used to either invoke the original method, or call the same method on a different + * object of the same type. + * @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $ + */ +public class MethodProxy { + private Signature sig1; + private Signature sig2; + private CreateInfo createInfo; + + private final Object initLock = new Object(); + private volatile FastClassInfo fastClassInfo; + + /** + * For internal use by {@link Enhancer} only; see the {@link com.fr.third.net.sf.cglib.reflect.FastMethod} class + * for similar functionality. + */ + public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) { + MethodProxy proxy = new MethodProxy(); + proxy.sig1 = new Signature(name1, desc); + proxy.sig2 = new Signature(name2, desc); + proxy.createInfo = new CreateInfo(c1, c2); + return proxy; + } + + private void init() + { + /* + * Using a volatile invariant allows us to initialize the FastClass and + * method index pairs atomically. + * + * Double-checked locking is safe with volatile in Java 5. Before 1.5 this + * code could allow fastClassInfo to be instantiated more than once, which + * appears to be benign. + */ + if (fastClassInfo == null) + { + synchronized (initLock) + { + if (fastClassInfo == null) + { + CreateInfo ci = createInfo; + + FastClassInfo fci = new FastClassInfo(); + fci.f1 = helper(ci, ci.c1); + fci.f2 = helper(ci, ci.c2); + fci.i1 = fci.f1.getIndex(sig1); + fci.i2 = fci.f2.getIndex(sig2); + fastClassInfo = fci; + createInfo = null; + } + } + } + } + + private static class FastClassInfo + { + FastClass f1; + FastClass f2; + int i1; + int i2; + } + + private static class CreateInfo + { + Class c1; + Class c2; + NamingPolicy namingPolicy; + GeneratorStrategy strategy; + boolean attemptLoad; + + public CreateInfo(Class c1, Class c2) + { + this.c1 = c1; + this.c2 = c2; + AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent(); + if (fromEnhancer != null) { + namingPolicy = fromEnhancer.getNamingPolicy(); + strategy = fromEnhancer.getStrategy(); + attemptLoad = fromEnhancer.getAttemptLoad(); + } + } + } + + private static FastClass helper(CreateInfo ci, Class type) { + FastClass.Generator g = new FastClass.Generator(); + g.setType(type); + g.setClassLoader(ci.c2.getClassLoader()); + g.setNamingPolicy(ci.namingPolicy); + g.setStrategy(ci.strategy); + g.setAttemptLoad(ci.attemptLoad); + return g.create(); + } + + private MethodProxy() { + } + + /** + * Return the signature of the proxied method. + */ + public Signature getSignature() { + return sig1; + } + + /** + * Return the name of the synthetic method created by CGLIB which is + * used by {@link #invokeSuper} to invoke the superclass + * (non-intercepted) method implementation. The parameter types are + * the same as the proxied method. + */ + public String getSuperName() { + return sig2.getName(); + } + + /** + * Return the {@link com.fr.third.net.sf.cglib.reflect.FastClass} method index + * for the method used by {@link #invokeSuper}. This index uniquely + * identifies the method within the generated proxy, and therefore + * can be useful to reference external metadata. + * @see #getSuperName + */ + public int getSuperIndex() { + init(); + return fastClassInfo.i2; + } + + // For testing + FastClass getFastClass() { + init(); + return fastClassInfo.f1; + } + + // For testing + FastClass getSuperFastClass() { + init(); + return fastClassInfo.f2; + } + + /** + * Return the MethodProxy used when intercepting the method + * matching the given signature. + * @param type the class generated by Enhancer + * @param sig the signature to match + * @return the MethodProxy instance, or null if no applicable matching method is found + * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor + */ + public static MethodProxy find(Class type, Signature sig) { + try { + Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME, + MethodInterceptorGenerator.FIND_PROXY_TYPES); + return (MethodProxy)m.invoke(null, new Object[]{ sig }); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor"); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e); + } + } + + /** + * Invoke the original method, on a different object of the same type. + * @param obj the compatible object; recursion will result if you use the object passed as the first + * argument to the MethodInterceptor (usually not what you want) + * @param args the arguments passed to the intercepted method; you may substitute a different + * argument array as long as the types are compatible + * @see MethodInterceptor#intercept + * @throws Throwable the bare exceptions thrown by the called method are passed through + * without wrapping in an InvocationTargetException + */ + public Object invoke(Object obj, Object[] args) throws Throwable { + try { + init(); + FastClassInfo fci = fastClassInfo; + return fci.f1.invoke(fci.i1, obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (IllegalArgumentException e) { + if (fastClassInfo.i1 < 0) + throw new IllegalArgumentException("Protected method: " + sig1); + throw e; + } + } + + /** + * Invoke the original (super) method on the specified object. + * @param obj the enhanced object, must be the object passed as the first + * argument to the MethodInterceptor + * @param args the arguments passed to the intercepted method; you may substitute a different + * argument array as long as the types are compatible + * @see MethodInterceptor#intercept + * @throws Throwable the bare exceptions thrown by the called method are passed through + * without wrapping in an InvocationTargetException + */ + public Object invokeSuper(Object obj, Object[] args) throws Throwable { + try { + init(); + FastClassInfo fci = fastClassInfo; + return fci.f2.invoke(fci.i2, obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Mixin.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Mixin.java new file mode 100644 index 000000000..07a3669df --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Mixin.java @@ -0,0 +1,242 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.security.ProtectionDomain; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; + + + +/** + * Mixin allows + * multiple objects to be combined into a single larger object. The + * methods in the generated object simply call the original methods in the + * underlying "delegate" objects. + * @author Chris Nokleberg + * @version $Id: Mixin.java,v 1.7 2005/09/27 11:42:27 baliuka Exp $ + */ +abstract public class Mixin { + private static final MixinKey KEY_FACTORY = + (MixinKey)KeyFactory.create(MixinKey.class, KeyFactory.CLASS_BY_NAME); + private static final Map ROUTE_CACHE = Collections.synchronizedMap(new HashMap()); + + public static final int STYLE_INTERFACES = 0; + public static final int STYLE_BEANS = 1; + public static final int STYLE_EVERYTHING = 2; + + interface MixinKey { + public Object newInstance(int style, String[] classes, int[] route); + } + + abstract public Mixin newInstance(Object[] delegates); + + /** + * Helper method to create an interface mixin. For finer control over the + * generated instance, use a new instance of Mixin + * instead of this static method. + * TODO + */ + public static Mixin create(Object[] delegates) { + Generator gen = new Generator(); + gen.setDelegates(delegates); + return gen.create(); + } + + /** + * Helper method to create an interface mixin. For finer control over the + * generated instance, use a new instance of Mixin + * instead of this static method. + * TODO + */ + public static Mixin create(Class[] interfaces, Object[] delegates) { + Generator gen = new Generator(); + gen.setClasses(interfaces); + gen.setDelegates(delegates); + return gen.create(); + } + + + public static Mixin createBean(Object[] beans) { + + return createBean(null, beans); + + } + /** + * Helper method to create a bean mixin. For finer control over the + * generated instance, use a new instance of Mixin + * instead of this static method. + * TODO + */ + public static Mixin createBean(ClassLoader loader,Object[] beans) { + Generator gen = new Generator(); + gen.setStyle(STYLE_BEANS); + gen.setDelegates(beans); + gen.setClassLoader(loader); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(Mixin.class.getName()); + + private Class[] classes; + private Object[] delegates; + private int style = STYLE_INTERFACES; + + private int[] route; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return classes[0].getClassLoader(); // is this right? + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(classes[0]); + } + + public void setStyle(int style) { + switch (style) { + case STYLE_INTERFACES: + case STYLE_BEANS: + case STYLE_EVERYTHING: + this.style = style; + break; + default: + throw new IllegalArgumentException("Unknown mixin style: " + style); + } + } + + public void setClasses(Class[] classes) { + this.classes = classes; + } + + public void setDelegates(Object[] delegates) { + this.delegates = delegates; + } + + public Mixin create() { + if (classes == null && delegates == null) { + throw new IllegalStateException("Either classes or delegates must be set"); + } + switch (style) { + case STYLE_INTERFACES: + if (classes == null) { + Route r = route(delegates); + classes = r.classes; + route = r.route; + } + break; + case STYLE_BEANS: + // fall-through + case STYLE_EVERYTHING: + if (classes == null) { + classes = ReflectUtils.getClasses(delegates); + } else { + if (delegates != null) { + Class[] temp = ReflectUtils.getClasses(delegates); + if (classes.length != temp.length) { + throw new IllegalStateException("Specified classes are incompatible with delegates"); + } + for (int i = 0; i < classes.length; i++) { + if (!classes[i].isAssignableFrom(temp[i])) { + throw new IllegalStateException("Specified class " + classes[i] + " is incompatible with delegate class " + temp[i] + " (index " + i + ")"); + } + } + } + } + } + setNamePrefix(classes[ReflectUtils.findPackageProtected(classes)].getName()); + + return (Mixin)super.create(KEY_FACTORY.newInstance(style, ReflectUtils.getNames( classes ), route)); + } + + public void generateClass(ClassVisitor v) { + switch (style) { + case STYLE_INTERFACES: + new MixinEmitter(v, getClassName(), classes, route); + break; + case STYLE_BEANS: + new MixinBeanEmitter(v, getClassName(), classes); + break; + case STYLE_EVERYTHING: + new MixinEverythingEmitter(v, getClassName(), classes); + break; + } + } + + protected Object firstInstance(Class type) { + return ((Mixin)ReflectUtils.newInstance(type)).newInstance(delegates); + } + + protected Object nextInstance(Object instance) { + return ((Mixin)instance).newInstance(delegates); + } + } + + public static Class[] getClasses(Object[] delegates) { + return (Class[])route(delegates).classes.clone(); + } + +// public static int[] getRoute(Object[] delegates) { +// return (int[])route(delegates).route.clone(); +// } + + private static Route route(Object[] delegates) { + Object key = ClassesKey.create(delegates); + Route route = (Route)ROUTE_CACHE.get(key); + if (route == null) { + ROUTE_CACHE.put(key, route = new Route(delegates)); + } + return route; + } + + private static class Route + { + private Class[] classes; + private int[] route; + + Route(Object[] delegates) { + Map map = new HashMap(); + ArrayList collect = new ArrayList(); + for (int i = 0; i < delegates.length; i++) { + Class delegate = delegates[i].getClass(); + collect.clear(); + ReflectUtils.addAllInterfaces(delegate, collect); + for (Iterator it = collect.iterator(); it.hasNext();) { + Class iface = (Class)it.next(); + if (!map.containsKey(iface)) { + map.put(iface, new Integer(i)); + } + } + } + classes = new Class[map.size()]; + route = new int[map.size()]; + int index = 0; + for (Iterator it = map.keySet().iterator(); it.hasNext();) { + Class key = (Class)it.next(); + classes[index] = key; + route[index] = ((Integer)map.get(key)).intValue(); + index++; + } + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinBeanEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinBeanEmitter.java new file mode 100644 index 000000000..cf26d83dc --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinBeanEmitter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.Method; +import com.fr.third.net.sf.cglib.core.ReflectUtils; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +/** + * @author Chris Nokleberg + * @version $Id: MixinBeanEmitter.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $ + */ +class MixinBeanEmitter extends MixinEmitter { + public MixinBeanEmitter(ClassVisitor v, String className, Class[] classes) { + super(v, className, classes, null); + } + + protected Class[] getInterfaces(Class[] classes) { + return null; + } + + protected Method[] getMethods(Class type) { + return ReflectUtils.getPropertyMethods(ReflectUtils.getBeanProperties(type), true, true); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinEmitter.java new file mode 100644 index 000000000..be262043c --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinEmitter.java @@ -0,0 +1,93 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.Method; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Chris Nokleberg + * @version $Id: MixinEmitter.java,v 1.9 2006/08/27 21:04:37 herbyderby Exp $ + */ +class MixinEmitter extends ClassEmitter { + private static final String FIELD_NAME = "CGLIB$DELEGATES"; + private static final Signature CSTRUCT_OBJECT_ARRAY = + TypeUtils.parseConstructor("Object[]"); + private static final Type MIXIN = + TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Mixin"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", MIXIN, new Type[]{ Constants.TYPE_OBJECT_ARRAY }); + + public MixinEmitter(ClassVisitor v, String className, Class[] classes, int[] route) { + super(v); + + begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + className, + MIXIN, + TypeUtils.getTypes(getInterfaces(classes)), + Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + EmitUtils.factory_method(this, NEW_INSTANCE); + + declare_field(Constants.ACC_PRIVATE, FIELD_NAME, Constants.TYPE_OBJECT_ARRAY, null); + + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + e.load_arg(0); + e.putfield(FIELD_NAME); + e.return_value(); + e.end_method(); + + Set unique = new HashSet(); + for (int i = 0; i < classes.length; i++) { + Method[] methods = getMethods(classes[i]); + for (int j = 0; j < methods.length; j++) { + if (unique.add(MethodWrapper.create(methods[j]))) { + MethodInfo method = ReflectUtils.getMethodInfo(methods[j]); + int modifiers = Constants.ACC_PUBLIC; + if ((method.getModifiers() & Constants.ACC_VARARGS) == Constants.ACC_VARARGS) { + modifiers |= Constants.ACC_VARARGS; + } + e = EmitUtils.begin_method(this, method, modifiers); + e.load_this(); + e.getfield(FIELD_NAME); + e.aaload((route != null) ? route[i] : i); + e.checkcast(method.getClassInfo().getType()); + e.load_args(); + e.invoke(method); + e.return_value(); + e.end_method(); + } + } + } + + end_class(); + } + + protected Class[] getInterfaces(Class[] classes) { + return classes; + } + + protected Method[] getMethods(Class type) { + return type.getMethods(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinEverythingEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinEverythingEmitter.java new file mode 100644 index 000000000..9477b3f6e --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/MixinEverythingEmitter.java @@ -0,0 +1,49 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import com.fr.third.net.sf.cglib.core.CollectionUtils; +import com.fr.third.net.sf.cglib.core.ReflectUtils; +import com.fr.third.net.sf.cglib.core.RejectModifierPredicate; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +/** + * @author Chris Nokleberg + * @version $Id: MixinEverythingEmitter.java,v 1.3 2004/06/24 21:15:19 herbyderby Exp $ + */ +class MixinEverythingEmitter extends MixinEmitter { + + public MixinEverythingEmitter(ClassVisitor v, String className, Class[] classes) { + super(v, className, classes, null); + } + + protected Class[] getInterfaces(Class[] classes) { + List list = new ArrayList(); + for (int i = 0; i < classes.length; i++) { + ReflectUtils.addAllInterfaces(classes[i], list); + } + return (Class[])list.toArray(new Class[list.size()]); + } + + protected Method[] getMethods(Class type) { + List methods = new ArrayList(Arrays.asList(type.getMethods())); + CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.FINAL | Modifier.STATIC)); + return (Method[])methods.toArray(new Method[methods.size()]); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/NoOp.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/NoOp.java new file mode 100644 index 000000000..ad18ae30a --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/NoOp.java @@ -0,0 +1,28 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * Methods using this {@link Enhancer} callback will delegate directly to the + * default (super) implementation in the base class. + */ +public interface NoOp extends Callback +{ + /** + * A thread-safe singleton instance of the NoOp callback. + */ + public static final NoOp INSTANCE = new NoOp() { }; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/NoOpGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/NoOpGenerator.java new file mode 100644 index 000000000..830e74df4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/NoOpGenerator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.util.Iterator; +import java.util.List; +import com.fr.third.net.sf.cglib.core.*; + +class NoOpGenerator +implements CallbackGenerator +{ + public static final NoOpGenerator INSTANCE = new NoOpGenerator(); + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + if (TypeUtils.isBridge(method.getModifiers()) || ( + TypeUtils.isProtected(context.getOriginalModifiers(method)) && + TypeUtils.isPublic(method.getModifiers()))) { + CodeEmitter e = EmitUtils.begin_method(ce, method); + e.load_this(); + context.emitLoadArgsAndInvoke(e, method); + e.return_value(); + e.end_method(); + } + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Proxy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Proxy.java new file mode 100644 index 000000000..68d156f2a --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Proxy.java @@ -0,0 +1,101 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Member; +import com.fr.third.net.sf.cglib.core.CodeGenerationException; + +/** + * This class is meant to be used as replacement for + * java.lang.reflect.Proxy under JDK 1.2. There are some known + * subtle differences: + *
    + *
  • The exceptions returned by invoking getExceptionTypes + * on the Method passed to the invoke method + * are the exact set that can be thrown without resulting in an + * UndeclaredThrowableException being thrown. + *
  • {@link UndeclaredThrowableException} is used instead + * of java.lang.reflect.UndeclaredThrowableException. + *
+ *

+ * @version $Id: Proxy.java,v 1.6 2004/06/24 21:15:19 herbyderby Exp $ + */ +public class Proxy implements Serializable { + protected InvocationHandler h; + + private static final CallbackFilter BAD_OBJECT_METHOD_FILTER = new CallbackFilter() { + public int accept(Method method) { + if (method.getDeclaringClass().getName().equals("java.lang.Object")) { + String name = method.getName(); + if (!(name.equals("hashCode") || + name.equals("equals") || + name.equals("toString"))) { + return 1; + } + } + return 0; + } + }; + + protected Proxy(InvocationHandler h) { + Enhancer.registerCallbacks(getClass(), new Callback[]{ h, null }); + this.h = h; + } + + // private for security of isProxyClass + private static class ProxyImpl extends Proxy { + protected ProxyImpl(InvocationHandler h) { + super(h); + } + } + + public static InvocationHandler getInvocationHandler(Object proxy) { + if (!(proxy instanceof ProxyImpl)) { + throw new IllegalArgumentException("Object is not a proxy"); + } + return ((Proxy)proxy).h; + } + + public static Class getProxyClass(ClassLoader loader, Class[] interfaces) { + Enhancer e = new Enhancer(); + e.setSuperclass(ProxyImpl.class); + e.setInterfaces(interfaces); + e.setCallbackTypes(new Class[]{ + InvocationHandler.class, + NoOp.class, + }); + e.setCallbackFilter(BAD_OBJECT_METHOD_FILTER); + e.setUseFactory(false); + return e.createClass(); + } + + public static boolean isProxyClass(Class cl) { + return cl.getSuperclass().equals(ProxyImpl.class); + } + + public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) { + try { + Class clazz = getProxyClass(loader, interfaces); + return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h }); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/ProxyRefDispatcher.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/ProxyRefDispatcher.java new file mode 100644 index 000000000..e8831ceaa --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/ProxyRefDispatcher.java @@ -0,0 +1,31 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.proxy; + +/** + * Dispatching {@link Enhancer} callback. This is the same as the + * {@link Dispatcher} except for the addition of an argument + * which references the proxy object. + */ +public interface ProxyRefDispatcher extends Callback { + /** + * Return the object which the original method invocation should + * be dispatched. This method is called for every method invocation. + * @param proxy a reference to the proxy (generated) object + * @return an object that can invoke the method + */ + Object loadObject(Object proxy) throws Exception; +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/UndeclaredThrowableException.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/UndeclaredThrowableException.java new file mode 100644 index 000000000..1bdbda910 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/UndeclaredThrowableException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002,2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fr.third.net.sf.cglib.proxy; + +import com.fr.third.net.sf.cglib.core.CodeGenerationException; + +/** + * Used by {@link Proxy} as a replacement for java.lang.reflect.UndeclaredThrowableException. + * @author Juozas Baliuka + */ +public class UndeclaredThrowableException extends CodeGenerationException { + /** + * Creates a new instance of UndeclaredThrowableException without detail message. + */ + public UndeclaredThrowableException(Throwable t) { + super(t); + } + + public Throwable getUndeclaredThrowable() { + return getCause(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/ConstructorDelegate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/ConstructorDelegate.java new file mode 100644 index 000000000..64d3cef36 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/ConstructorDelegate.java @@ -0,0 +1,123 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.*; +import java.security.ProtectionDomain; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Chris Nokleberg + * @version $Id: ConstructorDelegate.java,v 1.20 2006/03/05 02:43:19 herbyderby Exp $ + */ +abstract public class ConstructorDelegate { + private static final ConstructorKey KEY_FACTORY = + (ConstructorKey)KeyFactory.create(ConstructorKey.class, KeyFactory.CLASS_BY_NAME); + + interface ConstructorKey { + public Object newInstance(String declaring, String iface); + } + + protected ConstructorDelegate() { + } + + public static ConstructorDelegate create(Class targetClass, Class iface) { + Generator gen = new Generator(); + gen.setTargetClass(targetClass); + gen.setInterface(iface); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(ConstructorDelegate.class.getName()); + private static final Type CONSTRUCTOR_DELEGATE = + TypeUtils.parseType("com.fr.third.net.sf.cglib.reflect.ConstructorDelegate"); + + private Class iface; + private Class targetClass; + + public Generator() { + super(SOURCE); + } + + public void setInterface(Class iface) { + this.iface = iface; + } + + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + public ConstructorDelegate create() { + setNamePrefix(targetClass.getName()); + Object key = KEY_FACTORY.newInstance(iface.getName(), targetClass.getName()); + return (ConstructorDelegate)super.create(key); + } + + protected ClassLoader getDefaultClassLoader() { + return targetClass.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(targetClass); + } + + public void generateClass(ClassVisitor v) { + setNamePrefix(targetClass.getName()); + + final Method newInstance = ReflectUtils.findNewInstance(iface); + if (!newInstance.getReturnType().isAssignableFrom(targetClass)) { + throw new IllegalArgumentException("incompatible return type"); + } + final Constructor constructor; + try { + constructor = targetClass.getDeclaredConstructor(newInstance.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("interface does not match any known constructor"); + } + + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + CONSTRUCTOR_DELEGATE, + new Type[]{ Type.getType(iface) }, + Constants.SOURCE_FILE); + Type declaring = Type.getType(constructor.getDeclaringClass()); + EmitUtils.null_constructor(ce); + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, + ReflectUtils.getSignature(newInstance), + ReflectUtils.getExceptionTypes(newInstance)); + e.new_instance(declaring); + e.dup(); + e.load_args(); + e.invoke_constructor(declaring, ReflectUtils.getSignature(constructor)); + e.return_value(); + e.end_method(); + ce.end_class(); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastClass.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastClass.java new file mode 100644 index 000000000..afa83ba17 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastClass.java @@ -0,0 +1,207 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import com.fr.third.net.sf.cglib.core.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +abstract public class FastClass +{ + private Class type; + + protected FastClass() { + throw new Error("Using the FastClass empty constructor--please report to the cglib-devel mailing list"); + } + + protected FastClass(Class type) { + this.type = type; + } + + public static FastClass create(Class type) { + + return create(type.getClassLoader(),type); + + } + public static FastClass create(ClassLoader loader, Class type) { + Generator gen = new Generator(); + gen.setType(type); + gen.setClassLoader(loader); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator + { + private static final Source SOURCE = new Source(FastClass.class.getName()); + private Class type; + + public Generator() { + super(SOURCE); + } + + public void setType(Class type) { + this.type = type; + } + + public FastClass create() { + setNamePrefix(type.getName()); + return (FastClass)super.create(type.getName()); + } + + protected ClassLoader getDefaultClassLoader() { + return type.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(type); + } + + public void generateClass(ClassVisitor v) throws Exception { + new FastClassEmitter(v, getClassName(), type); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type, + new Class[]{ Class.class }, + new Object[]{ this.type }); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } + + public Object invoke(String name, Class[] parameterTypes, Object obj, Object[] args) throws InvocationTargetException { + return invoke(getIndex(name, parameterTypes), obj, args); + } + + public Object newInstance() throws InvocationTargetException { + return newInstance(getIndex(Constants.EMPTY_CLASS_ARRAY), null); + } + + public Object newInstance(Class[] parameterTypes, Object[] args) throws InvocationTargetException { + return newInstance(getIndex(parameterTypes), args); + } + + public FastMethod getMethod(Method method) { + return new FastMethod(this, method); + } + + public FastConstructor getConstructor(Constructor constructor) { + return new FastConstructor(this, constructor); + } + + public FastMethod getMethod(String name, Class[] parameterTypes) { + try { + return getMethod(type.getMethod(name, parameterTypes)); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + public FastConstructor getConstructor(Class[] parameterTypes) { + try { + return getConstructor(type.getConstructor(parameterTypes)); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + public String getName() { + return type.getName(); + } + + public Class getJavaClass() { + return type; + } + + public String toString() { + return type.toString(); + } + + public int hashCode() { + return type.hashCode(); + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof FastClass)) { + return false; + } + return type.equals(((FastClass)o).type); + } + + /** + * Return the index of the matching method. The index may be used + * later to invoke the method with less overhead. If more than one + * method matches (i.e. they differ by return type only), one is + * chosen arbitrarily. + * @see #invoke(int, Object, Object[]) + * @param name the method name + * @param parameterTypes the parameter array + * @return the index, or -1 if none is found. + */ + abstract public int getIndex(String name, Class[] parameterTypes); + + /** + * Return the index of the matching constructor. The index may be used + * later to create a new instance with less overhead. + * @see #newInstance(int, Object[]) + * @param parameterTypes the parameter array + * @return the constructor index, or -1 if none is found. + */ + abstract public int getIndex(Class[] parameterTypes); + + /** + * Invoke the method with the specified index. + * @see getIndex(name, Class[]) + * @param index the method index + * @param obj the object the underlying method is invoked from + * @param args the arguments used for the method call + * @throws java.lang.reflect.InvocationTargetException if the underlying method throws an exception + */ + abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException; + + /** + * Create a new instance using the specified constructor index and arguments. + * @see getIndex(Class[]) + * @param index the constructor index + * @param args the arguments passed to the constructor + * @throws java.lang.reflect.InvocationTargetException if the constructor throws an exception + */ + abstract public Object newInstance(int index, Object[] args) throws InvocationTargetException; + + abstract public int getIndex(Signature sig); + + /** + * Returns the maximum method index for this class. + */ + abstract public int getMaxIndex(); + + protected static String getSignatureWithoutReturnType(String name, Class[] parameterTypes) { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append('('); + for (int i = 0; i < parameterTypes.length; i++) { + sb.append(Type.getDescriptor(parameterTypes[i])); + } + sb.append(')'); + return sb.toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastClassEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastClassEmitter.java new file mode 100644 index 000000000..022e27959 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastClassEmitter.java @@ -0,0 +1,226 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.*; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +class FastClassEmitter extends ClassEmitter { + private static final Signature CSTRUCT_CLASS = + TypeUtils.parseConstructor("Class"); + private static final Signature METHOD_GET_INDEX = + TypeUtils.parseSignature("int getIndex(String, Class[])"); + private static final Signature SIGNATURE_GET_INDEX = + new Signature("getIndex", Type.INT_TYPE, new Type[]{ Constants.TYPE_SIGNATURE }); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Signature CONSTRUCTOR_GET_INDEX = + TypeUtils.parseSignature("int getIndex(Class[])"); + private static final Signature INVOKE = + TypeUtils.parseSignature("Object invoke(int, Object, Object[])"); + private static final Signature NEW_INSTANCE = + TypeUtils.parseSignature("Object newInstance(int, Object[])"); + private static final Signature GET_MAX_INDEX = + TypeUtils.parseSignature("int getMaxIndex()"); + private static final Signature GET_SIGNATURE_WITHOUT_RETURN_TYPE = + TypeUtils.parseSignature("String getSignatureWithoutReturnType(String, Class[])"); + private static final Type FAST_CLASS = + TypeUtils.parseType("com.fr.third.net.sf.cglib.reflect.FastClass"); + private static final Type ILLEGAL_ARGUMENT_EXCEPTION = + TypeUtils.parseType("IllegalArgumentException"); + private static final Type INVOCATION_TARGET_EXCEPTION = + TypeUtils.parseType("java.lang.reflect.InvocationTargetException"); + private static final Type[] INVOCATION_TARGET_EXCEPTION_ARRAY = { INVOCATION_TARGET_EXCEPTION }; + + public FastClassEmitter(ClassVisitor v, String className, Class type) { + super(v); + + Type base = Type.getType(type); + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, FAST_CLASS, null, Constants.SOURCE_FILE); + + // constructor + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_CLASS, null); + e.load_this(); + e.load_args(); + e.super_invoke_constructor(CSTRUCT_CLASS); + e.return_value(); + e.end_method(); + + VisibilityPredicate vp = new VisibilityPredicate(type, false); + List methods = ReflectUtils.addAllMethods(type, new ArrayList()); + CollectionUtils.filter(methods, vp); + CollectionUtils.filter(methods, new DuplicatesPredicate()); + List constructors = new ArrayList(Arrays.asList(type.getDeclaredConstructors())); + CollectionUtils.filter(constructors, vp); + + // getIndex(String) + emitIndexBySignature(methods); + + // getIndex(String, Class[]) + emitIndexByClassArray(methods); + + // getIndex(Class[]) + e = begin_method(Constants.ACC_PUBLIC, CONSTRUCTOR_GET_INDEX, null); + e.load_args(); + List info = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance()); + EmitUtils.constructor_switch(e, info, new GetIndexCallback(e, info)); + e.end_method(); + + // invoke(int, Object, Object[]) + e = begin_method(Constants.ACC_PUBLIC, INVOKE, INVOCATION_TARGET_EXCEPTION_ARRAY); + e.load_arg(1); + e.checkcast(base); + e.load_arg(0); + invokeSwitchHelper(e, methods, 2, base); + e.end_method(); + + // newInstance(int, Object[]) + e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, INVOCATION_TARGET_EXCEPTION_ARRAY); + e.new_instance(base); + e.dup(); + e.load_arg(0); + invokeSwitchHelper(e, constructors, 1, base); + e.end_method(); + + // getMaxIndex() + e = begin_method(Constants.ACC_PUBLIC, GET_MAX_INDEX, null); + e.push(methods.size() - 1); + e.return_value(); + e.end_method(); + + end_class(); + } + + // TODO: support constructor indices ("") + private void emitIndexBySignature(List methods) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SIGNATURE_GET_INDEX, null); + List signatures = CollectionUtils.transform(methods, new Transformer() { + public Object transform(Object obj) { + return ReflectUtils.getSignature((Method)obj).toString(); + } + }); + e.load_arg(0); + e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); + signatureSwitchHelper(e, signatures); + e.end_method(); + } + + private static final int TOO_MANY_METHODS = 100; // TODO + private void emitIndexByClassArray(List methods) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, METHOD_GET_INDEX, null); + if (methods.size() > TOO_MANY_METHODS) { + // hack for big classes + List signatures = CollectionUtils.transform(methods, new Transformer() { + public Object transform(Object obj) { + String s = ReflectUtils.getSignature((Method)obj).toString(); + return s.substring(0, s.lastIndexOf(')') + 1); + } + }); + e.load_args(); + e.invoke_static(FAST_CLASS, GET_SIGNATURE_WITHOUT_RETURN_TYPE); + signatureSwitchHelper(e, signatures); + } else { + e.load_args(); + List info = CollectionUtils.transform(methods, MethodInfoTransformer.getInstance()); + EmitUtils.method_switch(e, info, new GetIndexCallback(e, info)); + } + e.end_method(); + } + + private void signatureSwitchHelper(final CodeEmitter e, final List signatures) { + ObjectSwitchCallback callback = new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + // TODO: remove linear indexOf + e.push(signatures.indexOf(key)); + e.return_value(); + } + public void processDefault() { + e.push(-1); + e.return_value(); + } + }; + EmitUtils.string_switch(e, + (String[])signatures.toArray(new String[signatures.size()]), + Constants.SWITCH_STYLE_HASH, + callback); + } + + private static void invokeSwitchHelper(final CodeEmitter e, List members, final int arg, final Type base) { + final List info = CollectionUtils.transform(members, MethodInfoTransformer.getInstance()); + final Label illegalArg = e.make_label(); + Block block = e.begin_block(); + e.process_switch(getIntRange(info.size()), new ProcessSwitchCallback() { + public void processCase(int key, Label end) { + MethodInfo method = (MethodInfo)info.get(key); + Type[] types = method.getSignature().getArgumentTypes(); + for (int i = 0; i < types.length; i++) { + e.load_arg(arg); + e.aaload(i); + e.unbox(types[i]); + } + // TODO: change method lookup process so MethodInfo will already reference base + // instead of superclass when superclass method is inaccessible + e.invoke(method, base); + if (!TypeUtils.isConstructor(method)) { + e.box(method.getSignature().getReturnType()); + } + e.return_value(); + } + public void processDefault() { + e.goTo(illegalArg); + } + }); + block.end(); + EmitUtils.wrap_throwable(block, INVOCATION_TARGET_EXCEPTION); + e.mark(illegalArg); + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Cannot find matching method/constructor"); + } + + private static class GetIndexCallback implements ObjectSwitchCallback { + private CodeEmitter e; + private Map indexes = new HashMap(); + + public GetIndexCallback(CodeEmitter e, List methods) { + this.e = e; + int index = 0; + for (Iterator it = methods.iterator(); it.hasNext();) { + indexes.put(it.next(), new Integer(index++)); + } + } + + public void processCase(Object key, Label end) { + e.push(((Integer)indexes.get(key)).intValue()); + e.return_value(); + } + + public void processDefault() { + e.push(-1); + e.return_value(); + } + } + + private static int[] getIntRange(int length) { + int[] range = new int[length]; + for (int i = 0; i < length; i++) { + range[i] = i; + } + return range; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastConstructor.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastConstructor.java new file mode 100644 index 000000000..408d47f9e --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastConstructor.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class FastConstructor extends FastMember +{ + FastConstructor(FastClass fc, Constructor constructor) { + super(fc, constructor, fc.getIndex(constructor.getParameterTypes())); + } + + public Class[] getParameterTypes() { + return ((Constructor)member).getParameterTypes(); + } + + public Class[] getExceptionTypes() { + return ((Constructor)member).getExceptionTypes(); + } + + public Object newInstance() throws InvocationTargetException { + return fc.newInstance(index, null); + } + + public Object newInstance(Object[] args) throws InvocationTargetException { + return fc.newInstance(index, args); + } + + public Constructor getJavaConstructor() { + return (Constructor)member; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastMember.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastMember.java new file mode 100644 index 000000000..76053102c --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastMember.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.Member; + +abstract public class FastMember +{ + protected FastClass fc; + protected Member member; + protected int index; + + protected FastMember(FastClass fc, Member member, int index) { + this.fc = fc; + this.member = member; + this.index = index; + } + + abstract public Class[] getParameterTypes(); + abstract public Class[] getExceptionTypes(); + + public int getIndex() { + return index; + } + + public String getName() { + return member.getName(); + } + + public Class getDeclaringClass() { + return fc.getJavaClass(); + } + + public int getModifiers() { + return member.getModifiers(); + } + + public String toString() { + return member.toString(); + } + + public int hashCode() { + return member.hashCode(); + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof FastMember)) { + return false; + } + return member.equals(((FastMember)o).member); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastMethod.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastMethod.java new file mode 100644 index 000000000..d748b9435 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/FastMethod.java @@ -0,0 +1,63 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.fr.third.net.sf.cglib.core.Signature; + +import com.fr.third.org.objectweb.asm.Type; + +public class FastMethod extends FastMember +{ + FastMethod(FastClass fc, Method method) { + super(fc, method, helper(fc, method)); + } + + private static int helper(FastClass fc, Method method) { + int index = fc.getIndex(new Signature(method.getName(), Type.getMethodDescriptor(method))); + if (index < 0) { + Class[] types = method.getParameterTypes(); + System.err.println("hash=" + method.getName().hashCode() + " size=" + types.length); + for (int i = 0; i < types.length; i++) { + System.err.println(" types[" + i + "]=" + types[i].getName()); + } + throw new IllegalArgumentException("Cannot find method " + method); + } + return index; + } + + public Class getReturnType() { + return ((Method)member).getReturnType(); + } + + public Class[] getParameterTypes() { + return ((Method)member).getParameterTypes(); + } + + public Class[] getExceptionTypes() { + return ((Method)member).getExceptionTypes(); + } + + public Object invoke(Object obj, Object[] args) throws InvocationTargetException { + return fc.invoke(index, obj, args); + } + + public Method getJavaMethod() { + return (Method)member; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/MethodDelegate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/MethodDelegate.java new file mode 100644 index 000000000..ae9070c0c --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/MethodDelegate.java @@ -0,0 +1,268 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.*; +import java.security.ProtectionDomain; +import com.fr.third.net.sf.cglib.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +// TODO: don't require exact match for return type + +/** + * DOCUMENTATION FROM APACHE AVALON DELEGATE CLASS + * + *

+ * Delegates are a typesafe pointer to another method. Since Java does not + * have language support for such a construct, this utility will construct + * a proxy that forwards method calls to any method with the same signature. + * This utility is inspired in part by the C# delegate mechanism. We + * implemented it in a Java-centric manner. + *

+ * + *

Delegate

+ *

+ * Any interface with one method can become the interface for a delegate. + * Consider the example below: + *

+ * + *
+ *   public interface MainDelegate {
+ *       int main(String[] args);
+ *   }
+ * 
+ * + *

+ * The interface above is an example of an interface that can become a + * delegate. It has only one method, and the interface is public. In + * order to create a delegate for that method, all we have to do is + * call MethodDelegate.create(this, "alternateMain", MainDelegate.class). + * The following program will show how to use it: + *

+ * + *
+ *   public class Main {
+ *       public static int main( String[] args ) {
+ *           Main newMain = new Main();
+ *           MainDelegate start = (MainDelegate)
+ *               MethodDelegate.create(newMain, "alternateMain", MainDelegate.class);
+ *           return start.main( args );
+ *       }
+ *
+ *       public int alternateMain( String[] args ) {
+ *           for (int i = 0; i < args.length; i++) {
+ *               System.out.println( args[i] );
+ *           }
+ *           return args.length;
+ *       }
+ *   }
+ * 
+ * + *

+ * By themselves, delegates don't do much. Their true power lies in the fact that + * they can be treated like objects, and passed to other methods. In fact that is + * one of the key building blocks of building Intelligent Agents which in tern are + * the foundation of artificial intelligence. In the above program, we could have + * easily created the delegate to match the static main method by + * substituting the delegate creation call with this: + * MethodDelegate.createStatic(getClass(), "main", MainDelegate.class). + *

+ *

+ * Another key use for Delegates is to register event listeners. It is much easier + * to have all the code for your events separated out into methods instead of individual + * classes. One of the ways Java gets around that is to create anonymous classes. + * They are particularly troublesome because many Debuggers do not know what to do + * with them. Anonymous classes tend to duplicate alot of code as well. We can + * use any interface with one declared method to forward events to any method that + * matches the signature (although the method name can be different). + *

+ * + *

Equality

+ * The criteria that we use to test if two delegates are equal are: + *
    + *
  • + * They both refer to the same instance. That is, the instance + * parameter passed to the newDelegate method was the same for both. The + * instances are compared with the identity equality operator, ==. + *
  • + *
  • They refer to the same method as resolved by Method.equals.
  • + *
+ * + * @version $Id: MethodDelegate.java,v 1.25 2006/03/05 02:43:19 herbyderby Exp $ + */ +abstract public class MethodDelegate { + private static final MethodDelegateKey KEY_FACTORY = + (MethodDelegateKey)KeyFactory.create(MethodDelegateKey.class, KeyFactory.CLASS_BY_NAME); + + protected Object target; + protected String eqMethod; + + interface MethodDelegateKey { + Object newInstance(Class delegateClass, String methodName, Class iface); + } + + public static MethodDelegate createStatic(Class targetClass, String methodName, Class iface) { + Generator gen = new Generator(); + gen.setTargetClass(targetClass); + gen.setMethodName(methodName); + gen.setInterface(iface); + return gen.create(); + } + + public static MethodDelegate create(Object target, String methodName, Class iface) { + Generator gen = new Generator(); + gen.setTarget(target); + gen.setMethodName(methodName); + gen.setInterface(iface); + return gen.create(); + } + + public boolean equals(Object obj) { + MethodDelegate other = (MethodDelegate)obj; + return (other != null && target == other.target) && eqMethod.equals(other.eqMethod); + } + + public int hashCode() { + return target.hashCode() ^ eqMethod.hashCode(); + } + + public Object getTarget() { + return target; + } + + abstract public MethodDelegate newInstance(Object target); + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(MethodDelegate.class.getName()); + private static final Type METHOD_DELEGATE = + TypeUtils.parseType("com.fr.third.net.sf.cglib.reflect.MethodDelegate"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", METHOD_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); + + private Object target; + private Class targetClass; + private String methodName; + private Class iface; + + public Generator() { + super(SOURCE); + } + + public void setTarget(Object target) { + this.target = target; + this.targetClass = target.getClass(); + } + + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public void setInterface(Class iface) { + this.iface = iface; + } + + protected ClassLoader getDefaultClassLoader() { + return targetClass.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(targetClass); + } + + public MethodDelegate create() { + setNamePrefix(targetClass.getName()); + Object key = KEY_FACTORY.newInstance(targetClass, methodName, iface); + return (MethodDelegate)super.create(key); + } + + protected Object firstInstance(Class type) { + return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target); + } + + protected Object nextInstance(Object instance) { + return ((MethodDelegate)instance).newInstance(target); + } + + public void generateClass(ClassVisitor v) throws NoSuchMethodException { + Method proxy = ReflectUtils.findInterfaceMethod(iface); + final Method method = targetClass.getMethod(methodName, proxy.getParameterTypes()); + if (!proxy.getReturnType().isAssignableFrom(method.getReturnType())) { + throw new IllegalArgumentException("incompatible return types"); + } + + MethodInfo methodInfo = ReflectUtils.getMethodInfo(method); + + boolean isStatic = TypeUtils.isStatic(methodInfo.getModifiers()); + if ((target == null) ^ isStatic) { + throw new IllegalArgumentException("Static method " + (isStatic ? "not " : "") + "expected"); + } + + ClassEmitter ce = new ClassEmitter(v); + CodeEmitter e; + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + METHOD_DELEGATE, + new Type[]{ Type.getType(iface) }, + Constants.SOURCE_FILE); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, "eqMethod", Constants.TYPE_STRING, null); + EmitUtils.null_constructor(ce); + + // generate proxied method + MethodInfo proxied = ReflectUtils.getMethodInfo(iface.getDeclaredMethods()[0]); + int modifiers = Constants.ACC_PUBLIC; + if ((proxied.getModifiers() & Constants.ACC_VARARGS) == Constants.ACC_VARARGS) { + modifiers |= Constants.ACC_VARARGS; + } + e = EmitUtils.begin_method(ce, proxied, modifiers); + e.load_this(); + e.super_getfield("target", Constants.TYPE_OBJECT); + e.checkcast(methodInfo.getClassInfo().getType()); + e.load_args(); + e.invoke(methodInfo); + e.return_value(); + e.end_method(); + + // newInstance + e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); + e.new_instance_this(); + e.dup(); + e.dup2(); + e.invoke_constructor_this(); + e.getfield("eqMethod"); + e.super_putfield("eqMethod", Constants.TYPE_STRING); + e.load_arg(0); + e.super_putfield("target", Constants.TYPE_OBJECT); + e.return_value(); + e.end_method(); + + // static initializer + e = ce.begin_static(); + e.push(methodInfo.getSignature().toString()); + e.putfield("eqMethod"); + e.return_value(); + e.end_method(); + + ce.end_class(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/MulticastDelegate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/MulticastDelegate.java new file mode 100644 index 000000000..ca80a2648 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/reflect/MulticastDelegate.java @@ -0,0 +1,179 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.reflect; + +import java.lang.reflect.*; +import java.security.ProtectionDomain; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.MethodVisitor; +import com.fr.third.org.objectweb.asm.Type; + +abstract public class MulticastDelegate implements Cloneable { + protected Object[] targets = {}; + + protected MulticastDelegate() { + } + + public List getTargets() { + return new ArrayList(Arrays.asList(targets)); + } + + abstract public MulticastDelegate add(Object target); + + protected MulticastDelegate addHelper(Object target) { + MulticastDelegate copy = newInstance(); + copy.targets = new Object[targets.length + 1]; + System.arraycopy(targets, 0, copy.targets, 0, targets.length); + copy.targets[targets.length] = target; + return copy; + } + + public MulticastDelegate remove(Object target) { + for (int i = targets.length - 1; i >= 0; i--) { + if (targets[i].equals(target)) { + MulticastDelegate copy = newInstance(); + copy.targets = new Object[targets.length - 1]; + System.arraycopy(targets, 0, copy.targets, 0, i); + System.arraycopy(targets, i + 1, copy.targets, i, targets.length - i - 1); + return copy; + } + } + return this; + } + + abstract public MulticastDelegate newInstance(); + + public static MulticastDelegate create(Class iface) { + Generator gen = new Generator(); + gen.setInterface(iface); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(MulticastDelegate.class.getName()); + private static final Type MULTICAST_DELEGATE = + TypeUtils.parseType("com.fr.third.net.sf.cglib.reflect.MulticastDelegate"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", MULTICAST_DELEGATE, new Type[0]); + private static final Signature ADD_DELEGATE = + new Signature("add", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); + private static final Signature ADD_HELPER = + new Signature("addHelper", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); + + private Class iface; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return iface.getClassLoader(); + } + + protected ProtectionDomain getProtectionDomain() { + return ReflectUtils.getProtectionDomain(iface); + } + + public void setInterface(Class iface) { + this.iface = iface; + } + + public MulticastDelegate create() { + setNamePrefix(MulticastDelegate.class.getName()); + return (MulticastDelegate)super.create(iface.getName()); + } + + public void generateClass(ClassVisitor cv) { + final MethodInfo method = ReflectUtils.getMethodInfo(ReflectUtils.findInterfaceMethod(iface)); + + ClassEmitter ce = new ClassEmitter(cv); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + MULTICAST_DELEGATE, + new Type[]{ Type.getType(iface) }, + Constants.SOURCE_FILE); + EmitUtils.null_constructor(ce); + + // generate proxied method + emitProxy(ce, method); + + // newInstance + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); + e.new_instance_this(); + e.dup(); + e.invoke_constructor_this(); + e.return_value(); + e.end_method(); + + // add + e = ce.begin_method(Constants.ACC_PUBLIC, ADD_DELEGATE, null); + e.load_this(); + e.load_arg(0); + e.checkcast(Type.getType(iface)); + e.invoke_virtual_this(ADD_HELPER); + e.return_value(); + e.end_method(); + + ce.end_class(); + } + + private void emitProxy(ClassEmitter ce, final MethodInfo method) { + int modifiers = Constants.ACC_PUBLIC; + if ((method.getModifiers() & Constants.ACC_VARARGS) == Constants.ACC_VARARGS) { + modifiers |= Constants.ACC_VARARGS; + } + final CodeEmitter e = EmitUtils.begin_method(ce, method, modifiers); + Type returnType = method.getSignature().getReturnType(); + final boolean returns = returnType != Type.VOID_TYPE; + Local result = null; + if (returns) { + result = e.make_local(returnType); + e.zero_or_null(returnType); + e.store_local(result); + } + e.load_this(); + e.super_getfield("targets", Constants.TYPE_OBJECT_ARRAY); + final Local result2 = result; + EmitUtils.process_array(e, Constants.TYPE_OBJECT_ARRAY, new ProcessArrayCallback() { + public void processElement(Type type) { + e.checkcast(Type.getType(iface)); + e.load_args(); + e.invoke(method); + if (returns) { + e.store_local(result2); + } + } + }); + if (returns) { + e.load_local(result); + } + e.return_value(); + e.end_method(); + } + + protected Object firstInstance(Class type) { + // make a new instance in case first object is used with a long list of targets + return ((MulticastDelegate)ReflectUtils.newInstance(type)).newInstance(); + } + + protected Object nextInstance(Object instance) { + return ((MulticastDelegate)instance).newInstance(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassFilterTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassFilterTransformer.java new file mode 100644 index 000000000..21c0c8be6 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassFilterTransformer.java @@ -0,0 +1,85 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.*; + +abstract public class AbstractClassFilterTransformer extends AbstractClassTransformer { + private ClassTransformer pass; + private ClassVisitor target; + + public void setTarget(ClassVisitor target) { + super.setTarget(target); + pass.setTarget(target); + } + + protected AbstractClassFilterTransformer(ClassTransformer pass) { + this.pass = pass; + } + + abstract protected boolean accept(int version, int access, String name, String signature, String superName, String[] interfaces); + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + target = accept(version, access, name, signature, superName, interfaces) ? pass : cv; + target.visit(version, access, name, signature, superName, interfaces); + } + + public void visitSource(String source, String debug) { + target.visitSource(source, debug); + } + + public void visitOuterClass(String owner, String name, String desc) { + target.visitOuterClass(owner, name, desc); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return target.visitAnnotation(desc, visible); + } + + public void visitAttribute(Attribute attr) { + target.visitAttribute(attr); + } + + public void visitInnerClass(String name, String outerName, String innerName, int access) { + target.visitInnerClass(name, outerName, innerName, access); + } + + public FieldVisitor visitField(int access, + String name, + String desc, + String signature, + Object value) { + return target.visitField(access, name, desc, signature, value); + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return target.visitMethod(access, name, desc, signature, exceptions); + } + + public void visitEnd() { + target.visitEnd(); + target = null; // just to be safe + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassLoader.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassLoader.java new file mode 100644 index 000000000..df807614e --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassLoader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.net.sf.cglib.core.CodeGenerationException; +import com.fr.third.net.sf.cglib.core.ClassGenerator; +import com.fr.third.net.sf.cglib.core.DebuggingClassWriter; +import com.fr.third.org.objectweb.asm.ClassReader; +import com.fr.third.org.objectweb.asm.ClassWriter; +import com.fr.third.org.objectweb.asm.Attribute; + +import java.io.IOException; + +abstract public class AbstractClassLoader extends ClassLoader { + private ClassFilter filter; + private ClassLoader classPath; + private static java.security.ProtectionDomain DOMAIN ; + + static{ + + DOMAIN = (java.security.ProtectionDomain) + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + return AbstractClassLoader.class.getProtectionDomain(); + } + }); + } + + protected AbstractClassLoader(ClassLoader parent, ClassLoader classPath, ClassFilter filter) { + super(parent); + this.filter = filter; + this.classPath = classPath; + } + + public Class loadClass(String name) throws ClassNotFoundException { + + Class loaded = findLoadedClass(name); + + if( loaded != null ){ + if( loaded.getClassLoader() == this ){ + return loaded; + }//else reload with this class loader + } + + if (!filter.accept(name)) { + return super.loadClass(name); + } + ClassReader r; + try { + + java.io.InputStream is = classPath.getResourceAsStream( + name.replace('.','/') + ".class" + ); + + if (is == null) { + + throw new ClassNotFoundException(name); + + } + try { + + r = new ClassReader(is); + + } finally { + + is.close(); + + } + } catch (IOException e) { + throw new ClassNotFoundException(name + ":" + e.getMessage()); + } + + try { + DebuggingClassWriter w = + new DebuggingClassWriter(ClassWriter.COMPUTE_FRAMES); + getGenerator(r).generateClass(w); + byte[] b = w.toByteArray(); + Class c = super.defineClass(name, b, 0, b.length, DOMAIN); + postProcess(c); + return c; + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + + protected ClassGenerator getGenerator(ClassReader r) { + return new ClassReaderGenerator(r, attributes(), getFlags()); + } + + protected int getFlags() { + return 0; + } + + protected Attribute[] attributes() { + return null; + } + + protected void postProcess(Class c) { + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassTransformer.java new file mode 100644 index 000000000..94e61cf71 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AbstractClassTransformer.java @@ -0,0 +1,29 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +abstract public class AbstractClassTransformer extends ClassTransformer { + protected AbstractClassTransformer() { + super(Opcodes.ASM6); + } + + public void setTarget(ClassVisitor target) { + cv = target; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AnnotationVisitorTee.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AnnotationVisitorTee.java new file mode 100644 index 000000000..dac8b57f2 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/AnnotationVisitorTee.java @@ -0,0 +1,61 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.AnnotationVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +public class AnnotationVisitorTee extends AnnotationVisitor { + private AnnotationVisitor av1, av2; + + public static AnnotationVisitor getInstance(AnnotationVisitor av1, AnnotationVisitor av2) { + if (av1 == null) + return av2; + if (av2 == null) + return av1; + return new AnnotationVisitorTee(av1, av2); + } + + public AnnotationVisitorTee(AnnotationVisitor av1, AnnotationVisitor av2) { + super(Opcodes.ASM6); + this.av1 = av1; + this.av2 = av2; + } + + public void visit(String name, Object value) { + av2.visit(name, value); + av2.visit(name, value); + } + + public void visitEnum(String name, String desc, String value) { + av1.visitEnum(name, desc, value); + av2.visitEnum(name, desc, value); + } + + public AnnotationVisitor visitAnnotation(String name, String desc) { + return getInstance(av1.visitAnnotation(name, desc), + av2.visitAnnotation(name, desc)); + } + + public AnnotationVisitor visitArray(String name) { + return getInstance(av1.visitArray(name), av2.visitArray(name)); + } + + public void visitEnd() { + av1.visitEnd(); + av2.visitEnd(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassEmitterTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassEmitterTransformer.java new file mode 100644 index 000000000..260a27e31 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassEmitterTransformer.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.net.sf.cglib.core.ClassEmitter; + +abstract public class ClassEmitterTransformer extends ClassEmitter { +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassFilter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassFilter.java new file mode 100644 index 000000000..f6ced9756 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassFilter.java @@ -0,0 +1,27 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fr.third.net.sf.cglib.transform; + +/** + * + * @author baliuka + */ +public interface ClassFilter { + + boolean accept(String className); + +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassFilterTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassFilterTransformer.java new file mode 100644 index 000000000..23932b9e9 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassFilterTransformer.java @@ -0,0 +1,31 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.*; + +public class ClassFilterTransformer extends AbstractClassFilterTransformer { + private ClassFilter filter; + + public ClassFilterTransformer(ClassFilter filter, ClassTransformer pass) { + super(pass); + this.filter = filter; + } + + protected boolean accept(int version, int access, String name, String signature, String superName, String[] interfaces) { + return filter.accept(name.replace('/', '.')); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassReaderGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassReaderGenerator.java new file mode 100644 index 000000000..8bbb10bc9 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassReaderGenerator.java @@ -0,0 +1,41 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.net.sf.cglib.core.ClassGenerator; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.ClassReader; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +public class ClassReaderGenerator implements ClassGenerator { + private final ClassReader r; + private final Attribute[] attrs; + private final int flags; + + public ClassReaderGenerator(ClassReader r, int flags) { + this(r, null, flags); + } + + public ClassReaderGenerator(ClassReader r, Attribute[] attrs, int flags) { + this.r = r; + this.attrs = (attrs != null) ? attrs : new Attribute[0]; + this.flags = flags; + } + + public void generateClass(ClassVisitor v) { + r.accept(v, attrs, flags); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformer.java new file mode 100644 index 000000000..95d41e7bd --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformer.java @@ -0,0 +1,29 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +public abstract class ClassTransformer extends ClassVisitor { + public ClassTransformer() { + super(Opcodes.ASM6); + } + public ClassTransformer(int opcode) { + super(opcode); + } + public abstract void setTarget(ClassVisitor target); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerChain.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerChain.java new file mode 100644 index 000000000..32fbe4efa --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerChain.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.*; + +public class ClassTransformerChain extends AbstractClassTransformer { + private ClassTransformer[] chain; + + public ClassTransformerChain(ClassTransformer[] chain) { + this.chain = (ClassTransformer[])chain.clone(); + } + + public void setTarget(ClassVisitor v) { + super.setTarget(chain[0]); + ClassVisitor next = v; + for (int i = chain.length - 1; i >= 0; i--) { + chain[i].setTarget(next); + next = chain[i]; + } + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return cv.visitMethod(access, name, desc, signature, exceptions); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("ClassTransformerChain{"); + for (int i = 0; i < chain.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(chain[i].toString()); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerFactory.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerFactory.java new file mode 100644 index 000000000..892702189 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerFactory.java @@ -0,0 +1,20 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +public interface ClassTransformerFactory { + ClassTransformer newInstance(); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerTee.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerTee.java new file mode 100644 index 000000000..44088b969 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassTransformerTee.java @@ -0,0 +1,32 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; + +public class ClassTransformerTee extends ClassTransformer { + private ClassVisitor branch; + + public ClassTransformerTee(ClassVisitor branch) { + super(Opcodes.ASM6); + this.branch = branch; + } + + public void setTarget(ClassVisitor target) { + cv = new ClassVisitorTee(branch, target); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassVisitorTee.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassVisitorTee.java new file mode 100644 index 000000000..58ad46684 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/ClassVisitorTee.java @@ -0,0 +1,103 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.*; + +public class ClassVisitorTee extends ClassVisitor { + private ClassVisitor cv1, cv2; + + public ClassVisitorTee(ClassVisitor cv1, ClassVisitor cv2) { + super(Opcodes.ASM6); + this.cv1 = cv1; + this.cv2 = cv2; + } + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + cv1.visit(version, access, name, signature, superName, interfaces); + cv2.visit(version, access, name, signature, superName, interfaces); + } + + public void visitEnd() { + cv1.visitEnd(); + cv2.visitEnd(); + cv1 = cv2 = null; + } + + public void visitInnerClass(String name, String outerName, String innerName, int access) { + cv1.visitInnerClass(name, outerName, innerName, access); + cv2.visitInnerClass(name, outerName, innerName, access); + } + + public FieldVisitor visitField(int access, + String name, + String desc, + String signature, + Object value) { + FieldVisitor fv1 = cv1.visitField(access, name, desc, signature, value); + FieldVisitor fv2 = cv2.visitField(access, name, desc, signature, value); + if (fv1 == null) + return fv2; + if (fv2 == null) + return fv1; + return new FieldVisitorTee(fv1, fv2); + } + + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + MethodVisitor mv1 = cv1.visitMethod(access, name, desc, signature, exceptions); + MethodVisitor mv2 = cv2.visitMethod(access, name, desc, signature, exceptions); + if (mv1 == null) + return mv2; + if (mv2 == null) + return mv1; + return new MethodVisitorTee(mv1, mv2); + } + + public void visitSource(String source, String debug) { + cv1.visitSource(source, debug); + cv2.visitSource(source, debug); + } + + public void visitOuterClass(String owner, String name, String desc) { + cv1.visitOuterClass(owner, name, desc); + cv2.visitOuterClass(owner, name, desc); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(cv1.visitAnnotation(desc, visible), + cv2.visitAnnotation(desc, visible)); + } + + public void visitAttribute(Attribute attrs) { + cv1.visitAttribute(attrs); + cv2.visitAttribute(attrs); + } + + public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(cv1.visitTypeAnnotation(typeRef, typePath, desc, visible), + cv2.visitTypeAnnotation(typeRef, typePath, desc, visible)); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/FieldVisitorTee.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/FieldVisitorTee.java new file mode 100644 index 000000000..526d93ab9 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/FieldVisitorTee.java @@ -0,0 +1,53 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.AnnotationVisitor; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.FieldVisitor; +import com.fr.third.org.objectweb.asm.Opcodes; +import com.fr.third.org.objectweb.asm.TypePath; + +public class FieldVisitorTee extends FieldVisitor { + private FieldVisitor fv1, fv2; + + public FieldVisitorTee(FieldVisitor fv1, FieldVisitor fv2) { + super(Opcodes.ASM6); + this.fv1 = fv1; + this.fv2 = fv2; + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(fv1.visitAnnotation(desc, visible), + fv2.visitAnnotation(desc, visible)); + } + + public void visitAttribute(Attribute attr) { + fv1.visitAttribute(attr); + fv2.visitAttribute(attr); + } + + public void visitEnd() { + fv1.visitEnd(); + fv2.visitEnd(); + } + + public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(fv1.visitTypeAnnotation(typeRef, typePath, desc, visible), + fv2.visitTypeAnnotation(typeRef, typePath, desc, visible)); + } +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodFilter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodFilter.java new file mode 100644 index 000000000..57eed786f --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodFilter.java @@ -0,0 +1,23 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.Attribute; + +public interface MethodFilter { + // TODO: pass class name too? + boolean accept(int access, String name, String desc, String signature, String[] exceptions); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodFilterTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodFilterTransformer.java new file mode 100644 index 000000000..017d88d8d --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodFilterTransformer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.*; + +public class MethodFilterTransformer extends AbstractClassTransformer { + private MethodFilter filter; + private ClassTransformer pass; + private ClassVisitor direct; + + public MethodFilterTransformer(MethodFilter filter, ClassTransformer pass) { + this.filter = filter; + this.pass = pass; + super.setTarget(pass); + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return (filter.accept(access, name, desc, signature, exceptions) ? pass : direct).visitMethod(access, name, desc, signature, exceptions); + } + + public void setTarget(ClassVisitor target) { + pass.setTarget(target); + direct = target; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodVisitorTee.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodVisitorTee.java new file mode 100644 index 000000000..bea2a3197 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/MethodVisitorTee.java @@ -0,0 +1,187 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.org.objectweb.asm.*; + +public class MethodVisitorTee extends MethodVisitor { + private final MethodVisitor mv1; + private final MethodVisitor mv2; + + public MethodVisitorTee(MethodVisitor mv1, MethodVisitor mv2) { + super(Opcodes.ASM6); + this.mv1 = mv1; + this.mv2 = mv2; + } + + public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { + mv1.visitFrame(type, nLocal, local, nStack, stack); + mv2.visitFrame(type, nLocal, local, nStack, stack); + } + + public AnnotationVisitor visitAnnotationDefault() { + return AnnotationVisitorTee.getInstance(mv1.visitAnnotationDefault(), + mv2.visitAnnotationDefault()); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitAnnotation(desc, visible), + mv2.visitAnnotation(desc, visible)); + } + + public AnnotationVisitor visitParameterAnnotation(int parameter, + String desc, + boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitParameterAnnotation(parameter, desc, visible), + mv2.visitParameterAnnotation(parameter, desc, visible)); + } + + public void visitAttribute(Attribute attr) { + mv1.visitAttribute(attr); + mv2.visitAttribute(attr); + } + + public void visitCode() { + mv1.visitCode(); + mv2.visitCode(); + } + + public void visitInsn(int opcode) { + mv1.visitInsn(opcode); + mv2.visitInsn(opcode); + } + + public void visitIntInsn(int opcode, int operand) { + mv1.visitIntInsn(opcode, operand); + mv2.visitIntInsn(opcode, operand); + } + + public void visitVarInsn(int opcode, int var) { + mv1.visitVarInsn(opcode, var); + mv2.visitVarInsn(opcode, var); + } + + public void visitTypeInsn(int opcode, String desc) { + mv1.visitTypeInsn(opcode, desc); + mv2.visitTypeInsn(opcode, desc); + } + + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + mv1.visitFieldInsn(opcode, owner, name, desc); + mv2.visitFieldInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + mv1.visitMethodInsn(opcode, owner, name, desc); + mv2.visitMethodInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + mv1.visitMethodInsn(opcode, owner, name, desc, itf); + mv2.visitMethodInsn(opcode, owner, name, desc, itf); + } + + public void visitJumpInsn(int opcode, Label label) { + mv1.visitJumpInsn(opcode, label); + mv2.visitJumpInsn(opcode, label); + } + + public void visitLabel(Label label) { + mv1.visitLabel(label); + mv2.visitLabel(label); + } + + public void visitLdcInsn(Object cst) { + mv1.visitLdcInsn(cst); + mv2.visitLdcInsn(cst); + } + + public void visitIincInsn(int var, int increment) { + mv1.visitIincInsn(var, increment); + mv2.visitIincInsn(var, increment); + } + + public void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]) { + mv1.visitTableSwitchInsn(min, max, dflt, labels); + mv2.visitTableSwitchInsn(min, max, dflt, labels); + } + + public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]) { + mv1.visitLookupSwitchInsn(dflt, keys, labels); + mv2.visitLookupSwitchInsn(dflt, keys, labels); + } + + public void visitMultiANewArrayInsn(String desc, int dims) { + mv1.visitMultiANewArrayInsn(desc, dims); + mv2.visitMultiANewArrayInsn(desc, dims); + } + + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + mv1.visitTryCatchBlock(start, end, handler, type); + mv2.visitTryCatchBlock(start, end, handler, type); + } + + public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { + mv1.visitLocalVariable(name, desc, signature, start, end, index); + mv2.visitLocalVariable(name, desc, signature, start, end, index); + } + + public void visitLineNumber(int line, Label start) { + mv1.visitLineNumber(line, start); + mv2.visitLineNumber(line, start); + } + + public void visitMaxs(int maxStack, int maxLocals) { + mv1.visitMaxs(maxStack, maxLocals); + mv2.visitMaxs(maxStack, maxLocals); + } + + public void visitEnd() { + mv1.visitEnd(); + mv2.visitEnd(); + } + + public void visitParameter(String name, int access) { + mv1.visitParameter(name, access); + mv2.visitParameter(name, access); + } + + public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitTypeAnnotation(typeRef, typePath, desc, visible), + mv2.visitTypeAnnotation(typeRef, typePath, desc, visible)); + } + + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { + mv1.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + mv2.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + } + + public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitInsnAnnotation(typeRef, typePath, desc, visible), + mv2.visitInsnAnnotation(typeRef, typePath, desc, visible)); + } + + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitTryCatchAnnotation(typeRef, typePath, desc, visible), + mv2.visitTryCatchAnnotation(typeRef, typePath, desc, visible)); + } + + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible), + mv2.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible)); + } +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/TransformingClassGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/TransformingClassGenerator.java new file mode 100644 index 000000000..84fb715a4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/TransformingClassGenerator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import com.fr.third.net.sf.cglib.core.ClassGenerator; +import com.fr.third.net.sf.cglib.core.Transformer; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +public class TransformingClassGenerator implements ClassGenerator { + private ClassGenerator gen; + private ClassTransformer t; + + public TransformingClassGenerator(ClassGenerator gen, ClassTransformer t) { + this.gen = gen; + this.t = t; + } + + public void generateClass(ClassVisitor v) throws Exception { + t.setTarget(v); + gen.generateClass(t); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/TransformingClassLoader.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/TransformingClassLoader.java new file mode 100644 index 000000000..c26681a23 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/TransformingClassLoader.java @@ -0,0 +1,34 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform; + +import java.util.*; +import com.fr.third.net.sf.cglib.core.ClassGenerator; +import com.fr.third.org.objectweb.asm.*; + +public class TransformingClassLoader extends AbstractClassLoader { + private ClassTransformerFactory t; + + public TransformingClassLoader(ClassLoader parent, ClassFilter filter, ClassTransformerFactory t) { + super(parent, parent, filter); + this.t = t; + } + + protected ClassGenerator getGenerator(ClassReader r) { + ClassTransformer t2 = (ClassTransformer)t.newInstance(); + return new TransformingClassGenerator(super.getGenerator(r), t2); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AbstractInterceptFieldCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AbstractInterceptFieldCallback.java new file mode 100644 index 000000000..8619f95f5 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AbstractInterceptFieldCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +/** + * @author Chris Nokleberg + */ +public class AbstractInterceptFieldCallback implements InterceptFieldCallback { + + public int writeInt(Object obj, String name, int oldValue, int newValue) { return newValue; } + public char writeChar(Object obj, String name, char oldValue, char newValue) { return newValue; } + public byte writeByte(Object obj, String name, byte oldValue, byte newValue) { return newValue; } + public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) { return newValue; } + public short writeShort(Object obj, String name, short oldValue, short newValue) { return newValue; } + public float writeFloat(Object obj, String name, float oldValue, float newValue) { return newValue; } + public double writeDouble(Object obj, String name, double oldValue, double newValue) { return newValue; } + public long writeLong(Object obj, String name, long oldValue, long newValue) { return newValue; } + public Object writeObject(Object obj, String name, Object oldValue, Object newValue) { return newValue; } + + public int readInt(Object obj, String name, int oldValue) { return oldValue; } + public char readChar(Object obj, String name, char oldValue) { return oldValue; } + public byte readByte(Object obj, String name, byte oldValue) { return oldValue; } + public boolean readBoolean(Object obj, String name, boolean oldValue) { return oldValue; } + public short readShort(Object obj, String name, short oldValue) { return oldValue; } + public float readFloat(Object obj, String name, float oldValue) { return oldValue; } + public double readDouble(Object obj, String name, double oldValue) { return oldValue; } + public long readLong(Object obj, String name, long oldValue) { return oldValue; } + public Object readObject(Object obj, String name, Object oldValue) { return oldValue; } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AccessFieldTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AccessFieldTransformer.java new file mode 100644 index 000000000..957bca066 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AccessFieldTransformer.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.net.sf.cglib.transform.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.MethodVisitor; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; + +public class AccessFieldTransformer extends ClassEmitterTransformer { + private Callback callback; + + public AccessFieldTransformer(Callback callback) { + this.callback = callback; + } + + public interface Callback { + String getPropertyName(Type owner, String fieldName); + } + + public void declare_field(int access, final String name, Type type, Object value) { + super.declare_field(access, name, type, value); + + String property = TypeUtils.upperFirst(callback.getPropertyName(getClassType(), name)); + if (property != null) { + CodeEmitter e; + e = begin_method(Constants.ACC_PUBLIC, + new Signature("get" + property, + type, + Constants.TYPES_EMPTY), + null); + e.load_this(); + e.getfield(name); + e.return_value(); + e.end_method(); + + e = begin_method(Constants.ACC_PUBLIC, + new Signature("set" + property, + Type.VOID_TYPE, + new Type[]{ type }), + null); + e.load_this(); + e.load_arg(0); + e.putfield(name); + e.return_value(); + e.end_method(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddDelegateTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddDelegateTransformer.java new file mode 100644 index 000000000..3234966ed --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddDelegateTransformer.java @@ -0,0 +1,119 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.net.sf.cglib.transform.*; +import java.lang.reflect.*; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Juozas Baliuka + */ +public class AddDelegateTransformer extends ClassEmitterTransformer { + private static final String DELEGATE = "$CGLIB_DELEGATE"; + private static final Signature CSTRUCT_OBJECT = + TypeUtils.parseSignature("void (Object)"); + + private Class[] delegateIf; + private Class delegateImpl; + private Type delegateType; + + /** Creates a new instance of AddDelegateTransformer */ + public AddDelegateTransformer(Class delegateIf[], Class delegateImpl) { + try { + delegateImpl.getConstructor(new Class[]{ Object.class }); + this.delegateIf = delegateIf; + this.delegateImpl = delegateImpl; + delegateType = Type.getType(delegateImpl); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { + + if(!TypeUtils.isInterface(access)){ + + Type[] all = TypeUtils.add(interfaces, TypeUtils.getTypes(delegateIf)); + super.begin_class(version, access, className, superType, all, sourceFile); + + declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT, + DELEGATE, + delegateType, + null); + for (int i = 0; i < delegateIf.length; i++) { + Method[] methods = delegateIf[i].getMethods(); + for (int j = 0; j < methods.length; j++) { + if (Modifier.isAbstract(methods[j].getModifiers())) { + addDelegate(methods[j]); + } + } + } + }else{ + super.begin_class(version, access, className, superType, interfaces, sourceFile); + } + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + final CodeEmitter e = super.begin_method(access, sig, exceptions); + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { + return new CodeEmitter(e) { + private boolean transformInit = true; + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + if (transformInit && opcode == Constants.INVOKESPECIAL) { + load_this(); + new_instance(delegateType); + dup(); + load_this(); + invoke_constructor(delegateType, CSTRUCT_OBJECT); + putfield(DELEGATE); + transformInit = false; + } + } + }; + } + return e; + } + + private void addDelegate(Method m) { + Method delegate; + try { + delegate = delegateImpl.getMethod(m.getName(), m.getParameterTypes()); + if (!delegate.getReturnType().getName().equals(m.getReturnType().getName())){ + throw new IllegalArgumentException("Invalid delegate signature " + delegate); + } + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + + final Signature sig = ReflectUtils.getSignature(m); + Type[] exceptions = TypeUtils.getTypes(m.getExceptionTypes()); + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, sig, exceptions); + e.load_this(); + e.getfield(DELEGATE); + e.load_args(); + e.invoke_virtual(delegateType, sig); + e.return_value(); + e.end_method(); + } +} + + + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddInitTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddInitTransformer.java new file mode 100644 index 000000000..6708f2bbb --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddInitTransformer.java @@ -0,0 +1,63 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import java.lang.reflect.Method; + +import com.fr.third.net.sf.cglib.core.CodeEmitter; +import com.fr.third.net.sf.cglib.core.Constants; +import com.fr.third.net.sf.cglib.core.MethodInfo; +import com.fr.third.net.sf.cglib.core.ReflectUtils; +import com.fr.third.net.sf.cglib.core.Signature; +import com.fr.third.net.sf.cglib.transform.ClassEmitterTransformer; + +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Mark Hobson + */ +public class AddInitTransformer extends ClassEmitterTransformer { + private MethodInfo info; + + public AddInitTransformer(Method method) { + info = ReflectUtils.getMethodInfo(method); + + Type[] types = info.getSignature().getArgumentTypes(); + if (types.length != 1 || + !types[0].equals(Constants.TYPE_OBJECT) || + !info.getSignature().getReturnType().equals(Type.VOID_TYPE)) { + throw new IllegalArgumentException(method + " illegal signature"); + } + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + final CodeEmitter emitter = super.begin_method(access, sig, exceptions); + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { + return new CodeEmitter(emitter) { + public void visitInsn(int opcode) { + if (opcode == Constants.RETURN) { + load_this(); + invoke(info); + } + super.visitInsn(opcode); + } + }; + } + return emitter; + } +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddPropertyTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddPropertyTransformer.java new file mode 100644 index 000000000..5d9553526 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddPropertyTransformer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.net.sf.cglib.transform.*; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Type; + +public class AddPropertyTransformer extends ClassEmitterTransformer { + private final String[] names; + private final Type[] types; + + public AddPropertyTransformer(Map props) { + int size = props.size(); + names = (String[])props.keySet().toArray(new String[size]); + types = new Type[size]; + for (int i = 0; i < size; i++) { + types[i] = (Type)props.get(names[i]); + } + } + + public AddPropertyTransformer(String[] names, Type[] types) { + this.names = names; + this.types = types; + } + + public void end_class() { + if (!TypeUtils.isAbstract(getAccess())) { + EmitUtils.add_properties(this, names, types); + } + super.end_class(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddStaticInitTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddStaticInitTransformer.java new file mode 100644 index 000000000..dc3c36560 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/AddStaticInitTransformer.java @@ -0,0 +1,49 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import java.lang.reflect.Method; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.net.sf.cglib.transform.*; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class AddStaticInitTransformer extends ClassEmitterTransformer { + private MethodInfo info; + + public AddStaticInitTransformer(Method classInit) { + info = ReflectUtils.getMethodInfo(classInit); + if (!TypeUtils.isStatic(info.getModifiers())) { + throw new IllegalArgumentException(classInit + " is not static"); + } + Type[] types = info.getSignature().getArgumentTypes(); + if (types.length != 1 || + !types[0].equals(Constants.TYPE_CLASS) || + !info.getSignature().getReturnType().equals(Type.VOID_TYPE)) { + throw new IllegalArgumentException(classInit + " illegal signature"); + } + } + + protected void init() { + if (!TypeUtils.isInterface(getAccess())) { + CodeEmitter e = getStaticHook(); + EmitUtils.load_class_this(e); + e.invoke(info); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/FieldProvider.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/FieldProvider.java new file mode 100644 index 000000000..b7182d493 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/FieldProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +public interface FieldProvider { + + String[] getFieldNames(); + + Class[] getFieldTypes(); + + void setField(int index, Object value); + + Object getField(int index); + + + void setField(String name, Object value); + + Object getField(String name); + + +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/FieldProviderTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/FieldProviderTransformer.java new file mode 100644 index 000000000..5778f3db5 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/FieldProviderTransformer.java @@ -0,0 +1,208 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.net.sf.cglib.transform.*; +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +public class FieldProviderTransformer extends ClassEmitterTransformer { + + private static final String FIELD_NAMES = "CGLIB$FIELD_NAMES"; + private static final String FIELD_TYPES = "CGLIB$FIELD_TYPES"; + + private static final Type FIELD_PROVIDER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.transform.impl.FieldProvider"); + private static final Type ILLEGAL_ARGUMENT_EXCEPTION = + TypeUtils.parseType("IllegalArgumentException"); + private static final Signature PROVIDER_GET = + TypeUtils.parseSignature("Object getField(String)"); + private static final Signature PROVIDER_SET = + TypeUtils.parseSignature("void setField(String, Object)"); + private static final Signature PROVIDER_SET_BY_INDEX = + TypeUtils.parseSignature("void setField(int, Object)"); + private static final Signature PROVIDER_GET_BY_INDEX = + TypeUtils.parseSignature("Object getField(int)"); + private static final Signature PROVIDER_GET_TYPES = + TypeUtils.parseSignature("Class[] getFieldTypes()"); + private static final Signature PROVIDER_GET_NAMES = + TypeUtils.parseSignature("String[] getFieldNames()"); + + private int access; + private Map fields; + + public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { + if (!TypeUtils.isAbstract(access)) { + interfaces = TypeUtils.add(interfaces, FIELD_PROVIDER); + } + this.access = access; + fields = new HashMap(); + super.begin_class(version, access, className, superType, interfaces, sourceFile); + } + + public void declare_field(int access, String name, Type type, Object value) { + super.declare_field(access, name, type, value); + + if (!TypeUtils.isStatic(access)) { + fields.put(name, type); + } + } + + public void end_class() { + if (!TypeUtils.isInterface(access)) { + try { + generate(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + super.end_class(); + } + + private void generate() throws Exception { + final String[] names = (String[])fields.keySet().toArray(new String[fields.size()]); + + int indexes[] = new int[names.length]; + for (int i = 0; i < indexes.length; i++) { + indexes[i] = i; + } + + super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_NAMES, Constants.TYPE_STRING_ARRAY, null); + super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_TYPES, Constants.TYPE_CLASS_ARRAY, null); + + // use separate methods here because each process switch inner class needs a final CodeEmitter + initFieldProvider(names); + getNames(); + getTypes(); + getField(names); + setField(names); + setByIndex(names, indexes); + getByIndex(names, indexes); + } + + private void initFieldProvider(String[] names) { + CodeEmitter e = getStaticHook(); + EmitUtils.push_object(e, names); + e.putstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY); + + e.push(names.length); + e.newarray(Constants.TYPE_CLASS); + e.dup(); + for(int i = 0; i < names.length; i++ ){ + e.dup(); + e.push(i); + Type type = (Type)fields.get(names[i]); + EmitUtils.load_class(e, type); + e.aastore(); + } + e.putstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY); + } + + private void getNames() { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_NAMES, null); + e.getstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY); + e.return_value(); + e.end_method(); + } + + private void getTypes() { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_TYPES, null); + e.getstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY); + e.return_value(); + e.end_method(); + } + + private void setByIndex(final String[] names, final int[] indexes) throws Exception { + final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_SET_BY_INDEX, null); + e.load_this(); + e.load_arg(1); + e.load_arg(0); + e.process_switch(indexes, new ProcessSwitchCallback() { + public void processCase(int key, Label end) throws Exception { + Type type = (Type)fields.get(names[key]); + e.unbox(type); + e.putfield(names[key]); + e.return_value(); + } + public void processDefault() throws Exception { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index"); + } + }); + e.end_method(); + } + + private void getByIndex(final String[] names, final int[] indexes) throws Exception { + final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_BY_INDEX, null); + e.load_this(); + e.load_arg(0); + e.process_switch(indexes, new ProcessSwitchCallback() { + public void processCase(int key, Label end) throws Exception { + Type type = (Type)fields.get(names[key]); + e.getfield(names[key]); + e.box(type); + e.return_value(); + } + public void processDefault() throws Exception { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index"); + } + }); + e.end_method(); + } + + // TODO: if this is used to enhance class files SWITCH_STYLE_TRIE should be used + // to avoid JVM hashcode implementation incompatibilities + private void getField(String[] names) throws Exception { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_GET, null); + e.load_this(); + e.load_arg(0); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + Type type = (Type)fields.get(key); + e.getfield((String)key); + e.box(type); + e.return_value(); + } + public void processDefault() { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name"); + } + }); + e.end_method(); + } + + private void setField(String[] names) throws Exception { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_SET, null); + e.load_this(); + e.load_arg(1); + e.load_arg(0); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + Type type = (Type)fields.get(key); + e.unbox(type); + e.putfield((String)key); + e.return_value(); + } + public void processDefault() { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name"); + } + }); + e.end_method(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldCallback.java new file mode 100644 index 000000000..3a3e269c5 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +/** + * @author Juozas Baliuka + */ +public interface InterceptFieldCallback { + + int writeInt(Object obj, String name, int oldValue, int newValue); + char writeChar(Object obj, String name, char oldValue, char newValue); + byte writeByte(Object obj, String name, byte oldValue, byte newValue); + boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue); + short writeShort(Object obj, String name, short oldValue, short newValue); + float writeFloat(Object obj, String name, float oldValue, float newValue); + double writeDouble(Object obj, String name, double oldValue, double newValue); + long writeLong(Object obj, String name, long oldValue, long newValue); + Object writeObject(Object obj, String name, Object oldValue, Object newValue); + + int readInt(Object obj, String name, int oldValue); + char readChar(Object obj, String name, char oldValue); + byte readByte(Object obj, String name, byte oldValue); + boolean readBoolean(Object obj, String name, boolean oldValue); + short readShort(Object obj, String name, short oldValue); + float readFloat(Object obj, String name, float oldValue); + double readDouble(Object obj, String name, double oldValue); + long readLong(Object obj, String name, long oldValue); + Object readObject(Object obj, String name, Object oldValue); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldEnabled.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldEnabled.java new file mode 100644 index 000000000..6d35f52c8 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldEnabled.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +public interface InterceptFieldEnabled { + void setInterceptFieldCallback(InterceptFieldCallback callback); + InterceptFieldCallback getInterceptFieldCallback(); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldFilter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldFilter.java new file mode 100644 index 000000000..a20accfc2 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldFilter.java @@ -0,0 +1,23 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.org.objectweb.asm.Type; + +public interface InterceptFieldFilter { + boolean acceptRead(Type owner, String name); + boolean acceptWrite(Type owner, String name); +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldTransformer.java new file mode 100644 index 000000000..29111ce9f --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/InterceptFieldTransformer.java @@ -0,0 +1,210 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.net.sf.cglib.transform.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class InterceptFieldTransformer extends ClassEmitterTransformer { + private static final String CALLBACK_FIELD = "$CGLIB_READ_WRITE_CALLBACK"; + private static final Type CALLBACK = + TypeUtils.parseType("com.fr.third.net.sf.cglib.transform.impl.InterceptFieldCallback"); + private static final Type ENABLED = + TypeUtils.parseType("com.fr.third.net.sf.cglib.transform.impl.InterceptFieldEnabled"); + private static final Signature ENABLED_SET = + new Signature("setInterceptFieldCallback", Type.VOID_TYPE, new Type[]{ CALLBACK }); + private static final Signature ENABLED_GET = + new Signature("getInterceptFieldCallback", CALLBACK, new Type[0]); + + private InterceptFieldFilter filter; + + public InterceptFieldTransformer(InterceptFieldFilter filter) { + this.filter = filter; + } + + public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { + if (!TypeUtils.isInterface(access)) { + super.begin_class(version, access, className, superType, TypeUtils.add(interfaces, ENABLED), sourceFile); + + super.declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT, + CALLBACK_FIELD, + CALLBACK, + null); + + CodeEmitter e; + e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_GET, null); + e.load_this(); + e.getfield(CALLBACK_FIELD); + e.return_value(); + e.end_method(); + + e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_SET, null); + e.load_this(); + e.load_arg(0); + e.putfield(CALLBACK_FIELD); + e.return_value(); + e.end_method(); + } else { + super.begin_class(version, access, className, superType, interfaces, sourceFile); + } + } + + public void declare_field(int access, String name, Type type, Object value) { + super.declare_field(access, name, type, value); + if (!TypeUtils.isStatic(access)) { + if (filter.acceptRead(getClassType(), name)) { + addReadMethod(name, type); + } + if (filter.acceptWrite(getClassType(), name)) { + addWriteMethod(name, type); + } + } + } + + private void addReadMethod(String name, Type type) { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, + readMethodSig(name, type.getDescriptor()), + null); + e.load_this(); + e.getfield(name); + e.load_this(); + e.invoke_interface(ENABLED,ENABLED_GET); + Label intercept = e.make_label(); + e.ifnonnull(intercept); + e.return_value(); + + e.mark(intercept); + Local result = e.make_local(type); + e.store_local(result); + e.load_this(); + e.invoke_interface(ENABLED,ENABLED_GET); + e.load_this(); + e.push(name); + e.load_local(result); + e.invoke_interface(CALLBACK, readCallbackSig(type)); + if (!TypeUtils.isPrimitive(type)) { + e.checkcast(type); + } + e.return_value(); + e.end_method(); + } + + private void addWriteMethod(String name, Type type) { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, + writeMethodSig(name, type.getDescriptor()), + null); + e.load_this(); + e.dup(); + e.invoke_interface(ENABLED,ENABLED_GET); + Label skip = e.make_label(); + e.ifnull(skip); + + e.load_this(); + e.invoke_interface(ENABLED,ENABLED_GET); + e.load_this(); + e.push(name); + e.load_this(); + e.getfield(name); + e.load_arg(0); + e.invoke_interface(CALLBACK, writeCallbackSig(type)); + if (!TypeUtils.isPrimitive(type)) { + e.checkcast(type); + } + Label go = e.make_label(); + e.goTo(go); + e.mark(skip); + e.load_arg(0); + e.mark(go); + e.putfield(name); + e.return_value(); + e.end_method(); + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + return new CodeEmitter(super.begin_method(access, sig, exceptions)) { + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + Type towner = TypeUtils.fromInternalName(owner); + switch (opcode) { + case Constants.GETFIELD: + if (filter.acceptRead(towner, name)) { + helper(towner, readMethodSig(name, desc)); + return; + } + break; + case Constants.PUTFIELD: + if (filter.acceptWrite(towner, name)) { + helper(towner, writeMethodSig(name, desc)); + return; + } + break; + } + super.visitFieldInsn(opcode, owner, name, desc); + } + + private void helper(Type owner, Signature sig) { + invoke_virtual(owner, sig); + } + }; + } + + private static Signature readMethodSig(String name, String desc) { + return new Signature("$cglib_read_" + name, "()" + desc); + } + + private static Signature writeMethodSig(String name, String desc) { + return new Signature("$cglib_write_" + name, "(" + desc + ")V"); + } + + private static Signature readCallbackSig(Type type) { + Type remap = remap(type); + return new Signature("read" + callbackName(remap), + remap, + new Type[]{ Constants.TYPE_OBJECT, + Constants.TYPE_STRING, + remap }); + } + + private static Signature writeCallbackSig(Type type) { + Type remap = remap(type); + return new Signature("write" + callbackName(remap), + remap, + new Type[]{ Constants.TYPE_OBJECT, + Constants.TYPE_STRING, + remap, + remap }); + } + + private static Type remap(Type type) { + switch (type.getSort()) { + case Type.OBJECT: + case Type.ARRAY: + return Constants.TYPE_OBJECT; + default: + return type; + } + } + + private static String callbackName(Type type) { + return (type == Constants.TYPE_OBJECT) ? + "Object" : + TypeUtils.upperFirst(TypeUtils.getClassName(type)); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/UndeclaredThrowableStrategy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/UndeclaredThrowableStrategy.java new file mode 100644 index 000000000..61213f683 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/UndeclaredThrowableStrategy.java @@ -0,0 +1,61 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import com.fr.third.net.sf.cglib.core.ClassGenerator; +import com.fr.third.net.sf.cglib.core.DefaultGeneratorStrategy; +import com.fr.third.net.sf.cglib.core.GeneratorStrategy; +import com.fr.third.net.sf.cglib.core.TypeUtils; +import com.fr.third.net.sf.cglib.transform.ClassTransformer; +import com.fr.third.net.sf.cglib.transform.MethodFilter; +import com.fr.third.net.sf.cglib.transform.MethodFilterTransformer; +import com.fr.third.net.sf.cglib.transform.TransformingClassGenerator; + +/** + * A {@link GeneratorStrategy} suitable for use with {@link com.fr.third.net.sf.cglib.Enhancer} which + * causes all undeclared exceptions thrown from within a proxied method to be wrapped + * in an alternative exception of your choice. + */ +public class UndeclaredThrowableStrategy extends DefaultGeneratorStrategy { + + + private Class wrapper; + + /** + * Create a new instance of this strategy. + * @param wrapper a class which extends either directly or + * indirectly from Throwable and which has at least one + * constructor that takes a single argument of type + * Throwable, for example + * java.lang.reflect.UndeclaredThrowableException.class + */ + public UndeclaredThrowableStrategy(Class wrapper) { + this.wrapper = wrapper; + } + + private static final MethodFilter TRANSFORM_FILTER = new MethodFilter() { + public boolean accept(int access, String name, String desc, String signature, String[] exceptions) { + return !TypeUtils.isPrivate(access) && name.indexOf('$') < 0; + } + }; + + protected ClassGenerator transform(ClassGenerator cg) throws Exception { + ClassTransformer tr = new UndeclaredThrowableTransformer(wrapper); + tr = new MethodFilterTransformer(TRANSFORM_FILTER, tr); + return new TransformingClassGenerator(cg, tr); + } +} + diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/UndeclaredThrowableTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/UndeclaredThrowableTransformer.java new file mode 100644 index 000000000..619fab259 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/transform/impl/UndeclaredThrowableTransformer.java @@ -0,0 +1,60 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.transform.impl; + +import java.lang.reflect.Constructor; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.net.sf.cglib.transform.*; +import com.fr.third.org.objectweb.asm.Attribute; +import com.fr.third.org.objectweb.asm.Type; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +public class UndeclaredThrowableTransformer extends ClassEmitterTransformer { + private Type wrapper; + + public UndeclaredThrowableTransformer(Class wrapper) { + this.wrapper = Type.getType(wrapper); + boolean found = false; + Constructor[] cstructs = wrapper.getConstructors(); + for (int i = 0; i < cstructs.length; i++) { + Class[] types = cstructs[i].getParameterTypes(); + if (types.length == 1 && types[0].equals(Throwable.class)) { + found = true; + break; + } + } + if (!found) + throw new IllegalArgumentException(wrapper + " does not have a single-arg constructor that takes a Throwable"); + } + + public CodeEmitter begin_method(int access, final Signature sig, final Type[] exceptions) { + CodeEmitter e = super.begin_method(access, sig, exceptions); + if (TypeUtils.isAbstract(access) || sig.equals(Constants.SIG_STATIC)) { + return e; + } + return new CodeEmitter(e) { + private Block handler; + /* init */ { + handler = begin_block(); + } + public void visitMaxs(int maxStack, int maxLocals) { + handler.end(); + EmitUtils.wrap_undeclared_throwable(this, handler, exceptions, wrapper); + super.visitMaxs(maxStack, maxLocals); + } + }; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/util/ParallelSorter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/util/ParallelSorter.java new file mode 100644 index 000000000..e7bd121b7 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/util/ParallelSorter.java @@ -0,0 +1,294 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.util; + +import java.lang.reflect.*; +import java.util.Comparator; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; + +/** + * For the efficient sorting of multiple arrays in parallel. + *

+ * Given two arrays of equal length and varying types, the standard + * technique for sorting them in parallel is to create a new temporary + * object for each row, store the objects in a temporary array, sort the + * array using a custom comparator, and the extract the original values + * back into their respective arrays. This is wasteful in both time and + * memory. + *

+ * This class generates bytecode customized to the particular set of + * arrays you need to sort, in such a way that both arrays are sorted + * in-place, simultaneously. + *

+ * Two sorting algorithms are provided. + * Quicksort is best when you only need to sort by a single column, as + * it requires very few comparisons and swaps. Mergesort is best used + * when sorting multiple columns, as it is a "stable" sort--that is, it + * does not affect the relative order of equal objects from previous sorts. + *

+ * The mergesort algorithm here is an "in-place" variant, which while + * slower, does not require a temporary array. + * + * @author Chris Nokleberg + */ +abstract public class ParallelSorter extends SorterTemplate { + protected Object[] a; + private Comparer comparer; + + protected ParallelSorter() { + } + + abstract public ParallelSorter newInstance(Object[] arrays); + + /** + * Create a new ParallelSorter object for a set of arrays. You may + * sort the arrays multiple times via the same ParallelSorter object. + * @param arrays An array of arrays to sort. The arrays may be a mix + * of primitive and non-primitive types, but should all be the same + * length. + * @param loader ClassLoader for generated class, uses "current" if null + */ + public static ParallelSorter create(Object[] arrays) { + Generator gen = new Generator(); + gen.setArrays(arrays); + return gen.create(); + } + + private int len() { + return ((Object[])a[0]).length; + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + */ + public void quickSort(int index) { + quickSort(index, 0, len(), null); + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + */ + public void quickSort(int index, int lo, int hi) { + quickSort(index, lo, hi, null); + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + * @param cmp Comparator to use if the specified column is non-primitive + */ + public void quickSort(int index, Comparator cmp) { + quickSort(index, 0, len(), cmp); + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + * @param cmp Comparator to use if the specified column is non-primitive + */ + public void quickSort(int index, int lo, int hi, Comparator cmp) { + chooseComparer(index, cmp); + super.quickSort(lo, hi - 1); + } + + /** + * @param index array (column) to sort by + */ + public void mergeSort(int index) { + mergeSort(index, 0, len(), null); + } + + /** + * Sort the arrays using an in-place merge sort. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + */ + public void mergeSort(int index, int lo, int hi) { + mergeSort(index, lo, hi, null); + } + + /** + * Sort the arrays using an in-place merge sort. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + */ + public void mergeSort(int index, Comparator cmp) { + mergeSort(index, 0, len(), cmp); + } + + /** + * Sort the arrays using an in-place merge sort. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + * @param cmp Comparator to use if the specified column is non-primitive + */ + public void mergeSort(int index, int lo, int hi, Comparator cmp) { + chooseComparer(index, cmp); + super.mergeSort(lo, hi - 1); + } + + private void chooseComparer(int index, Comparator cmp) { + Object array = a[index]; + Class type = array.getClass().getComponentType(); + if (type.equals(Integer.TYPE)) { + comparer = new IntComparer((int[])array); + } else if (type.equals(Long.TYPE)) { + comparer = new LongComparer((long[])array); + } else if (type.equals(Double.TYPE)) { + comparer = new DoubleComparer((double[])array); + } else if (type.equals(Float.TYPE)) { + comparer = new FloatComparer((float[])array); + } else if (type.equals(Short.TYPE)) { + comparer = new ShortComparer((short[])array); + } else if (type.equals(Byte.TYPE)) { + comparer = new ByteComparer((byte[])array); + } else if (cmp != null) { + comparer = new ComparatorComparer((Object[])array, cmp); + } else { + comparer = new ObjectComparer((Object[])array); + } + } + + protected int compare(int i, int j) { + return comparer.compare(i, j); + } + + interface Comparer { + int compare(int i, int j); + } + + static class ComparatorComparer implements Comparer { + private Object[] a; + private Comparator cmp; + + public ComparatorComparer(Object[] a, Comparator cmp) { + this.a = a; + this.cmp = cmp; + } + + public int compare(int i, int j) { + return cmp.compare(a[i], a[j]); + } + } + + static class ObjectComparer implements Comparer { + private Object[] a; + public ObjectComparer(Object[] a) { this.a = a; } + public int compare(int i, int j) { + return ((Comparable)a[i]).compareTo(a[j]); + } + } + + static class IntComparer implements Comparer { + private int[] a; + public IntComparer(int[] a) { this.a = a; } + public int compare(int i, int j) { return a[i] - a[j]; } + } + + static class LongComparer implements Comparer { + private long[] a; + public LongComparer(long[] a) { this.a = a; } + public int compare(int i, int j) { + long vi = a[i]; + long vj = a[j]; + return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; + } + } + + static class FloatComparer implements Comparer { + private float[] a; + public FloatComparer(float[] a) { this.a = a; } + public int compare(int i, int j) { + float vi = a[i]; + float vj = a[j]; + return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; + } + } + + static class DoubleComparer implements Comparer { + private double[] a; + public DoubleComparer(double[] a) { this.a = a; } + public int compare(int i, int j) { + double vi = a[i]; + double vj = a[j]; + return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; + } + } + + static class ShortComparer implements Comparer { + private short[] a; + public ShortComparer(short[] a) { this.a = a; } + public int compare(int i, int j) { return a[i] - a[j]; } + } + + static class ByteComparer implements Comparer { + private byte[] a; + public ByteComparer(byte[] a) { this.a = a; } + public int compare(int i, int j) { return a[i] - a[j]; } + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(ParallelSorter.class.getName()); + + private Object[] arrays; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return null; // TODO + } + + public void setArrays(Object[] arrays) { + this.arrays = arrays; + } + + public ParallelSorter create() { + return (ParallelSorter)super.create(ClassesKey.create(arrays)); + } + + public void generateClass(ClassVisitor v) throws Exception { + if (arrays.length == 0) { + throw new IllegalArgumentException("No arrays specified to sort"); + } + for (int i = 0; i < arrays.length; i++) { + if (!arrays[i].getClass().isArray()) { + throw new IllegalArgumentException(arrays[i].getClass() + " is not an array"); + } + } + new ParallelSorterEmitter(v, getClassName(), arrays); + } + + protected Object firstInstance(Class type) { + return ((ParallelSorter)ReflectUtils.newInstance(type)).newInstance(arrays); + } + + protected Object nextInstance(Object instance) { + return ((ParallelSorter)instance).newInstance(arrays); + } + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/util/ParallelSorterEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/util/ParallelSorterEmitter.java new file mode 100644 index 000000000..eefb63013 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/util/ParallelSorterEmitter.java @@ -0,0 +1,100 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.util; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Type; + +class ParallelSorterEmitter extends ClassEmitter { + private static final Type PARALLEL_SORTER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.util.ParallelSorter"); + private static final Signature CSTRUCT_OBJECT_ARRAY = + TypeUtils.parseConstructor("Object[]"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", PARALLEL_SORTER, new Type[]{ Constants.TYPE_OBJECT_ARRAY }); + private static final Signature SWAP = + TypeUtils.parseSignature("void swap(int, int)"); + + public ParallelSorterEmitter(ClassVisitor v, String className, Object[] arrays) { + super(v); + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, PARALLEL_SORTER, null, Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + EmitUtils.factory_method(this, NEW_INSTANCE); + generateConstructor(arrays); + generateSwap(arrays); + end_class(); + } + + private String getFieldName(int index) { + return "FIELD_" + index; + } + + private void generateConstructor(Object[] arrays) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + e.load_arg(0); + e.super_putfield("a", Constants.TYPE_OBJECT_ARRAY); + for (int i = 0; i < arrays.length; i++) { + Type type = Type.getType(arrays[i].getClass()); + declare_field(Constants.ACC_PRIVATE, getFieldName(i), type, null); + e.load_this(); + e.load_arg(0); + e.push(i); + e.aaload(); + e.checkcast(type); + e.putfield(getFieldName(i)); + } + e.return_value(); + e.end_method(); + } + + private void generateSwap(final Object[] arrays) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SWAP, null); + for (int i = 0; i < arrays.length; i++) { + Type type = Type.getType(arrays[i].getClass()); + Type component = TypeUtils.getComponentType(type); + Local T = e.make_local(type); + + e.load_this(); + e.getfield(getFieldName(i)); + e.store_local(T); + + e.load_local(T); + e.load_arg(0); + + e.load_local(T); + e.load_arg(1); + e.array_load(component); + + e.load_local(T); + e.load_arg(1); + + e.load_local(T); + e.load_arg(0); + e.array_load(component); + + e.array_store(component); + e.array_store(component); + } + e.return_value(); + e.end_method(); + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/util/SorterTemplate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/util/SorterTemplate.java new file mode 100644 index 000000000..e855beca5 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/util/SorterTemplate.java @@ -0,0 +1,173 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.util; + +import java.util.*; + +abstract class SorterTemplate { + private static final int MERGESORT_THRESHOLD = 12; + private static final int QUICKSORT_THRESHOLD = 7; + + abstract protected void swap(int i, int j); + abstract protected int compare(int i, int j); + + protected void quickSort(int lo, int hi) { + quickSortHelper(lo, hi); + insertionSort(lo, hi); + } + + private void quickSortHelper(int lo, int hi) { + for (;;) { + int diff = hi - lo; + if (diff <= QUICKSORT_THRESHOLD) { + break; + } + int i = (hi + lo) / 2; + if (compare(lo, i) > 0) { + swap(lo, i); + } + if (compare(lo, hi) > 0) { + swap(lo, hi); + } + if (compare(i, hi) > 0) { + swap(i, hi); + } + int j = hi - 1; + swap(i, j); + i = lo; + int v = j; + for (;;) { + while (compare(++i, v) < 0) { + /* nothing */; + } + while (compare(--j, v) > 0) { + /* nothing */; + } + if (j < i) { + break; + } + swap(i, j); + } + swap(i, hi - 1); + if (j - lo <= hi - i + 1) { + quickSortHelper(lo, j); + lo = i + 1; + } else { + quickSortHelper(i + 1, hi); + hi = j; + } + } + } + + private void insertionSort(int lo, int hi) { + for (int i = lo + 1 ; i <= hi; i++) { + for (int j = i; j > lo; j--) { + if (compare(j - 1, j) > 0) { + swap(j - 1, j); + } else { + break; + } + } + } + } + + protected void mergeSort(int lo, int hi) { + int diff = hi - lo; + if (diff <= MERGESORT_THRESHOLD) { + insertionSort(lo, hi); + return; + } + int mid = lo + diff / 2; + mergeSort(lo, mid); + mergeSort(mid, hi); + merge(lo, mid, hi, mid - lo, hi - mid); + } + + private void merge(int lo, int pivot, int hi, int len1, int len2) { + if (len1 == 0 || len2 == 0) { + return; + } + if (len1 + len2 == 2) { + if (compare(pivot, lo) < 0) { + swap(pivot, lo); + } + return; + } + int first_cut, second_cut; + int len11, len22; + if (len1 > len2) { + len11 = len1 / 2; + first_cut = lo + len11; + second_cut = lower(pivot, hi, first_cut); + len22 = second_cut - pivot; + } else { + len22 = len2 / 2; + second_cut = pivot + len22; + first_cut = upper(lo, pivot, second_cut); + len11 = first_cut - lo; + } + rotate(first_cut, pivot, second_cut); + int new_mid = first_cut + len22; + merge(lo, first_cut, new_mid, len11, len22); + merge(new_mid, second_cut, hi, len1 - len11, len2 - len22); + } + + private void rotate(int lo, int mid, int hi) { + int lot = lo; + int hit = mid - 1; + while (lot < hit) { + swap(lot++, hit--); + } + lot = mid; hit = hi - 1; + while (lot < hit) { + swap(lot++, hit--); + } + lot = lo; hit = hi - 1; + while (lot < hit) { + swap(lot++, hit--); + } + } + + private int lower(int lo, int hi, int val) { + int len = hi - lo; + while (len > 0) { + int half = len / 2; + int mid= lo + half; + if (compare(mid, val) < 0) { + lo = mid + 1; + len = len - half -1; + } else { + len = half; + } + } + return lo; + } + + private int upper(int lo, int hi, int val) { + int len = hi - lo; + while (len > 0) { + int half = len / 2; + int mid = lo + half; + if (compare(val, mid) < 0) { + len = half; + } else { + lo = mid + 1; + len = len - half -1; + } + } + return lo; + } +} diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/util/StringSwitcher.java b/fine-cglib/src/com/fr/third/net/sf/cglib/util/StringSwitcher.java new file mode 100644 index 000000000..126b76e31 --- /dev/null +++ b/fine-cglib/src/com/fr/third/net/sf/cglib/util/StringSwitcher.java @@ -0,0 +1,154 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fr.third.net.sf.cglib.util; + +import java.util.*; +import com.fr.third.net.sf.cglib.core.*; +import com.fr.third.org.objectweb.asm.ClassVisitor; +import com.fr.third.org.objectweb.asm.Label; +import com.fr.third.org.objectweb.asm.Type; + +/** + * This class implements a simple String->int mapping for a fixed set of keys. + */ +abstract public class StringSwitcher { + private static final Type STRING_SWITCHER = + TypeUtils.parseType("com.fr.third.net.sf.cglib.util.StringSwitcher"); + private static final Signature INT_VALUE = + TypeUtils.parseSignature("int intValue(String)"); + private static final StringSwitcherKey KEY_FACTORY = + (StringSwitcherKey)KeyFactory.create(StringSwitcherKey.class); + + interface StringSwitcherKey { + public Object newInstance(String[] strings, int[] ints, boolean fixedInput); + } + + /** + * Helper method to create a StringSwitcher. + * For finer control over the generated instance, use a new instance of StringSwitcher.Generator + * instead of this static method. + * @param strings the array of String keys; must be the same length as the value array + * @param ints the array of integer results; must be the same length as the key array + * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as -1; if true, + * the result will be undefined, and the resulting code will be faster + */ + public static StringSwitcher create(String[] strings, int[] ints, boolean fixedInput) { + Generator gen = new Generator(); + gen.setStrings(strings); + gen.setInts(ints); + gen.setFixedInput(fixedInput); + return gen.create(); + } + + protected StringSwitcher() { + } + + /** + * Return the integer associated with the given key. + * @param s the key + * @return the associated integer value, or -1 if the key is unknown (unless + * fixedInput was specified when this StringSwitcher was created, + * in which case the return value for an unknown key is undefined) + */ + abstract public int intValue(String s); + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(StringSwitcher.class.getName()); + + private String[] strings; + private int[] ints; + private boolean fixedInput; + + public Generator() { + super(SOURCE); + } + + /** + * Set the array of recognized Strings. + * @param strings the array of String keys; must be the same length as the value array + * @see #setInts + */ + public void setStrings(String[] strings) { + this.strings = strings; + } + + /** + * Set the array of integer results. + * @param ints the array of integer results; must be the same length as the key array + * @see #setStrings + */ + public void setInts(int[] ints) { + this.ints = ints; + } + + /** + * Configure how unknown String keys will be handled. + * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as -1; if true, + * the result will be undefined, and the resulting code will be faster + */ + public void setFixedInput(boolean fixedInput) { + this.fixedInput = fixedInput; + } + + protected ClassLoader getDefaultClassLoader() { + return getClass().getClassLoader(); + } + + /** + * Generate the StringSwitcher. + */ + public StringSwitcher create() { + setNamePrefix(StringSwitcher.class.getName()); + Object key = KEY_FACTORY.newInstance(strings, ints, fixedInput); + return (StringSwitcher)super.create(key); + } + + public void generateClass(ClassVisitor v) throws Exception { + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + STRING_SWITCHER, + null, + Constants.SOURCE_FILE); + EmitUtils.null_constructor(ce); + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, INT_VALUE, null); + e.load_arg(0); + final List stringList = Arrays.asList(strings); + int style = fixedInput ? Constants.SWITCH_STYLE_HASHONLY : Constants.SWITCH_STYLE_HASH; + EmitUtils.string_switch(e, strings, style, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + e.push(ints[stringList.indexOf(key)]); + e.return_value(); + } + public void processDefault() { + e.push(-1); + e.return_value(); + } + }); + e.end_method(); + ce.end_class(); + } + + protected Object firstInstance(Class type) { + return (StringSwitcher)ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/AnnotationVisitor.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/AnnotationVisitor.java new file mode 100644 index 000000000..085489a90 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/AnnotationVisitor.java @@ -0,0 +1,169 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A visitor to visit a Java annotation. The methods of this class must be + * called in the following order: ( visit | visitEnum | + * visitAnnotation | visitArray )* visitEnd. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public abstract class AnnotationVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + protected final int api; + + /** + * The annotation visitor to which this visitor must delegate method calls. + * May be null. + */ + protected AnnotationVisitor av; + + /** + * Constructs a new {@link AnnotationVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + public AnnotationVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link AnnotationVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + * @param av + * the annotation visitor to which this visitor must delegate + * method calls. May be null. + */ + public AnnotationVisitor(final int api, final AnnotationVisitor av) { + if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { + throw new IllegalArgumentException(); + } + this.api = api; + this.av = av; + } + + /** + * Visits a primitive value of the annotation. + * + * @param name + * the value name. + * @param value + * the actual value, whose type must be {@link Byte}, + * {@link Boolean}, {@link Character}, {@link Short}, + * {@link Integer} , {@link Long}, {@link Float}, {@link Double}, + * {@link String} or {@link Type} of OBJECT or ARRAY sort. This + * value can also be an array of byte, boolean, short, char, int, + * long, float or double values (this is equivalent to using + * {@link #visitArray visitArray} and visiting each array element + * in turn, but is more convenient). + */ + public void visit(String name, Object value) { + if (av != null) { + av.visit(name, value); + } + } + + /** + * Visits an enumeration value of the annotation. + * + * @param name + * the value name. + * @param desc + * the class descriptor of the enumeration class. + * @param value + * the actual enumeration value. + */ + public void visitEnum(String name, String desc, String value) { + if (av != null) { + av.visitEnum(name, desc, value); + } + } + + /** + * Visits a nested annotation value of the annotation. + * + * @param name + * the value name. + * @param desc + * the class descriptor of the nested annotation class. + * @return a visitor to visit the actual nested annotation value, or + * null if this visitor is not interested in visiting this + * nested annotation. The nested annotation value must be fully + * visited before calling other methods on this annotation + * visitor. + */ + public AnnotationVisitor visitAnnotation(String name, String desc) { + if (av != null) { + return av.visitAnnotation(name, desc); + } + return null; + } + + /** + * Visits an array value of the annotation. Note that arrays of primitive + * types (such as byte, boolean, short, char, int, long, float or double) + * can be passed as value to {@link #visit visit}. This is what + * {@link ClassReader} does. + * + * @param name + * the value name. + * @return a visitor to visit the actual array value elements, or + * null if this visitor is not interested in visiting these + * values. The 'name' parameters passed to the methods of this + * visitor are ignored. All the array values must be visited + * before calling other methods on this annotation visitor. + */ + public AnnotationVisitor visitArray(String name) { + if (av != null) { + return av.visitArray(name); + } + return null; + } + + /** + * Visits the end of the annotation. + */ + public void visitEnd() { + if (av != null) { + av.visitEnd(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/AnnotationWriter.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/AnnotationWriter.java new file mode 100644 index 000000000..d0385ae58 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/AnnotationWriter.java @@ -0,0 +1,371 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * An {@link AnnotationVisitor} that generates annotations in bytecode form. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +final class AnnotationWriter extends AnnotationVisitor { + + /** + * The class writer to which this annotation must be added. + */ + private final ClassWriter cw; + + /** + * The number of values in this annotation. + */ + private int size; + + /** + * true if values are named, false otherwise. Annotation + * writers used for annotation default and annotation arrays use unnamed + * values. + */ + private final boolean named; + + /** + * The annotation values in bytecode form. This byte vector only contains + * the values themselves, i.e. the number of values must be stored as a + * unsigned short just before these bytes. + */ + private final ByteVector bv; + + /** + * The byte vector to be used to store the number of values of this + * annotation. See {@link #bv}. + */ + private final ByteVector parent; + + /** + * Where the number of values of this annotation must be stored in + * {@link #parent}. + */ + private final int offset; + + /** + * Next annotation writer. This field is used to store annotation lists. + */ + AnnotationWriter next; + + /** + * Previous annotation writer. This field is used to store annotation lists. + */ + AnnotationWriter prev; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link AnnotationWriter}. + * + * @param cw + * the class writer to which this annotation must be added. + * @param named + * true if values are named, false otherwise. + * @param bv + * where the annotation values must be stored. + * @param parent + * where the number of annotation values must be stored. + * @param offset + * where in parent the number of annotation values must + * be stored. + */ + AnnotationWriter(final ClassWriter cw, final boolean named, + final ByteVector bv, final ByteVector parent, final int offset) { + super(Opcodes.ASM6); + this.cw = cw; + this.named = named; + this.bv = bv; + this.parent = parent; + this.offset = offset; + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public void visit(final String name, final Object value) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + if (value instanceof String) { + bv.put12('s', cw.newUTF8((String) value)); + } else if (value instanceof Byte) { + bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); + } else if (value instanceof Boolean) { + int v = ((Boolean) value).booleanValue() ? 1 : 0; + bv.put12('Z', cw.newInteger(v).index); + } else if (value instanceof Character) { + bv.put12('C', cw.newInteger(((Character) value).charValue()).index); + } else if (value instanceof Short) { + bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); + } else if (value instanceof Type) { + bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); + } else if (value instanceof byte[]) { + byte[] v = (byte[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('B', cw.newInteger(v[i]).index); + } + } else if (value instanceof boolean[]) { + boolean[] v = (boolean[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); + } + } else if (value instanceof short[]) { + short[] v = (short[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('S', cw.newInteger(v[i]).index); + } + } else if (value instanceof char[]) { + char[] v = (char[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('C', cw.newInteger(v[i]).index); + } + } else if (value instanceof int[]) { + int[] v = (int[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('I', cw.newInteger(v[i]).index); + } + } else if (value instanceof long[]) { + long[] v = (long[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('J', cw.newLong(v[i]).index); + } + } else if (value instanceof float[]) { + float[] v = (float[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('F', cw.newFloat(v[i]).index); + } + } else if (value instanceof double[]) { + double[] v = (double[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('D', cw.newDouble(v[i]).index); + } + } else { + Item i = cw.newConstItem(value); + bv.put12(".s.IFJDCS".charAt(i.type), i.index); + } + } + + @Override + public void visitEnum(final String name, final String desc, + final String value) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); + } + + @Override + public AnnotationVisitor visitAnnotation(final String name, + final String desc) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + // write tag and type, and reserve space for values count + bv.put12('@', cw.newUTF8(desc)).putShort(0); + return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); + } + + @Override + public AnnotationVisitor visitArray(final String name) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + // write tag, and reserve space for array size + bv.put12('[', 0); + return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); + } + + @Override + public void visitEnd() { + if (parent != null) { + byte[] data = parent.data; + data[offset] = (byte) (size >>> 8); + data[offset + 1] = (byte) size; + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the size of this annotation writer list. + * + * @return the size of this annotation writer list. + */ + int getSize() { + int size = 0; + AnnotationWriter aw = this; + while (aw != null) { + size += aw.bv.length; + aw = aw.next; + } + return size; + } + + /** + * Puts the annotations of this annotation writer list into the given byte + * vector. + * + * @param out + * where the annotations must be put. + */ + void put(final ByteVector out) { + int n = 0; + int size = 2; + AnnotationWriter aw = this; + AnnotationWriter last = null; + while (aw != null) { + ++n; + size += aw.bv.length; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putInt(size); + out.putShort(n); + aw = last; + while (aw != null) { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } + + /** + * Puts the given annotation lists into the given byte vector. + * + * @param panns + * an array of annotation writer lists. + * @param off + * index of the first annotation to be written. + * @param out + * where the annotations must be put. + */ + static void put(final AnnotationWriter[] panns, final int off, + final ByteVector out) { + int size = 1 + 2 * (panns.length - off); + for (int i = off; i < panns.length; ++i) { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + out.putInt(size).putByte(panns.length - off); + for (int i = off; i < panns.length; ++i) { + AnnotationWriter aw = panns[i]; + AnnotationWriter last = null; + int n = 0; + while (aw != null) { + ++n; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putShort(n); + aw = last; + while (aw != null) { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } + } + + /** + * Puts the given type reference and type path into the given bytevector. + * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. + * + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param out + * where the type reference and type path must be put. + */ + static void putTarget(int typeRef, TypePath typePath, ByteVector out) { + switch (typeRef >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + out.putShort(typeRef >>> 16); + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + out.putByte(typeRef >>> 24); + break; + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + out.putInt(typeRef); + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8); + break; + } + if (typePath == null) { + out.putByte(0); + } else { + int length = typePath.b[typePath.offset] * 2 + 1; + out.putByteArray(typePath.b, typePath.offset, length); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Attribute.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Attribute.java new file mode 100644 index 000000000..37d2a9dc4 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Attribute.java @@ -0,0 +1,255 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A non standard class, field, method or code attribute. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class Attribute { + + /** + * The type of this attribute. + */ + public final String type; + + /** + * The raw value of this attribute, used only for unknown attributes. + */ + byte[] value; + + /** + * The next attribute in this attribute list. May be null. + */ + Attribute next; + + /** + * Constructs a new empty attribute. + * + * @param type + * the type of the attribute. + */ + protected Attribute(final String type) { + this.type = type; + } + + /** + * Returns true if this type of attribute is unknown. The default + * implementation of this method always returns true. + * + * @return true if this type of attribute is unknown. + */ + public boolean isUnknown() { + return true; + } + + /** + * Returns true if this type of attribute is a code attribute. + * + * @return true if this type of attribute is a code attribute. + */ + public boolean isCodeAttribute() { + return false; + } + + /** + * Returns the labels corresponding to this attribute. + * + * @return the labels corresponding to this attribute, or null if + * this attribute is not a code attribute that contains labels. + */ + protected Label[] getLabels() { + return null; + } + + /** + * Reads a {@link #type type} attribute. This method must return a + * new {@link Attribute} object, of type {@link #type type}, + * corresponding to the len bytes starting at the given offset, in + * the given class reader. + * + * @param cr + * the class that contains the attribute to be read. + * @param off + * index of the first byte of the attribute's content in + * {@link ClassReader#b cr.b}. The 6 attribute header bytes, + * containing the type and the length of the attribute, are not + * taken into account here. + * @param len + * the length of the attribute's content. + * @param buf + * buffer to be used to call {@link ClassReader#readUTF8 + * readUTF8}, {@link ClassReader#readClass(int,char[]) readClass} + * or {@link ClassReader#readConst readConst}. + * @param codeOff + * index of the first byte of code's attribute content in + * {@link ClassReader#b cr.b}, or -1 if the attribute to be read + * is not a code attribute. The 6 attribute header bytes, + * containing the type and the length of the attribute, are not + * taken into account here. + * @param labels + * the labels of the method's code, or null if the + * attribute to be read is not a code attribute. + * @return a new {@link Attribute} object corresponding to the given + * bytes. + */ + protected Attribute read(final ClassReader cr, final int off, + final int len, final char[] buf, final int codeOff, + final Label[] labels) { + Attribute attr = new Attribute(type); + attr.value = new byte[len]; + System.arraycopy(cr.b, off, attr.value, 0, len); + return attr; + } + + /** + * Returns the byte array form of this attribute. + * + * @param cw + * the class to which this attribute must be added. This + * parameter can be used to add to the constant pool of this + * class the items that corresponds to this attribute. + * @param code + * the bytecode of the method corresponding to this code + * attribute, or null if this attribute is not a code + * attributes. + * @param len + * the length of the bytecode of the method corresponding to this + * code attribute, or null if this attribute is not a + * code attribute. + * @param maxStack + * the maximum stack size of the method corresponding to this + * code attribute, or -1 if this attribute is not a code + * attribute. + * @param maxLocals + * the maximum number of local variables of the method + * corresponding to this code attribute, or -1 if this attribute + * is not a code attribute. + * @return the byte array form of this attribute. + */ + protected ByteVector write(final ClassWriter cw, final byte[] code, + final int len, final int maxStack, final int maxLocals) { + ByteVector v = new ByteVector(); + v.data = value; + v.length = value.length; + return v; + } + + /** + * Returns the length of the attribute list that begins with this attribute. + * + * @return the length of the attribute list that begins with this attribute. + */ + final int getCount() { + int count = 0; + Attribute attr = this; + while (attr != null) { + count += 1; + attr = attr.next; + } + return count; + } + + /** + * Returns the size of all the attributes in this attribute list. + * + * @param cw + * the class writer to be used to convert the attributes into + * byte arrays, with the {@link #write write} method. + * @param code + * the bytecode of the method corresponding to these code + * attributes, or null if these attributes are not code + * attributes. + * @param len + * the length of the bytecode of the method corresponding to + * these code attributes, or null if these attributes + * are not code attributes. + * @param maxStack + * the maximum stack size of the method corresponding to these + * code attributes, or -1 if these attributes are not code + * attributes. + * @param maxLocals + * the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these + * attributes are not code attributes. + * @return the size of all the attributes in this attribute list. This size + * includes the size of the attribute headers. + */ + final int getSize(final ClassWriter cw, final byte[] code, final int len, + final int maxStack, final int maxLocals) { + Attribute attr = this; + int size = 0; + while (attr != null) { + cw.newUTF8(attr.type); + size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; + attr = attr.next; + } + return size; + } + + /** + * Writes all the attributes of this attribute list in the given byte + * vector. + * + * @param cw + * the class writer to be used to convert the attributes into + * byte arrays, with the {@link #write write} method. + * @param code + * the bytecode of the method corresponding to these code + * attributes, or null if these attributes are not code + * attributes. + * @param len + * the length of the bytecode of the method corresponding to + * these code attributes, or null if these attributes + * are not code attributes. + * @param maxStack + * the maximum stack size of the method corresponding to these + * code attributes, or -1 if these attributes are not code + * attributes. + * @param maxLocals + * the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these + * attributes are not code attributes. + * @param out + * where the attributes must be written. + */ + final void put(final ClassWriter cw, final byte[] code, final int len, + final int maxStack, final int maxLocals, final ByteVector out) { + Attribute attr = this; + while (attr != null) { + ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); + out.putShort(cw.newUTF8(attr.type)).putInt(b.length); + out.putByteArray(b.data, 0, b.length); + attr = attr.next; + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/ByteVector.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/ByteVector.java new file mode 100644 index 000000000..5181dbceb --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/ByteVector.java @@ -0,0 +1,339 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A dynamically extensible vector of bytes. This class is roughly equivalent to + * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. + * + * @author Eric Bruneton + */ +public class ByteVector { + + /** + * The content of this vector. + */ + byte[] data; + + /** + * Actual number of bytes in this vector. + */ + int length; + + /** + * Constructs a new {@link ByteVector ByteVector} with a default initial + * size. + */ + public ByteVector() { + data = new byte[64]; + } + + /** + * Constructs a new {@link ByteVector ByteVector} with the given initial + * size. + * + * @param initialSize + * the initial size of the byte vector to be constructed. + */ + public ByteVector(final int initialSize) { + data = new byte[initialSize]; + } + + /** + * Puts a byte into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b + * a byte. + * @return this byte vector. + */ + public ByteVector putByte(final int b) { + int length = this.length; + if (length + 1 > data.length) { + enlarge(1); + } + data[length++] = (byte) b; + this.length = length; + return this; + } + + /** + * Puts two bytes into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b1 + * a byte. + * @param b2 + * another byte. + * @return this byte vector. + */ + ByteVector put11(final int b1, final int b2) { + int length = this.length; + if (length + 2 > data.length) { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte) b1; + data[length++] = (byte) b2; + this.length = length; + return this; + } + + /** + * Puts a short into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param s + * a short. + * @return this byte vector. + */ + public ByteVector putShort(final int s) { + int length = this.length; + if (length + 2 > data.length) { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte) (s >>> 8); + data[length++] = (byte) s; + this.length = length; + return this; + } + + /** + * Puts a byte and a short into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b + * a byte. + * @param s + * a short. + * @return this byte vector. + */ + ByteVector put12(final int b, final int s) { + int length = this.length; + if (length + 3 > data.length) { + enlarge(3); + } + byte[] data = this.data; + data[length++] = (byte) b; + data[length++] = (byte) (s >>> 8); + data[length++] = (byte) s; + this.length = length; + return this; + } + + /** + * Puts an int into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param i + * an int. + * @return this byte vector. + */ + public ByteVector putInt(final int i) { + int length = this.length; + if (length + 4 > data.length) { + enlarge(4); + } + byte[] data = this.data; + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + this.length = length; + return this; + } + + /** + * Puts a long into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param l + * a long. + * @return this byte vector. + */ + public ByteVector putLong(final long l) { + int length = this.length; + if (length + 8 > data.length) { + enlarge(8); + } + byte[] data = this.data; + int i = (int) (l >>> 32); + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + i = (int) l; + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + this.length = length; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param s + * a String whose UTF8 encoded length must be less than 65536. + * @return this byte vector. + */ + public ByteVector putUTF8(final String s) { + int charLength = s.length(); + if (charLength > 65535) { + throw new IllegalArgumentException(); + } + int len = length; + if (len + 2 + charLength > data.length) { + enlarge(2 + charLength); + } + byte[] data = this.data; + // optimistic algorithm: instead of computing the byte length and then + // serializing the string (which requires two loops), we assume the byte + // length is equal to char length (which is the most frequent case), and + // we start serializing the string right away. During the serialization, + // if we find that this assumption is wrong, we continue with the + // general method. + data[len++] = (byte) (charLength >>> 8); + data[len++] = (byte) charLength; + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + if (c >= '\001' && c <= '\177') { + data[len++] = (byte) c; + } else { + length = len; + return encodeUTF8(s, i, 65535); + } + } + length = len; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. The string length is encoded in two + * bytes before the encoded characters, if there is space for that (i.e. if + * this.length - i - 2 >= 0). + * + * @param s + * the String to encode. + * @param i + * the index of the first character to encode. The previous + * characters are supposed to have already been encoded, using + * only one byte per character. + * @param maxByteLength + * the maximum byte length of the encoded string, including the + * already encoded characters. + * @return this byte vector. + */ + ByteVector encodeUTF8(final String s, int i, int maxByteLength) { + int charLength = s.length(); + int byteLength = i; + char c; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + if (byteLength > maxByteLength) { + throw new IllegalArgumentException(); + } + int start = length - i - 2; + if (start >= 0) { + data[start] = (byte) (byteLength >>> 8); + data[start + 1] = (byte) byteLength; + } + if (length + byteLength - i > data.length) { + enlarge(byteLength - i); + } + int len = length; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + data[len++] = (byte) c; + } else if (c > '\u07FF') { + data[len++] = (byte) (0xE0 | c >> 12 & 0xF); + data[len++] = (byte) (0x80 | c >> 6 & 0x3F); + data[len++] = (byte) (0x80 | c & 0x3F); + } else { + data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); + data[len++] = (byte) (0x80 | c & 0x3F); + } + } + length = len; + return this; + } + + /** + * Puts an array of bytes into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b + * an array of bytes. May be null to put len + * null bytes into this byte vector. + * @param off + * index of the fist byte of b that must be copied. + * @param len + * number of bytes of b that must be copied. + * @return this byte vector. + */ + public ByteVector putByteArray(final byte[] b, final int off, final int len) { + if (length + len > data.length) { + enlarge(len); + } + if (b != null) { + System.arraycopy(b, off, data, length, len); + } + length += len; + return this; + } + + /** + * Enlarge this byte vector so that it can receive n more bytes. + * + * @param size + * number of additional bytes that this byte vector should be + * able to receive. + */ + private void enlarge(final int size) { + int length1 = 2 * data.length; + int length2 = length + size; + byte[] newData = new byte[length1 > length2 ? length1 : length2]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassReader.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassReader.java new file mode 100644 index 000000000..1d8e2221a --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassReader.java @@ -0,0 +1,2756 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A Java class parser to make a {@link ClassVisitor} visit an existing class. + * This class parses a byte array conforming to the Java class file format and + * calls the appropriate visit methods of a given class visitor for each field, + * method and bytecode instruction encountered. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ClassReader { + + /** + * Flag to skip method code. If this class is set CODE + * attribute won't be visited. This can be used, for example, to retrieve + * annotations for methods and method parameters. + */ + public static final int SKIP_CODE = 1; + + /** + * Flag to skip the debug information in the class. If this flag is set the + * debug information of the class is not visited, i.e. the + * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and + * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be + * called. + */ + public static final int SKIP_DEBUG = 2; + + /** + * Flag to skip the stack map frames in the class. If this flag is set the + * stack map frames of the class is not visited, i.e. the + * {@link MethodVisitor#visitFrame visitFrame} method will not be called. + * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is + * used: it avoids visiting frames that will be ignored and recomputed from + * scratch in the class writer. + */ + public static final int SKIP_FRAMES = 4; + + /** + * Flag to expand the stack map frames. By default stack map frames are + * visited in their original format (i.e. "expanded" for classes whose + * version is less than V1_6, and "compressed" for the other classes). If + * this flag is set, stack map frames are always visited in expanded format + * (this option adds a decompression/recompression step in ClassReader and + * ClassWriter which degrades performances quite a lot). + */ + public static final int EXPAND_FRAMES = 8; + + /** + * Flag to expand the ASM pseudo instructions into an equivalent sequence of + * standard bytecode instructions. When resolving a forward jump it may + * happen that the signed 2 bytes offset reserved for it is not sufficient + * to store the bytecode offset. In this case the jump instruction is + * replaced with a temporary ASM pseudo instruction using an unsigned 2 + * bytes offset (see Label#resolve). This internal flag is used to re-read + * classes containing such instructions, in order to replace them with + * standard instructions. In addition, when this flag is used, GOTO_W and + * JSR_W are not converted into GOTO and JSR, to make sure that + * infinite loops where a GOTO_W is replaced with a GOTO in ClassReader and + * converted back to a GOTO_W in ClassWriter cannot occur. + */ + static final int EXPAND_ASM_INSNS = 256; + + /** + * The class to be parsed. The content of this array must not be + * modified. This field is intended for {@link Attribute} sub classes, and + * is normally not needed by class generators or adapters. + */ + public final byte[] b; + + /** + * The start index of each constant pool item in {@link #b b}, plus one. The + * one byte offset skips the constant pool item tag that indicates its type. + */ + private final int[] items; + + /** + * The String objects corresponding to the CONSTANT_Utf8 items. This cache + * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, + * which GREATLY improves performances (by a factor 2 to 3). This caching + * strategy could be extended to all constant pool items, but its benefit + * would not be so great for these items (because they are much less + * expensive to parse than CONSTANT_Utf8 items). + */ + private final String[] strings; + + /** + * Maximum length of the strings contained in the constant pool of the + * class. + */ + private final int maxStringLength; + + /** + * Start index of the class header information (access, name...) in + * {@link #b b}. + */ + public final int header; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link ClassReader} object. + * + * @param b + * the bytecode of the class to be read. + */ + public ClassReader(final byte[] b) { + this(b, 0, b.length); + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param b + * the bytecode of the class to be read. + * @param off + * the start offset of the class data. + * @param len + * the length of the class data. + */ + public ClassReader(final byte[] b, final int off, final int len) { + this.b = b; + // checks the class version + if (readShort(off + 6) > Opcodes.V9) { + throw new IllegalArgumentException(); + } + // parses the constant pool + items = new int[readUnsignedShort(off + 8)]; + int n = items.length; + strings = new String[n]; + int max = 0; + int index = off + 10; + for (int i = 1; i < n; ++i) { + items[i] = index + 1; + int size; + switch (b[index]) { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + case ClassWriter.INT: + case ClassWriter.FLOAT: + case ClassWriter.NAME_TYPE: + case ClassWriter.INDY: + size = 5; + break; + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + size = 9; + ++i; + break; + case ClassWriter.UTF8: + size = 3 + readUnsignedShort(index + 1); + if (size > max) { + max = size; + } + break; + case ClassWriter.HANDLE: + size = 4; + break; + // case ClassWriter.CLASS: + // case ClassWriter.STR: + // case ClassWriter.MTYPE + // case ClassWriter.PACKAGE: + // case ClassWriter.MODULE: + default: + size = 3; + break; + } + index += size; + } + maxStringLength = max; + // the class header information starts just after the constant pool + header = index; + } + + /** + * Returns the class's access flags (see {@link Opcodes}). This value may + * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 + * and those flags are represented by attributes. + * + * @return the class access flags + * + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public int getAccess() { + return readUnsignedShort(header); + } + + /** + * Returns the internal name of the class (see + * {@link Type#getInternalName() getInternalName}). + * + * @return the internal class name + * + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public String getClassName() { + return readClass(header + 2, new char[maxStringLength]); + } + + /** + * Returns the internal of name of the super class (see + * {@link Type#getInternalName() getInternalName}). For interfaces, the + * super class is {@link Object}. + * + * @return the internal name of super class, or null for + * {@link Object} class. + * + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public String getSuperName() { + return readClass(header + 4, new char[maxStringLength]); + } + + /** + * Returns the internal names of the class's interfaces (see + * {@link Type#getInternalName() getInternalName}). + * + * @return the array of internal names for all implemented interfaces or + * null. + * + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public String[] getInterfaces() { + int index = header + 6; + int n = readUnsignedShort(index); + String[] interfaces = new String[n]; + if (n > 0) { + char[] buf = new char[maxStringLength]; + for (int i = 0; i < n; ++i) { + index += 2; + interfaces[i] = readClass(index, buf); + } + } + return interfaces; + } + + /** + * Copies the constant pool data into the given {@link ClassWriter}. Should + * be called before the {@link #accept(ClassVisitor,int)} method. + * + * @param classWriter + * the {@link ClassWriter} to copy constant pool into. + */ + void copyPool(final ClassWriter classWriter) { + char[] buf = new char[maxStringLength]; + int ll = items.length; + Item[] items2 = new Item[ll]; + for (int i = 1; i < ll; i++) { + int index = items[i]; + int tag = b[index - 1]; + Item item = new Item(i); + int nameType; + switch (tag) { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + nameType = items[readUnsignedShort(index + 2)]; + item.set(tag, readClass(index, buf), readUTF8(nameType, buf), + readUTF8(nameType + 2, buf)); + break; + case ClassWriter.INT: + item.set(readInt(index)); + break; + case ClassWriter.FLOAT: + item.set(Float.intBitsToFloat(readInt(index))); + break; + case ClassWriter.NAME_TYPE: + item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf), + null); + break; + case ClassWriter.LONG: + item.set(readLong(index)); + ++i; + break; + case ClassWriter.DOUBLE: + item.set(Double.longBitsToDouble(readLong(index))); + ++i; + break; + case ClassWriter.UTF8: { + String s = strings[i]; + if (s == null) { + index = items[i]; + s = strings[i] = readUTF(index + 2, + readUnsignedShort(index), buf); + } + item.set(tag, s, null, null); + break; + } + case ClassWriter.HANDLE: { + int fieldOrMethodRef = items[readUnsignedShort(index + 1)]; + nameType = items[readUnsignedShort(fieldOrMethodRef + 2)]; + item.set(ClassWriter.HANDLE_BASE + readByte(index), + readClass(fieldOrMethodRef, buf), + readUTF8(nameType, buf), readUTF8(nameType + 2, buf)); + break; + } + case ClassWriter.INDY: + if (classWriter.bootstrapMethods == null) { + copyBootstrapMethods(classWriter, items2, buf); + } + nameType = items[readUnsignedShort(index + 2)]; + item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf), + readUnsignedShort(index)); + break; + // case ClassWriter.STR: + // case ClassWriter.CLASS: + // case ClassWriter.MTYPE: + // case ClassWriter.MODULE: + // case ClassWriter.PACKAGE: + default: + item.set(tag, readUTF8(index, buf), null, null); + break; + } + + int index2 = item.hashCode % items2.length; + item.next = items2[index2]; + items2[index2] = item; + } + + int off = items[1] - 1; + classWriter.pool.putByteArray(b, off, header - off); + classWriter.items = items2; + classWriter.threshold = (int) (0.75d * ll); + classWriter.index = ll; + } + + /** + * Copies the bootstrap method data into the given {@link ClassWriter}. + * Should be called before the {@link #accept(ClassVisitor,int)} method. + * + * @param classWriter + * the {@link ClassWriter} to copy bootstrap methods into. + */ + private void copyBootstrapMethods(final ClassWriter classWriter, + final Item[] items, final char[] c) { + // finds the "BootstrapMethods" attribute + int u = getAttributes(); + boolean found = false; + for (int i = readUnsignedShort(u); i > 0; --i) { + String attrName = readUTF8(u + 2, c); + if ("BootstrapMethods".equals(attrName)) { + found = true; + break; + } + u += 6 + readInt(u + 4); + } + if (!found) { + return; + } + // copies the bootstrap methods in the class writer + int boostrapMethodCount = readUnsignedShort(u + 8); + for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) { + int position = v - u - 10; + int hashCode = readConst(readUnsignedShort(v), c).hashCode(); + for (int k = readUnsignedShort(v + 2); k > 0; --k) { + hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode(); + v += 2; + } + v += 4; + Item item = new Item(j); + item.set(position, hashCode & 0x7FFFFFFF); + int index = item.hashCode % items.length; + item.next = items[index]; + items[index] = item; + } + int attrSize = readInt(u + 4); + ByteVector bootstrapMethods = new ByteVector(attrSize + 62); + bootstrapMethods.putByteArray(b, u + 10, attrSize - 2); + classWriter.bootstrapMethodsCount = boostrapMethodCount; + classWriter.bootstrapMethods = bootstrapMethods; + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param is + * an input stream from which to read the class. + * @throws IOException + * if a problem occurs during reading. + */ + public ClassReader(final InputStream is) throws IOException { + this(readClass(is, false)); + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param name + * the binary qualified name of the class to be read. + * @throws IOException + * if an exception occurs during reading. + */ + public ClassReader(final String name) throws IOException { + this(readClass( + ClassLoader.getSystemResourceAsStream(name.replace('.', '/') + + ".class"), true)); + } + + /** + * Reads the bytecode of a class. + * + * @param is + * an input stream from which to read the class. + * @param close + * true to close the input stream after reading. + * @return the bytecode read from the given input stream. + * @throws IOException + * if a problem occurs during reading. + */ + private static byte[] readClass(final InputStream is, boolean close) + throws IOException { + if (is == null) { + throw new IOException("Class not found"); + } + try { + byte[] b = new byte[is.available()]; + int len = 0; + while (true) { + int n = is.read(b, len, b.length - len); + if (n == -1) { + if (len < b.length) { + byte[] c = new byte[len]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + return b; + } + len += n; + if (len == b.length) { + int last = is.read(); + if (last < 0) { + return b; + } + byte[] c = new byte[b.length + 1000]; + System.arraycopy(b, 0, c, 0, len); + c[len++] = (byte) last; + b = c; + } + } + } finally { + if (close) { + is.close(); + } + } + } + + // ------------------------------------------------------------------------ + // Public methods + // ------------------------------------------------------------------------ + + /** + * Makes the given visitor visit the Java class of this {@link ClassReader} + * . This class is the one specified in the constructor (see + * {@link #ClassReader(byte[]) ClassReader}). + * + * @param classVisitor + * the visitor that must visit this class. + * @param flags + * option flags that can be used to modify the default behavior + * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} + * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. + */ + public void accept(final ClassVisitor classVisitor, final int flags) { + accept(classVisitor, new Attribute[0], flags); + } + + /** + * Makes the given visitor visit the Java class of this {@link ClassReader}. + * This class is the one specified in the constructor (see + * {@link #ClassReader(byte[]) ClassReader}). + * + * @param classVisitor + * the visitor that must visit this class. + * @param attrs + * prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to + * the type of one the prototypes will not be parsed: its byte + * array value will be passed unchanged to the ClassWriter. + * This may corrupt it if this value contains references to + * the constant pool, or has syntactic or semantic links with a + * class element that has been transformed by a class adapter + * between the reader and the writer. + * @param flags + * option flags that can be used to modify the default behavior + * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} + * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. + */ + public void accept(final ClassVisitor classVisitor, + final Attribute[] attrs, final int flags) { + int u = header; // current offset in the class file + char[] c = new char[maxStringLength]; // buffer used to read strings + + Context context = new Context(); + context.attrs = attrs; + context.flags = flags; + context.buffer = c; + + // reads the class declaration + int access = readUnsignedShort(u); + String name = readClass(u + 2, c); + String superClass = readClass(u + 4, c); + String[] interfaces = new String[readUnsignedShort(u + 6)]; + u += 8; + for (int i = 0; i < interfaces.length; ++i) { + interfaces[i] = readClass(u, c); + u += 2; + } + + // reads the class attributes + String signature = null; + String sourceFile = null; + String sourceDebug = null; + String enclosingOwner = null; + String enclosingName = null; + String enclosingDesc = null; + String moduleMainClass = null; + int anns = 0; + int ianns = 0; + int tanns = 0; + int itanns = 0; + int innerClasses = 0; + int module = 0; + int packages = 0; + Attribute attributes = null; + + u = getAttributes(); + for (int i = readUnsignedShort(u); i > 0; --i) { + String attrName = readUTF8(u + 2, c); + // tests are sorted in decreasing frequency order + // (based on frequencies observed on typical classes) + if ("SourceFile".equals(attrName)) { + sourceFile = readUTF8(u + 8, c); + } else if ("InnerClasses".equals(attrName)) { + innerClasses = u + 8; + } else if ("EnclosingMethod".equals(attrName)) { + enclosingOwner = readClass(u + 8, c); + int item = readUnsignedShort(u + 10); + if (item != 0) { + enclosingName = readUTF8(items[item], c); + enclosingDesc = readUTF8(items[item] + 2, c); + } + } else if ("Signature".equals(attrName)) { + signature = readUTF8(u + 8, c); + } else if ("RuntimeVisibleAnnotations".equals(attrName)) { + anns = u + 8; + } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; + } else if ("Deprecated".equals(attrName)) { + access |= Opcodes.ACC_DEPRECATED; + } else if ("Synthetic".equals(attrName)) { + access |= Opcodes.ACC_SYNTHETIC + | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; + } else if ("SourceDebugExtension".equals(attrName)) { + int len = readInt(u + 4); + sourceDebug = readUTF(u + 8, len, new char[len]); + } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { + ianns = u + 8; + } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; + } else if ("Module".equals(attrName)) { + module = u + 8; + } else if ("ModuleMainClass".equals(attrName)) { + moduleMainClass = readClass(u + 8, c); + } else if ("ModulePackages".equals(attrName)) { + packages = u + 10; + } else if ("BootstrapMethods".equals(attrName)) { + int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; + for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { + bootstrapMethods[j] = v; + v += 2 + readUnsignedShort(v + 2) << 1; + } + context.bootstrapMethods = bootstrapMethods; + } else { + Attribute attr = readAttribute(attrs, attrName, u + 8, + readInt(u + 4), c, -1, null); + if (attr != null) { + attr.next = attributes; + attributes = attr; + } + } + u += 6 + readInt(u + 4); + } + + // visits the class declaration + classVisitor.visit(readInt(items[1] - 7), access, name, signature, + superClass, interfaces); + + // visits the source and debug info + if ((flags & SKIP_DEBUG) == 0 + && (sourceFile != null || sourceDebug != null)) { + classVisitor.visitSource(sourceFile, sourceDebug); + } + + // visits the module info and associated attributes + if (module != 0) { + readModule(classVisitor, context, module, + moduleMainClass, packages); + } + + // visits the outer class + if (enclosingOwner != null) { + classVisitor.visitOuterClass(enclosingOwner, enclosingName, + enclosingDesc); + } + + // visits the class annotations and type annotations + if (anns != 0) { + for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitAnnotation(readUTF8(v, c), true)); + } + } + if (ianns != 0) { + for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitAnnotation(readUTF8(v, c), false)); + } + } + if (tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } + + // visits the attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + classVisitor.visitAttribute(attributes); + attributes = attr; + } + + // visits the inner classes + if (innerClasses != 0) { + int v = innerClasses + 2; + for (int i = readUnsignedShort(innerClasses); i > 0; --i) { + classVisitor.visitInnerClass(readClass(v, c), + readClass(v + 2, c), readUTF8(v + 4, c), + readUnsignedShort(v + 6)); + v += 8; + } + } + + // visits the fields and methods + u = header + 10 + 2 * interfaces.length; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + u = readField(classVisitor, context, u); + } + u += 2; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + u = readMethod(classVisitor, context, u); + } + + // visits the end of the class + classVisitor.visitEnd(); + } + + /** + * Reads the module attribute and visit it. + * + * @param classVisitor + * the current class visitor + * @param context + * information about the class being parsed. + * @param u + * start offset of the module attribute in the class file. + * @param mainClass + * name of the main class of a module or null. + * @param packages + * start offset of the concealed package attribute. + */ + private void readModule(final ClassVisitor classVisitor, + final Context context, int u, + final String mainClass, int packages) { + + char[] buffer = context.buffer; + + // reads module name, flags and version + String name = readModule(u, buffer); + int flags = readUnsignedShort(u + 2); + String version = readUTF8(u + 4, buffer); + u += 6; + + ModuleVisitor mv = classVisitor.visitModule(name, flags, version); + if (mv == null) { + return; + } + + // module attributes (main class, packages) + if (mainClass != null) { + mv.visitMainClass(mainClass); + } + + if (packages != 0) { + for (int i = readUnsignedShort(packages - 2); i > 0; --i) { + String packaze = readPackage(packages, buffer); + mv.visitPackage(packaze); + packages += 2; + } + } + + // reads requires + u += 2; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + String module = readModule(u, buffer); + int access = readUnsignedShort(u + 2); + String requireVersion = readUTF8(u + 4, buffer); + mv.visitRequire(module, access, requireVersion); + u += 6; + } + + // reads exports + u += 2; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + String export = readPackage(u, buffer); + int access = readUnsignedShort(u + 2); + int exportToCount = readUnsignedShort(u + 4); + u += 6; + String[] tos = null; + if (exportToCount != 0) { + tos = new String[exportToCount]; + for (int j = 0; j < tos.length; ++j) { + tos[j] = readModule(u, buffer); + u += 2; + } + } + mv.visitExport(export, access, tos); + } + + // reads opens + u += 2; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + String open = readPackage(u, buffer); + int access = readUnsignedShort(u + 2); + int openToCount = readUnsignedShort(u + 4); + u += 6; + String[] tos = null; + if (openToCount != 0) { + tos = new String[openToCount]; + for (int j = 0; j < tos.length; ++j) { + tos[j] = readModule(u, buffer); + u += 2; + } + } + mv.visitOpen(open, access, tos); + } + + // read uses + u += 2; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + mv.visitUse(readClass(u, buffer)); + u += 2; + } + + // read provides + u += 2; + for (int i = readUnsignedShort(u - 2); i > 0; --i) { + String service = readClass(u, buffer); + int provideWithCount = readUnsignedShort(u + 2); + u += 4; + String[] withs = new String[provideWithCount]; + for (int j = 0; j < withs.length; ++j) { + withs[j] = readClass(u, buffer); + u += 2; + } + mv.visitProvide(service, withs); + } + + mv.visitEnd(); + } + + /** + * Reads a field and makes the given visitor visit it. + * + * @param classVisitor + * the visitor that must visit the field. + * @param context + * information about the class being parsed. + * @param u + * the start offset of the field in the class file. + * @return the offset of the first byte following the field in the class. + */ + private int readField(final ClassVisitor classVisitor, + final Context context, int u) { + // reads the field declaration + char[] c = context.buffer; + int access = readUnsignedShort(u); + String name = readUTF8(u + 2, c); + String desc = readUTF8(u + 4, c); + u += 6; + + // reads the field attributes + String signature = null; + int anns = 0; + int ianns = 0; + int tanns = 0; + int itanns = 0; + Object value = null; + Attribute attributes = null; + + for (int i = readUnsignedShort(u); i > 0; --i) { + String attrName = readUTF8(u + 2, c); + // tests are sorted in decreasing frequency order + // (based on frequencies observed on typical classes) + if ("ConstantValue".equals(attrName)) { + int item = readUnsignedShort(u + 8); + value = item == 0 ? null : readConst(item, c); + } else if ("Signature".equals(attrName)) { + signature = readUTF8(u + 8, c); + } else if ("Deprecated".equals(attrName)) { + access |= Opcodes.ACC_DEPRECATED; + } else if ("Synthetic".equals(attrName)) { + access |= Opcodes.ACC_SYNTHETIC + | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; + } else if ("RuntimeVisibleAnnotations".equals(attrName)) { + anns = u + 8; + } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; + } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { + ianns = u + 8; + } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; + } else { + Attribute attr = readAttribute(context.attrs, attrName, u + 8, + readInt(u + 4), c, -1, null); + if (attr != null) { + attr.next = attributes; + attributes = attr; + } + } + u += 6 + readInt(u + 4); + } + u += 2; + + // visits the field declaration + FieldVisitor fv = classVisitor.visitField(access, name, desc, + signature, value); + if (fv == null) { + return u; + } + + // visits the field annotations and type annotations + if (anns != 0) { + for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { + v = readAnnotationValues(v + 2, c, true, + fv.visitAnnotation(readUTF8(v, c), true)); + } + } + if (ianns != 0) { + for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { + v = readAnnotationValues(v + 2, c, true, + fv.visitAnnotation(readUTF8(v, c), false)); + } + } + if (tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + fv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + fv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } + + // visits the field attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + fv.visitAttribute(attributes); + attributes = attr; + } + + // visits the end of the field + fv.visitEnd(); + + return u; + } + + /** + * Reads a method and makes the given visitor visit it. + * + * @param classVisitor + * the visitor that must visit the method. + * @param context + * information about the class being parsed. + * @param u + * the start offset of the method in the class file. + * @return the offset of the first byte following the method in the class. + */ + private int readMethod(final ClassVisitor classVisitor, + final Context context, int u) { + // reads the method declaration + char[] c = context.buffer; + context.access = readUnsignedShort(u); + context.name = readUTF8(u + 2, c); + context.desc = readUTF8(u + 4, c); + u += 6; + + // reads the method attributes + int code = 0; + int exception = 0; + String[] exceptions = null; + String signature = null; + int methodParameters = 0; + int anns = 0; + int ianns = 0; + int tanns = 0; + int itanns = 0; + int dann = 0; + int mpanns = 0; + int impanns = 0; + int firstAttribute = u; + Attribute attributes = null; + + for (int i = readUnsignedShort(u); i > 0; --i) { + String attrName = readUTF8(u + 2, c); + // tests are sorted in decreasing frequency order + // (based on frequencies observed on typical classes) + if ("Code".equals(attrName)) { + if ((context.flags & SKIP_CODE) == 0) { + code = u + 8; + } + } else if ("Exceptions".equals(attrName)) { + exceptions = new String[readUnsignedShort(u + 8)]; + exception = u + 10; + for (int j = 0; j < exceptions.length; ++j) { + exceptions[j] = readClass(exception, c); + exception += 2; + } + } else if ("Signature".equals(attrName)) { + signature = readUTF8(u + 8, c); + } else if ("Deprecated".equals(attrName)) { + context.access |= Opcodes.ACC_DEPRECATED; + } else if ("RuntimeVisibleAnnotations".equals(attrName)) { + anns = u + 8; + } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; + } else if ("AnnotationDefault".equals(attrName)) { + dann = u + 8; + } else if ("Synthetic".equals(attrName)) { + context.access |= Opcodes.ACC_SYNTHETIC + | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; + } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { + ianns = u + 8; + } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; + } else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) { + mpanns = u + 8; + } else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) { + impanns = u + 8; + } else if ("MethodParameters".equals(attrName)) { + methodParameters = u + 8; + } else { + Attribute attr = readAttribute(context.attrs, attrName, u + 8, + readInt(u + 4), c, -1, null); + if (attr != null) { + attr.next = attributes; + attributes = attr; + } + } + u += 6 + readInt(u + 4); + } + u += 2; + + // visits the method declaration + MethodVisitor mv = classVisitor.visitMethod(context.access, + context.name, context.desc, signature, exceptions); + if (mv == null) { + return u; + } + + /* + * if the returned MethodVisitor is in fact a MethodWriter, it means + * there is no method adapter between the reader and the writer. If, in + * addition, the writer's constant pool was copied from this reader + * (mw.cw.cr == this), and the signature and exceptions of the method + * have not been changed, then it is possible to skip all visit events + * and just copy the original code of the method to the writer (the + * access, name and descriptor can have been changed, this is not + * important since they are not copied as is from the reader). + */ + if (mv instanceof MethodWriter) { + MethodWriter mw = (MethodWriter) mv; + if (mw.cw.cr == this && signature == mw.signature) { + boolean sameExceptions = false; + if (exceptions == null) { + sameExceptions = mw.exceptionCount == 0; + } else if (exceptions.length == mw.exceptionCount) { + sameExceptions = true; + for (int j = exceptions.length - 1; j >= 0; --j) { + exception -= 2; + if (mw.exceptions[j] != readUnsignedShort(exception)) { + sameExceptions = false; + break; + } + } + } + if (sameExceptions) { + /* + * we do not copy directly the code into MethodWriter to + * save a byte array copy operation. The real copy will be + * done in ClassWriter.toByteArray(). + */ + mw.classReaderOffset = firstAttribute; + mw.classReaderLength = u - firstAttribute; + return u; + } + } + } + + // visit the method parameters + if (methodParameters != 0) { + for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { + mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); + } + } + + // visits the method annotations + if (dann != 0) { + AnnotationVisitor dv = mv.visitAnnotationDefault(); + readAnnotationValue(dann, c, null, dv); + if (dv != null) { + dv.visitEnd(); + } + } + if (anns != 0) { + for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { + v = readAnnotationValues(v + 2, c, true, + mv.visitAnnotation(readUTF8(v, c), true)); + } + } + if (ianns != 0) { + for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { + v = readAnnotationValues(v + 2, c, true, + mv.visitAnnotation(readUTF8(v, c), false)); + } + } + if (tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + mv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + mv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } + if (mpanns != 0) { + readParameterAnnotations(mv, context, mpanns, true); + } + if (impanns != 0) { + readParameterAnnotations(mv, context, impanns, false); + } + + // visits the method attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + mv.visitAttribute(attributes); + attributes = attr; + } + + // visits the method code + if (code != 0) { + mv.visitCode(); + readCode(mv, context, code); + } + + // visits the end of the method + mv.visitEnd(); + + return u; + } + + /** + * Reads the bytecode of a method and makes the given visitor visit it. + * + * @param mv + * the visitor that must visit the method's code. + * @param context + * information about the class being parsed. + * @param u + * the start offset of the code attribute in the class file. + */ + private void readCode(final MethodVisitor mv, final Context context, int u) { + // reads the header + byte[] b = this.b; + char[] c = context.buffer; + int maxStack = readUnsignedShort(u); + int maxLocals = readUnsignedShort(u + 2); + int codeLength = readInt(u + 4); + u += 8; + + // reads the bytecode to find the labels + int codeStart = u; + int codeEnd = u + codeLength; + Label[] labels = context.labels = new Label[codeLength + 2]; + createLabel(codeLength + 1, labels); + while (u < codeEnd) { + int offset = u - codeStart; + int opcode = b[u] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + u += 1; + break; + case ClassWriter.LABEL_INSN: + createLabel(offset + readShort(u + 1), labels); + u += 3; + break; + case ClassWriter.ASM_LABEL_INSN: + createLabel(offset + readUnsignedShort(u + 1), labels); + u += 3; + break; + case ClassWriter.LABELW_INSN: + case ClassWriter.ASM_LABELW_INSN: + createLabel(offset + readInt(u + 1), labels); + u += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + u += 6; + } else { + u += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + u = u + 4 - (offset & 3); + // reads instruction + createLabel(offset + readInt(u), labels); + for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { + createLabel(offset + readInt(u + 12), labels); + u += 4; + } + u += 12; + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + u = u + 4 - (offset & 3); + // reads instruction + createLabel(offset + readInt(u), labels); + for (int i = readInt(u + 4); i > 0; --i) { + createLabel(offset + readInt(u + 12), labels); + u += 8; + } + u += 8; + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + u += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + u += 3; + break; + case ClassWriter.ITFMETH_INSN: + case ClassWriter.INDYMETH_INSN: + u += 5; + break; + // case MANA_INSN: + default: + u += 4; + break; + } + } + + // reads the try catch entries to find the labels, and also visits them + for (int i = readUnsignedShort(u); i > 0; --i) { + Label start = createLabel(readUnsignedShort(u + 2), labels); + Label end = createLabel(readUnsignedShort(u + 4), labels); + Label handler = createLabel(readUnsignedShort(u + 6), labels); + String type = readUTF8(items[readUnsignedShort(u + 8)], c); + mv.visitTryCatchBlock(start, end, handler, type); + u += 8; + } + u += 2; + + // reads the code attributes + int[] tanns = null; // start index of each visible type annotation + int[] itanns = null; // start index of each invisible type annotation + int tann = 0; // current index in tanns array + int itann = 0; // current index in itanns array + int ntoff = -1; // next visible type annotation code offset + int nitoff = -1; // next invisible type annotation code offset + int varTable = 0; + int varTypeTable = 0; + boolean zip = true; + boolean unzip = (context.flags & EXPAND_FRAMES) != 0; + int stackMap = 0; + int stackMapSize = 0; + int frameCount = 0; + Context frame = null; + Attribute attributes = null; + + for (int i = readUnsignedShort(u); i > 0; --i) { + String attrName = readUTF8(u + 2, c); + if ("LocalVariableTable".equals(attrName)) { + if ((context.flags & SKIP_DEBUG) == 0) { + varTable = u + 8; + for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { + int label = readUnsignedShort(v + 10); + createDebugLabel(label, labels); + label += readUnsignedShort(v + 12); + createDebugLabel(label, labels); + v += 10; + } + } + } else if ("LocalVariableTypeTable".equals(attrName)) { + varTypeTable = u + 8; + } else if ("LineNumberTable".equals(attrName)) { + if ((context.flags & SKIP_DEBUG) == 0) { + for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { + int label = readUnsignedShort(v + 10); + createDebugLabel(label, labels); + Label l = labels[label]; + while (l.line > 0) { + if (l.next == null) { + l.next = new Label(); + } + l = l.next; + } + l.line = readUnsignedShort(v + 12); + v += 4; + } + } + } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = readTypeAnnotations(mv, context, u + 8, true); + ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 + : readUnsignedShort(tanns[0] + 1); + } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = readTypeAnnotations(mv, context, u + 8, false); + nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 + : readUnsignedShort(itanns[0] + 1); + } else if ("StackMapTable".equals(attrName)) { + if ((context.flags & SKIP_FRAMES) == 0) { + stackMap = u + 10; + stackMapSize = readInt(u + 4); + frameCount = readUnsignedShort(u + 8); + } + /* + * here we do not extract the labels corresponding to the + * attribute content. This would require a full parsing of the + * attribute, which would need to be repeated in the second + * phase (see below). Instead the content of the attribute is + * read one frame at a time (i.e. after a frame has been + * visited, the next frame is read), and the labels it contains + * are also extracted one frame at a time. Thanks to the + * ordering of frames, having only a "one frame lookahead" is + * not a problem, i.e. it is not possible to see an offset + * smaller than the offset of the current insn and for which no + * Label exist. + */ + /* + * This is not true for UNINITIALIZED type offsets. We solve + * this by parsing the stack map table without a full decoding + * (see below). + */ + } else if ("StackMap".equals(attrName)) { + if ((context.flags & SKIP_FRAMES) == 0) { + zip = false; + stackMap = u + 10; + stackMapSize = readInt(u + 4); + frameCount = readUnsignedShort(u + 8); + } + /* + * IMPORTANT! here we assume that the frames are ordered, as in + * the StackMapTable attribute, although this is not guaranteed + * by the attribute format. + */ + } else { + for (int j = 0; j < context.attrs.length; ++j) { + if (context.attrs[j].type.equals(attrName)) { + Attribute attr = context.attrs[j].read(this, u + 8, + readInt(u + 4), c, codeStart - 8, labels); + if (attr != null) { + attr.next = attributes; + attributes = attr; + } + } + } + } + u += 6 + readInt(u + 4); + } + u += 2; + + // generates the first (implicit) stack map frame + if (stackMap != 0) { + /* + * for the first explicit frame the offset is not offset_delta + 1 + * but only offset_delta; setting the implicit frame offset to -1 + * allow the use of the "offset_delta + 1" rule in all cases + */ + frame = context; + frame.offset = -1; + frame.mode = 0; + frame.localCount = 0; + frame.localDiff = 0; + frame.stackCount = 0; + frame.local = new Object[maxLocals]; + frame.stack = new Object[maxStack]; + if (unzip) { + getImplicitFrame(context); + } + /* + * Finds labels for UNINITIALIZED frame types. Instead of decoding + * each element of the stack map table, we look for 3 consecutive + * bytes that "look like" an UNINITIALIZED type (tag 8, offset + * within code bounds, NEW instruction at this offset). We may find + * false positives (i.e. not real UNINITIALIZED types), but this + * should be rare, and the only consequence will be the creation of + * an unneeded label. This is better than creating a label for each + * NEW instruction, and faster than fully decoding the whole stack + * map table. + */ + for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) { + if (b[i] == 8) { // UNINITIALIZED FRAME TYPE + int v = readUnsignedShort(i + 1); + if (v >= 0 && v < codeLength) { + if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { + createLabel(v, labels); + } + } + } + } + } + if ((context.flags & EXPAND_ASM_INSNS) != 0 + && (context.flags & EXPAND_FRAMES) != 0) { + // Expanding the ASM pseudo instructions can introduce F_INSERT + // frames, even if the method does not currently have any frame. + // Also these inserted frames must be computed by simulating the + // effect of the bytecode instructions one by one, starting from the + // first one and the last existing frame (or the implicit first + // one). Finally, due to the way MethodWriter computes this (with + // the compute = INSERTED_FRAMES option), MethodWriter needs to know + // maxLocals before the first instruction is visited. For all these + // reasons we always visit the implicit first frame in this case + // (passing only maxLocals - the rest can be and is computed in + // MethodWriter). + mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null); + } + + // visits the instructions + int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0; + boolean insertFrame = false; + u = codeStart; + while (u < codeEnd) { + int offset = u - codeStart; + + // visits the label and line number for this offset, if any + Label l = labels[offset]; + if (l != null) { + Label next = l.next; + l.next = null; + mv.visitLabel(l); + if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) { + mv.visitLineNumber(l.line, l); + while (next != null) { + mv.visitLineNumber(next.line, l); + next = next.next; + } + } + } + + // visits the frame for this offset, if any + while (frame != null + && (frame.offset == offset || frame.offset == -1)) { + // if there is a frame for this offset, makes the visitor visit + // it, and reads the next frame if there is one. + if (frame.offset != -1) { + if (!zip || unzip) { + mv.visitFrame(Opcodes.F_NEW, frame.localCount, + frame.local, frame.stackCount, frame.stack); + } else { + mv.visitFrame(frame.mode, frame.localDiff, frame.local, + frame.stackCount, frame.stack); + } + // if there is already a frame for this offset, there is no + // need to insert a new one. + insertFrame = false; + } + if (frameCount > 0) { + stackMap = readFrame(stackMap, zip, unzip, frame); + --frameCount; + } else { + frame = null; + } + } + // inserts a frame for this offset, if requested by setting + // insertFrame to true during the previous iteration. The actual + // frame content will be computed in MethodWriter. + if (insertFrame) { + mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null); + insertFrame = false; + } + + // visits the instruction at this offset + int opcode = b[u] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + mv.visitInsn(opcode); + u += 1; + break; + case ClassWriter.IMPLVAR_INSN: + if (opcode > Opcodes.ISTORE) { + opcode -= 59; // ISTORE_0 + mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), + opcode & 0x3); + } else { + opcode -= 26; // ILOAD_0 + mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); + } + u += 1; + break; + case ClassWriter.LABEL_INSN: + mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]); + u += 3; + break; + case ClassWriter.LABELW_INSN: + mv.visitJumpInsn(opcode + opcodeDelta, labels[offset + + readInt(u + 1)]); + u += 5; + break; + case ClassWriter.ASM_LABEL_INSN: { + // changes temporary opcodes 202 to 217 (inclusive), 218 + // and 219 to IFEQ ... JSR (inclusive), IFNULL and + // IFNONNULL + opcode = opcode < 218 ? opcode - 49 : opcode - 20; + Label target = labels[offset + readUnsignedShort(u + 1)]; + // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx + // with IFNOTxxx GOTO_W L:..., where IFNOTxxx is + // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) + // and where designates the instruction just after + // the GOTO_W. + if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) { + mv.visitJumpInsn(opcode + 33, target); + } else { + opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1; + Label endif = createLabel(offset + 3, labels); + mv.visitJumpInsn(opcode, endif); + mv.visitJumpInsn(200, target); // GOTO_W + // endif designates the instruction just after GOTO_W, + // and is visited as part of the next instruction. Since + // it is a jump target, we need to insert a frame here. + insertFrame = true; + } + u += 3; + break; + } + case ClassWriter.ASM_LABELW_INSN: { + // replaces the pseudo GOTO_W instruction with a real one. + mv.visitJumpInsn(200, labels[offset + readInt(u + 1)]); + // The instruction just after is a jump target (because pseudo + // GOTO_W are used in patterns IFNOTxxx GOTO_W L:..., + // see MethodWriter), so we need to insert a frame here. + insertFrame = true; + u += 5; + break; + } + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4)); + u += 6; + } else { + mv.visitVarInsn(opcode, readUnsignedShort(u + 2)); + u += 4; + } + break; + case ClassWriter.TABL_INSN: { + // skips 0 to 3 padding bytes + u = u + 4 - (offset & 3); + // reads instruction + int label = offset + readInt(u); + int min = readInt(u + 4); + int max = readInt(u + 8); + Label[] table = new Label[max - min + 1]; + u += 12; + for (int i = 0; i < table.length; ++i) { + table[i] = labels[offset + readInt(u)]; + u += 4; + } + mv.visitTableSwitchInsn(min, max, labels[label], table); + break; + } + case ClassWriter.LOOK_INSN: { + // skips 0 to 3 padding bytes + u = u + 4 - (offset & 3); + // reads instruction + int label = offset + readInt(u); + int len = readInt(u + 4); + int[] keys = new int[len]; + Label[] values = new Label[len]; + u += 8; + for (int i = 0; i < len; ++i) { + keys[i] = readInt(u); + values[i] = labels[offset + readInt(u + 4)]; + u += 8; + } + mv.visitLookupSwitchInsn(labels[label], keys, values); + break; + } + case ClassWriter.VAR_INSN: + mv.visitVarInsn(opcode, b[u + 1] & 0xFF); + u += 2; + break; + case ClassWriter.SBYTE_INSN: + mv.visitIntInsn(opcode, b[u + 1]); + u += 2; + break; + case ClassWriter.SHORT_INSN: + mv.visitIntInsn(opcode, readShort(u + 1)); + u += 3; + break; + case ClassWriter.LDC_INSN: + mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c)); + u += 2; + break; + case ClassWriter.LDCW_INSN: + mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c)); + u += 3; + break; + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.ITFMETH_INSN: { + int cpIndex = items[readUnsignedShort(u + 1)]; + boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; + String iowner = readClass(cpIndex, c); + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String iname = readUTF8(cpIndex, c); + String idesc = readUTF8(cpIndex + 2, c); + if (opcode < Opcodes.INVOKEVIRTUAL) { + mv.visitFieldInsn(opcode, iowner, iname, idesc); + } else { + mv.visitMethodInsn(opcode, iowner, iname, idesc, itf); + } + if (opcode == Opcodes.INVOKEINTERFACE) { + u += 5; + } else { + u += 3; + } + break; + } + case ClassWriter.INDYMETH_INSN: { + int cpIndex = items[readUnsignedShort(u + 1)]; + int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)]; + Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); + int bsmArgCount = readUnsignedShort(bsmIndex + 2); + Object[] bsmArgs = new Object[bsmArgCount]; + bsmIndex += 4; + for (int i = 0; i < bsmArgCount; i++) { + bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c); + bsmIndex += 2; + } + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String iname = readUTF8(cpIndex, c); + String idesc = readUTF8(cpIndex + 2, c); + mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); + u += 5; + break; + } + case ClassWriter.TYPE_INSN: + mv.visitTypeInsn(opcode, readClass(u + 1, c)); + u += 3; + break; + case ClassWriter.IINC_INSN: + mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]); + u += 3; + break; + // case MANA_INSN: + default: + mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF); + u += 4; + break; + } + + // visit the instruction annotations, if any + while (tanns != null && tann < tanns.length && ntoff <= offset) { + if (ntoff == offset) { + int v = readAnnotationTarget(context, tanns[tann]); + readAnnotationValues(v + 2, c, true, + mv.visitInsnAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1 + : readUnsignedShort(tanns[tann] + 1); + } + while (itanns != null && itann < itanns.length && nitoff <= offset) { + if (nitoff == offset) { + int v = readAnnotationTarget(context, itanns[itann]); + readAnnotationValues(v + 2, c, true, + mv.visitInsnAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + nitoff = ++itann >= itanns.length + || readByte(itanns[itann]) < 0x43 ? -1 + : readUnsignedShort(itanns[itann] + 1); + } + } + if (labels[codeLength] != null) { + mv.visitLabel(labels[codeLength]); + } + + // visits the local variable tables + if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) { + int[] typeTable = null; + if (varTypeTable != 0) { + u = varTypeTable + 2; + typeTable = new int[readUnsignedShort(varTypeTable) * 3]; + for (int i = typeTable.length; i > 0;) { + typeTable[--i] = u + 6; // signature + typeTable[--i] = readUnsignedShort(u + 8); // index + typeTable[--i] = readUnsignedShort(u); // start + u += 10; + } + } + u = varTable + 2; + for (int i = readUnsignedShort(varTable); i > 0; --i) { + int start = readUnsignedShort(u); + int length = readUnsignedShort(u + 2); + int index = readUnsignedShort(u + 8); + String vsignature = null; + if (typeTable != null) { + for (int j = 0; j < typeTable.length; j += 3) { + if (typeTable[j] == start && typeTable[j + 1] == index) { + vsignature = readUTF8(typeTable[j + 2], c); + break; + } + } + } + mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c), + vsignature, labels[start], labels[start + length], + index); + u += 10; + } + } + + // visits the local variables type annotations + if (tanns != null) { + for (int i = 0; i < tanns.length; ++i) { + if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) { + int v = readAnnotationTarget(context, tanns[i]); + v = readAnnotationValues(v + 2, c, true, + mv.visitLocalVariableAnnotation(context.typeRef, + context.typePath, context.start, + context.end, context.index, readUTF8(v, c), + true)); + } + } + } + if (itanns != null) { + for (int i = 0; i < itanns.length; ++i) { + if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) { + int v = readAnnotationTarget(context, itanns[i]); + v = readAnnotationValues(v + 2, c, true, + mv.visitLocalVariableAnnotation(context.typeRef, + context.typePath, context.start, + context.end, context.index, readUTF8(v, c), + false)); + } + } + } + + // visits the code attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + mv.visitAttribute(attributes); + attributes = attr; + } + + // visits the max stack and max locals values + mv.visitMaxs(maxStack, maxLocals); + } + + /** + * Parses a type annotation table to find the labels, and to visit the try + * catch block annotations. + * + * @param u + * the start offset of a type annotation table. + * @param mv + * the method visitor to be used to visit the try catch block + * annotations. + * @param context + * information about the class being parsed. + * @param visible + * if the type annotation table to parse contains runtime visible + * annotations. + * @return the start offset of each type annotation in the parsed table. + */ + private int[] readTypeAnnotations(final MethodVisitor mv, + final Context context, int u, boolean visible) { + char[] c = context.buffer; + int[] offsets = new int[readUnsignedShort(u)]; + u += 2; + for (int i = 0; i < offsets.length; ++i) { + offsets[i] = u; + int target = readInt(u); + switch (target >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + u += 2; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + u += 1; + break; + case 0x40: // LOCAL_VARIABLE + case 0x41: // RESOURCE_VARIABLE + for (int j = readUnsignedShort(u + 1); j > 0; --j) { + int start = readUnsignedShort(u + 3); + int length = readUnsignedShort(u + 5); + createLabel(start, context.labels); + createLabel(start + length, context.labels); + u += 6; + } + u += 3; + break; + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + u += 4; + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + u += 3; + break; + } + int pathLength = readByte(u); + if ((target >>> 24) == 0x42) { + TypePath path = pathLength == 0 ? null : new TypePath(b, u); + u += 1 + 2 * pathLength; + u = readAnnotationValues(u + 2, c, true, + mv.visitTryCatchAnnotation(target, path, + readUTF8(u, c), visible)); + } else { + u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null); + } + } + return offsets; + } + + /** + * Parses the header of a type annotation to extract its target_type and + * target_path (the result is stored in the given context), and returns the + * start offset of the rest of the type_annotation structure (i.e. the + * offset to the type_index field, which is followed by + * num_element_value_pairs and then the name,value pairs). + * + * @param context + * information about the class being parsed. This is where the + * extracted target_type and target_path must be stored. + * @param u + * the start offset of a type_annotation structure. + * @return the start offset of the rest of the type_annotation structure. + */ + private int readAnnotationTarget(final Context context, int u) { + int target = readInt(u); + switch (target >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + target &= 0xFFFF0000; + u += 2; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + target &= 0xFF000000; + u += 1; + break; + case 0x40: // LOCAL_VARIABLE + case 0x41: { // RESOURCE_VARIABLE + target &= 0xFF000000; + int n = readUnsignedShort(u + 1); + context.start = new Label[n]; + context.end = new Label[n]; + context.index = new int[n]; + u += 3; + for (int i = 0; i < n; ++i) { + int start = readUnsignedShort(u); + int length = readUnsignedShort(u + 2); + context.start[i] = createLabel(start, context.labels); + context.end[i] = createLabel(start + length, context.labels); + context.index[i] = readUnsignedShort(u + 4); + u += 6; + } + break; + } + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + target &= 0xFF0000FF; + u += 4; + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000; + u += 3; + break; + } + int pathLength = readByte(u); + context.typeRef = target; + context.typePath = pathLength == 0 ? null : new TypePath(b, u); + return u + 1 + 2 * pathLength; + } + + /** + * Reads parameter annotations and makes the given visitor visit them. + * + * @param mv + * the visitor that must visit the annotations. + * @param context + * information about the class being parsed. + * @param v + * start offset in {@link #b b} of the annotations to be read. + * @param visible + * true if the annotations to be read are visible at + * runtime. + */ + private void readParameterAnnotations(final MethodVisitor mv, + final Context context, int v, final boolean visible) { + int i; + int n = b[v++] & 0xFF; + // workaround for a bug in javac (javac compiler generates a parameter + // annotation array whose size is equal to the number of parameters in + // the Java source file, while it should generate an array whose size is + // equal to the number of parameters in the method descriptor - which + // includes the synthetic parameters added by the compiler). This work- + // around supposes that the synthetic parameters are the first ones. + int synthetics = Type.getArgumentTypes(context.desc).length - n; + AnnotationVisitor av; + for (i = 0; i < synthetics; ++i) { + // virtual annotation to detect synthetic parameters in MethodWriter + av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false); + if (av != null) { + av.visitEnd(); + } + } + char[] c = context.buffer; + for (; i < n + synthetics; ++i) { + int j = readUnsignedShort(v); + v += 2; + for (; j > 0; --j) { + av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible); + v = readAnnotationValues(v + 2, c, true, av); + } + } + } + + /** + * Reads the values of an annotation and makes the given visitor visit them. + * + * @param v + * the start offset in {@link #b b} of the values to be read + * (including the unsigned short that gives the number of + * values). + * @param buf + * buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or {@link #readConst + * readConst}. + * @param named + * if the annotation values are named or not. + * @param av + * the visitor that must visit the values. + * @return the end offset of the annotation values. + */ + private int readAnnotationValues(int v, final char[] buf, + final boolean named, final AnnotationVisitor av) { + int i = readUnsignedShort(v); + v += 2; + if (named) { + for (; i > 0; --i) { + v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); + } + } else { + for (; i > 0; --i) { + v = readAnnotationValue(v, buf, null, av); + } + } + if (av != null) { + av.visitEnd(); + } + return v; + } + + /** + * Reads a value of an annotation and makes the given visitor visit it. + * + * @param v + * the start offset in {@link #b b} of the value to be read + * (not including the value name constant pool index). + * @param buf + * buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or {@link #readConst + * readConst}. + * @param name + * the name of the value to be read. + * @param av + * the visitor that must visit the value. + * @return the end offset of the annotation value. + */ + private int readAnnotationValue(int v, final char[] buf, final String name, + final AnnotationVisitor av) { + int i; + if (av == null) { + switch (b[v] & 0xFF) { + case 'e': // enum_const_value + return v + 5; + case '@': // annotation_value + return readAnnotationValues(v + 3, buf, true, null); + case '[': // array_value + return readAnnotationValues(v + 1, buf, false, null); + default: + return v + 3; + } + } + switch (b[v++] & 0xFF) { + case 'I': // pointer to CONSTANT_Integer + case 'J': // pointer to CONSTANT_Long + case 'F': // pointer to CONSTANT_Float + case 'D': // pointer to CONSTANT_Double + av.visit(name, readConst(readUnsignedShort(v), buf)); + v += 2; + break; + case 'B': // pointer to CONSTANT_Byte + av.visit(name, (byte) readInt(items[readUnsignedShort(v)])); + v += 2; + break; + case 'Z': // pointer to CONSTANT_Boolean + av.visit(name, + readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE + : Boolean.TRUE); + v += 2; + break; + case 'S': // pointer to CONSTANT_Short + av.visit(name, (short) readInt(items[readUnsignedShort(v)])); + v += 2; + break; + case 'C': // pointer to CONSTANT_Char + av.visit(name, (char) readInt(items[readUnsignedShort(v)])); + v += 2; + break; + case 's': // pointer to CONSTANT_Utf8 + av.visit(name, readUTF8(v, buf)); + v += 2; + break; + case 'e': // enum_const_value + av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); + v += 4; + break; + case 'c': // class_info + av.visit(name, Type.getType(readUTF8(v, buf))); + v += 2; + break; + case '@': // annotation_value + v = readAnnotationValues(v + 2, buf, true, + av.visitAnnotation(name, readUTF8(v, buf))); + break; + case '[': // array_value + int size = readUnsignedShort(v); + v += 2; + if (size == 0) { + return readAnnotationValues(v - 2, buf, false, + av.visitArray(name)); + } + switch (this.b[v++] & 0xFF) { + case 'B': + byte[] bv = new byte[size]; + for (i = 0; i < size; i++) { + bv[i] = (byte) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, bv); + --v; + break; + case 'Z': + boolean[] zv = new boolean[size]; + for (i = 0; i < size; i++) { + zv[i] = readInt(items[readUnsignedShort(v)]) != 0; + v += 3; + } + av.visit(name, zv); + --v; + break; + case 'S': + short[] sv = new short[size]; + for (i = 0; i < size; i++) { + sv[i] = (short) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, sv); + --v; + break; + case 'C': + char[] cv = new char[size]; + for (i = 0; i < size; i++) { + cv[i] = (char) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, cv); + --v; + break; + case 'I': + int[] iv = new int[size]; + for (i = 0; i < size; i++) { + iv[i] = readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, iv); + --v; + break; + case 'J': + long[] lv = new long[size]; + for (i = 0; i < size; i++) { + lv[i] = readLong(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, lv); + --v; + break; + case 'F': + float[] fv = new float[size]; + for (i = 0; i < size; i++) { + fv[i] = Float + .intBitsToFloat(readInt(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, fv); + --v; + break; + case 'D': + double[] dv = new double[size]; + for (i = 0; i < size; i++) { + dv[i] = Double + .longBitsToDouble(readLong(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, dv); + --v; + break; + default: + v = readAnnotationValues(v - 3, buf, false, av.visitArray(name)); + } + } + return v; + } + + /** + * Computes the implicit frame of the method currently being parsed (as + * defined in the given {@link Context}) and stores it in the given context. + * + * @param frame + * information about the class being parsed. + */ + private void getImplicitFrame(final Context frame) { + String desc = frame.desc; + Object[] locals = frame.local; + int local = 0; + if ((frame.access & Opcodes.ACC_STATIC) == 0) { + if ("".equals(frame.name)) { + locals[local++] = Opcodes.UNINITIALIZED_THIS; + } else { + locals[local++] = readClass(header + 2, frame.buffer); + } + } + int i = 1; + loop: while (true) { + int j = i; + switch (desc.charAt(i++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + locals[local++] = Opcodes.INTEGER; + break; + case 'F': + locals[local++] = Opcodes.FLOAT; + break; + case 'J': + locals[local++] = Opcodes.LONG; + break; + case 'D': + locals[local++] = Opcodes.DOUBLE; + break; + case '[': + while (desc.charAt(i) == '[') { + ++i; + } + if (desc.charAt(i) == 'L') { + ++i; + while (desc.charAt(i) != ';') { + ++i; + } + } + locals[local++] = desc.substring(j, ++i); + break; + case 'L': + while (desc.charAt(i) != ';') { + ++i; + } + locals[local++] = desc.substring(j + 1, i++); + break; + default: + break loop; + } + } + frame.localCount = local; + } + + /** + * Reads a stack map frame and stores the result in the given + * {@link Context} object. + * + * @param stackMap + * the start offset of a stack map frame in the class file. + * @param zip + * if the stack map frame at stackMap is compressed or not. + * @param unzip + * if the stack map frame must be uncompressed. + * @param frame + * where the parsed stack map frame must be stored. + * @return the offset of the first byte following the parsed frame. + */ + private int readFrame(int stackMap, boolean zip, boolean unzip, + Context frame) { + char[] c = frame.buffer; + Label[] labels = frame.labels; + int tag; + int delta; + if (zip) { + tag = b[stackMap++] & 0xFF; + } else { + tag = MethodWriter.FULL_FRAME; + frame.offset = -1; + } + frame.localDiff = 0; + if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) { + delta = tag; + frame.mode = Opcodes.F_SAME; + frame.stackCount = 0; + } else if (tag < MethodWriter.RESERVED) { + delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; + stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); + frame.mode = Opcodes.F_SAME1; + frame.stackCount = 1; + } else { + delta = readUnsignedShort(stackMap); + stackMap += 2; + if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); + frame.mode = Opcodes.F_SAME1; + frame.stackCount = 1; + } else if (tag >= MethodWriter.CHOP_FRAME + && tag < MethodWriter.SAME_FRAME_EXTENDED) { + frame.mode = Opcodes.F_CHOP; + frame.localDiff = MethodWriter.SAME_FRAME_EXTENDED - tag; + frame.localCount -= frame.localDiff; + frame.stackCount = 0; + } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) { + frame.mode = Opcodes.F_SAME; + frame.stackCount = 0; + } else if (tag < MethodWriter.FULL_FRAME) { + int local = unzip ? frame.localCount : 0; + for (int i = tag - MethodWriter.SAME_FRAME_EXTENDED; i > 0; i--) { + stackMap = readFrameType(frame.local, local++, stackMap, c, + labels); + } + frame.mode = Opcodes.F_APPEND; + frame.localDiff = tag - MethodWriter.SAME_FRAME_EXTENDED; + frame.localCount += frame.localDiff; + frame.stackCount = 0; + } else { // if (tag == FULL_FRAME) { + frame.mode = Opcodes.F_FULL; + int n = readUnsignedShort(stackMap); + stackMap += 2; + frame.localDiff = n; + frame.localCount = n; + for (int local = 0; n > 0; n--) { + stackMap = readFrameType(frame.local, local++, stackMap, c, + labels); + } + n = readUnsignedShort(stackMap); + stackMap += 2; + frame.stackCount = n; + for (int stack = 0; n > 0; n--) { + stackMap = readFrameType(frame.stack, stack++, stackMap, c, + labels); + } + } + } + frame.offset += delta + 1; + createLabel(frame.offset, labels); + return stackMap; + } + + /** + * Reads a stack map frame type and stores it at the given index in the + * given array. + * + * @param frame + * the array where the parsed type must be stored. + * @param index + * the index in 'frame' where the parsed type must be stored. + * @param v + * the start offset of the stack map frame type to read. + * @param buf + * a buffer to read strings. + * @param labels + * the labels of the method currently being parsed, indexed by + * their offset. If the parsed type is an Uninitialized type, a + * new label for the corresponding NEW instruction is stored in + * this array if it does not already exist. + * @return the offset of the first byte after the parsed type. + */ + private int readFrameType(final Object[] frame, final int index, int v, + final char[] buf, final Label[] labels) { + int type = b[v++] & 0xFF; + switch (type) { + case 0: + frame[index] = Opcodes.TOP; + break; + case 1: + frame[index] = Opcodes.INTEGER; + break; + case 2: + frame[index] = Opcodes.FLOAT; + break; + case 3: + frame[index] = Opcodes.DOUBLE; + break; + case 4: + frame[index] = Opcodes.LONG; + break; + case 5: + frame[index] = Opcodes.NULL; + break; + case 6: + frame[index] = Opcodes.UNINITIALIZED_THIS; + break; + case 7: // Object + frame[index] = readClass(v, buf); + v += 2; + break; + default: // Uninitialized + frame[index] = createLabel(readUnsignedShort(v), labels); + v += 2; + } + return v; + } + + /** + * Returns the label corresponding to the given offset. The default + * implementation of this method creates a label for the given offset if it + * has not been already created. + * + * @param offset + * a bytecode offset in a method. + * @param labels + * the already created labels, indexed by their offset. If a + * label already exists for offset this method must not create a + * new one. Otherwise it must store the new label in this array. + * @return a non null Label, which must be equal to labels[offset]. + */ + protected Label readLabel(int offset, Label[] labels) { + if (labels[offset] == null) { + labels[offset] = new Label(); + } + return labels[offset]; + } + + /** + * Creates a label without the Label.DEBUG flag set, for the given offset. + * The label is created with a call to {@link #readLabel} and its + * Label.DEBUG flag is cleared. + * + * @param offset + * a bytecode offset in a method. + * @param labels + * the already created labels, indexed by their offset. + * @return a Label without the Label.DEBUG flag set. + */ + private Label createLabel(int offset, Label[] labels) { + Label label = readLabel(offset, labels); + label.status &= ~Label.DEBUG; + return label; + } + + /** + * Creates a label with the Label.DEBUG flag set, if there is no already + * existing label for the given offset (otherwise does nothing). The label + * is created with a call to {@link #readLabel}. + * + * @param offset + * a bytecode offset in a method. + * @param labels + * the already created labels, indexed by their offset. + */ + private void createDebugLabel(int offset, Label[] labels) { + if (labels[offset] == null) { + readLabel(offset, labels).status |= Label.DEBUG; + } + } + + /** + * Returns the start index of the attribute_info structure of this class. + * + * @return the start index of the attribute_info structure of this class. + */ + private int getAttributes() { + // skips the header + int u = header + 8 + readUnsignedShort(header + 6) * 2; + // skips fields and methods + for (int i = readUnsignedShort(u); i > 0; --i) { + for (int j = readUnsignedShort(u + 8); j > 0; --j) { + u += 6 + readInt(u + 12); + } + u += 8; + } + u += 2; + for (int i = readUnsignedShort(u); i > 0; --i) { + for (int j = readUnsignedShort(u + 8); j > 0; --j) { + u += 6 + readInt(u + 12); + } + u += 8; + } + // the attribute_info structure starts just after the methods + return u + 2; + } + + /** + * Reads an attribute in {@link #b b}. + * + * @param attrs + * prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to + * the type of one the prototypes is ignored (i.e. an empty + * {@link Attribute} instance is returned). + * @param type + * the type of the attribute. + * @param off + * index of the first byte of the attribute's content in + * {@link #b b}. The 6 attribute header bytes, containing the + * type and the length of the attribute, are not taken into + * account here (they have already been read). + * @param len + * the length of the attribute's content. + * @param buf + * buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or {@link #readConst + * readConst}. + * @param codeOff + * index of the first byte of code's attribute content in + * {@link #b b}, or -1 if the attribute to be read is not a code + * attribute. The 6 attribute header bytes, containing the type + * and the length of the attribute, are not taken into account + * here. + * @param labels + * the labels of the method's code, or null if the + * attribute to be read is not a code attribute. + * @return the attribute that has been read, or null to skip this + * attribute. + */ + private Attribute readAttribute(final Attribute[] attrs, final String type, + final int off, final int len, final char[] buf, final int codeOff, + final Label[] labels) { + for (int i = 0; i < attrs.length; ++i) { + if (attrs[i].type.equals(type)) { + return attrs[i].read(this, off, len, buf, codeOff, labels); + } + } + return new Attribute(type).read(this, off, len, null, -1, null); + } + + // ------------------------------------------------------------------------ + // Utility methods: low level parsing + // ------------------------------------------------------------------------ + + /** + * Returns the number of constant pool items in {@link #b b}. + * + * @return the number of constant pool items in {@link #b b}. + */ + public int getItemCount() { + return items.length; + } + + /** + * Returns the start index of the constant pool item in {@link #b b}, plus + * one. This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param item + * the index a constant pool item. + * @return the start index of the constant pool item in {@link #b b}, plus + * one. + */ + public int getItem(final int item) { + return items[item]; + } + + /** + * Returns the maximum length of the strings contained in the constant pool + * of the class. + * + * @return the maximum length of the strings contained in the constant pool + * of the class. + */ + public int getMaxStringLength() { + return maxStringLength; + } + + /** + * Reads a byte value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readByte(final int index) { + return b[index] & 0xFF; + } + + /** + * Reads an unsigned short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readUnsignedShort(final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public short readShort(final int index) { + byte[] b = this.b; + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readInt(final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); + } + + /** + * Reads a signed long value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public long readLong(final int index) { + long l1 = readInt(index); + long l0 = readInt(index + 4) & 0xFFFFFFFFL; + return (l1 << 32) | l0; + } + + /** + * Reads an UTF8 string constant pool item in {@link #b b}. This method + * is intended for {@link Attribute} sub classes, and is normally not needed + * by class generators or adapters. + * + * @param index + * the start index of an unsigned short value in {@link #b b}, + * whose value is the index of an UTF8 constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 item. + */ + public String readUTF8(int index, final char[] buf) { + int item = readUnsignedShort(index); + if (index == 0 || item == 0) { + return null; + } + String s = strings[item]; + if (s != null) { + return s; + } + index = items[item]; + return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); + } + + /** + * Reads UTF8 string in {@link #b b}. + * + * @param index + * start offset of the UTF8 string to be read. + * @param utfLen + * length of the UTF8 string to be read. + * @param buf + * buffer to be used to read the string. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 string. + */ + private String readUTF(int index, final int utfLen, final char[] buf) { + int endIndex = index + utfLen; + byte[] b = this.b; + int strLen = 0; + int c; + int st = 0; + char cc = 0; + while (index < endIndex) { + c = b[index++]; + switch (st) { + case 0: + c = c & 0xFF; + if (c < 0x80) { // 0xxxxxxx + buf[strLen++] = (char) c; + } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx + cc = (char) (c & 0x1F); + st = 1; + } else { // 1110 xxxx 10xx xxxx 10xx xxxx + cc = (char) (c & 0x0F); + st = 2; + } + break; + + case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char + buf[strLen++] = (char) ((cc << 6) | (c & 0x3F)); + st = 0; + break; + + case 2: // byte 2 of 3-byte char + cc = (char) ((cc << 6) | (c & 0x3F)); + st = 1; + break; + } + } + return new String(buf, 0, strLen); + } + + /** + * Read a stringish constant item (CONSTANT_Class, CONSTANT_String, + * CONSTANT_MethodType, CONSTANT_Module or CONSTANT_Package + * @param index + * @param buf + * @return + */ + private String readStringish(final int index, final char[] buf) { + // computes the start index of the item in b + // and reads the CONSTANT_Utf8 item designated by + // the first two bytes of this item + return readUTF8(items[readUnsignedShort(index)], buf); + } + + /** + * Reads a class constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index + * the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a class constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified class item. + */ + public String readClass(final int index, final char[] buf) { + return readStringish(index, buf); + } + + /** + * Reads a module constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index + * the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a module constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified module item. + */ + public String readModule(final int index, final char[] buf) { + return readStringish(index, buf); + } + + /** + * Reads a module constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index + * the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a module constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified module item. + */ + public String readPackage(final int index, final char[] buf) { + return readStringish(index, buf); + } + + /** + * Reads a numeric or string constant pool item in {@link #b b}. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param item + * the index of a constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, + * {@link String}, {@link Type} or {@link Handle} corresponding to + * the given constant pool item. + */ + public Object readConst(final int item, final char[] buf) { + int index = items[item]; + switch (b[index - 1]) { + case ClassWriter.INT: + return readInt(index); + case ClassWriter.FLOAT: + return Float.intBitsToFloat(readInt(index)); + case ClassWriter.LONG: + return readLong(index); + case ClassWriter.DOUBLE: + return Double.longBitsToDouble(readLong(index)); + case ClassWriter.CLASS: + return Type.getObjectType(readUTF8(index, buf)); + case ClassWriter.STR: + return readUTF8(index, buf); + case ClassWriter.MTYPE: + return Type.getMethodType(readUTF8(index, buf)); + default: // case ClassWriter.HANDLE_BASE + [1..9]: + int tag = readByte(index); + int[] items = this.items; + int cpIndex = items[readUnsignedShort(index + 1)]; + boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; + String owner = readClass(cpIndex, buf); + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String name = readUTF8(cpIndex, buf); + String desc = readUTF8(cpIndex + 2, buf); + return new Handle(tag, owner, name, desc, itf); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassVisitor.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassVisitor.java new file mode 100644 index 000000000..2b857122c --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassVisitor.java @@ -0,0 +1,342 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A visitor to visit a Java class. The methods of this class must be called in + * the following order: visit [ visitSource ] [ + * visitModule ][ visitOuterClass ] ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* ( + * visitInnerClass | visitField | visitMethod )* + * visitEnd. + * + * @author Eric Bruneton + */ +public abstract class ClassVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + protected final int api; + + /** + * The class visitor to which this visitor must delegate method calls. May + * be null. + */ + protected ClassVisitor cv; + + /** + * Constructs a new {@link ClassVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + public ClassVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link ClassVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + * @param cv + * the class visitor to which this visitor must delegate method + * calls. May be null. + */ + public ClassVisitor(final int api, final ClassVisitor cv) { + if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { + throw new IllegalArgumentException(); + } + this.api = api; + this.cv = cv; + } + + /** + * Visits the header of the class. + * + * @param version + * the class version. + * @param access + * the class's access flags (see {@link Opcodes}). This parameter + * also indicates if the class is deprecated. + * @param name + * the internal name of the class (see + * {@link Type#getInternalName() getInternalName}). + * @param signature + * the signature of this class. May be null if the class + * is not a generic one, and does not extend or implement generic + * classes or interfaces. + * @param superName + * the internal of name of the super class (see + * {@link Type#getInternalName() getInternalName}). For + * interfaces, the super class is {@link Object}. May be + * null, but only for the {@link Object} class. + * @param interfaces + * the internal names of the class's interfaces (see + * {@link Type#getInternalName() getInternalName}). May be + * null. + */ + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + if (cv != null) { + cv.visit(version, access, name, signature, superName, interfaces); + } + } + + /** + * Visits the source of the class. + * + * @param source + * the name of the source file from which the class was compiled. + * May be null. + * @param debug + * additional debug information to compute the correspondance + * between source and compiled elements of the class. May be + * null. + */ + public void visitSource(String source, String debug) { + if (cv != null) { + cv.visitSource(source, debug); + } + } + + /** + * Visit the module corresponding to the class. + * @param name + * module name + * @param access + * module flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC} + * and {@code ACC_MANDATED}. + * @param version + * module version or null. + * @return a visitor to visit the module values, or null if + * this visitor is not interested in visiting this module. + */ + public ModuleVisitor visitModule(String name, int access, String version) { + if (api < Opcodes.ASM6) { + throw new RuntimeException(); + } + if (cv != null) { + return cv.visitModule(name, access, version); + } + return null; + } + + /** + * Visits the enclosing class of the class. This method must be called only + * if the class has an enclosing class. + * + * @param owner + * internal name of the enclosing class of the class. + * @param name + * the name of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + * @param desc + * the descriptor of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + */ + public void visitOuterClass(String owner, String name, String desc) { + if (cv != null) { + cv.visitOuterClass(owner, name, desc); + } + } + + /** + * Visits an annotation of the class. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (cv != null) { + return cv.visitAnnotation(desc, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the class signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#CLASS_TYPE_PARAMETER + * CLASS_TYPE_PARAMETER}, + * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND + * CLASS_TYPE_PARAMETER_BOUND} or + * {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See + * {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (cv != null) { + return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the class. + * + * @param attr + * an attribute. + */ + public void visitAttribute(Attribute attr) { + if (cv != null) { + cv.visitAttribute(attr); + } + } + + /** + * Visits information about an inner class. This inner class is not + * necessarily a member of the class being visited. + * + * @param name + * the internal name of an inner class (see + * {@link Type#getInternalName() getInternalName}). + * @param outerName + * the internal name of the class to which the inner class + * belongs (see {@link Type#getInternalName() getInternalName}). + * May be null for not member classes. + * @param innerName + * the (simple) name of the inner class inside its enclosing + * class. May be null for anonymous inner classes. + * @param access + * the access flags of the inner class as originally declared in + * the enclosing class. + */ + public void visitInnerClass(String name, String outerName, + String innerName, int access) { + if (cv != null) { + cv.visitInnerClass(name, outerName, innerName, access); + } + } + + /** + * Visits a field of the class. + * + * @param access + * the field's access flags (see {@link Opcodes}). This parameter + * also indicates if the field is synthetic and/or deprecated. + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link Type Type}). + * @param signature + * the field's signature. May be null if the field's + * type does not use generic types. + * @param value + * the field's initial value. This parameter, which may be + * null if the field does not have an initial value, + * must be an {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double} or a {@link String} (for int, + * float, long or String fields + * respectively). This parameter is only used for static + * fields. Its value is ignored for non static fields, which + * must be initialized through bytecode instructions in + * constructors or methods. + * @return a visitor to visit field annotations and attributes, or + * null if this class visitor is not interested in visiting + * these annotations and attributes. + */ + public FieldVisitor visitField(int access, String name, String desc, + String signature, Object value) { + if (cv != null) { + return cv.visitField(access, name, desc, signature, value); + } + return null; + } + + /** + * Visits a method of the class. This method must return a new + * {@link MethodVisitor} instance (or null) each time it is called, + * i.e., it should not return a previously returned visitor. + * + * @param access + * the method's access flags (see {@link Opcodes}). This + * parameter also indicates if the method is synthetic and/or + * deprecated. + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param signature + * the method's signature. May be null if the method + * parameters, return type and exceptions do not use generic + * types. + * @param exceptions + * the internal names of the method's exception classes (see + * {@link Type#getInternalName() getInternalName}). May be + * null. + * @return an object to visit the byte code of the method, or null + * if this class visitor is not interested in visiting the code of + * this method. + */ + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + if (cv != null) { + return cv.visitMethod(access, name, desc, signature, exceptions); + } + return null; + } + + /** + * Visits the end of the class. This method, which is the last one to be + * called, is used to inform the visitor that all the fields and methods of + * the class have been visited. + */ + public void visitEnd() { + if (cv != null) { + cv.visitEnd(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassWriter.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassWriter.java new file mode 100644 index 000000000..9b09cddfc --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/ClassWriter.java @@ -0,0 +1,1851 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A {@link ClassVisitor} that generates classes in bytecode form. More + * precisely this visitor generates a byte array conforming to the Java class + * file format. It can be used alone, to generate a Java class "from scratch", + * or with one or more {@link ClassReader ClassReader} and adapter class visitor + * to generate a modified class from one or more existing Java classes. + * + * @author Eric Bruneton + */ +public class ClassWriter extends ClassVisitor { + + /** + * Flag to automatically compute the maximum stack size and the maximum + * number of local variables of methods. If this flag is set, then the + * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the + * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} + * method will be ignored, and computed automatically from the signature and + * the bytecode of each method. + * + * @see #ClassWriter(int) + */ + public static final int COMPUTE_MAXS = 1; + + /** + * Flag to automatically compute the stack map frames of methods from + * scratch. If this flag is set, then the calls to the + * {@link MethodVisitor#visitFrame} method are ignored, and the stack map + * frames are recomputed from the methods bytecode. The arguments of the + * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and + * recomputed from the bytecode. In other words, COMPUTE_FRAMES implies + * COMPUTE_MAXS. + * + * @see #ClassWriter(int) + */ + public static final int COMPUTE_FRAMES = 2; + + /** + * Pseudo access flag to distinguish between the synthetic attribute and the + * synthetic access flag. + */ + static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000; + + /** + * Factor to convert from ACC_SYNTHETIC_ATTRIBUTE to Opcode.ACC_SYNTHETIC. + */ + static final int TO_ACC_SYNTHETIC = ACC_SYNTHETIC_ATTRIBUTE + / Opcodes.ACC_SYNTHETIC; + + /** + * The type of instructions without any argument. + */ + static final int NOARG_INSN = 0; + + /** + * The type of instructions with an signed byte argument. + */ + static final int SBYTE_INSN = 1; + + /** + * The type of instructions with an signed short argument. + */ + static final int SHORT_INSN = 2; + + /** + * The type of instructions with a local variable index argument. + */ + static final int VAR_INSN = 3; + + /** + * The type of instructions with an implicit local variable index argument. + */ + static final int IMPLVAR_INSN = 4; + + /** + * The type of instructions with a type descriptor argument. + */ + static final int TYPE_INSN = 5; + + /** + * The type of field and method invocations instructions. + */ + static final int FIELDORMETH_INSN = 6; + + /** + * The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction. + */ + static final int ITFMETH_INSN = 7; + + /** + * The type of the INVOKEDYNAMIC instruction. + */ + static final int INDYMETH_INSN = 8; + + /** + * The type of instructions with a 2 bytes bytecode offset label. + */ + static final int LABEL_INSN = 9; + + /** + * The type of instructions with a 4 bytes bytecode offset label. + */ + static final int LABELW_INSN = 10; + + /** + * The type of the LDC instruction. + */ + static final int LDC_INSN = 11; + + /** + * The type of the LDC_W and LDC2_W instructions. + */ + static final int LDCW_INSN = 12; + + /** + * The type of the IINC instruction. + */ + static final int IINC_INSN = 13; + + /** + * The type of the TABLESWITCH instruction. + */ + static final int TABL_INSN = 14; + + /** + * The type of the LOOKUPSWITCH instruction. + */ + static final int LOOK_INSN = 15; + + /** + * The type of the MULTIANEWARRAY instruction. + */ + static final int MANA_INSN = 16; + + /** + * The type of the WIDE instruction. + */ + static final int WIDE_INSN = 17; + + /** + * The type of the ASM pseudo instructions with an unsigned 2 bytes offset + * label (see Label#resolve). + */ + static final int ASM_LABEL_INSN = 18; + + /** + * The type of the ASM pseudo instructions with a 4 bytes offset label. + */ + static final int ASM_LABELW_INSN = 19; + + /** + * Represents a frame inserted between already existing frames. This kind of + * frame can only be used if the frame content can be computed from the + * previous existing frame and from the instructions between this existing + * frame and the inserted one, without any knowledge of the type hierarchy. + * This kind of frame is only used when an unconditional jump is inserted in + * a method while expanding an ASM pseudo instruction (see ClassReader). + */ + static final int F_INSERT = 256; + + /** + * The instruction types of all JVM opcodes. + */ + static final byte[] TYPE; + + /** + * The type of CONSTANT_Class constant pool items. + */ + static final int CLASS = 7; + + /** + * The type of CONSTANT_Fieldref constant pool items. + */ + static final int FIELD = 9; + + /** + * The type of CONSTANT_Methodref constant pool items. + */ + static final int METH = 10; + + /** + * The type of CONSTANT_InterfaceMethodref constant pool items. + */ + static final int IMETH = 11; + + /** + * The type of CONSTANT_String constant pool items. + */ + static final int STR = 8; + + /** + * The type of CONSTANT_Integer constant pool items. + */ + static final int INT = 3; + + /** + * The type of CONSTANT_Float constant pool items. + */ + static final int FLOAT = 4; + + /** + * The type of CONSTANT_Long constant pool items. + */ + static final int LONG = 5; + + /** + * The type of CONSTANT_Double constant pool items. + */ + static final int DOUBLE = 6; + + /** + * The type of CONSTANT_NameAndType constant pool items. + */ + static final int NAME_TYPE = 12; + + /** + * The type of CONSTANT_Utf8 constant pool items. + */ + static final int UTF8 = 1; + + /** + * The type of CONSTANT_MethodType constant pool items. + */ + static final int MTYPE = 16; + + /** + * The type of CONSTANT_MethodHandle constant pool items. + */ + static final int HANDLE = 15; + + /** + * The type of CONSTANT_InvokeDynamic constant pool items. + */ + static final int INDY = 18; + + /** + * The type of CONSTANT_Module constant pool items. + */ + static final int MODULE = 19; + + /** + * The type of CONSTANT_Package constant pool items. + */ + static final int PACKAGE = 20; + + /** + * The base value for all CONSTANT_MethodHandle constant pool items. + * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 + * different items (from 21 to 29). + */ + static final int HANDLE_BASE = 20; + + /** + * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ + static final int TYPE_NORMAL = 30; + + /** + * Uninitialized type Item stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. + */ + static final int TYPE_UNINIT = 31; + + /** + * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ + static final int TYPE_MERGED = 32; + + /** + * The type of BootstrapMethods items. These items are stored in a special + * class attribute named BootstrapMethods and not in the constant pool. + */ + static final int BSM = 33; + + /** + * The class reader from which this class writer was constructed, if any. + */ + ClassReader cr; + + /** + * Minor and major version numbers of the class to be generated. + */ + int version; + + /** + * Index of the next item to be added in the constant pool. + */ + int index; + + /** + * The constant pool of this class. + */ + final ByteVector pool; + + /** + * The constant pool's hash table data. + */ + Item[] items; + + /** + * The threshold of the constant pool's hash table. + */ + int threshold; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key2; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key3; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key4; + + /** + * A type table used to temporarily store internal names that will not + * necessarily be stored in the constant pool. This type table is used by + * the control flow and data flow analysis algorithm used to compute stack + * map frames from scratch. This array associates to each index i + * the Item whose index is i. All Item objects stored in this array + * are also stored in the {@link #items} hash table. These two arrays allow + * to retrieve an Item from its index or, conversely, to get the index of an + * Item from its value. Each Item stores an internal name in its + * {@link Item#strVal1} field. + */ + Item[] typeTable; + + /** + * Number of elements in the {@link #typeTable} array. + */ + private short typeCount; + + /** + * The access flags of this class. + */ + private int access; + + /** + * The constant pool item that contains the internal name of this class. + */ + private int name; + + /** + * The internal name of this class. + */ + String thisName; + + /** + * The constant pool item that contains the signature of this class. + */ + private int signature; + + /** + * The constant pool item that contains the internal name of the super class + * of this class. + */ + private int superName; + + /** + * Number of interfaces implemented or extended by this class or interface. + */ + private int interfaceCount; + + /** + * The interfaces implemented or extended by this class or interface. More + * precisely, this array contains the indexes of the constant pool items + * that contain the internal names of these interfaces. + */ + private int[] interfaces; + + /** + * The index of the constant pool item that contains the name of the source + * file from which this class was compiled. + */ + private int sourceFile; + + /** + * The SourceDebug attribute of this class. + */ + private ByteVector sourceDebug; + + /** + * The module attribute of this class. + */ + private ModuleWriter moduleWriter; + + /** + * The constant pool item that contains the name of the enclosing class of + * this class. + */ + private int enclosingMethodOwner; + + /** + * The constant pool item that contains the name and descriptor of the + * enclosing method of this class. + */ + private int enclosingMethod; + + /** + * The runtime visible annotations of this class. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this class. + */ + private AnnotationWriter ianns; + + /** + * The runtime visible type annotations of this class. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this class. + */ + private AnnotationWriter itanns; + + /** + * The non standard attributes of this class. + */ + private Attribute attrs; + + /** + * The number of entries in the InnerClasses attribute. + */ + private int innerClassesCount; + + /** + * The InnerClasses attribute. + */ + private ByteVector innerClasses; + + /** + * The number of entries in the BootstrapMethods attribute. + */ + int bootstrapMethodsCount; + + /** + * The BootstrapMethods attribute. + */ + ByteVector bootstrapMethods; + + /** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#fv} field. This field stores the first element of this + * list. + */ + FieldWriter firstField; + + /** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#fv} field. This field stores the last element of this + * list. + */ + FieldWriter lastField; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#mv} field. This field stores the first element of + * this list. + */ + MethodWriter firstMethod; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#mv} field. This field stores the last element of this + * list. + */ + MethodWriter lastMethod; + + /** + * Indicates what must be automatically computed. + * + * @see MethodWriter#compute + */ + private int compute; + + /** + * true if some methods have wide forward jumps using ASM pseudo + * instructions, which need to be expanded into sequences of standard + * bytecode instructions. In this case the class is re-read and re-written + * with a ClassReader -> ClassWriter chain to perform this transformation. + */ + boolean hasAsmInsns; + + // ------------------------------------------------------------------------ + // Static initializer + // ------------------------------------------------------------------------ + + /** + * Computes the instruction types of JVM opcodes. + */ + static { + int i; + byte[] b = new byte[221]; + String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" + + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA" + + "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSST"; + for (i = 0; i < b.length; ++i) { + b[i] = (byte) (s.charAt(i) - 'A'); + } + TYPE = b; + + // code to generate the above string + // + // // SBYTE_INSN instructions + // b[Constants.NEWARRAY] = SBYTE_INSN; + // b[Constants.BIPUSH] = SBYTE_INSN; + // + // // SHORT_INSN instructions + // b[Constants.SIPUSH] = SHORT_INSN; + // + // // (IMPL)VAR_INSN instructions + // b[Constants.RET] = VAR_INSN; + // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 + // b[i] = IMPLVAR_INSN; + // } + // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 + // b[i] = IMPLVAR_INSN; + // } + // + // // TYPE_INSN instructions + // b[Constants.NEW] = TYPE_INSN; + // b[Constants.ANEWARRAY] = TYPE_INSN; + // b[Constants.CHECKCAST] = TYPE_INSN; + // b[Constants.INSTANCEOF] = TYPE_INSN; + // + // // (Set)FIELDORMETH_INSN instructions + // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { + // b[i] = FIELDORMETH_INSN; + // } + // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; + // b[Constants.INVOKEDYNAMIC] = INDYMETH_INSN; + // + // // LABEL(W)_INSN instructions + // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { + // b[i] = LABEL_INSN; + // } + // b[Constants.IFNULL] = LABEL_INSN; + // b[Constants.IFNONNULL] = LABEL_INSN; + // b[200] = LABELW_INSN; // GOTO_W + // b[201] = LABELW_INSN; // JSR_W + // // temporary opcodes used internally by ASM - see Label and + // MethodWriter + // for (i = 202; i < 220; ++i) { + // b[i] = ASM_LABEL_INSN; + // } + // b[220] = ASM_LABELW_INSN; + // + // // LDC(_W) instructions + // b[Constants.LDC] = LDC_INSN; + // b[19] = LDCW_INSN; // LDC_W + // b[20] = LDCW_INSN; // LDC2_W + // + // // special instructions + // b[Constants.IINC] = IINC_INSN; + // b[Constants.TABLESWITCH] = TABL_INSN; + // b[Constants.LOOKUPSWITCH] = LOOK_INSN; + // b[Constants.MULTIANEWARRAY] = MANA_INSN; + // b[196] = WIDE_INSN; // WIDE + // + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('A' + b[i])); + // } + // System.err.println(); + } + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link ClassWriter} object. + * + * @param flags + * option flags that can be used to modify the default behavior + * of this class. See {@link #COMPUTE_MAXS}, + * {@link #COMPUTE_FRAMES}. + */ + public ClassWriter(final int flags) { + super(Opcodes.ASM6); + index = 1; + pool = new ByteVector(); + items = new Item[256]; + threshold = (int) (0.75d * items.length); + key = new Item(); + key2 = new Item(); + key3 = new Item(); + key4 = new Item(); + this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES + : ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS + : MethodWriter.NOTHING); + } + + /** + * Constructs a new {@link ClassWriter} object and enables optimizations for + * "mostly add" bytecode transformations. These optimizations are the + * following: + * + *

    + *
  • The constant pool from the original class is copied as is in the new + * class, which saves time. New constant pool entries will be added at the + * end if necessary, but unused constant pool entries won't be + * removed.
  • + *
  • Methods that are not transformed are copied as is in the new class, + * directly from the original class bytecode (i.e. without emitting visit + * events for all the method instructions), which saves a lot of + * time. Untransformed methods are detected by the fact that the + * {@link ClassReader} receives {@link MethodVisitor} objects that come from + * a {@link ClassWriter} (and not from any other {@link ClassVisitor} + * instance).
  • + *
+ * + * @param classReader + * the {@link ClassReader} used to read the original class. It + * will be used to copy the entire constant pool from the + * original class and also to copy other fragments of original + * bytecode where applicable. + * @param flags + * option flags that can be used to modify the default behavior + * of this class. These option flags do not affect methods + * that are copied as is in the new class. This means that + * neither the maximum stack size nor the stack frames will be + * computed for these methods. See {@link #COMPUTE_MAXS}, + * {@link #COMPUTE_FRAMES}. + */ + public ClassWriter(final ClassReader classReader, final int flags) { + this(flags); + classReader.copyPool(this); + this.cr = classReader; + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public final void visit(final int version, final int access, + final String name, final String signature, final String superName, + final String[] interfaces) { + this.version = version; + this.access = access; + this.name = newClass(name); + thisName = name; + if (signature != null) { + this.signature = newUTF8(signature); + } + this.superName = superName == null ? 0 : newClass(superName); + if (interfaces != null && interfaces.length > 0) { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for (int i = 0; i < interfaceCount; ++i) { + this.interfaces[i] = newClass(interfaces[i]); + } + } + } + + @Override + public final void visitSource(final String file, final String debug) { + if (file != null) { + sourceFile = newUTF8(file); + } + if (debug != null) { + sourceDebug = new ByteVector().encodeUTF8(debug, 0, + Integer.MAX_VALUE); + } + } + + @Override + public final ModuleVisitor visitModule(final String name, + final int access, final String version) { + return moduleWriter = new ModuleWriter(this, + newModule(name), access, + version == null ? 0 : newUTF8(version)); + } + + @Override + public final void visitOuterClass(final String owner, final String name, + final String desc) { + enclosingMethodOwner = newClass(owner); + if (name != null && desc != null) { + enclosingMethod = newNameType(name, desc); + } + } + + @Override + public final AnnotationVisitor visitAnnotation(final String desc, + final boolean visible) { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + @Override + public final AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, final String desc, final boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + + @Override + public final void visitAttribute(final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + @Override + public final void visitInnerClass(final String name, + final String outerName, final String innerName, final int access) { + if (innerClasses == null) { + innerClasses = new ByteVector(); + } + // Sec. 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the + // constant_pool table which represents a class or interface C that is + // not a package member must have exactly one corresponding entry in the + // classes array". To avoid duplicates we keep track in the intVal field + // of the Item of each CONSTANT_Class_info entry C whether an inner + // class entry has already been added for C (this field is unused for + // class entries, and changing its value does not change the hashcode + // and equality tests). If so we store the index of this inner class + // entry (plus one) in intVal. This hack allows duplicate detection in + // O(1) time. + Item nameItem = newStringishItem(CLASS, name); + if (nameItem.intVal == 0) { + ++innerClassesCount; + innerClasses.putShort(nameItem.index); + innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); + innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); + innerClasses.putShort(access); + nameItem.intVal = innerClassesCount; + } else { + // Compare the inner classes entry nameItem.intVal - 1 with the + // arguments of this method and throw an exception if there is a + // difference? + } + } + + @Override + public final FieldVisitor visitField(final int access, final String name, + final String desc, final String signature, final Object value) { + return new FieldWriter(this, access, name, desc, signature, value); + } + + @Override + public final MethodVisitor visitMethod(final int access, final String name, + final String desc, final String signature, final String[] exceptions) { + return new MethodWriter(this, access, name, desc, signature, + exceptions, compute); + } + + @Override + public final void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Other public methods + // ------------------------------------------------------------------------ + + /** + * Returns the bytecode of the class that was build with this class writer. + * + * @return the bytecode of the class that was build with this class writer. + */ + public byte[] toByteArray() { + if (index > 0xFFFF) { + throw new RuntimeException("Class file too large!"); + } + // computes the real size of the bytecode of this class + int size = 24 + 2 * interfaceCount; + int nbFields = 0; + FieldWriter fb = firstField; + while (fb != null) { + ++nbFields; + size += fb.getSize(); + fb = (FieldWriter) fb.fv; + } + int nbMethods = 0; + MethodWriter mb = firstMethod; + while (mb != null) { + ++nbMethods; + size += mb.getSize(); + mb = (MethodWriter) mb.mv; + } + int attributeCount = 0; + if (bootstrapMethods != null) { + // we put it as first attribute in order to improve a bit + // ClassReader.copyBootstrapMethods + ++attributeCount; + size += 8 + bootstrapMethods.length; + newUTF8("BootstrapMethods"); + } + if (signature != 0) { + ++attributeCount; + size += 8; + newUTF8("Signature"); + } + if (sourceFile != 0) { + ++attributeCount; + size += 8; + newUTF8("SourceFile"); + } + if (sourceDebug != null) { + ++attributeCount; + size += sourceDebug.length + 6; + newUTF8("SourceDebugExtension"); + } + if (enclosingMethodOwner != 0) { + ++attributeCount; + size += 10; + newUTF8("EnclosingMethod"); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + size += 6; + newUTF8("Deprecated"); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((version & 0xFFFF) < Opcodes.V1_5 + || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { + ++attributeCount; + size += 6; + newUTF8("Synthetic"); + } + } + if (innerClasses != null) { + ++attributeCount; + size += 8 + innerClasses.length; + newUTF8("InnerClasses"); + } + if (anns != null) { + ++attributeCount; + size += 8 + anns.getSize(); + newUTF8("RuntimeVisibleAnnotations"); + } + if (ianns != null) { + ++attributeCount; + size += 8 + ianns.getSize(); + newUTF8("RuntimeInvisibleAnnotations"); + } + if (tanns != null) { + ++attributeCount; + size += 8 + tanns.getSize(); + newUTF8("RuntimeVisibleTypeAnnotations"); + } + if (itanns != null) { + ++attributeCount; + size += 8 + itanns.getSize(); + newUTF8("RuntimeInvisibleTypeAnnotations"); + } + if (moduleWriter != null) { + attributeCount += 1 + moduleWriter.attributeCount; + size += 6 + moduleWriter.size + moduleWriter.attributesSize; + newUTF8("Module"); + } + if (attrs != null) { + attributeCount += attrs.getCount(); + size += attrs.getSize(this, null, 0, -1, -1); + } + size += pool.length; + // allocates a byte vector of this size, in order to avoid unnecessary + // arraycopy operations in the ByteVector.enlarge() method + ByteVector out = new ByteVector(size); + out.putInt(0xCAFEBABE).putInt(version); + out.putShort(index).putByteArray(pool.data, 0, pool.length); + int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE + | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC); + out.putShort(access & ~mask).putShort(name).putShort(superName); + out.putShort(interfaceCount); + for (int i = 0; i < interfaceCount; ++i) { + out.putShort(interfaces[i]); + } + out.putShort(nbFields); + fb = firstField; + while (fb != null) { + fb.put(out); + fb = (FieldWriter) fb.fv; + } + out.putShort(nbMethods); + mb = firstMethod; + while (mb != null) { + mb.put(out); + mb = (MethodWriter) mb.mv; + } + out.putShort(attributeCount); + if (bootstrapMethods != null) { + out.putShort(newUTF8("BootstrapMethods")); + out.putInt(bootstrapMethods.length + 2).putShort( + bootstrapMethodsCount); + out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); + } + if (signature != 0) { + out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); + } + if (sourceFile != 0) { + out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); + } + if (sourceDebug != null) { + int len = sourceDebug.length; + out.putShort(newUTF8("SourceDebugExtension")).putInt(len); + out.putByteArray(sourceDebug.data, 0, len); + } + if (moduleWriter != null) { + out.putShort(newUTF8("Module")); + moduleWriter.put(out); + moduleWriter.putAttributes(out); + } + if (enclosingMethodOwner != 0) { + out.putShort(newUTF8("EnclosingMethod")).putInt(4); + out.putShort(enclosingMethodOwner).putShort(enclosingMethod); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(newUTF8("Deprecated")).putInt(0); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((version & 0xFFFF) < Opcodes.V1_5 + || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { + out.putShort(newUTF8("Synthetic")).putInt(0); + } + } + if (innerClasses != null) { + out.putShort(newUTF8("InnerClasses")); + out.putInt(innerClasses.length + 2).putShort(innerClassesCount); + out.putByteArray(innerClasses.data, 0, innerClasses.length); + } + if (anns != null) { + out.putShort(newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ianns != null) { + out.putShort(newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (tanns != null) { + out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (itanns != null) { + out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } + if (attrs != null) { + attrs.put(this, null, 0, -1, -1, out); + } + if (hasAsmInsns) { + boolean hasFrames = false; + mb = firstMethod; + while (mb != null) { + hasFrames |= mb.frameCount > 0; + mb = (MethodWriter) mb.mv; + } + anns = null; + ianns = null; + attrs = null; + moduleWriter = null; + firstField = null; + lastField = null; + firstMethod = null; + lastMethod = null; + compute = + hasFrames ? MethodWriter.INSERTED_FRAMES : MethodWriter.NOTHING; + hasAsmInsns = false; + new ClassReader(out.data).accept(this, + (hasFrames ? ClassReader.EXPAND_FRAMES : 0) + | ClassReader.EXPAND_ASM_INSNS); + return toByteArray(); + } + return out.data; + } + + // ------------------------------------------------------------------------ + // Utility methods: constant pool management + // ------------------------------------------------------------------------ + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * + * @param cst + * the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double}, a {@link String} or a + * {@link Type}. + * @return a new or already existing constant item with the given value. + */ + Item newConstItem(final Object cst) { + if (cst instanceof Integer) { + int val = ((Integer) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Byte) { + int val = ((Byte) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Character) { + int val = ((Character) cst).charValue(); + return newInteger(val); + } else if (cst instanceof Short) { + int val = ((Short) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Boolean) { + int val = ((Boolean) cst).booleanValue() ? 1 : 0; + return newInteger(val); + } else if (cst instanceof Float) { + float val = ((Float) cst).floatValue(); + return newFloat(val); + } else if (cst instanceof Long) { + long val = ((Long) cst).longValue(); + return newLong(val); + } else if (cst instanceof Double) { + double val = ((Double) cst).doubleValue(); + return newDouble(val); + } else if (cst instanceof String) { + return newStringishItem(STR, (String) cst); + } else if (cst instanceof Type) { + Type t = (Type) cst; + int s = t.getSort(); + if (s == Type.OBJECT) { + return newStringishItem(CLASS, t.getInternalName()); + } else if (s == Type.METHOD) { + return newStringishItem(MTYPE, t.getDescriptor()); + } else { // s == primitive type or array + return newStringishItem(CLASS, t.getDescriptor()); + } + } else if (cst instanceof Handle) { + Handle h = (Handle) cst; + return newHandleItem(h.tag, h.owner, h.name, h.desc, h.itf); + } else { + throw new IllegalArgumentException("value " + cst); + } + } + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param cst + * the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double} or a {@link String}. + * @return the index of a new or already existing constant item with the + * given value. + */ + public int newConst(final Object cst) { + return newConstItem(cst).index; + } + + /** + * Adds an UTF8 string to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param value + * the String value. + * @return the index of a new or already existing UTF8 item. + */ + public int newUTF8(final String value) { + key.set(UTF8, value, null, null); + Item result = get(key); + if (result == null) { + pool.putByte(UTF8).putUTF8(value); + result = new Item(index++, key); + put(result); + } + return result.index; + } + + /** + * Adds a string reference, a class reference, a method type, a module + * or a package to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param type + * a type among STR, CLASS, MTYPE, MODULE or PACKAGE + * @param value + * string value of the reference. + * @return a new or already existing reference item. + */ + Item newStringishItem(final int type, final String value) { + key2.set(type, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(type, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param value + * the internal name of the class. + * @return the index of a new or already existing class reference item. + */ + public int newClass(final String value) { + return newStringishItem(CLASS, value).index; + } + + /** + * Adds a method type reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param methodDesc + * method descriptor of the method type. + * @return the index of a new or already existing method type reference + * item. + */ + public int newMethodType(final String methodDesc) { + return newStringishItem(MTYPE, methodDesc).index; + } + + /** + * Adds a module reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param moduleName + * name of the module. + * @return the index of a new or already existing module reference + * item. + */ + public int newModule(final String moduleName) { + return newStringishItem(MODULE, moduleName).index; + } + + /** + * Adds a package reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param packageName + * name of the package in its internal form. + * @return the index of a new or already existing module reference + * item. + */ + public int newPackage(final String packageName) { + return newStringishItem(PACKAGE, packageName).index; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param tag + * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, + * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, + * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the field or method owner class. + * @param name + * the name of the field or method. + * @param desc + * the descriptor of the field or method. + * @param itf + * true if the owner is an interface. + * @return a new or an already existing method type reference item. + */ + Item newHandleItem(final int tag, final String owner, final String name, + final String desc, final boolean itf) { + key4.set(HANDLE_BASE + tag, owner, name, desc); + Item result = get(key4); + if (result == null) { + if (tag <= Opcodes.H_PUTSTATIC) { + put112(HANDLE, tag, newField(owner, name, desc)); + } else { + put112(HANDLE, + tag, + newMethod(owner, name, desc, itf)); + } + result = new Item(index++, key4); + put(result); + } + return result; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param tag + * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, + * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, + * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the field or method owner class. + * @param name + * the name of the field or method. + * @param desc + * the descriptor of the field or method. + * @return the index of a new or already existing method type reference + * item. + * + * @deprecated this method is superseded by + * {@link #newHandle(int, String, String, String, boolean)}. + */ + @Deprecated + public int newHandle(final int tag, final String owner, final String name, + final String desc) { + return newHandle(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE); + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param tag + * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, + * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, + * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the field or method owner class. + * @param name + * the name of the field or method. + * @param desc + * the descriptor of the field or method. + * @param itf + * true if the owner is an interface. + * @return the index of a new or already existing method type reference + * item. + */ + public int newHandle(final int tag, final String owner, final String name, + final String desc, final boolean itf) { + return newHandleItem(tag, owner, name, desc, itf).index; + } + + /** + * Adds an invokedynamic reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param name + * name of the invoked method. + * @param desc + * descriptor of the invoke method. + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. + * + * @return a new or an already existing invokedynamic type reference item. + */ + Item newInvokeDynamicItem(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + // cache for performance + ByteVector bootstrapMethods = this.bootstrapMethods; + if (bootstrapMethods == null) { + bootstrapMethods = this.bootstrapMethods = new ByteVector(); + } + + int position = bootstrapMethods.length; // record current position + + int hashCode = bsm.hashCode(); + bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name, + bsm.desc, bsm.isInterface())); + + int argsLength = bsmArgs.length; + bootstrapMethods.putShort(argsLength); + + for (int i = 0; i < argsLength; i++) { + Object bsmArg = bsmArgs[i]; + hashCode ^= bsmArg.hashCode(); + bootstrapMethods.putShort(newConst(bsmArg)); + } + + byte[] data = bootstrapMethods.data; + int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments) + hashCode &= 0x7FFFFFFF; + Item result = items[hashCode % items.length]; + loop: while (result != null) { + if (result.type != BSM || result.hashCode != hashCode) { + result = result.next; + continue; + } + + // because the data encode the size of the argument + // we don't need to test if these size are equals + int resultPosition = result.intVal; + for (int p = 0; p < length; p++) { + if (data[position + p] != data[resultPosition + p]) { + result = result.next; + continue loop; + } + } + break; + } + + int bootstrapMethodIndex; + if (result != null) { + bootstrapMethodIndex = result.index; + bootstrapMethods.length = position; // revert to old position + } else { + bootstrapMethodIndex = bootstrapMethodsCount++; + result = new Item(bootstrapMethodIndex); + result.set(position, hashCode); + put(result); + } + + // now, create the InvokeDynamic constant + key3.set(name, desc, bootstrapMethodIndex); + result = get(key3); + if (result == null) { + put122(INDY, bootstrapMethodIndex, newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds an invokedynamic reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param name + * name of the invoked method. + * @param desc + * descriptor of the invoke method. + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. + * + * @return the index of a new or already existing invokedynamic reference + * item. + */ + public int newInvokeDynamic(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + return newInvokeDynamicItem(name, desc, bsm, bsmArgs).index; + } + + /** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner + * the internal name of the field's owner class. + * @param name + * the field's name. + * @param desc + * the field's descriptor. + * @return a new or already existing field reference item. + */ + Item newFieldItem(final String owner, final String name, final String desc) { + key3.set(FIELD, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(FIELD, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param owner + * the internal name of the field's owner class. + * @param name + * the field's name. + * @param desc + * the field's descriptor. + * @return the index of a new or already existing field reference item. + */ + public int newField(final String owner, final String name, final String desc) { + return newFieldItem(owner, name, desc).index; + } + + /** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner + * the internal name of the method's owner class. + * @param name + * the method's name. + * @param desc + * the method's descriptor. + * @param itf + * true if owner is an interface. + * @return a new or already existing method reference item. + */ + Item newMethodItem(final String owner, final String name, + final String desc, final boolean itf) { + int type = itf ? IMETH : METH; + key3.set(type, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(type, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param owner + * the internal name of the method's owner class. + * @param name + * the method's name. + * @param desc + * the method's descriptor. + * @param itf + * true if owner is an interface. + * @return the index of a new or already existing method reference item. + */ + public int newMethod(final String owner, final String name, + final String desc, final boolean itf) { + return newMethodItem(owner, name, desc, itf).index; + } + + /** + * Adds an integer to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param value + * the int value. + * @return a new or already existing int item. + */ + Item newInteger(final int value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(INT).putInt(value); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a float to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the float value. + * @return a new or already existing float item. + */ + Item newFloat(final float value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(FLOAT).putInt(key.intVal); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a long to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the long value. + * @return a new or already existing long item. + */ + Item newLong(final long value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(LONG).putLong(value); + result = new Item(index, key); + index += 2; + put(result); + } + return result; + } + + /** + * Adds a double to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the double value. + * @return a new or already existing double item. + */ + Item newDouble(final double value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(DOUBLE).putLong(key.longVal); + result = new Item(index, key); + index += 2; + put(result); + } + return result; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param name + * a name. + * @param desc + * a type descriptor. + * @return the index of a new or already existing name and type item. + */ + public int newNameType(final String name, final String desc) { + return newNameTypeItem(name, desc).index; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param name + * a name. + * @param desc + * a type descriptor. + * @return a new or already existing name and type item. + */ + Item newNameTypeItem(final String name, final String desc) { + key2.set(NAME_TYPE, name, desc, null); + Item result = get(key2); + if (result == null) { + put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds the given internal name to {@link #typeTable} and returns its index. + * Does nothing if the type table already contains this internal name. + * + * @param type + * the internal name to be added to the type table. + * @return the index of this internal name in the type table. + */ + int addType(final String type) { + key.set(TYPE_NORMAL, type, null, null); + Item result = get(key); + if (result == null) { + result = addType(key); + } + return result.index; + } + + /** + * Adds the given "uninitialized" type to {@link #typeTable} and returns its + * index. This method is used for UNINITIALIZED types, made of an internal + * name and a bytecode offset. + * + * @param type + * the internal name to be added to the type table. + * @param offset + * the bytecode offset of the NEW instruction that created this + * UNINITIALIZED type value. + * @return the index of this internal name in the type table. + */ + int addUninitializedType(final String type, final int offset) { + key.type = TYPE_UNINIT; + key.intVal = offset; + key.strVal1 = type; + key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); + Item result = get(key); + if (result == null) { + result = addType(key); + } + return result.index; + } + + /** + * Adds the given Item to {@link #typeTable}. + * + * @param item + * the value to be added to the type table. + * @return the added Item, which a new Item instance with the same value as + * the given Item. + */ + private Item addType(final Item item) { + ++typeCount; + Item result = new Item(typeCount, key); + put(result); + if (typeTable == null) { + typeTable = new Item[16]; + } + if (typeCount == typeTable.length) { + Item[] newTable = new Item[2 * typeTable.length]; + System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); + typeTable = newTable; + } + typeTable[typeCount] = result; + return result; + } + + /** + * Returns the index of the common super type of the two given types. This + * method calls {@link #getCommonSuperClass} and caches the result in the + * {@link #items} hash table to speedup future calls with the same + * parameters. + * + * @param type1 + * index of an internal name in {@link #typeTable}. + * @param type2 + * index of an internal name in {@link #typeTable}. + * @return the index of the common super type of the two given types. + */ + int getMergedType(final int type1, final int type2) { + key2.type = TYPE_MERGED; + key2.longVal = type1 | (((long) type2) << 32); + key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); + Item result = get(key2); + if (result == null) { + String t = typeTable[type1].strVal1; + String u = typeTable[type2].strVal1; + key2.intVal = addType(getCommonSuperClass(t, u)); + result = new Item((short) 0, key2); + put(result); + } + return result.intVal; + } + + /** + * Returns the common super type of the two given types. The default + * implementation of this method loads the two given classes and uses + * the java.lang.Class methods to find the common super class. It can be + * overridden to compute this common super type in other ways, in particular + * without actually loading any class, or to take into account the class + * that is currently being generated by this ClassWriter, which can of + * course not be loaded since it is under construction. + * + * @param type1 + * the internal name of a class. + * @param type2 + * the internal name of another class. + * @return the internal name of the common super class of the two given + * classes. + */ + protected String getCommonSuperClass(final String type1, final String type2) { + Class c, d; + ClassLoader classLoader = getClass().getClassLoader(); + try { + c = Class.forName(type1.replace('/', '.'), false, classLoader); + d = Class.forName(type2.replace('/', '.'), false, classLoader); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + if (c.isAssignableFrom(d)) { + return type1; + } + if (d.isAssignableFrom(c)) { + return type2; + } + if (c.isInterface() || d.isInterface()) { + return "java/lang/Object"; + } else { + do { + c = c.getSuperclass(); + } while (!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } + } + + /** + * Returns the constant pool's hash table item which is equal to the given + * item. + * + * @param key + * a constant pool item. + * @return the constant pool's hash table item which is equal to the given + * item, or null if there is no such item. + */ + private Item get(final Item key) { + Item i = items[key.hashCode % items.length]; + while (i != null && (i.type != key.type || !key.isEqualTo(i))) { + i = i.next; + } + return i; + } + + /** + * Puts the given item in the constant pool's hash table. The hash table + * must not already contains this item. + * + * @param i + * the item to be added to the constant pool's hash table. + */ + private void put(final Item i) { + if (index + typeCount > threshold) { + int ll = items.length; + int nl = ll * 2 + 1; + Item[] newItems = new Item[nl]; + for (int l = ll - 1; l >= 0; --l) { + Item j = items[l]; + while (j != null) { + int index = j.hashCode % newItems.length; + Item k = j.next; + j.next = newItems[index]; + newItems[index] = j; + j = k; + } + } + items = newItems; + threshold = (int) (nl * 0.75); + } + int index = i.hashCode % items.length; + i.next = items[index]; + items[index] = i; + } + + /** + * Puts one byte and two shorts into the constant pool. + * + * @param b + * a byte. + * @param s1 + * a short. + * @param s2 + * another short. + */ + private void put122(final int b, final int s1, final int s2) { + pool.put12(b, s1).putShort(s2); + } + + /** + * Puts two bytes and one short into the constant pool. + * + * @param b1 + * a byte. + * @param b2 + * another byte. + * @param s + * a short. + */ + private void put112(final int b1, final int b2, final int s) { + pool.put11(b1, b2).putShort(s); + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Context.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Context.java new file mode 100644 index 000000000..1fe4d979a --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Context.java @@ -0,0 +1,145 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.fr.third.org.objectweb.asm; + +/** + * Information about a class being parsed in a {@link ClassReader}. + * + * @author Eric Bruneton + */ +class Context { + + /** + * Prototypes of the attributes that must be parsed for this class. + */ + Attribute[] attrs; + + /** + * The {@link ClassReader} option flags for the parsing of this class. + */ + int flags; + + /** + * The buffer used to read strings. + */ + char[] buffer; + + /** + * The start index of each bootstrap method. + */ + int[] bootstrapMethods; + + /** + * The access flags of the method currently being parsed. + */ + int access; + + /** + * The name of the method currently being parsed. + */ + String name; + + /** + * The descriptor of the method currently being parsed. + */ + String desc; + + /** + * The label objects, indexed by bytecode offset, of the method currently + * being parsed (only bytecode offsets for which a label is needed have a + * non null associated Label object). + */ + Label[] labels; + + /** + * The target of the type annotation currently being parsed. + */ + int typeRef; + + /** + * The path of the type annotation currently being parsed. + */ + TypePath typePath; + + /** + * The offset of the latest stack map frame that has been parsed. + */ + int offset; + + /** + * The labels corresponding to the start of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] start; + + /** + * The labels corresponding to the end of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] end; + + /** + * The local variable indices for each local variable range in the local + * variable type annotation currently being parsed. + */ + int[] index; + + /** + * The encoding of the latest stack map frame that has been parsed. + */ + int mode; + + /** + * The number of locals in the latest stack map frame that has been parsed. + */ + int localCount; + + /** + * The number locals in the latest stack map frame that has been parsed, + * minus the number of locals in the previous frame. + */ + int localDiff; + + /** + * The local values of the latest stack map frame that has been parsed. + */ + Object[] local; + + /** + * The stack size of the latest stack map frame that has been parsed. + */ + int stackCount; + + /** + * The stack values of the latest stack map frame that has been parsed. + */ + Object[] stack; +} \ No newline at end of file diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/CurrentFrame.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/CurrentFrame.java new file mode 100644 index 000000000..d4283db80 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/CurrentFrame.java @@ -0,0 +1,56 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.fr.third.org.objectweb.asm; + +/** + * Information about the input stack map frame at the "current" instruction of a + * method. This is implemented as a Frame subclass for a "basic block" + * containing only one instruction. + * + * @author Eric Bruneton + */ +class CurrentFrame extends Frame { + + /** + * Sets this CurrentFrame to the input stack map frame of the next "current" + * instruction, i.e. the instruction just after the given one. It is assumed + * that the value of this object when this method is called is the stack map + * frame status just before the given instruction is executed. + */ + @Override + void execute(int opcode, int arg, ClassWriter cw, Item item) { + super.execute(opcode, arg, cw, item); + Frame successor = new Frame(); + merge(cw, successor, 0); + set(successor); + owner.inputStackTop = 0; + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Edge.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Edge.java new file mode 100644 index 000000000..f68824fdc --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Edge.java @@ -0,0 +1,75 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * An edge in the control flow graph of a method body. See {@link Label Label}. + * + * @author Eric Bruneton + */ +class Edge { + + /** + * Denotes a normal control flow graph edge. + */ + static final int NORMAL = 0; + + /** + * Denotes a control flow graph edge corresponding to an exception handler. + * More precisely any {@link Edge} whose {@link #info} is strictly positive + * corresponds to an exception handler. The actual value of {@link #info} is + * the index, in the {@link ClassWriter} type table, of the exception that + * is catched. + */ + static final int EXCEPTION = 0x7FFFFFFF; + + /** + * Information about this control flow graph edge. If + * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) + * stack size in the basic block from which this edge originates. This size + * is equal to the stack size at the "jump" instruction to which this edge + * corresponds, relatively to the stack size at the beginning of the + * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, + * this field is the kind of this control flow graph edge (i.e. NORMAL or + * EXCEPTION). + */ + int info; + + /** + * The successor block of the basic block from which this edge originates. + */ + Label successor; + + /** + * The next edge in the list of successors of the originating basic block. + * See {@link Label#successors successors}. + */ + Edge next; +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/FieldVisitor.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/FieldVisitor.java new file mode 100644 index 000000000..a9011b22b --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/FieldVisitor.java @@ -0,0 +1,150 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A visitor to visit a Java field. The methods of this class must be called in + * the following order: ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* visitEnd. + * + * @author Eric Bruneton + */ +public abstract class FieldVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + protected final int api; + + /** + * The field visitor to which this visitor must delegate method calls. May + * be null. + */ + protected FieldVisitor fv; + + /** + * Constructs a new {@link FieldVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + public FieldVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link FieldVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + * @param fv + * the field visitor to which this visitor must delegate method + * calls. May be null. + */ + public FieldVisitor(final int api, final FieldVisitor fv) { + if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { + throw new IllegalArgumentException(); + } + this.api = api; + this.fv = fv; + } + + /** + * Visits an annotation of the field. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (fv != null) { + return fv.visitAnnotation(desc, visible); + } + return null; + } + + /** + * Visits an annotation on the type of the field. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#FIELD FIELD}. See + * {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (fv != null) { + return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the field. + * + * @param attr + * an attribute. + */ + public void visitAttribute(Attribute attr) { + if (fv != null) { + fv.visitAttribute(attr); + } + } + + /** + * Visits the end of the field. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the field have been visited. + */ + public void visitEnd() { + if (fv != null) { + fv.visitEnd(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/FieldWriter.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/FieldWriter.java new file mode 100644 index 000000000..02e73eaf7 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/FieldWriter.java @@ -0,0 +1,323 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * An {@link FieldVisitor} that generates Java fields in bytecode form. + * + * @author Eric Bruneton + */ +final class FieldWriter extends FieldVisitor { + + /** + * The class writer to which this field must be added. + */ + private final ClassWriter cw; + + /** + * Access flags of this field. + */ + private final int access; + + /** + * The index of the constant pool item that contains the name of this + * method. + */ + private final int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * field. + */ + private final int desc; + + /** + * The index of the constant pool item that contains the signature of this + * field. + */ + private int signature; + + /** + * The index of the constant pool item that contains the constant value of + * this field. + */ + private int value; + + /** + * The runtime visible annotations of this field. May be null. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this field. May be null. + */ + private AnnotationWriter ianns; + + /** + * The runtime visible type annotations of this field. May be null. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this field. May be + * null. + */ + private AnnotationWriter itanns; + + /** + * The non standard attributes of this field. May be null. + */ + private Attribute attrs; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link FieldWriter}. + * + * @param cw + * the class writer to which this field must be added. + * @param access + * the field's access flags (see {@link Opcodes}). + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link Type}). + * @param signature + * the field's signature. May be null. + * @param value + * the field's constant value. May be null. + */ + FieldWriter(final ClassWriter cw, final int access, final String name, + final String desc, final String signature, final Object value) { + super(Opcodes.ASM6); + if (cw.firstField == null) { + cw.firstField = this; + } else { + cw.lastField.fv = this; + } + cw.lastField = this; + this.cw = cw; + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + if (signature != null) { + this.signature = cw.newUTF8(signature); + } + if (value != null) { + this.value = cw.newConstItem(value).index; + } + } + + // ------------------------------------------------------------------------ + // Implementation of the FieldVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public AnnotationVisitor visitAnnotation(final String desc, + final boolean visible) { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + + @Override + public void visitAttribute(final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + @Override + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the size of this field. + * + * @return the size of this field. + */ + int getSize() { + int size = 8; + if (value != 0) { + cw.newUTF8("ConstantValue"); + size += 8; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + cw.newUTF8("Synthetic"); + size += 6; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (signature != 0) { + cw.newUTF8("Signature"); + size += 8; + } + if (anns != null) { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if (ianns != null) { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if (tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the content of this field into the given byte vector. + * + * @param out + * where the content of this field must be put. + */ + void put(final ByteVector out) { + final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; + int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE + | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); + out.putShort(access & ~mask).putShort(name).putShort(desc); + int attributeCount = 0; + if (value != 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + ++attributeCount; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (signature != 0) { + ++attributeCount; + } + if (anns != null) { + ++attributeCount; + } + if (ianns != null) { + ++attributeCount; + } + if (tanns != null) { + ++attributeCount; + } + if (itanns != null) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (value != 0) { + out.putShort(cw.newUTF8("ConstantValue")); + out.putInt(2).putShort(value); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (signature != 0) { + out.putShort(cw.newUTF8("Signature")); + out.putInt(2).putShort(signature); + } + if (anns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ianns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Frame.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Frame.java new file mode 100644 index 000000000..31f3d4fdf --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Frame.java @@ -0,0 +1,1566 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * Information about the input and output stack map frames of a basic block. + * + * @author Eric Bruneton + */ +class Frame { + + /* + * Frames are computed in a two steps process: during the visit of each + * instruction, the state of the frame at the end of current basic block is + * updated by simulating the action of the instruction on the previous state + * of this so called "output frame". In visitMaxs, a fix point algorithm is + * used to compute the "input frame" of each basic block, i.e. the stack map + * frame at the beginning of the basic block, starting from the input frame + * of the first basic block (which is computed from the method descriptor), + * and by using the previously computed output frames to compute the input + * state of the other blocks. + * + * All output and input frames are stored as arrays of integers. Reference + * and array types are represented by an index into a type table (which is + * not the same as the constant pool of the class, in order to avoid adding + * unnecessary constants in the pool - not all computed frames will end up + * being stored in the stack map table). This allows very fast type + * comparisons. + * + * Output stack map frames are computed relatively to the input frame of the + * basic block, which is not yet known when output frames are computed. It + * is therefore necessary to be able to represent abstract types such as + * "the type at position x in the input frame locals" or "the type at + * position x from the top of the input frame stack" or even "the type at + * position x in the input frame, with y more (or less) array dimensions". + * This explains the rather complicated type format used in output frames. + * + * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a + * signed number of array dimensions (from -8 to 7). KIND is either BASE, + * LOCAL or STACK. BASE is used for types that are not relative to the input + * frame. LOCAL is used for types that are relative to the input local + * variable types. STACK is used for types that are relative to the input + * stack types. VALUE depends on KIND. For LOCAL types, it is an index in + * the input local variable types. For STACK types, it is a position + * relatively to the top of input frame stack. For BASE types, it is either + * one of the constants defined below, or for OBJECT and UNINITIALIZED + * types, a tag and an index in the type table. + * + * Output frames can contain types of any kind and with a positive or + * negative dimension (and even unassigned types, represented by 0 - which + * does not correspond to any valid type value). Input frames can only + * contain BASE types of positive or null dimension. In all cases the type + * table contains only internal type names (array type descriptors are + * forbidden - dimensions must be represented through the DIM field). + * + * The LONG and DOUBLE types are always represented by using two slots (LONG + * + TOP or DOUBLE + TOP), for local variable types as well as in the + * operand stack. This is necessary to be able to simulate DUPx_y + * instructions, whose effect would be dependent on the actual type values + * if types were always represented by a single slot in the stack (and this + * is not possible, since actual type values are not always known - cf LOCAL + * and STACK type kinds). + */ + + /** + * Mask to get the dimension of a frame type. This dimension is a signed + * integer between -8 and 7. + */ + static final int DIM = 0xF0000000; + + /** + * Constant to be added to a type to get a type with one more dimension. + */ + static final int ARRAY_OF = 0x10000000; + + /** + * Constant to be added to a type to get a type with one less dimension. + */ + static final int ELEMENT_OF = 0xF0000000; + + /** + * Mask to get the kind of a frame type. + * + * @see #BASE + * @see #LOCAL + * @see #STACK + */ + static final int KIND = 0xF000000; + + /** + * Flag used for LOCAL and STACK types. Indicates that if this type happens + * to be a long or double type (during the computations of input frames), + * then it must be set to TOP because the second word of this value has been + * reused to store other data in the basic block. Hence the first word no + * longer stores a valid long or double value. + */ + static final int TOP_IF_LONG_OR_DOUBLE = 0x800000; + + /** + * Mask to get the value of a frame type. + */ + static final int VALUE = 0x7FFFFF; + + /** + * Mask to get the kind of base types. + */ + static final int BASE_KIND = 0xFF00000; + + /** + * Mask to get the value of base types. + */ + static final int BASE_VALUE = 0xFFFFF; + + /** + * Kind of the types that are not relative to an input stack map frame. + */ + static final int BASE = 0x1000000; + + /** + * Base kind of the base reference types. The BASE_VALUE of such types is an + * index into the type table. + */ + static final int OBJECT = BASE | 0x700000; + + /** + * Base kind of the uninitialized base types. The BASE_VALUE of such types + * in an index into the type table (the Item at that index contains both an + * instruction offset and an internal class name). + */ + static final int UNINITIALIZED = BASE | 0x800000; + + /** + * Kind of the types that are relative to the local variable types of an + * input stack map frame. The value of such types is a local variable index. + */ + private static final int LOCAL = 0x2000000; + + /** + * Kind of the the types that are relative to the stack of an input stack + * map frame. The value of such types is a position relatively to the top of + * this stack. + */ + private static final int STACK = 0x3000000; + + /** + * The TOP type. This is a BASE type. + */ + static final int TOP = BASE | 0; + + /** + * The BOOLEAN type. This is a BASE type mainly used for array types. + */ + static final int BOOLEAN = BASE | 9; + + /** + * The BYTE type. This is a BASE type mainly used for array types. + */ + static final int BYTE = BASE | 10; + + /** + * The CHAR type. This is a BASE type mainly used for array types. + */ + static final int CHAR = BASE | 11; + + /** + * The SHORT type. This is a BASE type mainly used for array types. + */ + static final int SHORT = BASE | 12; + + /** + * The INTEGER type. This is a BASE type. + */ + static final int INTEGER = BASE | 1; + + /** + * The FLOAT type. This is a BASE type. + */ + static final int FLOAT = BASE | 2; + + /** + * The DOUBLE type. This is a BASE type. + */ + static final int DOUBLE = BASE | 3; + + /** + * The LONG type. This is a BASE type. + */ + static final int LONG = BASE | 4; + + /** + * The NULL type. This is a BASE type. + */ + static final int NULL = BASE | 5; + + /** + * The UNINITIALIZED_THIS type. This is a BASE type. + */ + static final int UNINITIALIZED_THIS = BASE | 6; + + /** + * The stack size variation corresponding to each JVM instruction. This + * stack variation is equal to the size of the values produced by an + * instruction, minus the size of the values consumed by this instruction. + */ + static final int[] SIZE; + + /** + * Computes the stack size variation corresponding to each JVM instruction. + */ + static { + int i; + int[] b = new int[202]; + String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" + + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" + + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" + + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; + for (i = 0; i < b.length; ++i) { + b[i] = s.charAt(i) - 'E'; + } + SIZE = b; + + // code to generate the above string + // + // int NA = 0; // not applicable (unused opcode or variable size opcode) + // + // b = new int[] { + // 0, //NOP, // visitInsn + // 1, //ACONST_NULL, // - + // 1, //ICONST_M1, // - + // 1, //ICONST_0, // - + // 1, //ICONST_1, // - + // 1, //ICONST_2, // - + // 1, //ICONST_3, // - + // 1, //ICONST_4, // - + // 1, //ICONST_5, // - + // 2, //LCONST_0, // - + // 2, //LCONST_1, // - + // 1, //FCONST_0, // - + // 1, //FCONST_1, // - + // 1, //FCONST_2, // - + // 2, //DCONST_0, // - + // 2, //DCONST_1, // - + // 1, //BIPUSH, // visitIntInsn + // 1, //SIPUSH, // - + // 1, //LDC, // visitLdcInsn + // NA, //LDC_W, // - + // NA, //LDC2_W, // - + // 1, //ILOAD, // visitVarInsn + // 2, //LLOAD, // - + // 1, //FLOAD, // - + // 2, //DLOAD, // - + // 1, //ALOAD, // - + // NA, //ILOAD_0, // - + // NA, //ILOAD_1, // - + // NA, //ILOAD_2, // - + // NA, //ILOAD_3, // - + // NA, //LLOAD_0, // - + // NA, //LLOAD_1, // - + // NA, //LLOAD_2, // - + // NA, //LLOAD_3, // - + // NA, //FLOAD_0, // - + // NA, //FLOAD_1, // - + // NA, //FLOAD_2, // - + // NA, //FLOAD_3, // - + // NA, //DLOAD_0, // - + // NA, //DLOAD_1, // - + // NA, //DLOAD_2, // - + // NA, //DLOAD_3, // - + // NA, //ALOAD_0, // - + // NA, //ALOAD_1, // - + // NA, //ALOAD_2, // - + // NA, //ALOAD_3, // - + // -1, //IALOAD, // visitInsn + // 0, //LALOAD, // - + // -1, //FALOAD, // - + // 0, //DALOAD, // - + // -1, //AALOAD, // - + // -1, //BALOAD, // - + // -1, //CALOAD, // - + // -1, //SALOAD, // - + // -1, //ISTORE, // visitVarInsn + // -2, //LSTORE, // - + // -1, //FSTORE, // - + // -2, //DSTORE, // - + // -1, //ASTORE, // - + // NA, //ISTORE_0, // - + // NA, //ISTORE_1, // - + // NA, //ISTORE_2, // - + // NA, //ISTORE_3, // - + // NA, //LSTORE_0, // - + // NA, //LSTORE_1, // - + // NA, //LSTORE_2, // - + // NA, //LSTORE_3, // - + // NA, //FSTORE_0, // - + // NA, //FSTORE_1, // - + // NA, //FSTORE_2, // - + // NA, //FSTORE_3, // - + // NA, //DSTORE_0, // - + // NA, //DSTORE_1, // - + // NA, //DSTORE_2, // - + // NA, //DSTORE_3, // - + // NA, //ASTORE_0, // - + // NA, //ASTORE_1, // - + // NA, //ASTORE_2, // - + // NA, //ASTORE_3, // - + // -3, //IASTORE, // visitInsn + // -4, //LASTORE, // - + // -3, //FASTORE, // - + // -4, //DASTORE, // - + // -3, //AASTORE, // - + // -3, //BASTORE, // - + // -3, //CASTORE, // - + // -3, //SASTORE, // - + // -1, //POP, // - + // -2, //POP2, // - + // 1, //DUP, // - + // 1, //DUP_X1, // - + // 1, //DUP_X2, // - + // 2, //DUP2, // - + // 2, //DUP2_X1, // - + // 2, //DUP2_X2, // - + // 0, //SWAP, // - + // -1, //IADD, // - + // -2, //LADD, // - + // -1, //FADD, // - + // -2, //DADD, // - + // -1, //ISUB, // - + // -2, //LSUB, // - + // -1, //FSUB, // - + // -2, //DSUB, // - + // -1, //IMUL, // - + // -2, //LMUL, // - + // -1, //FMUL, // - + // -2, //DMUL, // - + // -1, //IDIV, // - + // -2, //LDIV, // - + // -1, //FDIV, // - + // -2, //DDIV, // - + // -1, //IREM, // - + // -2, //LREM, // - + // -1, //FREM, // - + // -2, //DREM, // - + // 0, //INEG, // - + // 0, //LNEG, // - + // 0, //FNEG, // - + // 0, //DNEG, // - + // -1, //ISHL, // - + // -1, //LSHL, // - + // -1, //ISHR, // - + // -1, //LSHR, // - + // -1, //IUSHR, // - + // -1, //LUSHR, // - + // -1, //IAND, // - + // -2, //LAND, // - + // -1, //IOR, // - + // -2, //LOR, // - + // -1, //IXOR, // - + // -2, //LXOR, // - + // 0, //IINC, // visitIincInsn + // 1, //I2L, // visitInsn + // 0, //I2F, // - + // 1, //I2D, // - + // -1, //L2I, // - + // -1, //L2F, // - + // 0, //L2D, // - + // 0, //F2I, // - + // 1, //F2L, // - + // 1, //F2D, // - + // -1, //D2I, // - + // 0, //D2L, // - + // -1, //D2F, // - + // 0, //I2B, // - + // 0, //I2C, // - + // 0, //I2S, // - + // -3, //LCMP, // - + // -1, //FCMPL, // - + // -1, //FCMPG, // - + // -3, //DCMPL, // - + // -3, //DCMPG, // - + // -1, //IFEQ, // visitJumpInsn + // -1, //IFNE, // - + // -1, //IFLT, // - + // -1, //IFGE, // - + // -1, //IFGT, // - + // -1, //IFLE, // - + // -2, //IF_ICMPEQ, // - + // -2, //IF_ICMPNE, // - + // -2, //IF_ICMPLT, // - + // -2, //IF_ICMPGE, // - + // -2, //IF_ICMPGT, // - + // -2, //IF_ICMPLE, // - + // -2, //IF_ACMPEQ, // - + // -2, //IF_ACMPNE, // - + // 0, //GOTO, // - + // 1, //JSR, // - + // 0, //RET, // visitVarInsn + // -1, //TABLESWITCH, // visiTableSwitchInsn + // -1, //LOOKUPSWITCH, // visitLookupSwitch + // -1, //IRETURN, // visitInsn + // -2, //LRETURN, // - + // -1, //FRETURN, // - + // -2, //DRETURN, // - + // -1, //ARETURN, // - + // 0, //RETURN, // - + // NA, //GETSTATIC, // visitFieldInsn + // NA, //PUTSTATIC, // - + // NA, //GETFIELD, // - + // NA, //PUTFIELD, // - + // NA, //INVOKEVIRTUAL, // visitMethodInsn + // NA, //INVOKESPECIAL, // - + // NA, //INVOKESTATIC, // - + // NA, //INVOKEINTERFACE, // - + // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn + // 1, //NEW, // visitTypeInsn + // 0, //NEWARRAY, // visitIntInsn + // 0, //ANEWARRAY, // visitTypeInsn + // 0, //ARRAYLENGTH, // visitInsn + // NA, //ATHROW, // - + // 0, //CHECKCAST, // visitTypeInsn + // 0, //INSTANCEOF, // - + // -1, //MONITORENTER, // visitInsn + // -1, //MONITOREXIT, // - + // NA, //WIDE, // NOT VISITED + // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn + // -1, //IFNULL, // visitJumpInsn + // -1, //IFNONNULL, // - + // NA, //GOTO_W, // - + // NA, //JSR_W, // - + // }; + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('E' + b[i])); + // } + // System.err.println(); + } + + /** + * The label (i.e. basic block) to which these input and output stack map + * frames correspond. + */ + Label owner; + + /** + * The input stack map frame locals. + */ + int[] inputLocals; + + /** + * The input stack map frame stack. + */ + int[] inputStack; + + /** + * The output stack map frame locals. + */ + private int[] outputLocals; + + /** + * The output stack map frame stack. + */ + private int[] outputStack; + + /** + * Relative size of the output stack. The exact semantics of this field + * depends on the algorithm that is used. + * + * When only the maximum stack size is computed, this field is the size of + * the output stack relatively to the top of the input stack. + * + * When the stack map frames are completely computed, this field is the + * actual number of types in {@link #outputStack}. + */ + int outputStackTop; + + /** + * Number of types that are initialized in the basic block. + * + * @see #initializations + */ + private int initializationCount; + + /** + * The types that are initialized in the basic block. A constructor + * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace + * every occurence of this type in the local variables and in the + * operand stack. This cannot be done during the first phase of the + * algorithm since, during this phase, the local variables and the operand + * stack are not completely computed. It is therefore necessary to store the + * types on which constructors are invoked in the basic block, in order to + * do this replacement during the second phase of the algorithm, where the + * frames are fully computed. Note that this array can contain types that + * are relative to input locals or to the input stack (see below for the + * description of the algorithm). + */ + private int[] initializations; + + /** + * Sets this frame to the given value. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param nLocal + * the number of local variables. + * @param local + * the local variable types. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, + * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are + * represented by a single element). Reference types are + * represented by String objects (representing internal names), + * and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized + * value). + * @param nStack + * the number of operand stack elements. + * @param stack + * the operand stack types (same format as the "local" array). + */ + final void set(ClassWriter cw, final int nLocal, final Object[] local, + final int nStack, final Object[] stack) { + int i = convert(cw, nLocal, local, inputLocals); + while (i < local.length) { + inputLocals[i++] = TOP; + } + int nStackTop = 0; + for (int j = 0; j < nStack; ++j) { + if (stack[j] == Opcodes.LONG || stack[j] == Opcodes.DOUBLE) { + ++nStackTop; + } + } + inputStack = new int[nStack + nStackTop]; + convert(cw, nStack, stack, inputStack); + outputStackTop = 0; + initializationCount = 0; + } + + /** + * Converts types from the MethodWriter.visitFrame() format to the Frame + * format. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param nInput + * the number of types to convert. + * @param input + * the types to convert. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, + * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are + * represented by a single element). Reference types are + * represented by String objects (representing internal names), + * and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized + * value). + * @param output + * where to store the converted types. + * @return the number of output elements. + */ + private static int convert(ClassWriter cw, int nInput, Object[] input, + int[] output) { + int i = 0; + for (int j = 0; j < nInput; ++j) { + if (input[j] instanceof Integer) { + output[i++] = BASE | ((Integer) input[j]).intValue(); + if (input[j] == Opcodes.LONG || input[j] == Opcodes.DOUBLE) { + output[i++] = TOP; + } + } else if (input[j] instanceof String) { + output[i++] = type(cw, Type.getObjectType((String) input[j]) + .getDescriptor()); + } else { + output[i++] = UNINITIALIZED + | cw.addUninitializedType("", + ((Label) input[j]).position); + } + } + return i; + } + + /** + * Sets this frame to the value of the given frame. WARNING: after this + * method is called the two frames share the same data structures. It is + * recommended to discard the given frame f to avoid unexpected side + * effects. + * + * @param f + * The new frame value. + */ + final void set(final Frame f) { + inputLocals = f.inputLocals; + inputStack = f.inputStack; + outputLocals = f.outputLocals; + outputStack = f.outputStack; + outputStackTop = f.outputStackTop; + initializationCount = f.initializationCount; + initializations = f.initializations; + } + + /** + * Returns the output frame local variable type at the given index. + * + * @param local + * the index of the local that must be returned. + * @return the output frame local variable type at the given index. + */ + private int get(final int local) { + if (outputLocals == null || local >= outputLocals.length) { + // this local has never been assigned in this basic block, + // so it is still equal to its value in the input frame + return LOCAL | local; + } else { + int type = outputLocals[local]; + if (type == 0) { + // this local has never been assigned in this basic block, + // so it is still equal to its value in the input frame + type = outputLocals[local] = LOCAL | local; + } + return type; + } + } + + /** + * Sets the output frame local variable type at the given index. + * + * @param local + * the index of the local that must be set. + * @param type + * the value of the local that must be set. + */ + private void set(final int local, final int type) { + // creates and/or resizes the output local variables array if necessary + if (outputLocals == null) { + outputLocals = new int[10]; + } + int n = outputLocals.length; + if (local >= n) { + int[] t = new int[Math.max(local + 1, 2 * n)]; + System.arraycopy(outputLocals, 0, t, 0, n); + outputLocals = t; + } + // sets the local variable + outputLocals[local] = type; + } + + /** + * Pushes a new type onto the output frame stack. + * + * @param type + * the type that must be pushed. + */ + private void push(final int type) { + // creates and/or resizes the output stack array if necessary + if (outputStack == null) { + outputStack = new int[10]; + } + int n = outputStack.length; + if (outputStackTop >= n) { + int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; + System.arraycopy(outputStack, 0, t, 0, n); + outputStack = t; + } + // pushes the type on the output stack + outputStack[outputStackTop++] = type; + // updates the maximum height reached by the output stack, if needed + int top = owner.inputStackTop + outputStackTop; + if (top > owner.outputStackMax) { + owner.outputStackMax = top; + } + } + + /** + * Pushes a new type onto the output frame stack. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param desc + * the descriptor of the type to be pushed. Can also be a method + * descriptor (in this case this method pushes its return type + * onto the output frame stack). + */ + private void push(final ClassWriter cw, final String desc) { + int type = type(cw, desc); + if (type != 0) { + push(type); + if (type == LONG || type == DOUBLE) { + push(TOP); + } + } + } + + /** + * Returns the int encoding of the given type. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param desc + * a type descriptor. + * @return the int encoding of the given type. + */ + static int type(final ClassWriter cw, final String desc) { + String t; + int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; + switch (desc.charAt(index)) { + case 'V': + return 0; + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + return INTEGER; + case 'F': + return FLOAT; + case 'J': + return LONG; + case 'D': + return DOUBLE; + case 'L': + // stores the internal name, not the descriptor! + t = desc.substring(index + 1, desc.length() - 1); + return OBJECT | cw.addType(t); + // case '[': + default: + // extracts the dimensions and the element type + int data; + int dims = index + 1; + while (desc.charAt(dims) == '[') { + ++dims; + } + switch (desc.charAt(dims)) { + case 'Z': + data = BOOLEAN; + break; + case 'C': + data = CHAR; + break; + case 'B': + data = BYTE; + break; + case 'S': + data = SHORT; + break; + case 'I': + data = INTEGER; + break; + case 'F': + data = FLOAT; + break; + case 'J': + data = LONG; + break; + case 'D': + data = DOUBLE; + break; + // case 'L': + default: + // stores the internal name, not the descriptor + t = desc.substring(dims + 1, desc.length() - 1); + data = OBJECT | cw.addType(t); + } + return (dims - index) << 28 | data; + } + } + + /** + * Pops a type from the output frame stack and returns its value. + * + * @return the type that has been popped from the output frame stack. + */ + private int pop() { + if (outputStackTop > 0) { + return outputStack[--outputStackTop]; + } else { + // if the output frame stack is empty, pops from the input stack + return STACK | -(--owner.inputStackTop); + } + } + + /** + * Pops the given number of types from the output frame stack. + * + * @param elements + * the number of types that must be popped. + */ + private void pop(final int elements) { + if (outputStackTop >= elements) { + outputStackTop -= elements; + } else { + // if the number of elements to be popped is greater than the number + // of elements in the output stack, clear it, and pops the remaining + // elements from the input stack. + owner.inputStackTop -= elements - outputStackTop; + outputStackTop = 0; + } + } + + /** + * Pops a type from the output frame stack. + * + * @param desc + * the descriptor of the type to be popped. Can also be a method + * descriptor (in this case this method pops the types + * corresponding to the method arguments). + */ + private void pop(final String desc) { + char c = desc.charAt(0); + if (c == '(') { + pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1); + } else if (c == 'J' || c == 'D') { + pop(2); + } else { + pop(1); + } + } + + /** + * Adds a new type to the list of types on which a constructor is invoked in + * the basic block. + * + * @param var + * a type on a which a constructor is invoked. + */ + private void init(final int var) { + // creates and/or resizes the initializations array if necessary + if (initializations == null) { + initializations = new int[2]; + } + int n = initializations.length; + if (initializationCount >= n) { + int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; + System.arraycopy(initializations, 0, t, 0, n); + initializations = t; + } + // stores the type to be initialized + initializations[initializationCount++] = var; + } + + /** + * Replaces the given type with the appropriate type if it is one of the + * types on which a constructor is invoked in the basic block. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param t + * a type + * @return t or, if t is one of the types on which a constructor is invoked + * in the basic block, the type corresponding to this constructor. + */ + private int init(final ClassWriter cw, final int t) { + int s; + if (t == UNINITIALIZED_THIS) { + s = OBJECT | cw.addType(cw.thisName); + } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) { + String type = cw.typeTable[t & BASE_VALUE].strVal1; + s = OBJECT | cw.addType(type); + } else { + return t; + } + for (int j = 0; j < initializationCount; ++j) { + int u = initializations[j]; + int dim = u & DIM; + int kind = u & KIND; + if (kind == LOCAL) { + u = dim + inputLocals[u & VALUE]; + } else if (kind == STACK) { + u = dim + inputStack[inputStack.length - (u & VALUE)]; + } + if (t == u) { + return s; + } + } + return t; + } + + /** + * Initializes the input frame of the first basic block from the method + * descriptor. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param access + * the access flags of the method to which this label belongs. + * @param args + * the formal parameter types of this method. + * @param maxLocals + * the maximum number of local variables of this method. + */ + final void initInputFrame(final ClassWriter cw, final int access, + final Type[] args, final int maxLocals) { + inputLocals = new int[maxLocals]; + inputStack = new int[0]; + int i = 0; + if ((access & Opcodes.ACC_STATIC) == 0) { + if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) { + inputLocals[i++] = OBJECT | cw.addType(cw.thisName); + } else { + inputLocals[i++] = UNINITIALIZED_THIS; + } + } + for (int j = 0; j < args.length; ++j) { + int t = type(cw, args[j].getDescriptor()); + inputLocals[i++] = t; + if (t == LONG || t == DOUBLE) { + inputLocals[i++] = TOP; + } + } + while (i < maxLocals) { + inputLocals[i++] = TOP; + } + } + + /** + * Simulates the action of the given instruction on the output stack frame. + * + * @param opcode + * the opcode of the instruction. + * @param arg + * the operand of the instruction, if any. + * @param cw + * the class writer to which this label belongs. + * @param item + * the operand of the instructions, if any. + */ + void execute(final int opcode, final int arg, final ClassWriter cw, + final Item item) { + int t1, t2, t3, t4; + switch (opcode) { + case Opcodes.NOP: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.GOTO: + case Opcodes.RETURN: + break; + case Opcodes.ACONST_NULL: + push(NULL); + break; + 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: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + case Opcodes.ILOAD: + push(INTEGER); + break; + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.LLOAD: + push(LONG); + push(TOP); + break; + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.FLOAD: + push(FLOAT); + break; + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.DLOAD: + push(DOUBLE); + push(TOP); + break; + case Opcodes.LDC: + switch (item.type) { + case ClassWriter.INT: + push(INTEGER); + break; + case ClassWriter.LONG: + push(LONG); + push(TOP); + break; + case ClassWriter.FLOAT: + push(FLOAT); + break; + case ClassWriter.DOUBLE: + push(DOUBLE); + push(TOP); + break; + case ClassWriter.CLASS: + push(OBJECT | cw.addType("java/lang/Class")); + break; + case ClassWriter.STR: + push(OBJECT | cw.addType("java/lang/String")); + break; + case ClassWriter.MTYPE: + push(OBJECT | cw.addType("java/lang/invoke/MethodType")); + break; + // case ClassWriter.HANDLE_BASE + [1..9]: + default: + push(OBJECT | cw.addType("java/lang/invoke/MethodHandle")); + } + break; + case Opcodes.ALOAD: + push(get(arg)); + break; + case Opcodes.IALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + pop(2); + push(INTEGER); + break; + case Opcodes.LALOAD: + case Opcodes.D2L: + pop(2); + push(LONG); + push(TOP); + break; + case Opcodes.FALOAD: + pop(2); + push(FLOAT); + break; + case Opcodes.DALOAD: + case Opcodes.L2D: + pop(2); + push(DOUBLE); + push(TOP); + break; + case Opcodes.AALOAD: + pop(1); + t1 = pop(); + push(t1 == NULL ? t1 : ELEMENT_OF + t1); + break; + case Opcodes.ISTORE: + case Opcodes.FSTORE: + case Opcodes.ASTORE: + t1 = pop(); + set(arg, t1); + if (arg > 0) { + t2 = get(arg - 1); + // if t2 is of kind STACK or LOCAL we cannot know its size! + if (t2 == LONG || t2 == DOUBLE) { + set(arg - 1, TOP); + } else if ((t2 & KIND) != BASE) { + set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); + } + } + break; + case Opcodes.LSTORE: + case Opcodes.DSTORE: + pop(1); + t1 = pop(); + set(arg, t1); + set(arg + 1, TOP); + if (arg > 0) { + t2 = get(arg - 1); + // if t2 is of kind STACK or LOCAL we cannot know its size! + if (t2 == LONG || t2 == DOUBLE) { + set(arg - 1, TOP); + } else if ((t2 & KIND) != BASE) { + set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); + } + } + break; + case Opcodes.IASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.FASTORE: + case Opcodes.AASTORE: + pop(3); + break; + case Opcodes.LASTORE: + case Opcodes.DASTORE: + pop(4); + 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: + pop(1); + 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: + pop(2); + break; + case Opcodes.DUP: + t1 = pop(); + push(t1); + push(t1); + break; + case Opcodes.DUP_X1: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2: + t1 = pop(); + t2 = pop(); + push(t2); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X1: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t2); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + t4 = pop(); + push(t2); + push(t1); + push(t4); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.SWAP: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + 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: + pop(2); + push(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: + pop(4); + push(LONG); + push(TOP); + break; + case Opcodes.FADD: + case Opcodes.FSUB: + case Opcodes.FMUL: + case Opcodes.FDIV: + case Opcodes.FREM: + case Opcodes.L2F: + case Opcodes.D2F: + pop(2); + push(FLOAT); + break; + case Opcodes.DADD: + case Opcodes.DSUB: + case Opcodes.DMUL: + case Opcodes.DDIV: + case Opcodes.DREM: + pop(4); + push(DOUBLE); + push(TOP); + break; + case Opcodes.LSHL: + case Opcodes.LSHR: + case Opcodes.LUSHR: + pop(3); + push(LONG); + push(TOP); + break; + case Opcodes.IINC: + set(arg, INTEGER); + break; + case Opcodes.I2L: + case Opcodes.F2L: + pop(1); + push(LONG); + push(TOP); + break; + case Opcodes.I2F: + pop(1); + push(FLOAT); + break; + case Opcodes.I2D: + case Opcodes.F2D: + pop(1); + push(DOUBLE); + push(TOP); + break; + case Opcodes.F2I: + case Opcodes.ARRAYLENGTH: + case Opcodes.INSTANCEOF: + pop(1); + push(INTEGER); + break; + case Opcodes.LCMP: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + pop(4); + push(INTEGER); + break; + case Opcodes.JSR: + case Opcodes.RET: + throw new RuntimeException( + "JSR/RET are not supported with computeFrames option"); + case Opcodes.GETSTATIC: + push(cw, item.strVal3); + break; + case Opcodes.PUTSTATIC: + pop(item.strVal3); + break; + case Opcodes.GETFIELD: + pop(1); + push(cw, item.strVal3); + break; + case Opcodes.PUTFIELD: + pop(item.strVal3); + pop(); + break; + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: + pop(item.strVal3); + if (opcode != Opcodes.INVOKESTATIC) { + t1 = pop(); + if (opcode == Opcodes.INVOKESPECIAL + && item.strVal2.charAt(0) == '<') { + init(t1); + } + } + push(cw, item.strVal3); + break; + case Opcodes.INVOKEDYNAMIC: + pop(item.strVal2); + push(cw, item.strVal2); + break; + case Opcodes.NEW: + push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); + break; + case Opcodes.NEWARRAY: + pop(); + switch (arg) { + case Opcodes.T_BOOLEAN: + push(ARRAY_OF | BOOLEAN); + break; + case Opcodes.T_CHAR: + push(ARRAY_OF | CHAR); + break; + case Opcodes.T_BYTE: + push(ARRAY_OF | BYTE); + break; + case Opcodes.T_SHORT: + push(ARRAY_OF | SHORT); + break; + case Opcodes.T_INT: + push(ARRAY_OF | INTEGER); + break; + case Opcodes.T_FLOAT: + push(ARRAY_OF | FLOAT); + break; + case Opcodes.T_DOUBLE: + push(ARRAY_OF | DOUBLE); + break; + // case Opcodes.T_LONG: + default: + push(ARRAY_OF | LONG); + break; + } + break; + case Opcodes.ANEWARRAY: + String s = item.strVal1; + pop(); + if (s.charAt(0) == '[') { + push(cw, '[' + s); + } else { + push(ARRAY_OF | OBJECT | cw.addType(s)); + } + break; + case Opcodes.CHECKCAST: + s = item.strVal1; + pop(); + if (s.charAt(0) == '[') { + push(cw, s); + } else { + push(OBJECT | cw.addType(s)); + } + break; + // case Opcodes.MULTIANEWARRAY: + default: + pop(arg); + push(cw, item.strVal1); + break; + } + } + + /** + * Merges the input frame of the given basic block with the input and output + * frames of this basic block. Returns true if the input frame of + * the given label has been changed by this operation. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param frame + * the basic block whose input frame must be updated. + * @param edge + * the kind of the {@link Edge} between this label and 'label'. + * See {@link Edge#info}. + * @return true if the input frame of the given label has been + * changed by this operation. + */ + final boolean merge(final ClassWriter cw, final Frame frame, final int edge) { + boolean changed = false; + int i, s, dim, kind, t; + + int nLocal = inputLocals.length; + int nStack = inputStack.length; + if (frame.inputLocals == null) { + frame.inputLocals = new int[nLocal]; + changed = true; + } + + for (i = 0; i < nLocal; ++i) { + if (outputLocals != null && i < outputLocals.length) { + s = outputLocals[i]; + if (s == 0) { + t = inputLocals[i]; + } else { + dim = s & DIM; + kind = s & KIND; + if (kind == BASE) { + t = s; + } else { + if (kind == LOCAL) { + t = dim + inputLocals[s & VALUE]; + } else { + t = dim + inputStack[nStack - (s & VALUE)]; + } + if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 + && (t == LONG || t == DOUBLE)) { + t = TOP; + } + } + } + } else { + t = inputLocals[i]; + } + if (initializations != null) { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputLocals, i); + } + + if (edge > 0) { + for (i = 0; i < nLocal; ++i) { + t = inputLocals[i]; + changed |= merge(cw, t, frame.inputLocals, i); + } + if (frame.inputStack == null) { + frame.inputStack = new int[1]; + changed = true; + } + changed |= merge(cw, edge, frame.inputStack, 0); + return changed; + } + + int nInputStack = inputStack.length + owner.inputStackTop; + if (frame.inputStack == null) { + frame.inputStack = new int[nInputStack + outputStackTop]; + changed = true; + } + + for (i = 0; i < nInputStack; ++i) { + t = inputStack[i]; + if (initializations != null) { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputStack, i); + } + for (i = 0; i < outputStackTop; ++i) { + s = outputStack[i]; + dim = s & DIM; + kind = s & KIND; + if (kind == BASE) { + t = s; + } else { + if (kind == LOCAL) { + t = dim + inputLocals[s & VALUE]; + } else { + t = dim + inputStack[nStack - (s & VALUE)]; + } + if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 + && (t == LONG || t == DOUBLE)) { + t = TOP; + } + } + if (initializations != null) { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputStack, nInputStack + i); + } + return changed; + } + + /** + * Merges the type at the given index in the given type array with the given + * type. Returns true if the type array has been modified by this + * operation. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param t + * the type with which the type array element must be merged. + * @param types + * an array of types. + * @param index + * the index of the type that must be merged in 'types'. + * @return true if the type array has been modified by this + * operation. + */ + private static boolean merge(final ClassWriter cw, int t, + final int[] types, final int index) { + int u = types[index]; + if (u == t) { + // if the types are equal, merge(u,t)=u, so there is no change + return false; + } + if ((t & ~DIM) == NULL) { + if (u == NULL) { + return false; + } + t = NULL; + } + if (u == 0) { + // if types[index] has never been assigned, merge(u,t)=t + types[index] = t; + return true; + } + int v; + if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) { + // if u is a reference type of any dimension + if (t == NULL) { + // if t is the NULL type, merge(u,t)=u, so there is no change + return false; + } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { + // if t and u have the same dimension and same base kind + if ((u & BASE_KIND) == OBJECT) { + // if t is also a reference type, and if u and t have the + // same dimension merge(u,t) = dim(t) | common parent of the + // element types of u and t + v = (t & DIM) | OBJECT + | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); + } else { + // if u and t are array types, but not with the same element + // type, merge(u,t) = dim(u) - 1 | java/lang/Object + int vdim = ELEMENT_OF + (u & DIM); + v = vdim | OBJECT | cw.addType("java/lang/Object"); + } + } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { + // if t is any other reference or array type, the merged type + // is min(udim, tdim) | java/lang/Object, where udim is the + // array dimension of u, minus 1 if u is an array type with a + // primitive element type (and similarly for tdim). + int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (t & DIM); + int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (u & DIM); + v = Math.min(tdim, udim) | OBJECT + | cw.addType("java/lang/Object"); + } else { + // if t is any other type, merge(u,t)=TOP + v = TOP; + } + } else if (u == NULL) { + // if u is the NULL type, merge(u,t)=t, + // or TOP if t is not a reference type + v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; + } else { + // if u is any other type, merge(u,t)=TOP whatever t + v = TOP; + } + if (u != v) { + types[index] = v; + return true; + } + return false; + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Handle.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Handle.java new file mode 100644 index 000000000..fd670857e --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Handle.java @@ -0,0 +1,222 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.fr.third.org.objectweb.asm; + +/** + * A reference to a field or a method. + * + * @author Remi Forax + * @author Eric Bruneton + */ +public final class Handle { + + /** + * The kind of field or method designated by this Handle. Should be + * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + */ + final int tag; + + /** + * The internal name of the class that owns the field or method designated + * by this handle. + */ + final String owner; + + /** + * The name of the field or method designated by this handle. + */ + final String name; + + /** + * The descriptor of the field or method designated by this handle. + */ + final String desc; + + + /** + * Indicate if the owner is an interface or not. + */ + final boolean itf; + + /** + * Constructs a new field or method handle. + * + * @param tag + * the kind of field or method designated by this Handle. Must be + * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the class that owns the field or method + * designated by this handle. + * @param name + * the name of the field or method designated by this handle. + * @param desc + * the descriptor of the field or method designated by this + * handle. + * + * @deprecated this constructor has been superseded + * by {@link #Handle(int, String, String, String, boolean)}. + */ + @Deprecated + public Handle(int tag, String owner, String name, String desc) { + this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE); + } + + /** + * Constructs a new field or method handle. + * + * @param tag + * the kind of field or method designated by this Handle. Must be + * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the class that owns the field or method + * designated by this handle. + * @param name + * the name of the field or method designated by this handle. + * @param desc + * the descriptor of the field or method designated by this + * handle. + * @param itf + * true if the owner is an interface. + */ + public Handle(int tag, String owner, String name, String desc, boolean itf) { + this.tag = tag; + this.owner = owner; + this.name = name; + this.desc = desc; + this.itf = itf; + } + + /** + * Returns the kind of field or method designated by this handle. + * + * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + */ + public int getTag() { + return tag; + } + + /** + * Returns the internal name of the class that owns the field or method + * designated by this handle. + * + * @return the internal name of the class that owns the field or method + * designated by this handle. + */ + public String getOwner() { + return owner; + } + + /** + * Returns the name of the field or method designated by this handle. + * + * @return the name of the field or method designated by this handle. + */ + public String getName() { + return name; + } + + /** + * Returns the descriptor of the field or method designated by this handle. + * + * @return the descriptor of the field or method designated by this handle. + */ + public String getDesc() { + return desc; + } + + /** + * Returns true if the owner of the field or method designated + * by this handle is an interface. + * + * @return true if the owner of the field or method designated + * by this handle is an interface. + */ + public boolean isInterface() { + return itf; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Handle)) { + return false; + } + Handle h = (Handle) obj; + return tag == h.tag && itf == h.itf && owner.equals(h.owner) + && name.equals(h.name) && desc.equals(h.desc); + } + + @Override + public int hashCode() { + return tag + (itf? 64: 0) + owner.hashCode() * name.hashCode() * desc.hashCode(); + } + + /** + * Returns the textual representation of this handle. The textual + * representation is: + * + *
+     * for a reference to a class:
+     * owner '.' name desc ' ' '(' tag ')'
+     * for a reference to an interface:
+     * owner '.' name desc ' ' '(' tag ' ' itf ')'
+     * 
+ * + * . As this format is unambiguous, it can be parsed if necessary. + */ + @Override + public String toString() { + return owner + '.' + name + desc + " (" + tag + (itf? " itf": "") + ')'; + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Handler.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Handler.java new file mode 100644 index 000000000..ff97bf328 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Handler.java @@ -0,0 +1,121 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * Information about an exception handler block. + * + * @author Eric Bruneton + */ +class Handler { + + /** + * Beginning of the exception handler's scope (inclusive). + */ + Label start; + + /** + * End of the exception handler's scope (exclusive). + */ + Label end; + + /** + * Beginning of the exception handler's code. + */ + Label handler; + + /** + * Internal name of the type of exceptions handled by this handler, or + * null to catch any exceptions. + */ + String desc; + + /** + * Constant pool index of the internal name of the type of exceptions + * handled by this handler, or 0 to catch any exceptions. + */ + int type; + + /** + * Next exception handler block info. + */ + Handler next; + + /** + * Removes the range between start and end from the given exception + * handlers. + * + * @param h + * an exception handler list. + * @param start + * the start of the range to be removed. + * @param end + * the end of the range to be removed. Maybe null. + * @return the exception handler list with the start-end range removed. + */ + static Handler remove(Handler h, Label start, Label end) { + if (h == null) { + return null; + } else { + h.next = remove(h.next, start, end); + } + int hstart = h.start.position; + int hend = h.end.position; + int s = start.position; + int e = end == null ? Integer.MAX_VALUE : end.position; + // if [hstart,hend[ and [s,e[ intervals intersect... + if (s < hend && e > hstart) { + if (s <= hstart) { + if (e >= hend) { + // [hstart,hend[ fully included in [s,e[, h removed + h = h.next; + } else { + // [hstart,hend[ minus [s,e[ = [e,hend[ + h.start = end; + } + } else if (e >= hend) { + // [hstart,hend[ minus [s,e[ = [hstart,s[ + h.end = start; + } else { + // [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[ + Handler g = new Handler(); + g.start = end; + g.end = h.end; + g.handler = h.handler; + g.desc = h.desc; + g.type = h.type; + g.next = h.next; + h.end = start; + h.next = g; + } + } + return h; + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Item.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Item.java new file mode 100644 index 000000000..966392a09 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Item.java @@ -0,0 +1,318 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A constant pool item. Constant pool items can be created with the 'newXXX' + * methods in the {@link ClassWriter} class. + * + * @author Eric Bruneton + */ +final class Item { + + /** + * Index of this item in the constant pool. + */ + int index; + + /** + * Type of this constant pool item. A single class is used to represent all + * constant pool item types, in order to minimize the bytecode size of this + * package. The value of this field is one of {@link ClassWriter#INT}, + * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, + * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, + * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, + * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, + * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}, + * {@link ClassWriter#MODULE}, {@link ClassWriter#PACKAGE}, + * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. + * + * MethodHandle constant 9 variations are stored using a range of 9 values + * from {@link ClassWriter#HANDLE_BASE} + 1 to + * {@link ClassWriter#HANDLE_BASE} + 9. + * + * Special Item types are used for Items that are stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. These special item types are + * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and + * {@link ClassWriter#TYPE_MERGED}. + */ + int type; + + /** + * Value of this item, for an integer item. + */ + int intVal; + + /** + * Value of this item, for a long item. + */ + long longVal; + + /** + * First part of the value of this item, for items that do not hold a + * primitive value. + */ + String strVal1; + + /** + * Second part of the value of this item, for items that do not hold a + * primitive value. + */ + String strVal2; + + /** + * Third part of the value of this item, for items that do not hold a + * primitive value. + */ + String strVal3; + + /** + * The hash code value of this constant pool item. + */ + int hashCode; + + /** + * Link to another constant pool item, used for collision lists in the + * constant pool's hash table. + */ + Item next; + + /** + * Constructs an uninitialized {@link Item}. + */ + Item() { + } + + /** + * Constructs an uninitialized {@link Item} for constant pool element at + * given position. + * + * @param index + * index of the item to be constructed. + */ + Item(final int index) { + this.index = index; + } + + /** + * Constructs a copy of the given item. + * + * @param index + * index of the item to be constructed. + * @param i + * the item that must be copied into the item to be constructed. + */ + Item(final int index, final Item i) { + this.index = index; + type = i.type; + intVal = i.intVal; + longVal = i.longVal; + strVal1 = i.strVal1; + strVal2 = i.strVal2; + strVal3 = i.strVal3; + hashCode = i.hashCode; + } + + /** + * Sets this item to an integer item. + * + * @param intVal + * the value of this item. + */ + void set(final int intVal) { + this.type = ClassWriter.INT; + this.intVal = intVal; + this.hashCode = 0x7FFFFFFF & (type + intVal); + } + + /** + * Sets this item to a long item. + * + * @param longVal + * the value of this item. + */ + void set(final long longVal) { + this.type = ClassWriter.LONG; + this.longVal = longVal; + this.hashCode = 0x7FFFFFFF & (type + (int) longVal); + } + + /** + * Sets this item to a float item. + * + * @param floatVal + * the value of this item. + */ + void set(final float floatVal) { + this.type = ClassWriter.FLOAT; + this.intVal = Float.floatToRawIntBits(floatVal); + this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); + } + + /** + * Sets this item to a double item. + * + * @param doubleVal + * the value of this item. + */ + void set(final double doubleVal) { + this.type = ClassWriter.DOUBLE; + this.longVal = Double.doubleToRawLongBits(doubleVal); + this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); + } + + /** + * Sets this item to an item that do not hold a primitive value. + * + * @param type + * the type of this item. + * @param strVal1 + * first part of the value of this item. + * @param strVal2 + * second part of the value of this item. + * @param strVal3 + * third part of the value of this item. + */ + @SuppressWarnings("fallthrough") + void set(final int type, final String strVal1, final String strVal2, + final String strVal3) { + this.type = type; + this.strVal1 = strVal1; + this.strVal2 = strVal2; + this.strVal3 = strVal3; + switch (type) { + case ClassWriter.CLASS: + this.intVal = 0; // intVal of a class must be zero, see visitInnerClass + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.MTYPE: + case ClassWriter.MODULE: + case ClassWriter.PACKAGE: + case ClassWriter.TYPE_NORMAL: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); + return; + case ClassWriter.NAME_TYPE: { + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode()); + return; + } + // ClassWriter.FIELD: + // ClassWriter.METH: + // ClassWriter.IMETH: + // ClassWriter.HANDLE_BASE + 1..9 + default: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode() * strVal3.hashCode()); + } + } + + /** + * Sets the item to an InvokeDynamic item. + * + * @param name + * invokedynamic's name. + * @param desc + * invokedynamic's desc. + * @param bsmIndex + * zero based index into the class attribute BootrapMethods. + */ + void set(String name, String desc, int bsmIndex) { + this.type = ClassWriter.INDY; + this.longVal = bsmIndex; + this.strVal1 = name; + this.strVal2 = desc; + this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex + * strVal1.hashCode() * strVal2.hashCode()); + } + + /** + * Sets the item to a BootstrapMethod item. + * + * @param position + * position in byte in the class attribute BootrapMethods. + * @param hashCode + * hashcode of the item. This hashcode is processed from the + * hashcode of the bootstrap method and the hashcode of all + * bootstrap arguments. + */ + void set(int position, int hashCode) { + this.type = ClassWriter.BSM; + this.intVal = position; + this.hashCode = hashCode; + } + + /** + * Indicates if the given item is equal to this one. This method assumes + * that the two items have the same {@link #type}. + * + * @param i + * the item to be compared to this one. Both items must have the + * same {@link #type}. + * @return true if the given item if equal to this one, + * false otherwise. + */ + boolean isEqualTo(final Item i) { + switch (type) { + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + case ClassWriter.MODULE: + case ClassWriter.PACKAGE: + case ClassWriter.MTYPE: + case ClassWriter.TYPE_NORMAL: + return i.strVal1.equals(strVal1); + case ClassWriter.TYPE_MERGED: + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + return i.longVal == longVal; + case ClassWriter.INT: + case ClassWriter.FLOAT: + return i.intVal == intVal; + case ClassWriter.TYPE_UNINIT: + return i.intVal == intVal && i.strVal1.equals(strVal1); + case ClassWriter.NAME_TYPE: + return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2); + case ClassWriter.INDY: { + return i.longVal == longVal && i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2); + } + // case ClassWriter.FIELD: + // case ClassWriter.METH: + // case ClassWriter.IMETH: + // case ClassWriter.HANDLE_BASE + 1..9 + default: + return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2) + && i.strVal3.equals(strVal3); + } + } + +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Label.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Label.java new file mode 100644 index 000000000..db4fa3748 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Label.java @@ -0,0 +1,564 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A label represents a position in the bytecode of a method. Labels are used + * for jump, goto, and switch instructions, and for try catch blocks. A label + * designates the instruction that is just after. Note however that there + * can be other elements between a label and the instruction it designates (such + * as other labels, stack map frames, line numbers, etc.). + * + * @author Eric Bruneton + */ +public class Label { + + /** + * Indicates if this label is only used for debug attributes. Such a label + * is not the start of a basic block, the target of a jump instruction, or + * an exception handler. It can be safely ignored in control flow graph + * analysis algorithms (for optimization purposes). + */ + static final int DEBUG = 1; + + /** + * Indicates if the position of this label is known. + */ + static final int RESOLVED = 2; + + /** + * Indicates if this label has been updated, after instruction resizing. + */ + static final int RESIZED = 4; + + /** + * Indicates if this basic block has been pushed in the basic block stack. + * See {@link MethodWriter#visitMaxs visitMaxs}. + */ + static final int PUSHED = 8; + + /** + * Indicates if this label is the target of a jump instruction, or the start + * of an exception handler. + */ + static final int TARGET = 16; + + /** + * Indicates if a stack map frame must be stored for this label. + */ + static final int STORE = 32; + + /** + * Indicates if this label corresponds to a reachable basic block. + */ + static final int REACHABLE = 64; + + /** + * Indicates if this basic block ends with a JSR instruction. + */ + static final int JSR = 128; + + /** + * Indicates if this basic block ends with a RET instruction. + */ + static final int RET = 256; + + /** + * Indicates if this basic block is the start of a subroutine. + */ + static final int SUBROUTINE = 512; + + /** + * Indicates if this subroutine basic block has been visited by a + * visitSubroutine(null, ...) call. + */ + static final int VISITED = 1024; + + /** + * Indicates if this subroutine basic block has been visited by a + * visitSubroutine(!null, ...) call. + */ + static final int VISITED2 = 2048; + + /** + * Field used to associate user information to a label. Warning: this field + * is used by the ASM tree package. In order to use it with the ASM tree + * package you must override the + * {@link com.fr.third.org.objectweb.asm.tree.MethodNode#getLabelNode} method. + */ + public Object info; + + /** + * Flags that indicate the status of this label. + * + * @see #DEBUG + * @see #RESOLVED + * @see #RESIZED + * @see #PUSHED + * @see #TARGET + * @see #STORE + * @see #REACHABLE + * @see #JSR + * @see #RET + */ + int status; + + /** + * The line number corresponding to this label, if known. If there are + * several lines, each line is stored in a separate label, all linked via + * their next field (these links are created in ClassReader and removed just + * before visitLabel is called, so that this does not impact the rest of the + * code). + */ + int line; + + /** + * The position of this label in the code, if known. + */ + int position; + + /** + * Number of forward references to this label, times two. + */ + private int referenceCount; + + /** + * Informations about forward references. Each forward reference is + * described by two consecutive integers in this array: the first one is the + * position of the first byte of the bytecode instruction that contains the + * forward reference, while the second is the position of the first byte of + * the forward reference itself. In fact the sign of the first integer + * indicates if this reference uses 2 or 4 bytes, and its absolute value + * gives the position of the bytecode instruction. This array is also used + * as a bitset to store the subroutines to which a basic block belongs. This + * information is needed in {@linked MethodWriter#visitMaxs}, after all + * forward references have been resolved. Hence the same array can be used + * for both purposes without problems. + */ + private int[] srcAndRefPositions; + + // ------------------------------------------------------------------------ + + /* + * Fields for the control flow and data flow graph analysis algorithms (used + * to compute the maximum stack size or the stack map frames). A control + * flow graph contains one node per "basic block", and one edge per "jump" + * from one basic block to another. Each node (i.e., each basic block) is + * represented by the Label object that corresponds to the first instruction + * of this basic block. Each node also stores the list of its successors in + * the graph, as a linked list of Edge objects. + * + * The control flow analysis algorithms used to compute the maximum stack + * size or the stack map frames are similar and use two steps. The first + * step, during the visit of each instruction, builds information about the + * state of the local variables and the operand stack at the end of each + * basic block, called the "output frame", relatively to the frame + * state at the beginning of the basic block, which is called the "input + * frame", and which is unknown during this step. The second step, in + * {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes + * information about the input frame of each basic block, from the input + * state of the first basic block (known from the method signature), and by + * the using the previously computed relative output frames. + * + * The algorithm used to compute the maximum stack size only computes the + * relative output and absolute input stack heights, while the algorithm + * used to compute stack map frames computes relative output frames and + * absolute input frames. + */ + + /** + * Start of the output stack relatively to the input stack. The exact + * semantics of this field depends on the algorithm that is used. + * + * When only the maximum stack size is computed, this field is the number of + * elements in the input stack. + * + * When the stack map frames are completely computed, this field is the + * offset of the first output stack element relatively to the top of the + * input stack. This offset is always negative or null. A null offset means + * that the output stack must be appended to the input stack. A -n offset + * means that the first n output stack elements must replace the top n input + * stack elements, and that the other elements must be appended to the input + * stack. + */ + int inputStackTop; + + /** + * Maximum height reached by the output stack, relatively to the top of the + * input stack. This maximum is always positive or null. + */ + int outputStackMax; + + /** + * Information about the input and output stack map frames of this basic + * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} + * option is used. + */ + Frame frame; + + /** + * The successor of this label, in the order they are visited. This linked + * list does not include labels used for debug info only. If + * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it + * does not contain successive labels that denote the same bytecode position + * (in this case only the first label appears in this list). + */ + Label successor; + + /** + * The successors of this node in the control flow graph. These successors + * are stored in a linked list of {@link Edge Edge} objects, linked to each + * other by their {@link Edge#next} field. + */ + Edge successors; + + /** + * The next basic block in the basic block stack. This stack is used in the + * main loop of the fix point algorithm used in the second step of the + * control flow analysis algorithms. It is also used in + * {@link #visitSubroutine} to avoid using a recursive method, and in + * ClassReader to temporarily store multiple source lines for a label. + * + * @see MethodWriter#visitMaxs + */ + Label next; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new label. + */ + public Label() { + } + + // ------------------------------------------------------------------------ + // Methods to compute offsets and to manage forward references + // ------------------------------------------------------------------------ + + /** + * Returns the offset corresponding to this label. This offset is computed + * from the start of the method's bytecode. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @return the offset corresponding to this label. + * @throws IllegalStateException + * if this label is not resolved yet. + */ + public int getOffset() { + if ((status & RESOLVED) == 0) { + throw new IllegalStateException( + "Label offset position has not been resolved yet"); + } + return position; + } + + /** + * Puts a reference to this label in the bytecode of a method. If the + * position of the label is known, the offset is computed and written + * directly. Otherwise, a null offset is written and a new forward reference + * is declared for this label. + * + * @param owner + * the code writer that calls this method. + * @param out + * the bytecode of the method. + * @param source + * the position of first byte of the bytecode instruction that + * contains this label. + * @param wideOffset + * true if the reference must be stored in 4 bytes, or + * false if it must be stored with 2 bytes. + * @throws IllegalArgumentException + * if this label has not been created by the given code writer. + */ + void put(final MethodWriter owner, final ByteVector out, final int source, + final boolean wideOffset) { + if ((status & RESOLVED) == 0) { + if (wideOffset) { + addReference(-1 - source, out.length); + out.putInt(-1); + } else { + addReference(source, out.length); + out.putShort(-1); + } + } else { + if (wideOffset) { + out.putInt(position - source); + } else { + out.putShort(position - source); + } + } + } + + /** + * Adds a forward reference to this label. This method must be called only + * for a true forward reference, i.e. only if this label is not resolved + * yet. For backward references, the offset of the reference can be, and + * must be, computed and stored directly. + * + * @param sourcePosition + * the position of the referencing instruction. This position + * will be used to compute the offset of this forward reference. + * @param referencePosition + * the position where the offset for this forward reference must + * be stored. + */ + private void addReference(final int sourcePosition, + final int referencePosition) { + if (srcAndRefPositions == null) { + srcAndRefPositions = new int[6]; + } + if (referenceCount >= srcAndRefPositions.length) { + int[] a = new int[srcAndRefPositions.length + 6]; + System.arraycopy(srcAndRefPositions, 0, a, 0, + srcAndRefPositions.length); + srcAndRefPositions = a; + } + srcAndRefPositions[referenceCount++] = sourcePosition; + srcAndRefPositions[referenceCount++] = referencePosition; + } + + /** + * Resolves all forward references to this label. This method must be called + * when this label is added to the bytecode of the method, i.e. when its + * position becomes known. This method fills in the blanks that where left + * in the bytecode by each forward reference previously added to this label. + * + * @param owner + * the code writer that calls this method. + * @param position + * the position of this label in the bytecode. + * @param data + * the bytecode of the method. + * @return true if a blank that was left for this label was too + * small to store the offset. In such a case the corresponding jump + * instruction is replaced with a pseudo instruction (using unused + * opcodes) using an unsigned two bytes offset. These pseudo + * instructions will be replaced with standard bytecode instructions + * with wider offsets (4 bytes instead of 2), in ClassReader. + * @throws IllegalArgumentException + * if this label has already been resolved, or if it has not + * been created by the given code writer. + */ + boolean resolve(final MethodWriter owner, final int position, + final byte[] data) { + boolean needUpdate = false; + this.status |= RESOLVED; + this.position = position; + int i = 0; + while (i < referenceCount) { + int source = srcAndRefPositions[i++]; + int reference = srcAndRefPositions[i++]; + int offset; + if (source >= 0) { + offset = position - source; + if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { + /* + * changes the opcode of the jump instruction, in order to + * be able to find it later (see resizeInstructions in + * MethodWriter). These temporary opcodes are similar to + * jump instruction opcodes, except that the 2 bytes offset + * is unsigned (and can therefore represent values from 0 to + * 65535, which is sufficient since the size of a method is + * limited to 65535 bytes). + */ + int opcode = data[reference - 1] & 0xFF; + if (opcode <= Opcodes.JSR) { + // changes IFEQ ... JSR to opcodes 202 to 217 + data[reference - 1] = (byte) (opcode + 49); + } else { + // changes IFNULL and IFNONNULL to opcodes 218 and 219 + data[reference - 1] = (byte) (opcode + 20); + } + needUpdate = true; + } + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } else { + offset = position + source + 1; + data[reference++] = (byte) (offset >>> 24); + data[reference++] = (byte) (offset >>> 16); + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } + } + return needUpdate; + } + + /** + * Returns the first label of the series to which this label belongs. For an + * isolated label or for the first label in a series of successive labels, + * this method returns the label itself. For other labels it returns the + * first label of the series. + * + * @return the first label of the series to which this label belongs. + */ + Label getFirst() { + return frame == null ? this : frame.owner; + } + + // ------------------------------------------------------------------------ + // Methods related to subroutines + // ------------------------------------------------------------------------ + + /** + * Returns true is this basic block belongs to the given subroutine. + * + * @param id + * a subroutine id. + * @return true is this basic block belongs to the given subroutine. + */ + boolean inSubroutine(final long id) { + if ((status & Label.VISITED) != 0) { + return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0; + } + return false; + } + + /** + * Returns true if this basic block and the given one belong to a common + * subroutine. + * + * @param block + * another basic block. + * @return true if this basic block and the given one belong to a common + * subroutine. + */ + boolean inSameSubroutine(final Label block) { + if ((status & VISITED) == 0 || (block.status & VISITED) == 0) { + return false; + } + for (int i = 0; i < srcAndRefPositions.length; ++i) { + if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) { + return true; + } + } + return false; + } + + /** + * Marks this basic block as belonging to the given subroutine. + * + * @param id + * a subroutine id. + * @param nbSubroutines + * the total number of subroutines in the method. + */ + void addToSubroutine(final long id, final int nbSubroutines) { + if ((status & VISITED) == 0) { + status |= VISITED; + srcAndRefPositions = new int[nbSubroutines / 32 + 1]; + } + srcAndRefPositions[(int) (id >>> 32)] |= (int) id; + } + + /** + * Finds the basic blocks that belong to a given subroutine, and marks these + * blocks as belonging to this subroutine. This method follows the control + * flow graph to find all the blocks that are reachable from the current + * block WITHOUT following any JSR target. + * + * @param JSR + * a JSR block that jumps to this subroutine. If this JSR is not + * null it is added to the successor of the RET blocks found in + * the subroutine. + * @param id + * the id of this subroutine. + * @param nbSubroutines + * the total number of subroutines in the method. + */ + void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) { + // user managed stack of labels, to avoid using a recursive method + // (recursivity can lead to stack overflow with very large methods) + Label stack = this; + while (stack != null) { + // removes a label l from the stack + Label l = stack; + stack = l.next; + l.next = null; + + if (JSR != null) { + if ((l.status & VISITED2) != 0) { + continue; + } + l.status |= VISITED2; + // adds JSR to the successors of l, if it is a RET block + if ((l.status & RET) != 0) { + if (!l.inSameSubroutine(JSR)) { + Edge e = new Edge(); + e.info = l.inputStackTop; + e.successor = JSR.successors.successor; + e.next = l.successors; + l.successors = e; + } + } + } else { + // if the l block already belongs to subroutine 'id', continue + if (l.inSubroutine(id)) { + continue; + } + // marks the l block as belonging to subroutine 'id' + l.addToSubroutine(id, nbSubroutines); + } + // pushes each successor of l on the stack, except JSR targets + Edge e = l.successors; + while (e != null) { + // if the l block is a JSR block, then 'l.successors.next' leads + // to the JSR target (see {@link #visitJumpInsn}) and must + // therefore not be followed + if ((l.status & Label.JSR) == 0 || e != l.successors.next) { + // pushes e.successor on the stack if it not already added + if (e.successor.next == null) { + e.successor.next = stack; + stack = e.successor; + } + } + e = e.next; + } + } + } + + // ------------------------------------------------------------------------ + // Overriden Object methods + // ------------------------------------------------------------------------ + + /** + * Returns a string representation of this label. + * + * @return a string representation of this label. + */ + @Override + public String toString() { + return "L" + System.identityHashCode(this); + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/MethodVisitor.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/MethodVisitor.java new file mode 100644 index 000000000..76f5b61ca --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/MethodVisitor.java @@ -0,0 +1,881 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A visitor to visit a Java method. The methods of this class must be called in + * the following order: ( visitParameter )* [ + * visitAnnotationDefault ] ( visitAnnotation | + * visitParameterAnnotation visitTypeAnnotation | + * visitAttribute )* [ visitCode ( visitFrame | + * visitXInsn | visitLabel | + * visitInsnAnnotation | visitTryCatchBlock | + * visitTryCatchAnnotation | visitLocalVariable | + * visitLocalVariableAnnotation | visitLineNumber )* + * visitMaxs ] visitEnd. In addition, the + * visitXInsn and visitLabel methods must be called in + * the sequential order of the bytecode instructions of the visited code, + * visitInsnAnnotation must be called after the annotated + * instruction, visitTryCatchBlock must be called before the + * labels passed as arguments have been visited, + * visitTryCatchBlockAnnotation must be called after the + * corresponding try catch block has been visited, and the + * visitLocalVariable, visitLocalVariableAnnotation and + * visitLineNumber methods must be called after the labels + * passed as arguments have been visited. + * + * @author Eric Bruneton + */ +public abstract class MethodVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + protected final int api; + + /** + * The method visitor to which this visitor must delegate method calls. May + * be null. + */ + protected MethodVisitor mv; + + /** + * Constructs a new {@link MethodVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + public MethodVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link MethodVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + * @param mv + * the method visitor to which this visitor must delegate method + * calls. May be null. + */ + public MethodVisitor(final int api, final MethodVisitor mv) { + if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { + throw new IllegalArgumentException(); + } + this.api = api; + this.mv = mv; + } + + // ------------------------------------------------------------------------- + // Parameters, annotations and non standard attributes + // ------------------------------------------------------------------------- + + /** + * Visits a parameter of this method. + * + * @param name + * parameter name or null if none is provided. + * @param access + * the parameter's access flags, only ACC_FINAL, + * ACC_SYNTHETIC or/and ACC_MANDATED are + * allowed (see {@link Opcodes}). + */ + public void visitParameter(String name, int access) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + mv.visitParameter(name, access); + } + } + + /** + * Visits the default value of this annotation interface method. + * + * @return a visitor to the visit the actual default value of this + * annotation interface method, or null if this visitor is + * not interested in visiting this default value. The 'name' + * parameters passed to the methods of this annotation visitor are + * ignored. Moreover, exacly one visit method must be called on this + * annotation visitor, followed by visitEnd. + */ + public AnnotationVisitor visitAnnotationDefault() { + if (mv != null) { + return mv.visitAnnotationDefault(); + } + return null; + } + + /** + * Visits an annotation of this method. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (mv != null) { + return mv.visitAnnotation(desc, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the method signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#METHOD_TYPE_PARAMETER + * METHOD_TYPE_PARAMETER}, + * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND + * METHOD_TYPE_PARAMETER_BOUND}, + * {@link TypeReference#METHOD_RETURN METHOD_RETURN}, + * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER}, + * {@link TypeReference#METHOD_FORMAL_PARAMETER + * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS + * THROWS}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits an annotation of a parameter this method. + * + * @param parameter + * the parameter index. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitParameterAnnotation(int parameter, + String desc, boolean visible) { + if (mv != null) { + return mv.visitParameterAnnotation(parameter, desc, visible); + } + return null; + } + + /** + * Visits a non standard attribute of this method. + * + * @param attr + * an attribute. + */ + public void visitAttribute(Attribute attr) { + if (mv != null) { + mv.visitAttribute(attr); + } + } + + /** + * Starts the visit of the method's code, if any (i.e. non abstract method). + */ + public void visitCode() { + if (mv != null) { + mv.visitCode(); + } + } + + /** + * Visits the current state of the local variables and operand stack + * elements. This method must(*) be called just before any + * instruction i that follows an unconditional branch instruction + * such as GOTO or THROW, that is the target of a jump instruction, or that + * starts an exception handler block. The visited types must describe the + * values of the local variables and of the operand stack elements just + * before i is executed.
+ *
+ * (*) this is mandatory only for classes whose version is greater than or + * equal to {@link Opcodes#V1_6 V1_6}.
+ *
+ * The frames of a method must be given either in expanded form, or in + * compressed form (all frames must use the same format, i.e. you must not + * mix expanded and compressed frames within a single method): + *
    + *
  • In expanded form, all frames must have the F_NEW type.
  • + *
  • In compressed form, frames are basically "deltas" from the state of + * the previous frame: + *
      + *
    • {@link Opcodes#F_SAME} representing frame with exactly the same + * locals as the previous frame and with the empty stack.
    • + *
    • {@link Opcodes#F_SAME1} representing frame with exactly the same + * locals as the previous frame and with single value on the stack ( + * nStack is 1 and stack[0] contains value for the + * type of the stack item).
    • + *
    • {@link Opcodes#F_APPEND} representing frame with current locals are + * the same as the locals in the previous frame, except that additional + * locals are defined (nLocal is 1, 2 or 3 and + * local elements contains values representing added types).
    • + *
    • {@link Opcodes#F_CHOP} representing frame with current locals are the + * same as the locals in the previous frame, except that the last 1-3 locals + * are absent and with the empty stack (nLocals is 1, 2 or 3).
    • + *
    • {@link Opcodes#F_FULL} representing complete frame data.
    • + *
    + *
  • + *
+ *
+ * In both cases the first frame, corresponding to the method's parameters + * and access flags, is implicit and must not be visited. Also, it is + * illegal to visit two or more frames for the same code location (i.e., at + * least one instruction must be visited between two calls to visitFrame). + * + * @param type + * the type of this stack map frame. Must be + * {@link Opcodes#F_NEW} for expanded frames, or + * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, + * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or + * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for + * compressed frames. + * @param nLocal + * the number of local variables in the visited frame. + * @param local + * the local variable types in this frame. This array must not be + * modified. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, + * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are + * represented by a single element). Reference types are + * represented by String objects (representing internal names), + * and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized + * value). + * @param nStack + * the number of operand stack elements in the visited frame. + * @param stack + * the operand stack types in this frame. This array must not be + * modified. Its content has the same format as the "local" + * array. + * @throws IllegalStateException + * if a frame is visited just after another one, without any + * instruction between the two (unless this frame is a + * Opcodes#F_SAME frame, in which case it is silently ignored). + */ + public void visitFrame(int type, int nLocal, Object[] local, int nStack, + Object[] stack) { + if (mv != null) { + mv.visitFrame(type, nLocal, local, nStack, stack); + } + } + + // ------------------------------------------------------------------------- + // Normal instructions + // ------------------------------------------------------------------------- + + /** + * Visits a zero operand instruction. + * + * @param opcode + * the opcode of the instruction to be visited. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, + * ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, + * FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, + * LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, + * DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, + * IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, + * FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, + * IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, + * L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, + * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, + * or MONITOREXIT. + */ + public void visitInsn(int opcode) { + if (mv != null) { + mv.visitInsn(opcode); + } + } + + /** + * Visits an instruction with a single int operand. + * + * @param opcode + * the opcode of the instruction to be visited. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand + * the operand of the instruction to be visited.
+ * When opcode is BIPUSH, operand value should be between + * Byte.MIN_VALUE and Byte.MAX_VALUE.
+ * When opcode is SIPUSH, operand value should be between + * Short.MIN_VALUE and Short.MAX_VALUE.
+ * When opcode is NEWARRAY, operand value should be one of + * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, + * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, + * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, + * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. + */ + public void visitIntInsn(int opcode, int operand) { + if (mv != null) { + mv.visitIntInsn(opcode, operand); + } + } + + /** + * Visits a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode + * the opcode of the local variable instruction to be visited. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, + * ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var + * the operand of the instruction to be visited. This operand is + * the index of a local variable. + */ + public void visitVarInsn(int opcode, int var) { + if (mv != null) { + mv.visitVarInsn(opcode, var); + } + } + + /** + * Visits a type instruction. A type instruction is an instruction that + * takes the internal name of a class as parameter. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param type + * the operand of the instruction to be visited. This operand + * must be the internal name of an object or array class (see + * {@link Type#getInternalName() getInternalName}). + */ + public void visitTypeInsn(int opcode, String type) { + if (mv != null) { + mv.visitTypeInsn(opcode, type); + } + } + + /** + * Visits a field instruction. A field instruction is an instruction that + * loads or stores the value of a field of an object. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner + * the internal name of the field's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link Type Type}). + */ + public void visitFieldInsn(int opcode, String owner, String name, + String desc) { + if (mv != null) { + mv.visitFieldInsn(opcode, owner, name, desc); + } + } + + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + */ + @Deprecated + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + } + + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param itf + * if the method's owner class is an interface. + */ + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { + throw new IllegalArgumentException( + "INVOKESPECIAL/STATIC on interfaces require ASM 5"); + } + visitMethodInsn(opcode, owner, name, desc); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + + /** + * Visits an invokedynamic instruction. + * + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, + * {@link Double}, {@link String}, {@link Type} or {@link Handle} + * value. This method is allowed to modify the content of the + * array so a caller should expect that this array may change. + */ + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + if (mv != null) { + mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + } + } + + /** + * Visits a jump instruction. A jump instruction is an instruction that may + * jump to another instruction. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, + * IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label + * the operand of the instruction to be visited. This operand is + * a label that designates the instruction to which the jump + * instruction may jump. + */ + public void visitJumpInsn(int opcode, Label label) { + if (mv != null) { + mv.visitJumpInsn(opcode, label); + } + } + + /** + * Visits a label. A label designates the instruction that will be visited + * just after it. + * + * @param label + * a {@link Label Label} object. + */ + public void visitLabel(Label label) { + if (mv != null) { + mv.visitLabel(label); + } + } + + // ------------------------------------------------------------------------- + // Special instructions + // ------------------------------------------------------------------------- + + /** + * Visits a LDC instruction. Note that new constant types may be added in + * future versions of the Java Virtual Machine. To easily detect new + * constant types, implementations of this method should check for + * unexpected constant types, like this: + * + *
+     * if (cst instanceof Integer) {
+     *     // ...
+     * } else if (cst instanceof Float) {
+     *     // ...
+     * } else if (cst instanceof Long) {
+     *     // ...
+     * } else if (cst instanceof Double) {
+     *     // ...
+     * } else if (cst instanceof String) {
+     *     // ...
+     * } else if (cst instanceof Type) {
+     *     int sort = ((Type) cst).getSort();
+     *     if (sort == Type.OBJECT) {
+     *         // ...
+     *     } else if (sort == Type.ARRAY) {
+     *         // ...
+     *     } else if (sort == Type.METHOD) {
+     *         // ...
+     *     } else {
+     *         // throw an exception
+     *     }
+     * } else if (cst instanceof Handle) {
+     *     // ...
+     * } else {
+     *     // throw an exception
+     * }
+     * 
+ * + * @param cst + * the constant to be loaded on the stack. This parameter must be + * a non null {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double}, a {@link String}, a {@link Type} of OBJECT or + * ARRAY sort for .class constants, for classes whose + * version is 49.0, a {@link Type} of METHOD sort or a + * {@link Handle} for MethodType and MethodHandle constants, for + * classes whose version is 51.0. + */ + public void visitLdcInsn(Object cst) { + if (mv != null) { + mv.visitLdcInsn(cst); + } + } + + /** + * Visits an IINC instruction. + * + * @param var + * index of the local variable to be incremented. + * @param increment + * amount to increment the local variable by. + */ + public void visitIincInsn(int var, int increment) { + if (mv != null) { + mv.visitIincInsn(var, increment); + } + } + + /** + * Visits a TABLESWITCH instruction. + * + * @param min + * the minimum key value. + * @param max + * the maximum key value. + * @param dflt + * beginning of the default handler block. + * @param labels + * beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the min + i key. + */ + public void visitTableSwitchInsn(int min, int max, Label dflt, + Label... labels) { + if (mv != null) { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + } + + /** + * Visits a LOOKUPSWITCH instruction. + * + * @param dflt + * beginning of the default handler block. + * @param keys + * the values of the keys. + * @param labels + * beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the keys[i] key. + */ + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + if (mv != null) { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + } + + /** + * Visits a MULTIANEWARRAY instruction. + * + * @param desc + * an array type descriptor (see {@link Type Type}). + * @param dims + * number of dimensions of the array to allocate. + */ + public void visitMultiANewArrayInsn(String desc, int dims) { + if (mv != null) { + mv.visitMultiANewArrayInsn(desc, dims); + } + } + + /** + * Visits an annotation on an instruction. This method must be called just + * after the annotated instruction. It can be called several times + * for the same instruction. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#INSTANCEOF INSTANCEOF}, + * {@link TypeReference#NEW NEW}, + * {@link TypeReference#CONSTRUCTOR_REFERENCE + * CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE + * METHOD_REFERENCE}, {@link TypeReference#CAST CAST}, + * {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + // ------------------------------------------------------------------------- + // Exceptions table entries, debug information, max stack and max locals + // ------------------------------------------------------------------------- + + /** + * Visits a try catch block. + * + * @param start + * beginning of the exception handler's scope (inclusive). + * @param end + * end of the exception handler's scope (exclusive). + * @param handler + * beginning of the exception handler's code. + * @param type + * internal name of the type of exceptions handled by the + * handler, or null to catch any exceptions (for + * "finally" blocks). + * @throws IllegalArgumentException + * if one of the labels has already been visited by this visitor + * (by the {@link #visitLabel visitLabel} method). + */ + public void visitTryCatchBlock(Label start, Label end, Label handler, + String type) { + if (mv != null) { + mv.visitTryCatchBlock(start, end, handler, type); + } + } + + /** + * Visits an annotation on an exception handler type. This method must be + * called after the {@link #visitTryCatchBlock} for the annotated + * exception handler. It can be called several times for the same exception + * handler. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#EXCEPTION_PARAMETER + * EXCEPTION_PARAMETER}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits a local variable declaration. + * + * @param name + * the name of a local variable. + * @param desc + * the type descriptor of this local variable. + * @param signature + * the type signature of this local variable. May be + * null if the local variable type does not use generic + * types. + * @param start + * the first instruction corresponding to the scope of this local + * variable (inclusive). + * @param end + * the last instruction corresponding to the scope of this local + * variable (exclusive). + * @param index + * the local variable's index. + * @throws IllegalArgumentException + * if one of the labels has not already been visited by this + * visitor (by the {@link #visitLabel visitLabel} method). + */ + public void visitLocalVariable(String name, String desc, String signature, + Label start, Label end, int index) { + if (mv != null) { + mv.visitLocalVariable(name, desc, signature, start, end, index); + } + } + + /** + * Visits an annotation on a local variable type. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#LOCAL_VARIABLE + * LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE + * RESOURCE_VARIABLE}. See {@link TypeReference}. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * null if the annotation targets 'typeRef' as a whole. + * @param start + * the fist instructions corresponding to the continuous ranges + * that make the scope of this local variable (inclusive). + * @param end + * the last instructions corresponding to the continuous ranges + * that make the scope of this local variable (exclusive). This + * array must have the same size as the 'start' array. + * @param index + * the local variable's index in each range. This array must have + * the same size as the 'start' array. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitLocalVariableAnnotation(typeRef, typePath, start, + end, index, desc, visible); + } + return null; + } + + /** + * Visits a line number declaration. + * + * @param line + * a line number. This number refers to the source file from + * which the class was compiled. + * @param start + * the first instruction corresponding to this line number. + * @throws IllegalArgumentException + * if start has not already been visited by this + * visitor (by the {@link #visitLabel visitLabel} method). + */ + public void visitLineNumber(int line, Label start) { + if (mv != null) { + mv.visitLineNumber(line, start); + } + } + + /** + * Visits the maximum stack size and the maximum number of local variables + * of the method. + * + * @param maxStack + * maximum stack size of the method. + * @param maxLocals + * maximum number of local variables for the method. + */ + public void visitMaxs(int maxStack, int maxLocals) { + if (mv != null) { + mv.visitMaxs(maxStack, maxLocals); + } + } + + /** + * Visits the end of the method. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the method have been visited. + */ + public void visitEnd() { + if (mv != null) { + mv.visitEnd(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/MethodWriter.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/MethodWriter.java new file mode 100644 index 000000000..874782f58 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/MethodWriter.java @@ -0,0 +1,2373 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A {@link MethodVisitor} that generates methods in bytecode form. Each visit + * method of this class appends the bytecode corresponding to the visited + * instruction to a byte vector, in the order these methods are called. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +class MethodWriter extends MethodVisitor { + + /** + * Pseudo access flag used to denote constructors. + */ + static final int ACC_CONSTRUCTOR = 0x80000; + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. + */ + static final int SAME_FRAME = 0; // to 63 (0-3f) + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1 + */ + static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) + + /** + * Reserved for future use + */ + static final int RESERVED = 128; + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1. Offset is bigger then 63; + */ + static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 + + /** + * Frame where current locals are the same as the locals in the previous + * frame, except that the k last locals are absent. The value of k is given + * by the formula 251-frame_type. + */ + static final int CHOP_FRAME = 248; // to 250 (f8-fA) + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. Offset is bigger then 63; + */ + static final int SAME_FRAME_EXTENDED = 251; // fb + + /** + * Frame where current locals are the same as the locals in the previous + * frame, except that k additional locals are defined. The value of k is + * given by the formula frame_type-251. + */ + static final int APPEND_FRAME = 252; // to 254 // fc-fe + + /** + * Full frame + */ + static final int FULL_FRAME = 255; // ff + + /** + * Indicates that the stack map frames must be recomputed from scratch. In + * this case the maximum stack size and number of local variables is also + * recomputed from scratch. + * + * @see #compute + */ + static final int FRAMES = 0; + + /** + * Indicates that the stack map frames of type F_INSERT must be computed. + * The other frames are not (re)computed. They should all be of type F_NEW + * and should be sufficient to compute the content of the F_INSERT frames, + * together with the bytecode instructions between a F_NEW and a F_INSERT + * frame - and without any knowledge of the type hierarchy (by definition of + * F_INSERT). + * + * @see #compute + */ + static final int INSERTED_FRAMES = 1; + + /** + * Indicates that the maximum stack size and number of local variables must + * be automatically computed. + * + * @see #compute + */ + static final int MAXS = 2; + + /** + * Indicates that nothing must be automatically computed. + * + * @see #compute + */ + static final int NOTHING = 3; + + /** + * The class writer to which this method must be added. + */ + final ClassWriter cw; + + /** + * Access flags of this method. + */ + private int access; + + /** + * The index of the constant pool item that contains the name of this + * method. + */ + private final int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * method. + */ + private final int desc; + + /** + * The descriptor of this method. + */ + private final String descriptor; + + /** + * The signature of this method. + */ + String signature; + + /** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in cw.cr. More + * precisely, this field gives the index of the first byte to copied from + * cw.cr.b. + */ + int classReaderOffset; + + /** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in cw.cr. More + * precisely, this field gives the number of bytes to copied from + * cw.cr.b. + */ + int classReaderLength; + + /** + * Number of exceptions that can be thrown by this method. + */ + int exceptionCount; + + /** + * The exceptions that can be thrown by this method. More precisely, this + * array contains the indexes of the constant pool items that contain the + * internal names of these exception classes. + */ + int[] exceptions; + + /** + * The annotation default attribute of this method. May be null. + */ + private ByteVector annd; + + /** + * The runtime visible annotations of this method. May be null. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this method. May be null. + */ + private AnnotationWriter ianns; + + /** + * The runtime visible type annotations of this method. May be null + * . + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this method. May be + * null. + */ + private AnnotationWriter itanns; + + /** + * The runtime visible parameter annotations of this method. May be + * null. + */ + private AnnotationWriter[] panns; + + /** + * The runtime invisible parameter annotations of this method. May be + * null. + */ + private AnnotationWriter[] ipanns; + + /** + * The number of synthetic parameters of this method. + */ + private int synthetics; + + /** + * The non standard attributes of the method. + */ + private Attribute attrs; + + /** + * The bytecode of this method. + */ + private ByteVector code = new ByteVector(); + + /** + * Maximum stack size of this method. + */ + private int maxStack; + + /** + * Maximum number of local variables for this method. + */ + private int maxLocals; + + /** + * Number of local variables in the current stack map frame. + */ + private int currentLocals; + + /** + * Number of stack map frames in the StackMapTable attribute. + */ + int frameCount; + + /** + * The StackMapTable attribute. + */ + private ByteVector stackMap; + + /** + * The offset of the last frame that was written in the StackMapTable + * attribute. + */ + private int previousFrameOffset; + + /** + * The last frame that was written in the StackMapTable attribute. + * + * @see #frame + */ + private int[] previousFrame; + + /** + * The current stack map frame. The first element contains the offset of the + * instruction to which the frame corresponds, the second element is the + * number of locals and the third one is the number of stack elements. The + * local variables start at index 3 and are followed by the operand stack + * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = + * nStack, frame[3] = nLocal. All types are encoded as integers, with the + * same format as the one used in {@link Label}, but limited to BASE types. + */ + private int[] frame; + + /** + * Number of elements in the exception handler list. + */ + private int handlerCount; + + /** + * The first element in the exception handler list. + */ + private Handler firstHandler; + + /** + * The last element in the exception handler list. + */ + private Handler lastHandler; + + /** + * Number of entries in the MethodParameters attribute. + */ + private int methodParametersCount; + + /** + * The MethodParameters attribute. + */ + private ByteVector methodParameters; + + /** + * Number of entries in the LocalVariableTable attribute. + */ + private int localVarCount; + + /** + * The LocalVariableTable attribute. + */ + private ByteVector localVar; + + /** + * Number of entries in the LocalVariableTypeTable attribute. + */ + private int localVarTypeCount; + + /** + * The LocalVariableTypeTable attribute. + */ + private ByteVector localVarType; + + /** + * Number of entries in the LineNumberTable attribute. + */ + private int lineNumberCount; + + /** + * The LineNumberTable attribute. + */ + private ByteVector lineNumber; + + /** + * The start offset of the last visited instruction. + */ + private int lastCodeOffset; + + /** + * The runtime visible type annotations of the code. May be null. + */ + private AnnotationWriter ctanns; + + /** + * The runtime invisible type annotations of the code. May be null. + */ + private AnnotationWriter ictanns; + + /** + * The non standard attributes of the method's code. + */ + private Attribute cattrs; + + /** + * The number of subroutines in this method. + */ + private int subroutines; + + // ------------------------------------------------------------------------ + + /* + * Fields for the control flow graph analysis algorithm (used to compute the + * maximum stack size). A control flow graph contains one node per "basic + * block", and one edge per "jump" from one basic block to another. Each + * node (i.e., each basic block) is represented by the Label object that + * corresponds to the first instruction of this basic block. Each node also + * stores the list of its successors in the graph, as a linked list of Edge + * objects. + */ + + /** + * Indicates what must be automatically computed. + * + * @see #FRAMES + * @see #INSERTED_FRAMES + * @see #MAXS + * @see #NOTHING + */ + private final int compute; + + /** + * A list of labels. This list is the list of basic blocks in the method, + * i.e. a list of Label objects linked to each other by their + * {@link Label#successor} field, in the order they are visited by + * {@link MethodVisitor#visitLabel}, and starting with the first basic + * block. + */ + private Label labels; + + /** + * The previous basic block. + */ + private Label previousBlock; + + /** + * The current basic block. + */ + private Label currentBlock; + + /** + * The (relative) stack size after the last visited instruction. This size + * is relative to the beginning of the current basic block, i.e., the true + * stack size after the last visited instruction is equal to the + * {@link Label#inputStackTop beginStackSize} of the current basic block + * plus stackSize. + */ + private int stackSize; + + /** + * The (relative) maximum stack size after the last visited instruction. + * This size is relative to the beginning of the current basic block, i.e., + * the true maximum stack size after the last visited instruction is equal + * to the {@link Label#inputStackTop beginStackSize} of the current basic + * block plus stackSize. + */ + private int maxStackSize; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link MethodWriter}. + * + * @param cw + * the class writer in which the method must be added. + * @param access + * the method's access flags (see {@link Opcodes}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type}). + * @param signature + * the method's signature. May be null. + * @param exceptions + * the internal names of the method's exceptions. May be + * null. + * @param compute + * Indicates what must be automatically computed (see #compute). + */ + MethodWriter(final ClassWriter cw, final int access, final String name, + final String desc, final String signature, + final String[] exceptions, final int compute) { + super(Opcodes.ASM6); + if (cw.firstMethod == null) { + cw.firstMethod = this; + } else { + cw.lastMethod.mv = this; + } + cw.lastMethod = this; + this.cw = cw; + this.access = access; + if ("".equals(name)) { + this.access |= ACC_CONSTRUCTOR; + } + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + this.descriptor = desc; + this.signature = signature; + if (exceptions != null && exceptions.length > 0) { + exceptionCount = exceptions.length; + this.exceptions = new int[exceptionCount]; + for (int i = 0; i < exceptionCount; ++i) { + this.exceptions[i] = cw.newClass(exceptions[i]); + } + } + this.compute = compute; + if (compute != NOTHING) { + // updates maxLocals + int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; + if ((access & Opcodes.ACC_STATIC) != 0) { + --size; + } + maxLocals = size; + currentLocals = size; + // creates and visits the label for the first basic block + labels = new Label(); + labels.status |= Label.PUSHED; + visitLabel(labels); + } + } + + // ------------------------------------------------------------------------ + // Implementation of the MethodVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public void visitParameter(String name, int access) { + if (methodParameters == null) { + methodParameters = new ByteVector(); + } + ++methodParametersCount; + methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name)) + .putShort(access); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + annd = new ByteVector(); + return new AnnotationWriter(cw, false, annd, null, 0); + } + + @Override + public AnnotationVisitor visitAnnotation(final String desc, + final boolean visible) { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + + @Override + public AnnotationVisitor visitParameterAnnotation(final int parameter, + final String desc, final boolean visible) { + ByteVector bv = new ByteVector(); + if ("Ljava/lang/Synthetic;".equals(desc)) { + // workaround for a bug in javac with synthetic parameters + // see ClassReader.readParameterAnnotations + synthetics = Math.max(synthetics, parameter + 1); + return new AnnotationWriter(cw, false, bv, null, 0); + } + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + if (panns == null) { + panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = panns[parameter]; + panns[parameter] = aw; + } else { + if (ipanns == null) { + ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = ipanns[parameter]; + ipanns[parameter] = aw; + } + return aw; + } + + @Override + public void visitAttribute(final Attribute attr) { + if (attr.isCodeAttribute()) { + attr.next = cattrs; + cattrs = attr; + } else { + attr.next = attrs; + attrs = attr; + } + } + + @Override + public void visitCode() { + } + + @Override + public void visitFrame(final int type, final int nLocal, + final Object[] local, final int nStack, final Object[] stack) { + if (compute == FRAMES) { + return; + } + + if (compute == INSERTED_FRAMES) { + if (currentBlock.frame == null) { + // This should happen only once, for the implicit first frame + // (which is explicitly visited in ClassReader if the + // EXPAND_ASM_INSNS option is used). + currentBlock.frame = new CurrentFrame(); + currentBlock.frame.owner = currentBlock; + currentBlock.frame.initInputFrame(cw, access, + Type.getArgumentTypes(descriptor), nLocal); + visitImplicitFirstFrame(); + } else { + if (type == Opcodes.F_NEW) { + currentBlock.frame.set(cw, nLocal, local, nStack, stack); + } else { + // In this case type is equal to F_INSERT by hypothesis, and + // currentBlock.frame contains the stack map frame at the + // current instruction, computed from the last F_NEW frame + // and the bytecode instructions in between (via calls to + // CurrentFrame#execute). + } + visitFrame(currentBlock.frame); + } + } else if (type == Opcodes.F_NEW) { + if (previousFrame == null) { + visitImplicitFirstFrame(); + } + currentLocals = nLocal; + int frameIndex = startFrame(code.length, nLocal, nStack); + for (int i = 0; i < nLocal; ++i) { + if (local[i] instanceof String) { + String desc = Type.getObjectType((String) local[i]).getDescriptor(); + frame[frameIndex++] = Frame.type(cw, desc); + } else if (local[i] instanceof Integer) { + frame[frameIndex++] = Frame.BASE | ((Integer) local[i]).intValue(); + } else { + frame[frameIndex++] = Frame.UNINITIALIZED + | cw.addUninitializedType("", + ((Label) local[i]).position); + } + } + for (int i = 0; i < nStack; ++i) { + if (stack[i] instanceof String) { + String desc = Type.getObjectType((String) stack[i]).getDescriptor(); + frame[frameIndex++] = Frame.type(cw, desc); + } else if (stack[i] instanceof Integer) { + frame[frameIndex++] = Frame.BASE | ((Integer) stack[i]).intValue(); + } else { + frame[frameIndex++] = Frame.UNINITIALIZED + | cw.addUninitializedType("", + ((Label) stack[i]).position); + } + } + endFrame(); + } else { + int delta; + if (stackMap == null) { + stackMap = new ByteVector(); + delta = code.length; + } else { + delta = code.length - previousFrameOffset - 1; + if (delta < 0) { + if (type == Opcodes.F_SAME) { + return; + } else { + throw new IllegalStateException(); + } + } + } + + switch (type) { + case Opcodes.F_FULL: + currentLocals = nLocal; + stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal); + for (int i = 0; i < nLocal; ++i) { + writeFrameType(local[i]); + } + stackMap.putShort(nStack); + for (int i = 0; i < nStack; ++i) { + writeFrameType(stack[i]); + } + break; + case Opcodes.F_APPEND: + currentLocals += nLocal; + stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta); + for (int i = 0; i < nLocal; ++i) { + writeFrameType(local[i]); + } + break; + case Opcodes.F_CHOP: + currentLocals -= nLocal; + stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta); + break; + case Opcodes.F_SAME: + if (delta < 64) { + stackMap.putByte(delta); + } else { + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); + } + break; + case Opcodes.F_SAME1: + if (delta < 64) { + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + } else { + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + .putShort(delta); + } + writeFrameType(stack[0]); + break; + } + + previousFrameOffset = code.length; + ++frameCount; + } + + maxStack = Math.max(maxStack, nStack); + maxLocals = Math.max(maxLocals, currentLocals); + } + + @Override + public void visitInsn(final int opcode) { + lastCodeOffset = code.length; + // adds the instruction to the bytecode of the method + code.putByte(opcode); + // update currentBlock + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, 0, null, null); + } else { + // updates current and max stack sizes + int size = stackSize + Frame.SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // if opcode == ATHROW or xRETURN, ends current block (no successor) + if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) + || opcode == Opcodes.ATHROW) { + noSuccessor(); + } + } + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + lastCodeOffset = code.length; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, operand, null, null); + } else if (opcode != Opcodes.NEWARRAY) { + // updates current and max stack sizes only for NEWARRAY + // (stack size variation = 0 for BIPUSH or SIPUSH) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + if (opcode == Opcodes.SIPUSH) { + code.put12(opcode, operand); + } else { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + lastCodeOffset = code.length; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, var, null, null); + } else { + // updates current and max stack sizes + if (opcode == Opcodes.RET) { + // no stack change, but end of current block (no successor) + currentBlock.status |= Label.RET; + // save 'stackSize' here for future use + // (see {@link #findSubroutineSuccessors}) + currentBlock.inputStackTop = stackSize; + noSuccessor(); + } else { // xLOAD or xSTORE + int size = stackSize + Frame.SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + } + if (compute != NOTHING) { + // updates max locals + int n; + if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD + || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { + n = var + 2; + } else { + n = var + 1; + } + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if (var < 4 && opcode != Opcodes.RET) { + int opt; + if (opcode < Opcodes.ISTORE) { + /* ILOAD_0 */ + opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; + } else { + /* ISTORE_0 */ + opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; + } + code.putByte(opt); + } else if (var >= 256) { + code.putByte(196 /* WIDE */).put12(opcode, var); + } else { + code.put11(opcode, var); + } + if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) { + visitLabel(new Label()); + } + } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + lastCodeOffset = code.length; + Item i = cw.newStringishItem(ClassWriter.CLASS, type); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, code.length, cw, i); + } else if (opcode == Opcodes.NEW) { + // updates current and max stack sizes only if opcode == NEW + // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(opcode, i.index); + } + + @Override + public void visitFieldInsn(final int opcode, final String owner, + final String name, final String desc) { + lastCodeOffset = code.length; + Item i = cw.newFieldItem(owner, name, desc); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, 0, cw, i); + } else { + int size; + // computes the stack size variation + char c = desc.charAt(0); + switch (opcode) { + case Opcodes.GETSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); + break; + case Opcodes.PUTSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); + break; + case Opcodes.GETFIELD: + size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); + break; + // case Constants.PUTFIELD: + default: + size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); + break; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(opcode, i.index); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + lastCodeOffset = code.length; + Item i = cw.newMethodItem(owner, name, desc, itf); + int argSize = i.intVal; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, 0, cw, i); + } else { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the + * intVal field of this item to store this variation, once it + * has been computed. More precisely this intVal field stores + * the sizes of the arguments and of the return value + * corresponding to desc. + */ + if (argSize == 0) { + // the above sizes have not been computed yet, + // so we compute them... + argSize = Type.getArgumentsAndReturnSizes(desc); + // ... and we save them in order + // not to recompute them in the future + i.intVal = argSize; + } + int size; + if (opcode == Opcodes.INVOKESTATIC) { + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + } else { + size = stackSize - (argSize >> 2) + (argSize & 0x03); + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + if (opcode == Opcodes.INVOKEINTERFACE) { + if (argSize == 0) { + argSize = Type.getArgumentsAndReturnSizes(desc); + i.intVal = argSize; + } + code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); + } else { + code.put12(opcode, i.index); + } + } + + @Override + public void visitInvokeDynamicInsn(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + lastCodeOffset = code.length; + Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); + int argSize = i.intVal; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); + } else { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the + * intVal field of this item to store this variation, once it + * has been computed. More precisely this intVal field stores + * the sizes of the arguments and of the return value + * corresponding to desc. + */ + if (argSize == 0) { + // the above sizes have not been computed yet, + // so we compute them... + argSize = Type.getArgumentsAndReturnSizes(desc); + // ... and we save them in order + // not to recompute them in the future + i.intVal = argSize; + } + int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.INVOKEDYNAMIC, i.index); + code.putShort(0); + } + + @Override + public void visitJumpInsn(int opcode, final Label label) { + boolean isWide = opcode >= 200; // GOTO_W + opcode = isWide ? opcode - 33 : opcode; + lastCodeOffset = code.length; + Label nextInsn = null; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, 0, null, null); + // 'label' is the target of a jump instruction + label.getFirst().status |= Label.TARGET; + // adds 'label' as a successor of this basic block + addSuccessor(Edge.NORMAL, label); + if (opcode != Opcodes.GOTO) { + // creates a Label for the next basic block + nextInsn = new Label(); + } + } else if (compute == INSERTED_FRAMES) { + currentBlock.frame.execute(opcode, 0, null, null); + } else { + if (opcode == Opcodes.JSR) { + if ((label.status & Label.SUBROUTINE) == 0) { + label.status |= Label.SUBROUTINE; + ++subroutines; + } + currentBlock.status |= Label.JSR; + addSuccessor(stackSize + 1, label); + // creates a Label for the next basic block + nextInsn = new Label(); + /* + * note that, by construction in this method, a JSR block + * has at least two successors in the control flow graph: + * the first one leads the next instruction after the JSR, + * while the second one leads to the JSR target. + */ + } else { + // updates current stack size (max stack size unchanged + // because stack size variation always negative in this + // case) + stackSize += Frame.SIZE[opcode]; + addSuccessor(stackSize, label); + } + } + } + // adds the instruction to the bytecode of the method + if ((label.status & Label.RESOLVED) != 0 + && label.position - code.length < Short.MIN_VALUE) { + /* + * case of a backward jump with an offset < -32768. In this case we + * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx + * with IFNOTxxx GOTO_W L:..., where IFNOTxxx is the + * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where + * designates the instruction just after the GOTO_W. + */ + if (opcode == Opcodes.GOTO) { + code.putByte(200); // GOTO_W + } else if (opcode == Opcodes.JSR) { + code.putByte(201); // JSR_W + } else { + // if the IF instruction is transformed into IFNOT GOTO_W the + // next instruction becomes the target of the IFNOT instruction + if (nextInsn != null) { + nextInsn.status |= Label.TARGET; + } + code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1); + code.putShort(8); // jump offset + // ASM pseudo GOTO_W insn, see ClassReader. We don't use a real + // GOTO_W because we might need to insert a frame just after (as + // the target of the IFNOTxxx jump instruction). + code.putByte(220); + cw.hasAsmInsns = true; + } + label.put(this, code, code.length - 1, true); + } else if (isWide) { + /* + * case of a GOTO_W or JSR_W specified by the user (normally + * ClassReader when used to resize instructions). In this case we + * keep the original instruction. + */ + code.putByte(opcode + 33); + label.put(this, code, code.length - 1, true); + } else { + /* + * case of a backward jump with an offset >= -32768, or of a forward + * jump with, of course, an unknown offset. In these cases we store + * the offset in 2 bytes (which will be increased in + * resizeInstructions, if needed). + */ + code.putByte(opcode); + label.put(this, code, code.length - 1, false); + } + if (currentBlock != null) { + if (nextInsn != null) { + // if the jump instruction is not a GOTO, the next instruction + // is also a successor of this instruction. Calling visitLabel + // adds the label of this next instruction as a successor of the + // current block, and starts a new basic block + visitLabel(nextInsn); + } + if (opcode == Opcodes.GOTO) { + noSuccessor(); + } + } + } + + @Override + public void visitLabel(final Label label) { + // resolves previous forward references to label, if any + cw.hasAsmInsns |= label.resolve(this, code.length, code.data); + // updates currentBlock + if ((label.status & Label.DEBUG) != 0) { + return; + } + if (compute == FRAMES) { + if (currentBlock != null) { + if (label.position == currentBlock.position) { + // successive labels, do not start a new basic block + currentBlock.status |= (label.status & Label.TARGET); + label.frame = currentBlock.frame; + return; + } + // ends current block (with one new successor) + addSuccessor(Edge.NORMAL, label); + } + // begins a new current block + currentBlock = label; + if (label.frame == null) { + label.frame = new Frame(); + label.frame.owner = label; + } + // updates the basic block list + if (previousBlock != null) { + if (label.position == previousBlock.position) { + previousBlock.status |= (label.status & Label.TARGET); + label.frame = previousBlock.frame; + currentBlock = previousBlock; + return; + } + previousBlock.successor = label; + } + previousBlock = label; + } else if (compute == INSERTED_FRAMES) { + if (currentBlock == null) { + // This case should happen only once, for the visitLabel call in + // the constructor. Indeed, if compute is equal to + // INSERTED_FRAMES currentBlock can not be set back to null (see + // #noSuccessor). + currentBlock = label; + } else { + // Updates the frame owner so that a correct frame offset is + // computed in visitFrame(Frame). + currentBlock.frame.owner = label; + } + } else if (compute == MAXS) { + if (currentBlock != null) { + // ends current block (with one new successor) + currentBlock.outputStackMax = maxStackSize; + addSuccessor(stackSize, label); + } + // begins a new current block + currentBlock = label; + // resets the relative current and max stack sizes + stackSize = 0; + maxStackSize = 0; + // updates the basic block list + if (previousBlock != null) { + previousBlock.successor = label; + } + previousBlock = label; + } + } + + @Override + public void visitLdcInsn(final Object cst) { + lastCodeOffset = code.length; + Item i = cw.newConstItem(cst); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); + } else { + int size; + // computes the stack size variation + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + size = stackSize + 2; + } else { + size = stackSize + 1; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + int index = i.index; + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + code.put12(20 /* LDC2_W */, index); + } else if (index >= 256) { + code.put12(19 /* LDC_W */, index); + } else { + code.put11(Opcodes.LDC, index); + } + } + + @Override + public void visitIincInsn(final int var, final int increment) { + lastCodeOffset = code.length; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(Opcodes.IINC, var, null, null); + } + } + if (compute != NOTHING) { + // updates max locals + int n = var + 1; + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if ((var > 255) || (increment > 127) || (increment < -128)) { + code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var) + .putShort(increment); + } else { + code.putByte(Opcodes.IINC).put11(var, increment); + } + } + + @Override + public void visitTableSwitchInsn(final int min, final int max, + final Label dflt, final Label... labels) { + lastCodeOffset = code.length; + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.TABLESWITCH); + code.putByteArray(null, 0, (4 - code.length % 4) % 4); + dflt.put(this, code, source, true); + code.putInt(min).putInt(max); + for (int i = 0; i < labels.length; ++i) { + labels[i].put(this, code, source, true); + } + // updates currentBlock + visitSwitchInsn(dflt, labels); + } + + @Override + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, + final Label[] labels) { + lastCodeOffset = code.length; + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.LOOKUPSWITCH); + code.putByteArray(null, 0, (4 - code.length % 4) % 4); + dflt.put(this, code, source, true); + code.putInt(labels.length); + for (int i = 0; i < labels.length; ++i) { + code.putInt(keys[i]); + labels[i].put(this, code, source, true); + } + // updates currentBlock + visitSwitchInsn(dflt, labels); + } + + private void visitSwitchInsn(final Label dflt, final Label[] labels) { + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); + // adds current block successors + addSuccessor(Edge.NORMAL, dflt); + dflt.getFirst().status |= Label.TARGET; + for (int i = 0; i < labels.length; ++i) { + addSuccessor(Edge.NORMAL, labels[i]); + labels[i].getFirst().status |= Label.TARGET; + } + } else { + // updates current stack size (max stack size unchanged) + --stackSize; + // adds current block successors + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + } + // ends current block + noSuccessor(); + } + } + + @Override + public void visitMultiANewArrayInsn(final String desc, final int dims) { + lastCodeOffset = code.length; + Item i = cw.newStringishItem(ClassWriter.CLASS, desc); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES || compute == INSERTED_FRAMES) { + currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); + } else { + // updates current stack size (max stack size unchanged because + // stack size variation always negative or null) + stackSize += 1 - dims; + } + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); + } + + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + + @Override + public void visitTryCatchBlock(final Label start, final Label end, + final Label handler, final String type) { + ++handlerCount; + Handler h = new Handler(); + h.start = start; + h.end = end; + h.handler = handler; + h.desc = type; + h.type = type != null ? cw.newClass(type) : 0; + if (lastHandler == null) { + firstHandler = h; + } else { + lastHandler.next = h; + } + lastHandler = h; + } + + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + + @Override + public void visitLocalVariable(final String name, final String desc, + final String signature, final Label start, final Label end, + final int index) { + if (signature != null) { + if (localVarType == null) { + localVarType = new ByteVector(); + } + ++localVarTypeCount; + localVarType.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature)) + .putShort(index); + } + if (localVar == null) { + localVar = new ByteVector(); + } + ++localVarCount; + localVar.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc)) + .putShort(index); + if (compute != NOTHING) { + // updates max locals + char c = desc.charAt(0); + int n = index + (c == 'J' || c == 'D' ? 2 : 1); + if (n > maxLocals) { + maxLocals = n; + } + } + } + + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + bv.putByte(typeRef >>> 24).putShort(start.length); + for (int i = 0; i < start.length; ++i) { + bv.putShort(start[i].position) + .putShort(end[i].position - start[i].position) + .putShort(index[i]); + } + if (typePath == null) { + bv.putByte(0); + } else { + int length = typePath.b[typePath.offset] * 2 + 1; + bv.putByteArray(typePath.b, typePath.offset, length); + } + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + + @Override + public void visitLineNumber(final int line, final Label start) { + if (lineNumber == null) { + lineNumber = new ByteVector(); + } + ++lineNumberCount; + lineNumber.putShort(start.position); + lineNumber.putShort(line); + } + + @Override + public void visitMaxs(final int maxStack, final int maxLocals) { + if (compute == FRAMES) { + // completes the control flow graph with exception handler blocks + Handler handler = firstHandler; + while (handler != null) { + Label l = handler.start.getFirst(); + Label h = handler.handler.getFirst(); + Label e = handler.end.getFirst(); + // computes the kind of the edges to 'h' + String t = handler.desc == null ? "java/lang/Throwable" + : handler.desc; + int kind = Frame.OBJECT | cw.addType(t); + // h is an exception handler + h.status |= Label.TARGET; + // adds 'h' as a successor of labels between 'start' and 'end' + while (l != e) { + // creates an edge to 'h' + Edge b = new Edge(); + b.info = kind; + b.successor = h; + // adds it to the successors of 'l' + b.next = l.successors; + l.successors = b; + // goes to the next label + l = l.successor; + } + handler = handler.next; + } + + // creates and visits the first (implicit) frame + Frame f = labels.frame; + f.initInputFrame(cw, access, Type.getArgumentTypes(descriptor), + this.maxLocals); + visitFrame(f); + + /* + * fix point algorithm: mark the first basic block as 'changed' + * (i.e. put it in the 'changed' list) and, while there are changed + * basic blocks, choose one, mark it as unchanged, and update its + * successors (which can be changed in the process). + */ + int max = 0; + Label changed = labels; + while (changed != null) { + // removes a basic block from the list of changed basic blocks + Label l = changed; + changed = changed.next; + l.next = null; + f = l.frame; + // a reachable jump target must be stored in the stack map + if ((l.status & Label.TARGET) != 0) { + l.status |= Label.STORE; + } + // all visited labels are reachable, by definition + l.status |= Label.REACHABLE; + // updates the (absolute) maximum stack size + int blockMax = f.inputStack.length + l.outputStackMax; + if (blockMax > max) { + max = blockMax; + } + // updates the successors of the current basic block + Edge e = l.successors; + while (e != null) { + Label n = e.successor.getFirst(); + boolean change = f.merge(cw, n.frame, e.info); + if (change && n.next == null) { + // if n has changed and is not already in the 'changed' + // list, adds it to this list + n.next = changed; + changed = n; + } + e = e.next; + } + } + + // visits all the frames that must be stored in the stack map + Label l = labels; + while (l != null) { + f = l.frame; + if ((l.status & Label.STORE) != 0) { + visitFrame(f); + } + if ((l.status & Label.REACHABLE) == 0) { + // finds start and end of dead basic block + Label k = l.successor; + int start = l.position; + int end = (k == null ? code.length : k.position) - 1; + // if non empty basic block + if (end >= start) { + max = Math.max(max, 1); + // replaces instructions with NOP ... NOP ATHROW + for (int i = start; i < end; ++i) { + code.data[i] = Opcodes.NOP; + } + code.data[end] = (byte) Opcodes.ATHROW; + // emits a frame for this unreachable block + int frameIndex = startFrame(start, 0, 1); + frame[frameIndex] = Frame.OBJECT + | cw.addType("java/lang/Throwable"); + endFrame(); + // removes the start-end range from the exception + // handlers + firstHandler = Handler.remove(firstHandler, l, k); + } + } + l = l.successor; + } + + handler = firstHandler; + handlerCount = 0; + while (handler != null) { + handlerCount += 1; + handler = handler.next; + } + + this.maxStack = max; + } else if (compute == MAXS) { + // completes the control flow graph with exception handler blocks + Handler handler = firstHandler; + while (handler != null) { + Label l = handler.start; + Label h = handler.handler; + Label e = handler.end; + // adds 'h' as a successor of labels between 'start' and 'end' + while (l != e) { + // creates an edge to 'h' + Edge b = new Edge(); + b.info = Edge.EXCEPTION; + b.successor = h; + // adds it to the successors of 'l' + if ((l.status & Label.JSR) == 0) { + b.next = l.successors; + l.successors = b; + } else { + // if l is a JSR block, adds b after the first two edges + // to preserve the hypothesis about JSR block successors + // order (see {@link #visitJumpInsn}) + b.next = l.successors.next.next; + l.successors.next.next = b; + } + // goes to the next label + l = l.successor; + } + handler = handler.next; + } + + if (subroutines > 0) { + // completes the control flow graph with the RET successors + /* + * first step: finds the subroutines. This step determines, for + * each basic block, to which subroutine(s) it belongs. + */ + // finds the basic blocks that belong to the "main" subroutine + int id = 0; + labels.visitSubroutine(null, 1, subroutines); + // finds the basic blocks that belong to the real subroutines + Label l = labels; + while (l != null) { + if ((l.status & Label.JSR) != 0) { + // the subroutine is defined by l's TARGET, not by l + Label subroutine = l.successors.next.successor; + // if this subroutine has not been visited yet... + if ((subroutine.status & Label.VISITED) == 0) { + // ...assigns it a new id and finds its basic blocks + id += 1; + subroutine.visitSubroutine(null, (id / 32L) << 32 + | (1L << (id % 32)), subroutines); + } + } + l = l.successor; + } + // second step: finds the successors of RET blocks + l = labels; + while (l != null) { + if ((l.status & Label.JSR) != 0) { + Label L = labels; + while (L != null) { + L.status &= ~Label.VISITED2; + L = L.successor; + } + // the subroutine is defined by l's TARGET, not by l + Label subroutine = l.successors.next.successor; + subroutine.visitSubroutine(l, 0, subroutines); + } + l = l.successor; + } + } + + /* + * control flow analysis algorithm: while the block stack is not + * empty, pop a block from this stack, update the max stack size, + * compute the true (non relative) begin stack size of the + * successors of this block, and push these successors onto the + * stack (unless they have already been pushed onto the stack). + * Note: by hypothesis, the {@link Label#inputStackTop} of the + * blocks in the block stack are the true (non relative) beginning + * stack sizes of these blocks. + */ + int max = 0; + Label stack = labels; + while (stack != null) { + // pops a block from the stack + Label l = stack; + stack = stack.next; + // computes the true (non relative) max stack size of this block + int start = l.inputStackTop; + int blockMax = start + l.outputStackMax; + // updates the global max stack size + if (blockMax > max) { + max = blockMax; + } + // analyzes the successors of the block + Edge b = l.successors; + if ((l.status & Label.JSR) != 0) { + // ignores the first edge of JSR blocks (virtual successor) + b = b.next; + } + while (b != null) { + l = b.successor; + // if this successor has not already been pushed... + if ((l.status & Label.PUSHED) == 0) { + // computes its true beginning stack size... + l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start + + b.info; + // ...and pushes it onto the stack + l.status |= Label.PUSHED; + l.next = stack; + stack = l; + } + b = b.next; + } + } + this.maxStack = Math.max(maxStack, max); + } else { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + } + + @Override + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Utility methods: control flow analysis algorithm + // ------------------------------------------------------------------------ + + /** + * Adds a successor to the {@link #currentBlock currentBlock} block. + * + * @param info + * information about the control flow edge to be added. + * @param successor + * the successor block to be added to the current block. + */ + private void addSuccessor(final int info, final Label successor) { + // creates and initializes an Edge object... + Edge b = new Edge(); + b.info = info; + b.successor = successor; + // ...and adds it to the successor list of the currentBlock block + b.next = currentBlock.successors; + currentBlock.successors = b; + } + + /** + * Ends the current basic block. This method must be used in the case where + * the current basic block does not have any successor. + */ + private void noSuccessor() { + if (compute == FRAMES) { + Label l = new Label(); + l.frame = new Frame(); + l.frame.owner = l; + l.resolve(this, code.length, code.data); + previousBlock.successor = l; + previousBlock = l; + } else { + currentBlock.outputStackMax = maxStackSize; + } + if (compute != INSERTED_FRAMES) { + currentBlock = null; + } + } + + // ------------------------------------------------------------------------ + // Utility methods: stack map frames + // ------------------------------------------------------------------------ + + /** + * Visits a frame that has been computed from scratch. + * + * @param f + * the frame that must be visited. + */ + private void visitFrame(final Frame f) { + int i, t; + int nTop = 0; + int nLocal = 0; + int nStack = 0; + int[] locals = f.inputLocals; + int[] stacks = f.inputStack; + // computes the number of locals (ignores TOP types that are just after + // a LONG or a DOUBLE, and all trailing TOP types) + for (i = 0; i < locals.length; ++i) { + t = locals[i]; + if (t == Frame.TOP) { + ++nTop; + } else { + nLocal += nTop + 1; + nTop = 0; + } + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + // computes the stack size (ignores TOP types that are just after + // a LONG or a DOUBLE) + for (i = 0; i < stacks.length; ++i) { + t = stacks[i]; + ++nStack; + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + // visits the frame and its content + int frameIndex = startFrame(f.owner.position, nLocal, nStack); + for (i = 0; nLocal > 0; ++i, --nLocal) { + t = locals[i]; + frame[frameIndex++] = t; + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + for (i = 0; i < stacks.length; ++i) { + t = stacks[i]; + frame[frameIndex++] = t; + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + endFrame(); + } + + /** + * Visit the implicit first frame of this method. + */ + private void visitImplicitFirstFrame() { + // There can be at most descriptor.length() + 1 locals + int frameIndex = startFrame(0, descriptor.length() + 1, 0); + if ((access & Opcodes.ACC_STATIC) == 0) { + if ((access & ACC_CONSTRUCTOR) == 0) { + frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName); + } else { + frame[frameIndex++] = Frame.UNINITIALIZED_THIS; + } + } + int i = 1; + loop: while (true) { + int j = i; + switch (descriptor.charAt(i++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + frame[frameIndex++] = Frame.INTEGER; + break; + case 'F': + frame[frameIndex++] = Frame.FLOAT; + break; + case 'J': + frame[frameIndex++] = Frame.LONG; + break; + case 'D': + frame[frameIndex++] = Frame.DOUBLE; + break; + case '[': + while (descriptor.charAt(i) == '[') { + ++i; + } + if (descriptor.charAt(i) == 'L') { + ++i; + while (descriptor.charAt(i) != ';') { + ++i; + } + } + frame[frameIndex++] = Frame.type(cw, descriptor.substring(j, ++i)); + break; + case 'L': + while (descriptor.charAt(i) != ';') { + ++i; + } + frame[frameIndex++] = Frame.OBJECT + | cw.addType(descriptor.substring(j + 1, i++)); + break; + default: + break loop; + } + } + frame[1] = frameIndex - 3; + endFrame(); + } + + /** + * Starts the visit of a stack map frame. + * + * @param offset + * the offset of the instruction to which the frame corresponds. + * @param nLocal + * the number of local variables in the frame. + * @param nStack + * the number of stack elements in the frame. + * @return the index of the next element to be written in this frame. + */ + private int startFrame(final int offset, final int nLocal, final int nStack) { + int n = 3 + nLocal + nStack; + if (frame == null || frame.length < n) { + frame = new int[n]; + } + frame[0] = offset; + frame[1] = nLocal; + frame[2] = nStack; + return 3; + } + + /** + * Checks if the visit of the current frame {@link #frame} is finished, and + * if yes, write it in the StackMapTable attribute. + */ + private void endFrame() { + if (previousFrame != null) { // do not write the first frame + if (stackMap == null) { + stackMap = new ByteVector(); + } + writeFrame(); + ++frameCount; + } + previousFrame = frame; + frame = null; + } + + /** + * Compress and writes the current frame {@link #frame} in the StackMapTable + * attribute. + */ + private void writeFrame() { + int clocalsSize = frame[1]; + int cstackSize = frame[2]; + if ((cw.version & 0xFFFF) < Opcodes.V1_6) { + stackMap.putShort(frame[0]).putShort(clocalsSize); + writeFrameTypes(3, 3 + clocalsSize); + stackMap.putShort(cstackSize); + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); + return; + } + int localsSize = previousFrame[1]; + int type = FULL_FRAME; + int k = 0; + int delta; + if (frameCount == 0) { + delta = frame[0]; + } else { + delta = frame[0] - previousFrame[0] - 1; + } + if (cstackSize == 0) { + k = clocalsSize - localsSize; + switch (k) { + case -3: + case -2: + case -1: + type = CHOP_FRAME; + localsSize = clocalsSize; + break; + case 0: + type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; + break; + case 1: + case 2: + case 3: + type = APPEND_FRAME; + break; + } + } else if (clocalsSize == localsSize && cstackSize == 1) { + type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME + : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; + } + if (type != FULL_FRAME) { + // verify if locals are the same + int l = 3; + for (int j = 0; j < localsSize; j++) { + if (frame[l] != previousFrame[l]) { + type = FULL_FRAME; + break; + } + l++; + } + } + switch (type) { + case SAME_FRAME: + stackMap.putByte(delta); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort( + delta); + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); + break; + case SAME_FRAME_EXTENDED: + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); + break; + case CHOP_FRAME: + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); + break; + case APPEND_FRAME: + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); + writeFrameTypes(3 + localsSize, 3 + clocalsSize); + break; + // case FULL_FRAME: + default: + stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize); + writeFrameTypes(3, 3 + clocalsSize); + stackMap.putShort(cstackSize); + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); + } + } + + /** + * Writes some types of the current frame {@link #frame} into the + * StackMapTableAttribute. This method converts types from the format used + * in {@link Label} to the format used in StackMapTable attributes. In + * particular, it converts type table indexes to constant pool indexes. + * + * @param start + * index of the first type in {@link #frame} to write. + * @param end + * index of last type in {@link #frame} to write (exclusive). + */ + private void writeFrameTypes(final int start, final int end) { + for (int i = start; i < end; ++i) { + int t = frame[i]; + int d = t & Frame.DIM; + if (d == 0) { + int v = t & Frame.BASE_VALUE; + switch (t & Frame.BASE_KIND) { + case Frame.OBJECT: + stackMap.putByte(7).putShort( + cw.newClass(cw.typeTable[v].strVal1)); + break; + case Frame.UNINITIALIZED: + stackMap.putByte(8).putShort(cw.typeTable[v].intVal); + break; + default: + stackMap.putByte(v); + } + } else { + StringBuilder sb = new StringBuilder(); + d >>= 28; + while (d-- > 0) { + sb.append('['); + } + if ((t & Frame.BASE_KIND) == Frame.OBJECT) { + sb.append('L'); + sb.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); + sb.append(';'); + } else { + switch (t & 0xF) { + case 1: + sb.append('I'); + break; + case 2: + sb.append('F'); + break; + case 3: + sb.append('D'); + break; + case 9: + sb.append('Z'); + break; + case 10: + sb.append('B'); + break; + case 11: + sb.append('C'); + break; + case 12: + sb.append('S'); + break; + default: + sb.append('J'); + } + } + stackMap.putByte(7).putShort(cw.newClass(sb.toString())); + } + } + } + + private void writeFrameType(final Object type) { + if (type instanceof String) { + stackMap.putByte(7).putShort(cw.newClass((String) type)); + } else if (type instanceof Integer) { + stackMap.putByte(((Integer) type).intValue()); + } else { + stackMap.putByte(8).putShort(((Label) type).position); + } + } + + // ------------------------------------------------------------------------ + // Utility methods: dump bytecode array + // ------------------------------------------------------------------------ + + /** + * Returns the size of the bytecode of this method. + * + * @return the size of the bytecode of this method. + */ + final int getSize() { + if (classReaderOffset != 0) { + return 6 + classReaderLength; + } + int size = 8; + if (code.length > 0) { + if (code.length > 65535) { + throw new RuntimeException("Method code too large!"); + } + cw.newUTF8("Code"); + size += 18 + code.length + 8 * handlerCount; + if (localVar != null) { + cw.newUTF8("LocalVariableTable"); + size += 8 + localVar.length; + } + if (localVarType != null) { + cw.newUTF8("LocalVariableTypeTable"); + size += 8 + localVarType.length; + } + if (lineNumber != null) { + cw.newUTF8("LineNumberTable"); + size += 8 + lineNumber.length; + } + if (stackMap != null) { + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; + cw.newUTF8(zip ? "StackMapTable" : "StackMap"); + size += 8 + stackMap.length; + } + if (ctanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + ctanns.getSize(); + } + if (ictanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + ictanns.getSize(); + } + if (cattrs != null) { + size += cattrs.getSize(cw, code.data, code.length, maxStack, + maxLocals); + } + } + if (exceptionCount > 0) { + cw.newUTF8("Exceptions"); + size += 8 + 2 * exceptionCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + cw.newUTF8("Synthetic"); + size += 6; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (signature != null) { + cw.newUTF8("Signature"); + cw.newUTF8(signature); + size += 8; + } + if (methodParameters != null) { + cw.newUTF8("MethodParameters"); + size += 7 + methodParameters.length; + } + if (annd != null) { + cw.newUTF8("AnnotationDefault"); + size += 6 + annd.length; + } + if (anns != null) { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if (ianns != null) { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if (tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } + if (panns != null) { + cw.newUTF8("RuntimeVisibleParameterAnnotations"); + size += 7 + 2 * (panns.length - synthetics); + for (int i = panns.length - 1; i >= synthetics; --i) { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + } + if (ipanns != null) { + cw.newUTF8("RuntimeInvisibleParameterAnnotations"); + size += 7 + 2 * (ipanns.length - synthetics); + for (int i = ipanns.length - 1; i >= synthetics; --i) { + size += ipanns[i] == null ? 0 : ipanns[i].getSize(); + } + } + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the bytecode of this method in the given byte vector. + * + * @param out + * the byte vector into which the bytecode of this method must be + * copied. + */ + final void put(final ByteVector out) { + final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; + int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED + | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE + | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); + out.putShort(access & ~mask).putShort(name).putShort(desc); + if (classReaderOffset != 0) { + out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); + return; + } + int attributeCount = 0; + if (code.length > 0) { + ++attributeCount; + } + if (exceptionCount > 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + ++attributeCount; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (signature != null) { + ++attributeCount; + } + if (methodParameters != null) { + ++attributeCount; + } + if (annd != null) { + ++attributeCount; + } + if (anns != null) { + ++attributeCount; + } + if (ianns != null) { + ++attributeCount; + } + if (tanns != null) { + ++attributeCount; + } + if (itanns != null) { + ++attributeCount; + } + if (panns != null) { + ++attributeCount; + } + if (ipanns != null) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (code.length > 0) { + int size = 12 + code.length + 8 * handlerCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (localVarType != null) { + size += 8 + localVarType.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + if (stackMap != null) { + size += 8 + stackMap.length; + } + if (ctanns != null) { + size += 8 + ctanns.getSize(); + } + if (ictanns != null) { + size += 8 + ictanns.getSize(); + } + if (cattrs != null) { + size += cattrs.getSize(cw, code.data, code.length, maxStack, + maxLocals); + } + out.putShort(cw.newUTF8("Code")).putInt(size); + out.putShort(maxStack).putShort(maxLocals); + out.putInt(code.length).putByteArray(code.data, 0, code.length); + out.putShort(handlerCount); + if (handlerCount > 0) { + Handler h = firstHandler; + while (h != null) { + out.putShort(h.start.position).putShort(h.end.position) + .putShort(h.handler.position).putShort(h.type); + h = h.next; + } + } + attributeCount = 0; + if (localVar != null) { + ++attributeCount; + } + if (localVarType != null) { + ++attributeCount; + } + if (lineNumber != null) { + ++attributeCount; + } + if (stackMap != null) { + ++attributeCount; + } + if (ctanns != null) { + ++attributeCount; + } + if (ictanns != null) { + ++attributeCount; + } + if (cattrs != null) { + attributeCount += cattrs.getCount(); + } + out.putShort(attributeCount); + if (localVar != null) { + out.putShort(cw.newUTF8("LocalVariableTable")); + out.putInt(localVar.length + 2).putShort(localVarCount); + out.putByteArray(localVar.data, 0, localVar.length); + } + if (localVarType != null) { + out.putShort(cw.newUTF8("LocalVariableTypeTable")); + out.putInt(localVarType.length + 2).putShort(localVarTypeCount); + out.putByteArray(localVarType.data, 0, localVarType.length); + } + if (lineNumber != null) { + out.putShort(cw.newUTF8("LineNumberTable")); + out.putInt(lineNumber.length + 2).putShort(lineNumberCount); + out.putByteArray(lineNumber.data, 0, lineNumber.length); + } + if (stackMap != null) { + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; + out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); + out.putInt(stackMap.length + 2).putShort(frameCount); + out.putByteArray(stackMap.data, 0, stackMap.length); + } + if (ctanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + ctanns.put(out); + } + if (ictanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + ictanns.put(out); + } + if (cattrs != null) { + cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); + } + } + if (exceptionCount > 0) { + out.putShort(cw.newUTF8("Exceptions")).putInt( + 2 * exceptionCount + 2); + out.putShort(exceptionCount); + for (int i = 0; i < exceptionCount; ++i) { + out.putShort(exceptions[i]); + } + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (signature != null) { + out.putShort(cw.newUTF8("Signature")).putInt(2) + .putShort(cw.newUTF8(signature)); + } + if (methodParameters != null) { + out.putShort(cw.newUTF8("MethodParameters")); + out.putInt(methodParameters.length + 1).putByte( + methodParametersCount); + out.putByteArray(methodParameters.data, 0, methodParameters.length); + } + if (annd != null) { + out.putShort(cw.newUTF8("AnnotationDefault")); + out.putInt(annd.length); + out.putByteArray(annd.data, 0, annd.length); + } + if (anns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ianns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } + if (panns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); + AnnotationWriter.put(panns, synthetics, out); + } + if (ipanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); + AnnotationWriter.put(ipanns, synthetics, out); + } + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/ModuleVisitor.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/ModuleVisitor.java new file mode 100644 index 000000000..e68fa2ce8 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/ModuleVisitor.java @@ -0,0 +1,190 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * A visitor to visit a Java module. The methods of this class must be called in + * the following order: visitMainClass | ( visitPackage | + * visitRequire | visitExport | visitOpen | + * visitUse | visitProvide )* visitEnd. + * + * The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)}, + * {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)} + * take as parameter a package name or a module name. Unlike the other names which are internal names + * (names separated by slash), module and package names are qualified names (names separated by dot). + * + * @author Remi Forax + */ +public abstract class ModuleVisitor { + /** + * The ASM API version implemented by this visitor. The value of this field + * must be {@link Opcodes#ASM6}. + */ + protected final int api; + + /** + * The module visitor to which this visitor must delegate method calls. May + * be null. + */ + protected ModuleVisitor mv; + + /** + * Constructs a new {@link ModuleVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM6}. + */ + public ModuleVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link ModuleVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM6}. + * @param mv + * the module visitor to which this visitor must delegate method + * calls. May be null. + */ + public ModuleVisitor(final int api, final ModuleVisitor mv) { + if (api != Opcodes.ASM6) { + throw new IllegalArgumentException(); + } + this.api = api; + this.mv = mv; + } + + /** + * Visit the main class of the current module. + * + * @param mainClass the internal name of the main class of the current module. + */ + public void visitMainClass(String mainClass) { + if (mv != null) { + mv.visitMainClass(mainClass); + } + } + + /** + * Visit a package of the current module. + * + * @param packaze the qualified name of a package. + */ + public void visitPackage(String packaze) { + if (mv != null) { + mv.visitPackage(packaze); + } + } + + /** + * Visits a dependence of the current module. + * + * @param module the qualified name of the dependence. + * @param access the access flag of the dependence among + * ACC_TRANSITIVE, ACC_STATIC_PHASE, ACC_SYNTHETIC + * and ACC_MANDATED. + * @param version the module version at compile time or null. + */ + public void visitRequire(String module, int access, String version) { + if (mv != null) { + mv.visitRequire(module, access, version); + } + } + + /** + * Visit an exported package of the current module. + * + * @param packaze the qualified name of the exported package. + * @param access the access flag of the exported package, + * valid values are among {@code ACC_SYNTHETIC} and + * {@code ACC_MANDATED}. + * @param modules the qualified names of the modules that can access to + * the public classes of the exported package or + * null. + */ + public void visitExport(String packaze, int access, String... modules) { + if (mv != null) { + mv.visitExport(packaze, access, modules); + } + } + + /** + * Visit an open package of the current module. + * + * @param packaze the qualified name of the opened package. + * @param access the access flag of the opened package, + * valid values are among {@code ACC_SYNTHETIC} and + * {@code ACC_MANDATED}. + * @param modules the qualified names of the modules that can use deep + * reflection to the classes of the open package or + * null. + */ + public void visitOpen(String packaze, int access, String... modules) { + if (mv != null) { + mv.visitOpen(packaze, access, modules); + } + } + + /** + * Visit a service used by the current module. + * The name must be the internal name of an interface or a class. + * + * @param service the internal name of the service. + */ + public void visitUse(String service) { + if (mv != null) { + mv.visitUse(service); + } + } + + /** + * Visit an implementation of a service. + * + * @param service the internal name of the service + * @param providers the internal names of the implementations + * of the service (there is at least one provider). + */ + public void visitProvide(String service, String... providers) { + if (mv != null) { + mv.visitProvide(service, providers); + } + } + + /** + * Visits the end of the module. This method, which is the last one to be + * called, is used to inform the visitor that everything have been visited. + */ + public void visitEnd() { + if (mv != null) { + mv.visitEnd(); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/ModuleWriter.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/ModuleWriter.java new file mode 100644 index 000000000..8fb80bd00 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/ModuleWriter.java @@ -0,0 +1,293 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.fr.third.org.objectweb.asm; + +/** + * @author Remi Forax + */ +final class ModuleWriter extends ModuleVisitor { + /** + * The class writer to which this Module attribute must be added. + */ + private final ClassWriter cw; + + /** + * size in byte of the Module attribute. + */ + int size; + + /** + * Number of attributes associated with the current module + * (Version, ConcealPackages, etc) + */ + int attributeCount; + + /** + * Size in bytes of the attributes associated with the current module + */ + int attributesSize; + + /** + * module name index in the constant pool + */ + private final int name; + + /** + * module access flags + */ + private final int access; + + /** + * module version index in the constant pool or 0 + */ + private final int version; + + /** + * module main class index in the constant pool or 0 + */ + private int mainClass; + + /** + * number of packages + */ + private int packageCount; + + /** + * The packages in bytecode form. This byte vector only contains + * the items themselves, the number of items is store in packageCount + */ + private ByteVector packages; + + /** + * number of requires items + */ + private int requireCount; + + /** + * The requires items in bytecode form. This byte vector only contains + * the items themselves, the number of items is store in requireCount + */ + private ByteVector requires; + + /** + * number of exports items + */ + private int exportCount; + + /** + * The exports items in bytecode form. This byte vector only contains + * the items themselves, the number of items is store in exportCount + */ + private ByteVector exports; + + /** + * number of opens items + */ + private int openCount; + + /** + * The opens items in bytecode form. This byte vector only contains + * the items themselves, the number of items is store in openCount + */ + private ByteVector opens; + + /** + * number of uses items + */ + private int useCount; + + /** + * The uses items in bytecode form. This byte vector only contains + * the items themselves, the number of items is store in useCount + */ + private ByteVector uses; + + /** + * number of provides items + */ + private int provideCount; + + /** + * The uses provides in bytecode form. This byte vector only contains + * the items themselves, the number of items is store in provideCount + */ + private ByteVector provides; + + ModuleWriter(final ClassWriter cw, final int name, + final int access, final int version) { + super(Opcodes.ASM6); + this.cw = cw; + this.size = 16; // name + access + version + 5 counts + this.name = name; + this.access = access; + this.version = version; + } + + @Override + public void visitMainClass(String mainClass) { + if (this.mainClass == 0) { // protect against several calls to visitMainClass + cw.newUTF8("ModuleMainClass"); + attributeCount++; + attributesSize += 8; + } + this.mainClass = cw.newClass(mainClass); + } + + @Override + public void visitPackage(String packaze) { + if (packages == null) { + // protect against several calls to visitPackage + cw.newUTF8("ModulePackages"); + packages = new ByteVector(); + attributeCount++; + attributesSize += 8; + } + packages.putShort(cw.newPackage(packaze)); + packageCount++; + attributesSize += 2; + } + + @Override + public void visitRequire(String module, int access, String version) { + if (requires == null) { + requires = new ByteVector(); + } + requires.putShort(cw.newModule(module)) + .putShort(access) + .putShort(version == null? 0: cw.newUTF8(version)); + requireCount++; + size += 6; + } + + @Override + public void visitExport(String packaze, int access, String... modules) { + if (exports == null) { + exports = new ByteVector(); + } + exports.putShort(cw.newPackage(packaze)).putShort(access); + if (modules == null) { + exports.putShort(0); + size += 6; + } else { + exports.putShort(modules.length); + for(String module: modules) { + exports.putShort(cw.newModule(module)); + } + size += 6 + 2 * modules.length; + } + exportCount++; + } + + @Override + public void visitOpen(String packaze, int access, String... modules) { + if (opens == null) { + opens = new ByteVector(); + } + opens.putShort(cw.newPackage(packaze)).putShort(access); + if (modules == null) { + opens.putShort(0); + size += 6; + } else { + opens.putShort(modules.length); + for(String module: modules) { + opens.putShort(cw.newModule(module)); + } + size += 6 + 2 * modules.length; + } + openCount++; + } + + @Override + public void visitUse(String service) { + if (uses == null) { + uses = new ByteVector(); + } + uses.putShort(cw.newClass(service)); + useCount++; + size += 2; + } + + @Override + public void visitProvide(String service, String... providers) { + if (provides == null) { + provides = new ByteVector(); + } + provides.putShort(cw.newClass(service)); + provides.putShort(providers.length); + for(String provider: providers) { + provides.putShort(cw.newClass(provider)); + } + provideCount++; + size += 4 + 2 * providers.length; + } + + @Override + public void visitEnd() { + // empty + } + + void putAttributes(ByteVector out) { + if (mainClass != 0) { + out.putShort(cw.newUTF8("ModuleMainClass")).putInt(2).putShort(mainClass); + } + if (packages != null) { + out.putShort(cw.newUTF8("ModulePackages")) + .putInt(2 + 2 * packageCount) + .putShort(packageCount) + .putByteArray(packages.data, 0, packages.length); + } + } + + void put(ByteVector out) { + out.putInt(size); + out.putShort(name).putShort(access).putShort(version); + out.putShort(requireCount); + if (requires != null) { + out.putByteArray(requires.data, 0, requires.length); + } + out.putShort(exportCount); + if (exports != null) { + out.putByteArray(exports.data, 0, exports.length); + } + out.putShort(openCount); + if (opens != null) { + out.putByteArray(opens.data, 0, opens.length); + } + out.putShort(useCount); + if (uses != null) { + out.putByteArray(uses.data, 0, uses.length); + } + out.putShort(provideCount); + if (provides != null) { + out.putByteArray(provides.data, 0, provides.length); + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Opcodes.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Opcodes.java new file mode 100644 index 000000000..9b96a1d5f --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Opcodes.java @@ -0,0 +1,372 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +/** + * Defines the JVM opcodes, access flags and array type codes. This interface + * does not define all the JVM opcodes because some opcodes are automatically + * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced + * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n + * opcodes are therefore not defined in this interface. Likewise for LDC, + * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and + * JSR_W. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public interface Opcodes { + + // ASM API versions + + int ASM4 = 4 << 16 | 0 << 8 | 0; + int ASM5 = 5 << 16 | 0 << 8 | 0; + int ASM6 = 6 << 16 | 0 << 8 | 0; + + // versions + + int V1_1 = 3 << 16 | 45; + int V1_2 = 0 << 16 | 46; + int V1_3 = 0 << 16 | 47; + int V1_4 = 0 << 16 | 48; + int V1_5 = 0 << 16 | 49; + int V1_6 = 0 << 16 | 50; + int V1_7 = 0 << 16 | 51; + int V1_8 = 0 << 16 | 52; + int V9 = 0 << 16 | 53; + + // access flags + + int ACC_PUBLIC = 0x0001; // class, field, method + int ACC_PRIVATE = 0x0002; // class, field, method + int ACC_PROTECTED = 0x0004; // class, field, method + int ACC_STATIC = 0x0008; // field, method + int ACC_FINAL = 0x0010; // class, field, method, parameter + int ACC_SUPER = 0x0020; // class + int ACC_SYNCHRONIZED = 0x0020; // method + int ACC_OPEN = 0x0020; // module + int ACC_TRANSITIVE = 0x0020; // module requires + int ACC_VOLATILE = 0x0040; // field + int ACC_BRIDGE = 0x0040; // method + int ACC_STATIC_PHASE = 0x0040; // module requires + int ACC_VARARGS = 0x0080; // method + int ACC_TRANSIENT = 0x0080; // field + int ACC_NATIVE = 0x0100; // method + int ACC_INTERFACE = 0x0200; // class + int ACC_ABSTRACT = 0x0400; // class, method + int ACC_STRICT = 0x0800; // method + int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module * + int ACC_ANNOTATION = 0x2000; // class + int ACC_ENUM = 0x4000; // class(?) field inner + int ACC_MANDATED = 0x8000; // parameter, module, module * + int ACC_MODULE = 0x8000; // class + + + // ASM specific pseudo access flags + + int ACC_DEPRECATED = 0x20000; // class, field, method + + // types for NEWARRAY + + int T_BOOLEAN = 4; + int T_CHAR = 5; + int T_FLOAT = 6; + int T_DOUBLE = 7; + int T_BYTE = 8; + int T_SHORT = 9; + int T_INT = 10; + int T_LONG = 11; + + // tags for Handle + + int H_GETFIELD = 1; + int H_GETSTATIC = 2; + int H_PUTFIELD = 3; + int H_PUTSTATIC = 4; + int H_INVOKEVIRTUAL = 5; + int H_INVOKESTATIC = 6; + int H_INVOKESPECIAL = 7; + int H_NEWINVOKESPECIAL = 8; + int H_INVOKEINTERFACE = 9; + + // stack map frame types + + /** + * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. + */ + int F_NEW = -1; + + /** + * Represents a compressed frame with complete frame data. + */ + int F_FULL = 0; + + /** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that additional 1-3 locals are defined, and + * with an empty stack. + */ + int F_APPEND = 1; + + /** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that the last 1-3 locals are absent and with + * an empty stack. + */ + int F_CHOP = 2; + + /** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with an empty stack. + */ + int F_SAME = 3; + + /** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with a single value on the stack. + */ + int F_SAME1 = 4; + + // Do not try to change the following code to use auto-boxing, + // these values are compared by reference and not by value + // The constructor of Integer was deprecated in 9 + // but we are stuck with it by backward compatibility + @SuppressWarnings("deprecation") Integer TOP = new Integer(0); + @SuppressWarnings("deprecation") Integer INTEGER = new Integer(1); + @SuppressWarnings("deprecation") Integer FLOAT = new Integer(2); + @SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3); + @SuppressWarnings("deprecation") Integer LONG = new Integer(4); + @SuppressWarnings("deprecation") Integer NULL = new Integer(5); + @SuppressWarnings("deprecation") Integer UNINITIALIZED_THIS = new Integer(6); + + // opcodes // visit method (- = idem) + + int NOP = 0; // visitInsn + int ACONST_NULL = 1; // - + int ICONST_M1 = 2; // - + int ICONST_0 = 3; // - + int ICONST_1 = 4; // - + int ICONST_2 = 5; // - + int ICONST_3 = 6; // - + int ICONST_4 = 7; // - + int ICONST_5 = 8; // - + int LCONST_0 = 9; // - + int LCONST_1 = 10; // - + int FCONST_0 = 11; // - + int FCONST_1 = 12; // - + int FCONST_2 = 13; // - + int DCONST_0 = 14; // - + int DCONST_1 = 15; // - + int BIPUSH = 16; // visitIntInsn + int SIPUSH = 17; // - + int LDC = 18; // visitLdcInsn + // int LDC_W = 19; // - + // int LDC2_W = 20; // - + int ILOAD = 21; // visitVarInsn + int LLOAD = 22; // - + int FLOAD = 23; // - + int DLOAD = 24; // - + int ALOAD = 25; // - + // int ILOAD_0 = 26; // - + // int ILOAD_1 = 27; // - + // int ILOAD_2 = 28; // - + // int ILOAD_3 = 29; // - + // int LLOAD_0 = 30; // - + // int LLOAD_1 = 31; // - + // int LLOAD_2 = 32; // - + // int LLOAD_3 = 33; // - + // int FLOAD_0 = 34; // - + // int FLOAD_1 = 35; // - + // int FLOAD_2 = 36; // - + // int FLOAD_3 = 37; // - + // int DLOAD_0 = 38; // - + // int DLOAD_1 = 39; // - + // int DLOAD_2 = 40; // - + // int DLOAD_3 = 41; // - + // int ALOAD_0 = 42; // - + // int ALOAD_1 = 43; // - + // int ALOAD_2 = 44; // - + // int ALOAD_3 = 45; // - + int IALOAD = 46; // visitInsn + int LALOAD = 47; // - + int FALOAD = 48; // - + int DALOAD = 49; // - + int AALOAD = 50; // - + int BALOAD = 51; // - + int CALOAD = 52; // - + int SALOAD = 53; // - + int ISTORE = 54; // visitVarInsn + int LSTORE = 55; // - + int FSTORE = 56; // - + int DSTORE = 57; // - + int ASTORE = 58; // - + // int ISTORE_0 = 59; // - + // int ISTORE_1 = 60; // - + // int ISTORE_2 = 61; // - + // int ISTORE_3 = 62; // - + // int LSTORE_0 = 63; // - + // int LSTORE_1 = 64; // - + // int LSTORE_2 = 65; // - + // int LSTORE_3 = 66; // - + // int FSTORE_0 = 67; // - + // int FSTORE_1 = 68; // - + // int FSTORE_2 = 69; // - + // int FSTORE_3 = 70; // - + // int DSTORE_0 = 71; // - + // int DSTORE_1 = 72; // - + // int DSTORE_2 = 73; // - + // int DSTORE_3 = 74; // - + // int ASTORE_0 = 75; // - + // int ASTORE_1 = 76; // - + // int ASTORE_2 = 77; // - + // int ASTORE_3 = 78; // - + int IASTORE = 79; // visitInsn + int LASTORE = 80; // - + int FASTORE = 81; // - + int DASTORE = 82; // - + int AASTORE = 83; // - + int BASTORE = 84; // - + int CASTORE = 85; // - + int SASTORE = 86; // - + int POP = 87; // - + int POP2 = 88; // - + int DUP = 89; // - + int DUP_X1 = 90; // - + int DUP_X2 = 91; // - + int DUP2 = 92; // - + int DUP2_X1 = 93; // - + int DUP2_X2 = 94; // - + int SWAP = 95; // - + int IADD = 96; // - + int LADD = 97; // - + int FADD = 98; // - + int DADD = 99; // - + int ISUB = 100; // - + int LSUB = 101; // - + int FSUB = 102; // - + int DSUB = 103; // - + int IMUL = 104; // - + int LMUL = 105; // - + int FMUL = 106; // - + int DMUL = 107; // - + int IDIV = 108; // - + int LDIV = 109; // - + int FDIV = 110; // - + int DDIV = 111; // - + int IREM = 112; // - + int LREM = 113; // - + int FREM = 114; // - + int DREM = 115; // - + int INEG = 116; // - + int LNEG = 117; // - + int FNEG = 118; // - + int DNEG = 119; // - + int ISHL = 120; // - + int LSHL = 121; // - + int ISHR = 122; // - + int LSHR = 123; // - + int IUSHR = 124; // - + int LUSHR = 125; // - + int IAND = 126; // - + int LAND = 127; // - + int IOR = 128; // - + int LOR = 129; // - + int IXOR = 130; // - + int LXOR = 131; // - + int IINC = 132; // visitIincInsn + int I2L = 133; // visitInsn + int I2F = 134; // - + int I2D = 135; // - + int L2I = 136; // - + int L2F = 137; // - + int L2D = 138; // - + int F2I = 139; // - + int F2L = 140; // - + int F2D = 141; // - + int D2I = 142; // - + int D2L = 143; // - + int D2F = 144; // - + int I2B = 145; // - + int I2C = 146; // - + int I2S = 147; // - + int LCMP = 148; // - + int FCMPL = 149; // - + int FCMPG = 150; // - + int DCMPL = 151; // - + int DCMPG = 152; // - + int IFEQ = 153; // visitJumpInsn + int IFNE = 154; // - + int IFLT = 155; // - + int IFGE = 156; // - + int IFGT = 157; // - + int IFLE = 158; // - + int IF_ICMPEQ = 159; // - + int IF_ICMPNE = 160; // - + int IF_ICMPLT = 161; // - + int IF_ICMPGE = 162; // - + int IF_ICMPGT = 163; // - + int IF_ICMPLE = 164; // - + int IF_ACMPEQ = 165; // - + int IF_ACMPNE = 166; // - + int GOTO = 167; // - + int JSR = 168; // - + int RET = 169; // visitVarInsn + int TABLESWITCH = 170; // visiTableSwitchInsn + int LOOKUPSWITCH = 171; // visitLookupSwitch + int IRETURN = 172; // visitInsn + int LRETURN = 173; // - + int FRETURN = 174; // - + int DRETURN = 175; // - + int ARETURN = 176; // - + int RETURN = 177; // - + int GETSTATIC = 178; // visitFieldInsn + int PUTSTATIC = 179; // - + int GETFIELD = 180; // - + int PUTFIELD = 181; // - + int INVOKEVIRTUAL = 182; // visitMethodInsn + int INVOKESPECIAL = 183; // - + int INVOKESTATIC = 184; // - + int INVOKEINTERFACE = 185; // - + int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn + int NEW = 187; // visitTypeInsn + int NEWARRAY = 188; // visitIntInsn + int ANEWARRAY = 189; // visitTypeInsn + int ARRAYLENGTH = 190; // visitInsn + int ATHROW = 191; // - + int CHECKCAST = 192; // visitTypeInsn + int INSTANCEOF = 193; // - + int MONITORENTER = 194; // visitInsn + int MONITOREXIT = 195; // - + // int WIDE = 196; // NOT VISITED + int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn + int IFNULL = 198; // visitJumpInsn + int IFNONNULL = 199; // - + // int GOTO_W = 200; // - + // int JSR_W = 201; // - +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/Type.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/Type.java new file mode 100644 index 000000000..33dbb0854 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/Type.java @@ -0,0 +1,905 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * A Java field or method type. This class can be used to make it easier to + * manipulate type and method descriptors. + * + * @author Eric Bruneton + * @author Chris Nokleberg + */ +public class Type { + + /** + * The sort of the void type. See {@link #getSort getSort}. + */ + public static final int VOID = 0; + + /** + * The sort of the boolean type. See {@link #getSort getSort}. + */ + public static final int BOOLEAN = 1; + + /** + * The sort of the char type. See {@link #getSort getSort}. + */ + public static final int CHAR = 2; + + /** + * The sort of the byte type. See {@link #getSort getSort}. + */ + public static final int BYTE = 3; + + /** + * The sort of the short type. See {@link #getSort getSort}. + */ + public static final int SHORT = 4; + + /** + * The sort of the int type. See {@link #getSort getSort}. + */ + public static final int INT = 5; + + /** + * The sort of the float type. See {@link #getSort getSort}. + */ + public static final int FLOAT = 6; + + /** + * The sort of the long type. See {@link #getSort getSort}. + */ + public static final int LONG = 7; + + /** + * The sort of the double type. See {@link #getSort getSort}. + */ + public static final int DOUBLE = 8; + + /** + * The sort of array reference types. See {@link #getSort getSort}. + */ + public static final int ARRAY = 9; + + /** + * The sort of object reference types. See {@link #getSort getSort}. + */ + public static final int OBJECT = 10; + + /** + * The sort of method types. See {@link #getSort getSort}. + */ + public static final int METHOD = 11; + + /** + * The void type. + */ + public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) + | (5 << 16) | (0 << 8) | 0, 1); + + /** + * The boolean type. + */ + public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) + | (0 << 16) | (5 << 8) | 1, 1); + + /** + * The char type. + */ + public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) + | (0 << 16) | (6 << 8) | 1, 1); + + /** + * The byte type. + */ + public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) + | (0 << 16) | (5 << 8) | 1, 1); + + /** + * The short type. + */ + public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) + | (0 << 16) | (7 << 8) | 1, 1); + + /** + * The int type. + */ + public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) + | (0 << 16) | (0 << 8) | 1, 1); + + /** + * The float type. + */ + public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) + | (2 << 16) | (2 << 8) | 1, 1); + + /** + * The long type. + */ + public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) + | (1 << 16) | (1 << 8) | 2, 1); + + /** + * The double type. + */ + public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) + | (3 << 16) | (3 << 8) | 2, 1); + + // ------------------------------------------------------------------------ + // Fields + // ------------------------------------------------------------------------ + + /** + * The sort of this Java type. + */ + private final int sort; + + /** + * A buffer containing the internal name of this Java type. This field is + * only used for reference types. + */ + private final char[] buf; + + /** + * The offset of the internal name of this Java type in {@link #buf buf} or, + * for primitive types, the size, descriptor and getOpcode offsets for this + * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset + * for IALOAD or IASTORE, byte 3 the offset for all other instructions). + */ + private final int off; + + /** + * The length of the internal name of this Java type. + */ + private final int len; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a reference type. + * + * @param sort + * the sort of the reference type to be constructed. + * @param buf + * a buffer containing the descriptor of the previous type. + * @param off + * the offset of this descriptor in the previous buffer. + * @param len + * the length of this descriptor. + */ + private Type(final int sort, final char[] buf, final int off, final int len) { + this.sort = sort; + this.buf = buf; + this.off = off; + this.len = len; + } + + /** + * Returns the Java type corresponding to the given type descriptor. + * + * @param typeDescriptor + * a field or method type descriptor. + * @return the Java type corresponding to the given type descriptor. + */ + public static Type getType(final String typeDescriptor) { + return getType(typeDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java type corresponding to the given internal name. + * + * @param internalName + * an internal name. + * @return the Java type corresponding to the given internal name. + */ + public static Type getObjectType(final String internalName) { + char[] buf = internalName.toCharArray(); + return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length); + } + + /** + * Returns the Java type corresponding to the given method descriptor. + * Equivalent to Type.getType(methodDescriptor). + * + * @param methodDescriptor + * a method descriptor. + * @return the Java type corresponding to the given method descriptor. + */ + public static Type getMethodType(final String methodDescriptor) { + return getType(methodDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java method type corresponding to the given argument and + * return types. + * + * @param returnType + * the return type of the method. + * @param argumentTypes + * the argument types of the method. + * @return the Java type corresponding to the given argument and return + * types. + */ + public static Type getMethodType(final Type returnType, + final Type... argumentTypes) { + return getType(getMethodDescriptor(returnType, argumentTypes)); + } + + /** + * Returns the Java type corresponding to the given class. + * + * @param c + * a class. + * @return the Java type corresponding to the given class. + */ + public static Type getType(final Class c) { + if (c.isPrimitive()) { + if (c == Integer.TYPE) { + return INT_TYPE; + } else if (c == Void.TYPE) { + return VOID_TYPE; + } else if (c == Boolean.TYPE) { + return BOOLEAN_TYPE; + } else if (c == Byte.TYPE) { + return BYTE_TYPE; + } else if (c == Character.TYPE) { + return CHAR_TYPE; + } else if (c == Short.TYPE) { + return SHORT_TYPE; + } else if (c == Double.TYPE) { + return DOUBLE_TYPE; + } else if (c == Float.TYPE) { + return FLOAT_TYPE; + } else /* if (c == Long.TYPE) */{ + return LONG_TYPE; + } + } else { + return getType(getDescriptor(c)); + } + } + + /** + * Returns the Java method type corresponding to the given constructor. + * + * @param c + * a {@link Constructor Constructor} object. + * @return the Java method type corresponding to the given constructor. + */ + public static Type getType(final Constructor c) { + return getType(getConstructorDescriptor(c)); + } + + /** + * Returns the Java method type corresponding to the given method. + * + * @param m + * a {@link Method Method} object. + * @return the Java method type corresponding to the given method. + */ + public static Type getType(final Method m) { + return getType(getMethodDescriptor(m)); + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method descriptor. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java types corresponding to the argument types of the given + * method descriptor. + */ + public static Type[] getArgumentTypes(final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + int off = 1; + int size = 0; + while (true) { + char car = buf[off++]; + if (car == ')') { + break; + } else if (car == 'L') { + while (buf[off++] != ';') { + } + ++size; + } else if (car != '[') { + ++size; + } + } + Type[] args = new Type[size]; + off = 1; + size = 0; + while (buf[off] != ')') { + args[size] = getType(buf, off); + off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); + size += 1; + } + return args; + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method. + * + * @param method + * a method. + * @return the Java types corresponding to the argument types of the given + * method. + */ + public static Type[] getArgumentTypes(final Method method) { + Class[] classes = method.getParameterTypes(); + Type[] types = new Type[classes.length]; + for (int i = classes.length - 1; i >= 0; --i) { + types[i] = getType(classes[i]); + } + return types; + } + + /** + * Returns the Java type corresponding to the return type of the given + * method descriptor. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java type corresponding to the return type of the given + * method descriptor. + */ + public static Type getReturnType(final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + int off = 1; + while (true) { + char car = buf[off++]; + if (car == ')') { + return getType(buf, off); + } else if (car == 'L') { + while (buf[off++] != ';') { + } + } + } + } + + /** + * Returns the Java type corresponding to the return type of the given + * method. + * + * @param method + * a method. + * @return the Java type corresponding to the return type of the given + * method. + */ + public static Type getReturnType(final Method method) { + return getType(method.getReturnType()); + } + + /** + * Computes the size of the arguments and of the return value of a method. + * + * @param desc + * the descriptor of a method. + * @return the size of the arguments of the method (plus one for the + * implicit this argument), argSize, and the size of its return + * value, retSize, packed into a single int i = + * (argSize << 2) | retSize (argSize is therefore equal to + * i >> 2, and retSize to i & 0x03). + */ + public static int getArgumentsAndReturnSizes(final String desc) { + int n = 1; + int c = 1; + while (true) { + char car = desc.charAt(c++); + if (car == ')') { + car = desc.charAt(c); + return n << 2 + | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); + } else if (car == 'L') { + while (desc.charAt(c++) != ';') { + } + n += 1; + } else if (car == '[') { + while ((car = desc.charAt(c)) == '[') { + ++c; + } + if (car == 'D' || car == 'J') { + n -= 1; + } + } else if (car == 'D' || car == 'J') { + n += 2; + } else { + n += 1; + } + } + } + + /** + * Returns the Java type corresponding to the given type descriptor. For + * method descriptors, buf is supposed to contain nothing more than the + * descriptor itself. + * + * @param buf + * a buffer containing a type descriptor. + * @param off + * the offset of this descriptor in the previous buffer. + * @return the Java type corresponding to the given type descriptor. + */ + private static Type getType(final char[] buf, final int off) { + int len; + switch (buf[off]) { + case 'V': + return VOID_TYPE; + case 'Z': + return BOOLEAN_TYPE; + case 'C': + return CHAR_TYPE; + case 'B': + return BYTE_TYPE; + case 'S': + return SHORT_TYPE; + case 'I': + return INT_TYPE; + case 'F': + return FLOAT_TYPE; + case 'J': + return LONG_TYPE; + case 'D': + return DOUBLE_TYPE; + case '[': + len = 1; + while (buf[off + len] == '[') { + ++len; + } + if (buf[off + len] == 'L') { + ++len; + while (buf[off + len] != ';') { + ++len; + } + } + return new Type(ARRAY, buf, off, len + 1); + case 'L': + len = 1; + while (buf[off + len] != ';') { + ++len; + } + return new Type(OBJECT, buf, off + 1, len - 1); + // case '(': + default: + return new Type(METHOD, buf, off, buf.length - off); + } + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns the sort of this Java type. + * + * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, + * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, + * {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, + * {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD + * METHOD}. + */ + public int getSort() { + return sort; + } + + /** + * Returns the number of dimensions of this array type. This method should + * only be used for an array type. + * + * @return the number of dimensions of this array type. + */ + public int getDimensions() { + int i = 1; + while (buf[off + i] == '[') { + ++i; + } + return i; + } + + /** + * Returns the type of the elements of this array type. This method should + * only be used for an array type. + * + * @return Returns the type of the elements of this array type. + */ + public Type getElementType() { + return getType(buf, off + getDimensions()); + } + + /** + * Returns the binary name of the class corresponding to this type. This + * method must not be used on method types. + * + * @return the binary name of the class corresponding to this type. + */ + public String getClassName() { + switch (sort) { + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case CHAR: + return "char"; + case BYTE: + return "byte"; + case SHORT: + return "short"; + case INT: + return "int"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case DOUBLE: + return "double"; + case ARRAY: + StringBuilder sb = new StringBuilder(getElementType().getClassName()); + for (int i = getDimensions(); i > 0; --i) { + sb.append("[]"); + } + return sb.toString(); + case OBJECT: + return new String(buf, off, len).replace('/', '.'); + default: + return null; + } + } + + /** + * Returns the internal name of the class corresponding to this object or + * array type. The internal name of a class is its fully qualified name (as + * returned by Class.getName(), where '.' are replaced by '/'. This method + * should only be used for an object or array type. + * + * @return the internal name of the class corresponding to this object type. + */ + public String getInternalName() { + return new String(buf, off, len); + } + + /** + * Returns the argument types of methods of this type. This method should + * only be used for method types. + * + * @return the argument types of methods of this type. + */ + public Type[] getArgumentTypes() { + return getArgumentTypes(getDescriptor()); + } + + /** + * Returns the return type of methods of this type. This method should only + * be used for method types. + * + * @return the return type of methods of this type. + */ + public Type getReturnType() { + return getReturnType(getDescriptor()); + } + + /** + * Returns the size of the arguments and of the return value of methods of + * this type. This method should only be used for method types. + * + * @return the size of the arguments (plus one for the implicit this + * argument), argSize, and the size of the return value, retSize, + * packed into a single + * int i = (argSize << 2) | retSize + * (argSize is therefore equal to i >> 2, + * and retSize to i & 0x03). + */ + public int getArgumentsAndReturnSizes() { + return getArgumentsAndReturnSizes(getDescriptor()); + } + + // ------------------------------------------------------------------------ + // Conversion to type descriptors + // ------------------------------------------------------------------------ + + /** + * Returns the descriptor corresponding to this Java type. + * + * @return the descriptor corresponding to this Java type. + */ + public String getDescriptor() { + StringBuilder buf = new StringBuilder(); + getDescriptor(buf); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given argument and return + * types. + * + * @param returnType + * the return type of the method. + * @param argumentTypes + * the argument types of the method. + * @return the descriptor corresponding to the given argument and return + * types. + */ + public static String getMethodDescriptor(final Type returnType, + final Type... argumentTypes) { + StringBuilder buf = new StringBuilder(); + buf.append('('); + for (int i = 0; i < argumentTypes.length; ++i) { + argumentTypes[i].getDescriptor(buf); + } + buf.append(')'); + returnType.getDescriptor(buf); + return buf.toString(); + } + + /** + * Appends the descriptor corresponding to this Java type to the given + * string buffer. + * + * @param buf + * the string buffer to which the descriptor must be appended. + */ + private void getDescriptor(final StringBuilder buf) { + if (this.buf == null) { + // descriptor is in byte 3 of 'off' for primitive types (buf == + // null) + buf.append((char) ((off & 0xFF000000) >>> 24)); + } else if (sort == OBJECT) { + buf.append('L'); + buf.append(this.buf, off, len); + buf.append(';'); + } else { // sort == ARRAY || sort == METHOD + buf.append(this.buf, off, len); + } + } + + // ------------------------------------------------------------------------ + // Direct conversion from classes to type descriptors, + // without intermediate Type objects + // ------------------------------------------------------------------------ + + /** + * Returns the internal name of the given class. The internal name of a + * class is its fully qualified name, as returned by Class.getName(), where + * '.' are replaced by '/'. + * + * @param c + * an object or array class. + * @return the internal name of the given class. + */ + public static String getInternalName(final Class c) { + return c.getName().replace('.', '/'); + } + + /** + * Returns the descriptor corresponding to the given Java type. + * + * @param c + * an object class, a primitive class or an array class. + * @return the descriptor corresponding to the given class. + */ + public static String getDescriptor(final Class c) { + StringBuilder buf = new StringBuilder(); + getDescriptor(buf, c); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given constructor. + * + * @param c + * a {@link Constructor Constructor} object. + * @return the descriptor of the given constructor. + */ + public static String getConstructorDescriptor(final Constructor c) { + Class[] parameters = c.getParameterTypes(); + StringBuilder buf = new StringBuilder(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + return buf.append(")V").toString(); + } + + /** + * Returns the descriptor corresponding to the given method. + * + * @param m + * a {@link Method Method} object. + * @return the descriptor of the given method. + */ + public static String getMethodDescriptor(final Method m) { + Class[] parameters = m.getParameterTypes(); + StringBuilder buf = new StringBuilder(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + buf.append(')'); + getDescriptor(buf, m.getReturnType()); + return buf.toString(); + } + + /** + * Appends the descriptor of the given class to the given string buffer. + * + * @param buf + * the string buffer to which the descriptor must be appended. + * @param c + * the class whose descriptor must be computed. + */ + private static void getDescriptor(final StringBuilder buf, final Class c) { + Class d = c; + while (true) { + if (d.isPrimitive()) { + char car; + if (d == Integer.TYPE) { + car = 'I'; + } else if (d == Void.TYPE) { + car = 'V'; + } else if (d == Boolean.TYPE) { + car = 'Z'; + } else if (d == Byte.TYPE) { + car = 'B'; + } else if (d == Character.TYPE) { + car = 'C'; + } else if (d == Short.TYPE) { + car = 'S'; + } else if (d == Double.TYPE) { + car = 'D'; + } else if (d == Float.TYPE) { + car = 'F'; + } else /* if (d == Long.TYPE) */{ + car = 'J'; + } + buf.append(car); + return; + } else if (d.isArray()) { + buf.append('['); + d = d.getComponentType(); + } else { + buf.append('L'); + String name = d.getName(); + int len = name.length(); + for (int i = 0; i < len; ++i) { + char car = name.charAt(i); + buf.append(car == '.' ? '/' : car); + } + buf.append(';'); + return; + } + } + } + + // ------------------------------------------------------------------------ + // Corresponding size and opcodes + // ------------------------------------------------------------------------ + + /** + * Returns the size of values of this type. This method must not be used for + * method types. + * + * @return the size of values of this type, i.e., 2 for long and + * double, 0 for void and 1 otherwise. + */ + public int getSize() { + // the size is in byte 0 of 'off' for primitive types (buf == null) + return buf == null ? (off & 0xFF) : 1; + } + + /** + * Returns a JVM instruction opcode adapted to this Java type. This method + * must not be used for method types. + * + * @param opcode + * a JVM instruction opcode. This opcode must be one of ILOAD, + * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, + * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to + * this Java type. For example, if this type is float and + * opcode is IRETURN, this method returns FRETURN. + */ + public int getOpcode(final int opcode) { + if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { + // the offset for IALOAD or IASTORE is in byte 1 of 'off' for + // primitive types (buf == null) + return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4); + } else { + // the offset for other instructions is in byte 2 of 'off' for + // primitive types (buf == null) + return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4); + } + } + + // ------------------------------------------------------------------------ + // Equals, hashCode and toString + // ------------------------------------------------------------------------ + + /** + * Tests if the given object is equal to this type. + * + * @param o + * the object to be compared to this type. + * @return true if the given object is equal to this type. + */ + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Type)) { + return false; + } + Type t = (Type) o; + if (sort != t.sort) { + return false; + } + if (sort >= ARRAY) { + if (len != t.len) { + return false; + } + for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { + if (buf[i] != t.buf[j]) { + return false; + } + } + } + return true; + } + + /** + * Returns a hash code value for this type. + * + * @return a hash code value for this type. + */ + @Override + public int hashCode() { + int hc = 13 * sort; + if (sort >= ARRAY) { + for (int i = off, end = i + len; i < end; i++) { + hc = 17 * (hc + buf[i]); + } + } + return hc; + } + + /** + * Returns a string representation of this type. + * + * @return the descriptor of this type. + */ + @Override + public String toString() { + return getDescriptor(); + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/TypePath.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/TypePath.java new file mode 100644 index 000000000..bf6fb137d --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/TypePath.java @@ -0,0 +1,196 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.fr.third.org.objectweb.asm; + +/** + * The path to a type argument, wildcard bound, array element type, or static + * inner type within an enclosing type. + * + * @author Eric Bruneton + */ +public class TypePath { + + /** + * A type path step that steps into the element type of an array type. See + * {@link #getStep getStep}. + */ + public final static int ARRAY_ELEMENT = 0; + + /** + * A type path step that steps into the nested type of a class type. See + * {@link #getStep getStep}. + */ + public final static int INNER_TYPE = 1; + + /** + * A type path step that steps into the bound of a wildcard type. See + * {@link #getStep getStep}. + */ + public final static int WILDCARD_BOUND = 2; + + /** + * A type path step that steps into a type argument of a generic type. See + * {@link #getStep getStep}. + */ + public final static int TYPE_ARGUMENT = 3; + + /** + * The byte array where the path is stored, in Java class file format. + */ + byte[] b; + + /** + * The offset of the first byte of the type path in 'b'. + */ + int offset; + + /** + * Creates a new type path. + * + * @param b + * the byte array containing the type path in Java class file + * format. + * @param offset + * the offset of the first byte of the type path in 'b'. + */ + TypePath(byte[] b, int offset) { + this.b = b; + this.offset = offset; + } + + /** + * Returns the length of this path. + * + * @return the length of this path. + */ + public int getLength() { + return b[offset]; + } + + /** + * Returns the value of the given step of this path. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE + * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + */ + public int getStep(int index) { + return b[offset + 2 * index + 1]; + } + + /** + * Returns the index of the type argument that the given step is stepping + * into. This method should only be used for steps whose value is + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return the index of the type argument that the given step is stepping + * into. + */ + public int getStepArgument(int index) { + return b[offset + 2 * index + 2]; + } + + /** + * Converts a type path in string form, in the format used by + * {@link #toString()}, into a TypePath object. + * + * @param typePath + * a type path in string form, in the format used by + * {@link #toString()}. May be null or empty. + * @return the corresponding TypePath object, or null if the path is empty. + */ + public static TypePath fromString(final String typePath) { + if (typePath == null || typePath.length() == 0) { + return null; + } + int n = typePath.length(); + ByteVector out = new ByteVector(n); + out.putByte(0); + for (int i = 0; i < n;) { + char c = typePath.charAt(i++); + if (c == '[') { + out.put11(ARRAY_ELEMENT, 0); + } else if (c == '.') { + out.put11(INNER_TYPE, 0); + } else if (c == '*') { + out.put11(WILDCARD_BOUND, 0); + } else if (c >= '0' && c <= '9') { + int typeArg = c - '0'; + while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') { + typeArg = typeArg * 10 + c - '0'; + i += 1; + } + if (i < n && typePath.charAt(i) == ';') { + i += 1; + } + out.put11(TYPE_ARGUMENT, typeArg); + } + } + out.data[0] = (byte) (out.length / 2); + return new TypePath(out.data, 0); + } + + /** + * Returns a string representation of this type path. {@link #ARRAY_ELEMENT + * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE + * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps + * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type + * argument index in decimal form followed by ';'. + */ + @Override + public String toString() { + int length = getLength(); + StringBuilder result = new StringBuilder(length * 2); + for (int i = 0; i < length; ++i) { + switch (getStep(i)) { + case ARRAY_ELEMENT: + result.append('['); + break; + case INNER_TYPE: + result.append('.'); + break; + case WILDCARD_BOUND: + result.append('*'); + break; + case TYPE_ARGUMENT: + result.append(getStepArgument(i)).append(';'); + break; + default: + result.append('_'); + } + } + return result.toString(); + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/TypeReference.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/TypeReference.java new file mode 100644 index 000000000..2edbb5fa7 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/TypeReference.java @@ -0,0 +1,452 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.fr.third.org.objectweb.asm; + +/** + * A reference to a type appearing in a class, field or method declaration, or + * on an instruction. Such a reference designates the part of the class where + * the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws' + * clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable + * declaration, etc). + * + * @author Eric Bruneton + */ +public class TypeReference { + + /** + * The sort of type references that target a type parameter of a generic + * class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER = 0x00; + + /** + * The sort of type references that target a type parameter of a generic + * method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER = 0x01; + + /** + * The sort of type references that target the super class of a class or one + * of the interfaces it implements. See {@link #getSort getSort}. + */ + public final static int CLASS_EXTENDS = 0x10; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12; + + /** + * The sort of type references that target the type of a field. See + * {@link #getSort getSort}. + */ + public final static int FIELD = 0x13; + + /** + * The sort of type references that target the return type of a method. See + * {@link #getSort getSort}. + */ + public final static int METHOD_RETURN = 0x14; + + /** + * The sort of type references that target the receiver type of a method. + * See {@link #getSort getSort}. + */ + public final static int METHOD_RECEIVER = 0x15; + + /** + * The sort of type references that target the type of a formal parameter of + * a method. See {@link #getSort getSort}. + */ + public final static int METHOD_FORMAL_PARAMETER = 0x16; + + /** + * The sort of type references that target the type of an exception declared + * in the throws clause of a method. See {@link #getSort getSort}. + */ + public final static int THROWS = 0x17; + + /** + * The sort of type references that target the type of a local variable in a + * method. See {@link #getSort getSort}. + */ + public final static int LOCAL_VARIABLE = 0x40; + + /** + * The sort of type references that target the type of a resource variable + * in a method. See {@link #getSort getSort}. + */ + public final static int RESOURCE_VARIABLE = 0x41; + + /** + * The sort of type references that target the type of the exception of a + * 'catch' clause in a method. See {@link #getSort getSort}. + */ + public final static int EXCEPTION_PARAMETER = 0x42; + + /** + * The sort of type references that target the type declared in an + * 'instanceof' instruction. See {@link #getSort getSort}. + */ + public final static int INSTANCEOF = 0x43; + + /** + * The sort of type references that target the type of the object created by + * a 'new' instruction. See {@link #getSort getSort}. + */ + public final static int NEW = 0x44; + + /** + * The sort of type references that target the receiver type of a + * constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE = 0x45; + + /** + * The sort of type references that target the receiver type of a method + * reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE = 0x46; + + /** + * The sort of type references that target the type declared in an explicit + * or implicit cast instruction. See {@link #getSort getSort}. + */ + public final static int CAST = 0x47; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor call. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method call. See {@link #getSort getSort}. + */ + public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; + + /** + * The type reference value in Java class file format. + */ + private int value; + + /** + * Creates a new TypeReference. + * + * @param typeRef + * the int encoded value of the type reference, as received in a + * visit method related to type annotations, like + * visitTypeAnnotation. + */ + public TypeReference(int typeRef) { + this.value = typeRef; + } + + /** + * Returns a type reference of the given sort. + * + * @param sort + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or + * {@link #METHOD_REFERENCE METHOD_REFERENCE}. + * @return a type reference of the given sort. + */ + public static TypeReference newTypeReference(int sort) { + return new TypeReference(sort << 24); + } + + /** + * Returns a reference to a type parameter of a generic class or method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @return a reference to the given generic class or method type parameter. + */ + public static TypeReference newTypeParameterReference(int sort, + int paramIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16)); + } + + /** + * Returns a reference to a type parameter bound of a generic class or + * method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @param boundIndex + * the type bound index within the above type parameters. + * @return a reference to the given generic class or method type parameter + * bound. + */ + public static TypeReference newTypeParameterBoundReference(int sort, + int paramIndex, int boundIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16) + | (boundIndex << 8)); + } + + /** + * Returns a reference to the super class or to an interface of the + * 'implements' clause of a class. + * + * @param itfIndex + * the index of an interface in the 'implements' clause of a + * class, or -1 to reference the super class of the class. + * @return a reference to the given super type of a class. + */ + public static TypeReference newSuperTypeReference(int itfIndex) { + itfIndex &= 0xFFFF; + return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8)); + } + + /** + * Returns a reference to the type of a formal parameter of a method. + * + * @param paramIndex + * the formal parameter index. + * + * @return a reference to the type of the given method formal parameter. + */ + public static TypeReference newFormalParameterReference(int paramIndex) { + return new TypeReference((METHOD_FORMAL_PARAMETER << 24) + | (paramIndex << 16)); + } + + /** + * Returns a reference to the type of an exception, in a 'throws' clause of + * a method. + * + * @param exceptionIndex + * the index of an exception in a 'throws' clause of a method. + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newExceptionReference(int exceptionIndex) { + return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); + } + + /** + * Returns a reference to the type of the exception declared in a 'catch' + * clause of a method. + * + * @param tryCatchBlockIndex + * the index of a try catch block (using the order in which they + * are visited with visitTryCatchBlock). + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newTryCatchReference(int tryCatchBlockIndex) { + return new TypeReference((EXCEPTION_PARAMETER << 24) + | (tryCatchBlockIndex << 8)); + } + + /** + * Returns a reference to the type of a type argument in a constructor or + * method call or reference. + * + * @param sort + * {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + * @param argIndex + * the type argument index. + * + * @return a reference to the type of the given type argument. + */ + public static TypeReference newTypeArgumentReference(int sort, int argIndex) { + return new TypeReference((sort << 24) | argIndex); + } + + /** + * Returns the sort of this type reference. + * + * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_EXTENDS CLASS_EXTENDS}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND}, + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}, + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}, + * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, + * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + */ + public int getSort() { + return value >>> 24; + } + + /** + * Returns the index of the type parameter referenced by this type + * reference. This method must only be used for type references whose sort + * is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter index. + */ + public int getTypeParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the type parameter bound, within the type parameter + * {@link #getTypeParameterIndex}, referenced by this type reference. This + * method must only be used for type references whose sort is + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter bound index. + */ + public int getTypeParameterBoundIndex() { + return (value & 0x0000FF00) >> 8; + } + + /** + * Returns the index of the "super type" of a class that is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}. + * + * @return the index of an interface in the 'implements' clause of a class, + * or -1 if this type reference references the type of the super + * class. + */ + public int getSuperTypeIndex() { + return (short) ((value & 0x00FFFF00) >> 8); + } + + /** + * Returns the index of the formal parameter whose type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}. + * + * @return a formal parameter index. + */ + public int getFormalParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the exception, in a 'throws' clause of a method, + * whose type is referenced by this type reference. This method must only be + * used for type references whose sort is {@link #THROWS THROWS}. + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getExceptionIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the try catch block (using the order in which they + * are visited with visitTryCatchBlock), whose 'catch' type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} . + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getTryCatchBlockIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the type argument referenced by this type reference. + * This method must only be used for type references whose sort is + * {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. + * + * @return a type parameter index. + */ + public int getTypeArgumentIndex() { + return value & 0xFF; + } + + /** + * Returns the int encoded value of this type reference, suitable for use in + * visit methods related to type annotations, like visitTypeAnnotation. + * + * @return the int encoded value of this type reference. + */ + public int getValue() { + return value; + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/package.html b/fine-cglib/src/com/fr/third/org/objectweb/asm/package.html new file mode 100644 index 000000000..b25c22255 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/package.html @@ -0,0 +1,87 @@ + + + +Provides a small and fast bytecode manipulation framework. + +

+The ASM framework is organized +around the {@link com.fr.third.org.objectweb.asm.ClassVisitor ClassVisitor}, +{@link com.fr.third.org.objectweb.asm.FieldVisitor FieldVisitor}, +{@link com.fr.third.org.objectweb.asm.MethodVisitor MethodVisitor} and +{@link com.fr.third.org.objectweb.asm.AnnotationVisitor AnnotationVisitor} abstract classes, +which allow one to visit the fields, methods and annotations of a class, +including the bytecode instructions of each method. + +

+In addition to these main abstract classes, ASM provides a {@link +com.fr.third.org.objectweb.asm.ClassReader ClassReader} class, that can parse an +existing class and make a given visitor visit it. ASM also provides +a {@link com.fr.third.org.objectweb.asm.ClassWriter ClassWriter} class, which is +a visitor that generates Java class files. + +

+In order to generate a class from scratch, only the {@link +com.fr.third.org.objectweb.asm.ClassWriter ClassWriter} class is necessary. Indeed, +in order to generate a class, one must just call its visitXxx +methods with the appropriate arguments to generate the desired fields +and methods. See the "helloworld" example in the ASM distribution for +more details about class generation. + +

+In order to modify existing classes, one must use a {@link +com.fr.third.org.objectweb.asm.ClassReader ClassReader} class to analyze +the original class, a class modifier, and a {@link com.fr.third.org.objectweb.asm.ClassWriter +ClassWriter} to construct the modified class. The class modifier +is just a {@link com.fr.third.org.objectweb.asm.ClassVisitor ClassVisitor} +that delegates most of the work to another {@link com.fr.third.org.objectweb.asm.ClassVisitor +ClassVisitor}, but that sometimes changes some parameter values, +or call additional methods, in order to implement the desired +modification process. In order to make it easier to implement such +class modifiers, the {@link com.fr.third.org.objectweb.asm.ClassVisitor +ClassVisitor} and {@link com.fr.third.org.objectweb.asm.MethodVisitor MethodVisitor} +classes delegate by default all the method calls they receive to an +optional visitor. See the "adapt" example in the ASM +distribution for more details about class modification. + +

+The size of the core ASM library, asm.jar, is only 45KB, which is much +smaller than the size of the +BCEL library (504KB), and than the +size of the +SERP library (150KB). ASM is also +much faster than these tools. Indeed the overhead of a load time class +transformation process is of the order of 60% with ASM, 700% or more with BCEL, +and 1100% or more with SERP (see the test/perf directory in the ASM +distribution)! + +@since ASM 1.3 + + diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureReader.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureReader.java new file mode 100644 index 000000000..732491e27 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureReader.java @@ -0,0 +1,228 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm.signature; + +/** + * A type signature parser to make a signature visitor visit an existing + * signature. + * + * @author Thomas Hallgren + * @author Eric Bruneton + */ +public class SignatureReader { + + /** + * The signature to be read. + */ + private final String signature; + + /** + * Constructs a {@link SignatureReader} for the given signature. + * + * @param signature + * A ClassSignature, MethodTypeSignature, or + * FieldTypeSignature. + */ + public SignatureReader(final String signature) { + this.signature = signature; + } + + /** + * Makes the given visitor visit the signature of this + * {@link SignatureReader}. This signature is the one specified in the + * constructor (see {@link #SignatureReader(String) SignatureReader}). This + * method is intended to be called on a {@link SignatureReader} that was + * created using a ClassSignature (such as the signature + * parameter of the {@link com.fr.third.org.objectweb.asm.ClassVisitor#visit + * ClassVisitor.visit} method) or a MethodTypeSignature (such as the + * signature parameter of the + * {@link com.fr.third.org.objectweb.asm.ClassVisitor#visitMethod + * ClassVisitor.visitMethod} method). + * + * @param v + * the visitor that must visit this signature. + */ + public void accept(final SignatureVisitor v) { + String signature = this.signature; + int len = signature.length(); + int pos; + char c; + + if (signature.charAt(0) == '<') { + pos = 2; + do { + int end = signature.indexOf(':', pos); + v.visitFormalTypeParameter(signature.substring(pos - 1, end)); + pos = end + 1; + + c = signature.charAt(pos); + if (c == 'L' || c == '[' || c == 'T') { + pos = parseType(signature, pos, v.visitClassBound()); + } + + while ((c = signature.charAt(pos++)) == ':') { + pos = parseType(signature, pos, v.visitInterfaceBound()); + } + } while (c != '>'); + } else { + pos = 0; + } + + if (signature.charAt(pos) == '(') { + pos++; + while (signature.charAt(pos) != ')') { + pos = parseType(signature, pos, v.visitParameterType()); + } + pos = parseType(signature, pos + 1, v.visitReturnType()); + while (pos < len) { + pos = parseType(signature, pos + 1, v.visitExceptionType()); + } + } else { + pos = parseType(signature, pos, v.visitSuperclass()); + while (pos < len) { + pos = parseType(signature, pos, v.visitInterface()); + } + } + } + + /** + * Makes the given visitor visit the signature of this + * {@link SignatureReader}. This signature is the one specified in the + * constructor (see {@link #SignatureReader(String) SignatureReader}). This + * method is intended to be called on a {@link SignatureReader} that was + * created using a FieldTypeSignature, such as the + * signature parameter of the + * {@link com.fr.third.org.objectweb.asm.ClassVisitor#visitField ClassVisitor.visitField} + * or {@link com.fr.third.org.objectweb.asm.MethodVisitor#visitLocalVariable + * MethodVisitor.visitLocalVariable} methods. + * + * @param v + * the visitor that must visit this signature. + */ + public void acceptType(final SignatureVisitor v) { + parseType(this.signature, 0, v); + } + + /** + * Parses a field type signature and makes the given visitor visit it. + * + * @param signature + * a string containing the signature that must be parsed. + * @param pos + * index of the first character of the signature to parsed. + * @param v + * the visitor that must visit this signature. + * @return the index of the first character after the parsed signature. + */ + private static int parseType(final String signature, int pos, + final SignatureVisitor v) { + char c; + int start, end; + boolean visited, inner; + String name; + + switch (c = signature.charAt(pos++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + case 'F': + case 'J': + case 'D': + case 'V': + v.visitBaseType(c); + return pos; + + case '[': + return parseType(signature, pos, v.visitArrayType()); + + case 'T': + end = signature.indexOf(';', pos); + v.visitTypeVariable(signature.substring(pos, end)); + return end + 1; + + default: // case 'L': + start = pos; + visited = false; + inner = false; + for (;;) { + switch (c = signature.charAt(pos++)) { + case '.': + case ';': + if (!visited) { + name = signature.substring(start, pos - 1); + if (inner) { + v.visitInnerClassType(name); + } else { + v.visitClassType(name); + } + } + if (c == ';') { + v.visitEnd(); + return pos; + } + start = pos; + visited = false; + inner = true; + break; + + case '<': + name = signature.substring(start, pos - 1); + if (inner) { + v.visitInnerClassType(name); + } else { + v.visitClassType(name); + } + visited = true; + top: for (;;) { + switch (c = signature.charAt(pos)) { + case '>': + break top; + case '*': + ++pos; + v.visitTypeArgument(); + break; + case '+': + case '-': + pos = parseType(signature, pos + 1, + v.visitTypeArgument(c)); + break; + default: + pos = parseType(signature, pos, + v.visitTypeArgument('=')); + break; + } + } + } + } + } + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureVisitor.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureVisitor.java new file mode 100644 index 000000000..f18382270 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureVisitor.java @@ -0,0 +1,238 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm.signature; + +import com.fr.third.org.objectweb.asm.Opcodes; + +/** + * A visitor to visit a generic signature. The methods of this interface must be + * called in one of the three following orders (the last one is the only valid + * order for a {@link SignatureVisitor} that is returned by a method of this + * interface): + *

    + *
  • ClassSignature = ( visitFormalTypeParameter + * visitClassBound? visitInterfaceBound* )* ( + * visitSuperclass visitInterface* )
  • + *
  • MethodSignature = ( visitFormalTypeParameter + * visitClassBound? visitInterfaceBound* )* ( + * visitParameterType* visitReturnType + * visitExceptionType* )
  • + *
  • TypeSignature = visitBaseType | + * visitTypeVariable | visitArrayType | ( + * visitClassType visitTypeArgument* ( + * visitInnerClassType visitTypeArgument* )* visitEnd + * ) )
  • + *
+ * + * @author Thomas Hallgren + * @author Eric Bruneton + */ +public abstract class SignatureVisitor { + + /** + * Wildcard for an "extends" type argument. + */ + public final static char EXTENDS = '+'; + + /** + * Wildcard for a "super" type argument. + */ + public final static char SUPER = '-'; + + /** + * Wildcard for a normal type argument. + */ + public final static char INSTANCEOF = '='; + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + protected final int api; + + /** + * Constructs a new {@link SignatureVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. + */ + public SignatureVisitor(final int api) { + if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { + throw new IllegalArgumentException(); + } + this.api = api; + } + + /** + * Visits a formal type parameter. + * + * @param name + * the name of the formal parameter. + */ + public void visitFormalTypeParameter(String name) { + } + + /** + * Visits the class bound of the last visited formal type parameter. + * + * @return a non null visitor to visit the signature of the class bound. + */ + public SignatureVisitor visitClassBound() { + return this; + } + + /** + * Visits an interface bound of the last visited formal type parameter. + * + * @return a non null visitor to visit the signature of the interface bound. + */ + public SignatureVisitor visitInterfaceBound() { + return this; + } + + /** + * Visits the type of the super class. + * + * @return a non null visitor to visit the signature of the super class + * type. + */ + public SignatureVisitor visitSuperclass() { + return this; + } + + /** + * Visits the type of an interface implemented by the class. + * + * @return a non null visitor to visit the signature of the interface type. + */ + public SignatureVisitor visitInterface() { + return this; + } + + /** + * Visits the type of a method parameter. + * + * @return a non null visitor to visit the signature of the parameter type. + */ + public SignatureVisitor visitParameterType() { + return this; + } + + /** + * Visits the return type of the method. + * + * @return a non null visitor to visit the signature of the return type. + */ + public SignatureVisitor visitReturnType() { + return this; + } + + /** + * Visits the type of a method exception. + * + * @return a non null visitor to visit the signature of the exception type. + */ + public SignatureVisitor visitExceptionType() { + return this; + } + + /** + * Visits a signature corresponding to a primitive type. + * + * @param descriptor + * the descriptor of the primitive type, or 'V' for void + * . + */ + public void visitBaseType(char descriptor) { + } + + /** + * Visits a signature corresponding to a type variable. + * + * @param name + * the name of the type variable. + */ + public void visitTypeVariable(String name) { + } + + /** + * Visits a signature corresponding to an array type. + * + * @return a non null visitor to visit the signature of the array element + * type. + */ + public SignatureVisitor visitArrayType() { + return this; + } + + /** + * Starts the visit of a signature corresponding to a class or interface + * type. + * + * @param name + * the internal name of the class or interface. + */ + public void visitClassType(String name) { + } + + /** + * Visits an inner class. + * + * @param name + * the local name of the inner class in its enclosing class. + */ + public void visitInnerClassType(String name) { + } + + /** + * Visits an unbounded type argument of the last visited class or inner + * class type. + */ + public void visitTypeArgument() { + } + + /** + * Visits a type argument of the last visited class or inner class type. + * + * @param wildcard + * '+', '-' or '='. + * @return a non null visitor to visit the signature of the type argument. + */ + public SignatureVisitor visitTypeArgument(char wildcard) { + return this; + } + + /** + * Ends the visit of a signature corresponding to a class or interface type. + */ + public void visitEnd() { + } +} diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureWriter.java b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureWriter.java new file mode 100644 index 000000000..d90a89390 --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/SignatureWriter.java @@ -0,0 +1,227 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.org.objectweb.asm.signature; + +import com.fr.third.org.objectweb.asm.Opcodes; + +/** + * A signature visitor that generates signatures in string format. + * + * @author Thomas Hallgren + * @author Eric Bruneton + */ +public class SignatureWriter extends SignatureVisitor { + + /** + * Builder used to construct the signature. + */ + private final StringBuilder buf = new StringBuilder(); + + /** + * Indicates if the signature contains formal type parameters. + */ + private boolean hasFormals; + + /** + * Indicates if the signature contains method parameter types. + */ + private boolean hasParameters; + + /** + * Stack used to keep track of class types that have arguments. Each element + * of this stack is a boolean encoded in one bit. The top of the stack is + * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping = + * /2. + */ + private int argumentStack; + + /** + * Constructs a new {@link SignatureWriter} object. + */ + public SignatureWriter() { + super(Opcodes.ASM6); + } + + // ------------------------------------------------------------------------ + // Implementation of the SignatureVisitor interface + // ------------------------------------------------------------------------ + + @Override + public void visitFormalTypeParameter(final String name) { + if (!hasFormals) { + hasFormals = true; + buf.append('<'); + } + buf.append(name); + buf.append(':'); + } + + @Override + public SignatureVisitor visitClassBound() { + return this; + } + + @Override + public SignatureVisitor visitInterfaceBound() { + buf.append(':'); + return this; + } + + @Override + public SignatureVisitor visitSuperclass() { + endFormals(); + return this; + } + + @Override + public SignatureVisitor visitInterface() { + return this; + } + + @Override + public SignatureVisitor visitParameterType() { + endFormals(); + if (!hasParameters) { + hasParameters = true; + buf.append('('); + } + return this; + } + + @Override + public SignatureVisitor visitReturnType() { + endFormals(); + if (!hasParameters) { + buf.append('('); + } + buf.append(')'); + return this; + } + + @Override + public SignatureVisitor visitExceptionType() { + buf.append('^'); + return this; + } + + @Override + public void visitBaseType(final char descriptor) { + buf.append(descriptor); + } + + @Override + public void visitTypeVariable(final String name) { + buf.append('T'); + buf.append(name); + buf.append(';'); + } + + @Override + public SignatureVisitor visitArrayType() { + buf.append('['); + return this; + } + + @Override + public void visitClassType(final String name) { + buf.append('L'); + buf.append(name); + argumentStack *= 2; + } + + @Override + public void visitInnerClassType(final String name) { + endArguments(); + buf.append('.'); + buf.append(name); + argumentStack *= 2; + } + + @Override + public void visitTypeArgument() { + if (argumentStack % 2 == 0) { + ++argumentStack; + buf.append('<'); + } + buf.append('*'); + } + + @Override + public SignatureVisitor visitTypeArgument(final char wildcard) { + if (argumentStack % 2 == 0) { + ++argumentStack; + buf.append('<'); + } + if (wildcard != '=') { + buf.append(wildcard); + } + return this; + } + + @Override + public void visitEnd() { + endArguments(); + buf.append(';'); + } + + /** + * Returns the signature that was built by this signature writer. + * + * @return the signature that was built by this signature writer. + */ + @Override + public String toString() { + return buf.toString(); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Ends the formal type parameters section of the signature. + */ + private void endFormals() { + if (hasFormals) { + hasFormals = false; + buf.append('>'); + } + } + + /** + * Ends the type arguments of a class or inner class type. + */ + private void endArguments() { + if (argumentStack % 2 != 0) { + buf.append('>'); + } + argumentStack /= 2; + } +} \ No newline at end of file diff --git a/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/package.html b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/package.html new file mode 100644 index 000000000..0c07d120a --- /dev/null +++ b/fine-cglib/src/com/fr/third/org/objectweb/asm/signature/package.html @@ -0,0 +1,36 @@ + + + +Provides support for type signatures. + +@since ASM 2.0 + +