diff --git a/spring-core/spring-core.gradle b/spring-core/spring-core.gradle index bfa3a423b524..693d0ad2bc3e 100644 --- a/spring-core/spring-core.gradle +++ b/spring-core/spring-core.gradle @@ -76,8 +76,7 @@ jar { dependsOn cglibRepackJar from(zipTree(cglibRepackJar.archivePath)) { include "org/springframework/cglib/**" - exclude "org/springframework/cglib/beans/BeanMap.class" - exclude "org/springframework/cglib/beans/BeanMap\$*.class" + exclude "org/springframework/cglib/beans/**" exclude "org/springframework/cglib/core/AbstractClassGenerator*.class" exclude "org/springframework/cglib/core/AsmApi*.class" exclude "org/springframework/cglib/core/KeyFactory.class" diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.java new file mode 100644 index 000000000000..30a00e8d1d07 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.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 + * + * https://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 org.springframework.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.*; +import java.security.ProtectionDomain; +import org.springframework.cglib.core.*; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Type; +import java.util.*; + +/** + * @author Chris Nokleberg + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +abstract public class BeanCopier +{ + private static final BeanCopierKey KEY_FACTORY = + (BeanCopierKey)KeyFactory.create(BeanCopierKey.class); + private static final Type CONVERTER = + TypeUtils.parseType("org.springframework.cglib.core.Converter"); + private static final Type BEAN_COPIER = + TypeUtils.parseType("org.springframework.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; + // SPRING PATCH BEGIN + setContextClass(target); + // SPRING PATCH END + } + + 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_8, + 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/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java new file mode 100644 index 000000000000..d4f5e7af9a0a --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java @@ -0,0 +1,153 @@ +/* + * 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 + * + * https://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 org.springframework.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.security.ProtectionDomain; +import java.util.*; +import org.springframework.cglib.core.*; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Type; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +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; + // SPRING PATCH BEGIN + setContextClass(superclass); + // SPRING PATCH END + } + + 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_8, + 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/spring-core/src/main/java/org/springframework/cglib/beans/BeanMap.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanMap.java index 3a6dd8e02057..f73e7e34fb39 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/BeanMap.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanMap.java @@ -97,7 +97,9 @@ public void setBean(Object bean) { this.bean = bean; if (bean != null) { beanClass = bean.getClass(); + // SPRING PATCH BEGIN setContextClass(beanClass); + // SPRING PATCH END } } diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java new file mode 100644 index 000000000000..986aa02fd618 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java @@ -0,0 +1,193 @@ +/* + * 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 + * + * https://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 org.springframework.cglib.beans; + +import java.beans.*; +import java.util.*; +import org.springframework.cglib.core.*; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Label; +import org.springframework.asm.Type; + +@SuppressWarnings({"rawtypes", "unchecked"}) +class BeanMapEmitter extends ClassEmitter { + private static final Type BEAN_MAP = + TypeUtils.parseType("org.springframework.cglib.beans.BeanMap"); + private static final Type FIXED_KEY_SET = + TypeUtils.parseType("org.springframework.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_8, 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(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/spring-core/src/main/java/org/springframework/cglib/beans/BulkBean.java b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBean.java new file mode 100644 index 000000000000..fdcda8af01be --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBean.java @@ -0,0 +1,146 @@ +/* + * 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 + * + * https://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 org.springframework.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 org.springframework.cglib.core.*; +import org.springframework.asm.ClassVisitor; + +/** + * @author Juozas Baliuka + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +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 types.clone(); + } + + public String[] getGetters() { + return getters.clone(); + } + + public String[] getSetters() { + return 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; + // SPRING PATCH BEGIN + setContextClass(target); + // SPRING PATCH END + } + + 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/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java new file mode 100644 index 000000000000..0729361159a3 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java @@ -0,0 +1,157 @@ +/* + * 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 + * + * https://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 org.springframework.cglib.beans; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import org.springframework.cglib.core.*; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Type; + +@SuppressWarnings({"rawtypes", "unchecked"}) +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("org.springframework.cglib.beans.BulkBean"); + private static final Type BULK_BEAN_EXCEPTION = + TypeUtils.parseType("org.springframework.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_8, 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/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanException.java b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanException.java new file mode 100644 index 000000000000..6ad290b7aa98 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanException.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 + * + * https://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 org.springframework.cglib.beans; + +import org.springframework.cglib.core.CodeGenerationException; + +@SuppressWarnings({"rawtypes", "unchecked", "serial"}) +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/spring-core/src/main/java/org/springframework/cglib/beans/FixedKeySet.java b/spring-core/src/main/java/org/springframework/cglib/beans/FixedKeySet.java new file mode 100644 index 000000000000..399da30ee5b9 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/FixedKeySet.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 + * + * https://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 org.springframework.cglib.beans; + +import java.util.*; + +@SuppressWarnings({"rawtypes", "unchecked"}) +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/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java b/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java new file mode 100644 index 000000000000..dfc03594228a --- /dev/null +++ b/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java @@ -0,0 +1,132 @@ +/* + * 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 + * + * https://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 org.springframework.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; +import org.springframework.cglib.core.*; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Type; +/** + * @author Chris Nokleberg + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +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(); + // SPRING PATCH BEGIN + setContextClass(target); + // SPRING PATCH END + } + + 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_8, + 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/spring-core/src/main/java/org/springframework/cglib/beans/package-info.java b/spring-core/src/main/java/org/springframework/cglib/beans/package-info.java index 51cffc06e70a..10dd532ae0d8 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/package-info.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/package-info.java @@ -1,6 +1,6 @@ /** * Spring's repackaging of the - * CGLIB beans package + * CGLIB beans package * (for internal use only). * *

As this repackaging happens at the class file level, sources diff --git a/spring-core/src/main/java/org/springframework/cglib/core/package-info.java b/spring-core/src/main/java/org/springframework/cglib/core/package-info.java index 6d43d8c8bcc2..15c9faabb755 100644 --- a/spring-core/src/main/java/org/springframework/cglib/core/package-info.java +++ b/spring-core/src/main/java/org/springframework/cglib/core/package-info.java @@ -1,6 +1,6 @@ /** * Spring's repackaging of the - * CGLIB core package + * CGLIB core package * (for internal use only). * *

As this repackaging happens at the class file level, sources diff --git a/spring-core/src/main/java/org/springframework/cglib/proxy/package-info.java b/spring-core/src/main/java/org/springframework/cglib/proxy/package-info.java index 9f8cfe268e57..345b4175a137 100644 --- a/spring-core/src/main/java/org/springframework/cglib/proxy/package-info.java +++ b/spring-core/src/main/java/org/springframework/cglib/proxy/package-info.java @@ -1,6 +1,6 @@ /** * Spring's repackaging of the - * CGLIB proxy package + * CGLIB proxy package * (for internal use only). * *

As this repackaging happens at the class file level, sources