/* * Copyright 2008 Red Hat, Inc. and/or its affiliates. * * 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 org.drools.core.factmodel; import org.drools.core.factmodel.traits.Thing; import org.drools.core.factmodel.traits.TraitFieldTMS; import org.drools.core.factmodel.traits.TraitFieldTMSImpl; import org.drools.core.factmodel.traits.TraitTypeMap; import org.drools.core.factmodel.traits.TraitableBean; import org.drools.core.phreak.ReactiveObject; import org.kie.api.definition.type.FactField; import org.mvel2.asm.AnnotationVisitor; import org.mvel2.asm.ClassVisitor; import org.mvel2.asm.ClassWriter; import org.mvel2.asm.FieldVisitor; import org.mvel2.asm.Label; import org.mvel2.asm.MethodVisitor; import org.mvel2.asm.Opcodes; import org.mvel2.asm.Type; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import static org.drools.core.rule.builder.dialect.asm.ClassGenerator.createClassWriter; /** * A builder to dynamically build simple Javabean(TM) classes */ public class DefaultBeanClassBuilder implements Opcodes, BeanClassBuilder, Serializable { protected boolean debug = false; public DefaultBeanClassBuilder() { this( "true".equalsIgnoreCase( System.getProperty( "org.drools.classbuilder.debug" ) ) ); } public DefaultBeanClassBuilder(final boolean debug) { this.debug = debug; } /** * Dynamically builds, defines and loads a class based on the given class definition * * @param classDef the class definition object structure * * @return the Class instance for the given class definition * * @throws IOException * @throws InvocationTargetException * @throws IllegalAccessException * @throws NoSuchMethodException * @throws ClassNotFoundException * @throws IllegalArgumentException * @throws SecurityException * @throws NoSuchFieldException * @throws InstantiationException */ public byte[] buildClass( ClassDefinition classDef, ClassLoader classLoader ) throws IOException, SecurityException, IllegalArgumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { ClassWriter cw = this.buildClassHeader( classLoader, classDef ); this.buildFields( cw, classDef ); if ( classDef.isTraitable() ) { this.buildDynamicPropertyMap( cw, classDef ); this.buildTraitMap( cw, classDef ); this.buildFieldTMS( cw, classDef ); } this.buildConstructors( cw, classDef ); this.buildGettersAndSetters( cw, classDef ); this.buildEqualityMethods( cw, classDef ); this.buildToString( cw, classDef ); if ( classDef.isTraitable() ) { // must guarantee serialization order when enhancing fields are present this.buildSerializationMethods(cw, classDef); } if ( classDef.isReactive() ) { implementReactivity( cw, classDef ); } cw.visitEnd(); return cw.toByteArray(); } private void buildSerializationMethods(ClassWriter cw, ClassDefinition classDef) { MethodVisitor mv; { mv = cw.visitMethod(ACC_PUBLIC, "writeExternal", "(Ljava/io/ObjectOutput;)V", null, new String[]{"java/io/IOException"}); mv.visitCode(); for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { mv.visitVarInsn( ALOAD, 1 ); mv.visitVarInsn( ALOAD, 0 ); visitFieldOrGetter( mv, classDef, field ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectOutput", BuildUtils.serializationWriterName( field.getTypeName() ), "(" + ( BuildUtils.isPrimitive( field.getTypeName() ) ? BuildUtils.getTypeDescriptor( BuildUtils.serializationType( field.getTypeName() ) ) : "Ljava/lang/Object;" ) + ")V"); } if ( classDef.isTraitable() ) { mv.visitVarInsn( ALOAD, 1 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( Opcodes.GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.MAP_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectOutput", "writeObject", "(Ljava/lang/Object;)V" ); mv.visitVarInsn( ALOAD, 1 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( Opcodes.GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectOutput", "writeObject", "(Ljava/lang/Object;)V" ); if ( classDef.isFullTraiting() ) { mv.visitVarInsn( ALOAD, 1 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( Opcodes.GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectOutput", "writeObject", "(Ljava/lang/Object;)V" ); } } mv.visitInsn(RETURN); mv.visitMaxs( 0, 0 ); mv.visitEnd(); } { mv = cw.visitMethod(ACC_PUBLIC, "readExternal", "(Ljava/io/ObjectInput;)V", null, new String[]{"java/io/IOException", "java/lang/ClassNotFoundException"}); mv.visitCode(); for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectInput", BuildUtils.serializationReaderName( field.getTypeName() ), "()" + ( BuildUtils.isPrimitive( field.getTypeName() ) ? BuildUtils.getTypeDescriptor( field.getTypeName() ) : "Ljava/lang/Object;" ) ); if ( ! BuildUtils.isPrimitive( field.getTypeName() ) ) { mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( field.getTypeName() ) ); } mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), BuildUtils.setterName( field.getName(), field.getTypeName() ), "(" + BuildUtils.getTypeDescriptor( field.getTypeName() )+ ")V"); } if ( classDef.isTraitable() ) { mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectInput", "readObject", "()Ljava/lang/Object;"); mv.visitTypeInsn( CHECKCAST, "java/util/Map" ); mv.visitFieldInsn( Opcodes.PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.MAP_FIELD_NAME, Type.getDescriptor( Map.class ) ); // mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectInput", "readObject", "()Ljava/lang/Object;"); mv.visitTypeInsn( CHECKCAST, "java/util/Map" ); mv.visitFieldInsn( Opcodes.PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); if ( classDef.isFullTraiting() ) { mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, "java/io/ObjectInput", "readObject", "()Ljava/lang/Object;"); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( TraitFieldTMS.class ) ); mv.visitFieldInsn( Opcodes.PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); } } mv.visitInsn( RETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } protected void buildGettersAndSetters(ClassWriter cw, ClassDefinition classDef) { // Building methods for ( FieldDefinition fieldDef : classDef.getFieldsDefinitions() ) { if (! fieldDef.isInherited() || fieldDef.hasOverride()) { this.buildGetMethod( cw, classDef, fieldDef ); this.buildSetMethod( cw, classDef, fieldDef ); } } } protected void buildEqualityMethods(ClassWriter cw, ClassDefinition classDef) { boolean hasKey = false; for ( FieldDefinition fld : classDef.getFieldsDefinitions() ) { if ( fld.isKey() ) { hasKey = true; break; } } if (hasKey) { this.buildEquals( cw, classDef ); this.buildHashCode( cw, classDef ); } } protected void buildFields(ClassWriter cw, ClassDefinition classDef) { // Building fields for ( FieldDefinition fieldDef : classDef.getFieldsDefinitions() ) { if (! fieldDef.isInherited()) this.buildField( cw, fieldDef ); } } private void implementReactivity(ClassWriter cw, ClassDefinition classDef) { final String LEFT_TUPLES_FIELD_NAME = "_lts"; final String TYPE_NAME = BuildUtils.getInternalType( classDef.getClassName() ); FieldVisitor fv; /* private Collection<Tuple> _lts; */ { fv = cw.visitField( ACC_PRIVATE, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;", "Ljava/util/Collection<Lorg/drools/core/spi/Tuple;>;", null ); fv.visitEnd(); } MethodVisitor mv; /* public void addLeftTuple(Tuple leftTuple) { if (_lts == null) { _lts = new HashSet<Tuple>(); } _lts.add(leftTuple); } */ { mv = cw.visitMethod( ACC_PUBLIC, "addLeftTuple", "(Lorg/drools/core/spi/Tuple;)V", null, null ); mv.visitCode(); Label l0 = new Label(); mv.visitLabel( l0 ); mv.visitLineNumber( 30, l0 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, TYPE_NAME, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;" ); Label l1 = new Label(); mv.visitJumpInsn( IFNONNULL, l1 ); Label l2 = new Label(); mv.visitLabel( l2 ); mv.visitLineNumber( 31, l2 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitTypeInsn( NEW, "java/util/HashSet" ); mv.visitInsn( DUP ); mv.visitMethodInsn( INVOKESPECIAL, "java/util/HashSet", "<init>", "()V", false ); mv.visitFieldInsn( PUTFIELD, TYPE_NAME, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;" ); mv.visitLabel( l1 ); mv.visitLineNumber( 33, l1 ); mv.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, TYPE_NAME, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;" ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, "java/util/Collection", "add", "(Ljava/lang/Object;)Z", true ); mv.visitInsn( POP ); Label l3 = new Label(); mv.visitLabel( l3 ); mv.visitLineNumber( 34, l3 ); mv.visitInsn( RETURN ); Label l4 = new Label(); mv.visitLabel( l4 ); mv.visitLocalVariable( "this", "L" + TYPE_NAME + ";", null, l0, l4, 0 ); mv.visitLocalVariable( "leftTuple", "Lorg/drools/core/spi/Tuple;", null, l0, l4, 1 ); mv.visitMaxs( 3, 2 ); mv.visitEnd(); } /* public Collection<Tuple> getLeftTuples() { return _lts != null ? _lts : Collections.emptyList(); } */ { mv = cw.visitMethod( ACC_PUBLIC, "getLeftTuples", "()Ljava/util/Collection;", "()Ljava/util/Collection<Lorg/drools/core/spi/Tuple;>;", null ); mv.visitCode(); Label l0 = new Label(); mv.visitLabel( l0 ); mv.visitLineNumber( 37, l0 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, TYPE_NAME, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;"); Label l1 = new Label(); mv.visitJumpInsn( IFNULL, l1 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, TYPE_NAME, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;" ); Label l2 = new Label(); mv.visitJumpInsn( GOTO, l2 ); mv.visitLabel( l1 ); mv.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); mv.visitMethodInsn( INVOKESTATIC, "java/util/Collections", "emptyList", "()Ljava/util/List;", false ); mv.visitLabel( l2 ); mv.visitFrame( Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/util/Collection"} ); mv.visitInsn( ARETURN ); Label l3 = new Label(); mv.visitLabel( l3 ); mv.visitLocalVariable( "this", "L" + TYPE_NAME + ";", null, l0, l3, 0 ); mv.visitMaxs( 1, 1 ); mv.visitEnd(); } /* protected void notifyModification() { ReactiveObjectUtil.notifyModification(this); } */ { mv = cw.visitMethod( ACC_PROTECTED, "notifyModification", "()V", null, null ); mv.visitCode(); Label l0 = new Label(); mv.visitLabel( l0 ); mv.visitLineNumber( 41, l0 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKESTATIC, "org/drools/core/phreak/ReactiveObjectUtil", "notifyModification", "(Lorg/drools/core/phreak/ReactiveObject;)V", false ); Label l1 = new Label(); mv.visitLabel( l1 ); mv.visitLineNumber( 42, l1 ); mv.visitInsn( RETURN ); Label l2 = new Label(); mv.visitLabel( l2 ); mv.visitLocalVariable( "this", "L" + TYPE_NAME + ";", null, l0, l2, 0 ); mv.visitMaxs( 1, 1 ); mv.visitEnd(); } /* public void removeLeftTuple(Tuple leftTuple) { _lts.remove(leftTuple); } */ { mv = cw.visitMethod( ACC_PUBLIC, "removeLeftTuple", "(Lorg/drools/core/spi/Tuple;)V", null, null ); mv.visitCode(); Label l0 = new Label(); mv.visitLabel( l0 ); mv.visitLineNumber( 46, l0 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, TYPE_NAME, LEFT_TUPLES_FIELD_NAME, "Ljava/util/Collection;" ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, "java/util/Collection", "remove", "(Ljava/lang/Object;)Z", true ); mv.visitInsn( POP ); Label l1 = new Label(); mv.visitLabel( l1 ); mv.visitLineNumber( 47, l1 ); mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel( l2 ); mv.visitLocalVariable( "this", "Lorg/drools/core/phreak/AbstractReactiveObject;", null, l0, l2, 0 ); mv.visitLocalVariable( "leftTuple", "Lorg/drools/core/spi/Tuple;", null, l0, l2, 1 ); mv.visitMaxs( 2, 2 ); mv.visitEnd(); } } protected void buildConstructors(ClassWriter cw, ClassDefinition classDef) { // Building default constructor try { this.buildDefaultConstructor( cw, classDef ); } catch (Exception e) { e.printStackTrace(); } // Building constructor with all fields if (classDef.getFieldsDefinitions().size() > 0 && classDef.getFieldsDefinitions().size() < 120) { this.buildConstructorWithFields( cw, classDef, classDef.getFieldsDefinitions() ); } // Building constructor with key fields only List<FieldDefinition> keys = new LinkedList<FieldDefinition>(); for ( FieldDefinition fieldDef : classDef.getFieldsDefinitions() ) { if ( fieldDef.isKey() ) { keys.add( fieldDef ); } } if ( !keys.isEmpty() && keys.size() != classDef.getFieldsDefinitions().size() ) { this.buildConstructorWithFields( cw, classDef, keys ); } } /** * A traitable class is a special class with support for dynamic properties and types. * * This method builds the trait map, containing the references to the proxies * for each trait carried by an object at a given time. * * @param cw * @param classDef */ protected void buildTraitMap(ClassWriter cw, ClassDefinition classDef) { FieldVisitor fv = cw.visitField( ACC_PRIVATE, TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ), "Ljava/util/Map<Ljava/lang/String;Lorg/drools/core/factmodel/traits/Thing;>;", null ); fv.visitEnd(); MethodVisitor mv; mv = cw.visitMethod( ACC_PUBLIC, "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ), "()Ljava/util/Map<Ljava/lang/String;Lorg/drools/factmodel/traits/Thing;>;", null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitInsn(ARETURN); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "_setTraitMap", Type.getMethodDescriptor( Type.getType( void.class ), new Type[] { Type.getType( Map.class ) } ), null, null); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitFieldInsn( PUTFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class )); mv.visitInsn( RETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "addTrait", Type.getMethodDescriptor( Type.getType( void.class ), new Type[] { Type.getType( String.class ), Type.getType( Thing.class ) } ), "(Ljava/lang/String;Lorg/drools/core/factmodel/traits/Thing;)V", null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); mv.visitVarInsn( ALOAD, 1 ); mv.visitVarInsn( ALOAD, 2 ); mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( Map.class ), "put", Type.getMethodDescriptor( Type.getType( Object.class ), new Type[] { Type.getType( Object.class ), Type.getType( Object.class ) } ) ); mv.visitInsn( POP ); mv.visitInsn( RETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "getTrait", Type.getMethodDescriptor( Type.getType( Thing.class ), new Type[] { Type.getType( String.class ) } ), Type.getMethodDescriptor( Type.getType( Thing.class ), new Type[] { Type.getType( String.class ) } ), null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( Map.class ), "get", Type.getMethodDescriptor( Type.getType( Object.class ), new Type[] { Type.getType( Object.class ) } ) ); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( Thing.class ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "hasTrait", Type.getMethodDescriptor( Type.getType( boolean.class ), new Type[] { Type.getType( String.class ) } ), null, null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); Label l0 = new Label(); mv.visitJumpInsn( IFNONNULL, l0 ); mv.visitInsn( ICONST_0 ); mv.visitInsn( IRETURN ); mv.visitLabel( l0 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( Map.class ), "containsKey", Type.getMethodDescriptor( Type.getType( boolean.class ), new Type[] { Type.getType( Object.class ) } ) ); mv.visitInsn( IRETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "hasTraits", Type.getMethodDescriptor( Type.getType( boolean.class ), new Type[] {} ), null, null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ),TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); Label l5 = new Label(); mv.visitJumpInsn( IFNULL, l5 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( Map.class ), "isEmpty", Type.getMethodDescriptor( Type.BOOLEAN_TYPE, new Type[] {} ) ); mv.visitJumpInsn( IFNE, l5 ); mv.visitInsn( ICONST_1 ); Label l4 = new Label(); mv.visitJumpInsn( GOTO, l4 ); mv.visitLabel( l5 ); mv.visitInsn( ICONST_0 ); mv.visitLabel( l4 ); mv.visitInsn( IRETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "removeTrait", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( String.class ) } ), Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( String.class ) } ), null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( TraitTypeMap.class ) ); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName( TraitTypeMap.class ), "removeCascade", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( String.class )} ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "removeTrait", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( BitSet.class ) } ), Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( BitSet.class ) } ), null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( TraitTypeMap.class ) ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName( TraitTypeMap.class ), "removeCascade", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( BitSet.class )} ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "getTraits", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { } ), "()Ljava/util/Collection<Ljava/lang/String;>;", null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getName() ), "_getTraitMap", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ) ); mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( Map.class ), "keySet", Type.getMethodDescriptor( Type.getType( Set.class ), new Type[] {} ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "_setBottomTypeCode", Type.getMethodDescriptor( Type.getType( void.class ), new Type[] { Type.getType( BitSet.class ) } ), null, null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME , Type.getDescriptor( Map.class ) ); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( TraitTypeMap.class ) ); mv.visitVarInsn( ALOAD, 1 ); mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName( TraitTypeMap.class ), "setBottomCode", Type.getMethodDescriptor( Type.getType( void.class ), new Type[] { Type.getType( BitSet.class ) } ) ); mv.visitInsn( RETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "getMostSpecificTraits", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { } ) , "()Ljava/util/Collection<Lorg/drools/core/factmodel/traits/Thing;>;", null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME , Type.getDescriptor( Map.class ) ); Label l99 = new Label(); mv.visitJumpInsn( IFNULL, l99 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME , Type.getDescriptor( Map.class ) ); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( TraitTypeMap.class ) ); mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName( TraitTypeMap.class ), "getMostSpecificTraits", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { } ) ); mv.visitInsn( ARETURN ); mv.visitLabel( l99 ); mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName( Collections.class ), "emptySet", Type.getMethodDescriptor( Type.getType( Set.class ), new Type[] { } ) ); mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName( Collections.class ), "unmodifiableCollection", Type.getMethodDescriptor( Type.getType( Collection.class ), new Type[] { Type.getType( Collection.class ) } ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "getCurrentTypeCode", Type.getMethodDescriptor( Type.getType( BitSet.class ), new Type[] { } ) , null, null ); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); Label l3 = new Label(); mv.visitJumpInsn( IFNONNULL, l3 ); mv.visitInsn( ACONST_NULL ); mv.visitInsn( ARETURN ); mv.visitLabel( l3 ); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getName() ), TraitableBean.TRAITSET_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitTypeInsn( CHECKCAST, Type.getInternalName( TraitTypeMap.class ) ); mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName( TraitTypeMap.class ), "getCurrentTypeCode", Type.getMethodDescriptor( Type.getType( BitSet.class ), new Type[] { } ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); } /** * A traitable class is a special class with support for dynamic properties and types. * * This method builds the property map, containing the key/values pairs to implement * any property defined in a trait interface but not supported by the traited class * fields. * * @param cw * @param def */ protected void buildDynamicPropertyMap( ClassWriter cw, ClassDefinition def ) { FieldVisitor fv = cw.visitField( Opcodes.ACC_PRIVATE, TraitableBean.MAP_FIELD_NAME, Type.getDescriptor( Map.class ) , "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;", null); fv.visitEnd(); MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "_getDynamicProperties", Type.getMethodDescriptor( Type.getType( Map.class ), new Type[] {} ), "()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;", null); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType(def.getName()), TraitableBean.MAP_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "_setDynamicProperties", Type.getMethodDescriptor( Type.getType( void.class ), new Type[] { Type.getType( Map.class ) } ), "(Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)V", null); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitFieldInsn ( PUTFIELD, BuildUtils.getInternalType( def.getName() ), TraitableBean.MAP_FIELD_NAME, Type.getDescriptor( Map.class ) ); mv.visitInsn( RETURN) ; mv.visitMaxs( 0, 0 ); mv.visitEnd(); } /** * A traitable class is a special class with support for dynamic properties and types. * * A traitable class in logical mode provides additional control over the values * and type(s) of its fields. * * @param cw * @param def */ protected void buildFieldTMS( ClassWriter cw, ClassDefinition def ) { FieldVisitor fv = cw.visitField( Opcodes.ACC_PRIVATE, TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ), null, null ); fv.visitEnd(); MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "_getFieldTMS", Type.getMethodDescriptor( Type.getType( TraitFieldTMS.class ), new Type[] {} ), null, null); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( def.getName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); mv.visitInsn( ARETURN ); mv.visitMaxs( 0, 0 ); mv.visitEnd(); mv = cw.visitMethod( ACC_PUBLIC, "_setFieldTMS", Type.getMethodDescriptor( Type.VOID_TYPE, new Type[] { Type.getType( TraitFieldTMS.class ) } ), null, null); mv.visitCode(); mv.visitVarInsn( ALOAD, 0 ); mv.visitVarInsn( ALOAD, 1 ); mv.visitFieldInsn ( PUTFIELD, BuildUtils.getInternalType( def.getName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); mv.visitInsn( RETURN) ; mv.visitMaxs( 0, 0 ); mv.visitEnd(); } /** * Defines the class header for the given class definition */ protected ClassWriter buildClassHeader(ClassLoader classLoader, ClassDefinition classDef) { boolean reactive = classDef.isReactive(); String[] original = classDef.getInterfaces(); int interfacesNr = original.length + (reactive ? 2 : 1); String[] interfaces = new String[interfacesNr]; for ( int i = 0; i < original.length; i++ ) { interfaces[i] = BuildUtils.getInternalType( original[i] ); } interfaces[original.length] = BuildUtils.getInternalType( GeneratedFact.class.getName() ); if (reactive) { interfaces[original.length+1] = BuildUtils.getInternalType( ReactiveObject.class.getName() ); } int classModifiers = Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER; if ( classDef.isAbstrakt() ) { classModifiers += Opcodes.ACC_ABSTRACT; } ClassWriter cw = createClassWriter( classLoader, classModifiers, BuildUtils.getInternalType( classDef.getClassName() ), null, BuildUtils.getInternalType( classDef.getSuperClass() ), interfaces ); buildClassAnnotations(classDef, cw); cw.visitSource( classDef.getClassName() + ".java", null ); return cw; } /** * Creates the field defined by the given FieldDefinition * * @param cw * @param fieldDef */ protected void buildField( ClassVisitor cw, FieldDefinition fieldDef) { FieldVisitor fv = cw.visitField( Opcodes.ACC_PROTECTED, fieldDef.getName(), BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ), null, null ); buildFieldAnnotations( fieldDef, fv ); fv.visitEnd(); } /** * Creates a default constructor for the class * * @param cw */ protected void buildDefaultConstructor(ClassVisitor cw, ClassDefinition classDef) { MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "<init>", Type.getMethodDescriptor( Type.VOID_TYPE, new Type[]{} ), null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } boolean hasObjects = defaultConstructorStart( mv, classDef ); mv.visitInsn(Opcodes.RETURN); Label l1 = null; if ( this.debug ) { l1 = new Label(); mv.visitLabel( l1 ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, l1, 0 ); } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } protected boolean defaultConstructorStart( MethodVisitor mv, ClassDefinition classDef ) { // Building default constructor mv.visitVarInsn( Opcodes.ALOAD, 0 ); String sup = ""; try { sup = Type.getInternalName(Class.forName(classDef.getSuperClass())); } catch (ClassNotFoundException e) { sup = BuildUtils.getInternalType( classDef.getSuperClass() ); } mv.visitMethodInsn( Opcodes.INVOKESPECIAL, sup, "<init>", Type.getMethodDescriptor( Type.VOID_TYPE, new Type[]{} ) ); boolean hasObjects = false; for (FieldDefinition field : classDef.getFieldsDefinitions()) { hasObjects = hasObjects || initFieldWithDefaultValue( mv, classDef, field ); } if ( classDef.isTraitable() ) { initializeDynamicTypeStructures( mv, classDef ); } return hasObjects; } protected boolean initFieldWithDefaultValue( MethodVisitor mv, ClassDefinition classDef, FieldDefinition field ) { if ( field.getInitExpr() == null && field.isInherited() ) { return false; } // get simple init expression value Object val = BuildUtils.getDefaultValue(field); boolean hasObjects = false; if (val != null) { // there's a simple init expression mv.visitVarInsn(Opcodes.ALOAD, 0); if ( BuildUtils.isPrimitive( field.getTypeName() ) || BuildUtils.isBoxed( field.getTypeName() ) || String.class.getName().equals( field.getTypeName() ) ) { mv.visitLdcInsn(val); if ( BuildUtils.isBoxed(field.getTypeName()) ) { mv.visitMethodInsn(Opcodes.INVOKESTATIC, BuildUtils.getInternalType(field.getTypeName()), "valueOf", "("+BuildUtils.unBox(field.getTypeName())+")"+BuildUtils.getTypeDescriptor(field.getTypeName())); } } else { hasObjects = true; String type = BuildUtils.getInternalType( val.getClass().getName() ); mv.visitTypeInsn( NEW, type ); mv.visitInsn(DUP); mv.visitMethodInsn( INVOKESPECIAL, type, "<init>", "()V"); } } else { // there's a complex init expression if ( field.getInitExpr() != null ) { mv.visitVarInsn( ALOAD, 0 ); mv.visitLdcInsn( field.getInitExpr() ); mv.visitMethodInsn( INVOKESTATIC, "org/mvel2/MVEL", "eval", "(Ljava/lang/String;)Ljava/lang/Object;"); mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( field.getTypeName() ) ); val = field.getInitExpr(); } } if ( val != null ) { if (! field.isInherited()) { mv.visitFieldInsn( Opcodes.PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), field.getName(), BuildUtils.getTypeDescriptor( field.getTypeName() ) ); } else { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getClassName() ), field.getWriteMethod(), Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(BuildUtils.getTypeDescriptor(field.getTypeName()))} )); } } return hasObjects; } /** * Initializes the trait map and dynamic property map to empty values * @param mv * @param classDef */ protected void initializeDynamicTypeStructures( MethodVisitor mv, ClassDefinition classDef) { if ( classDef.isFullTraiting() ) { mv.visitVarInsn( ALOAD, 0 ); mv.visitTypeInsn( NEW, Type.getInternalName( TraitFieldTMSImpl.class ) ); mv.visitInsn( DUP ); mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName( TraitFieldTMSImpl.class ), "<init>", "()V" ); mv.visitFieldInsn( PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); for ( FactField hardField : classDef.getFields() ) { FieldDefinition fld = (FieldDefinition) hardField; mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); mv.visitLdcInsn( Type.getType( BuildUtils.getTypeDescriptor( classDef.getClassName() ) ) ); mv.visitLdcInsn( fld.resolveAlias() ); if ( BuildUtils.isPrimitive( fld.getTypeName() ) ) { // mv.visitFieldInsn( GETSTATIC, BuildUtils.getInternalType( BuildUtils.box( fld.getTypeName() ) ), "TYPE", Type.getDescriptor( Class.class ) ); mv.visitLdcInsn( Type.getType( BuildUtils.getTypeDescriptor( BuildUtils.box( fld.getTypeName() ) ) ) ); } else { mv.visitLdcInsn( Type.getType( BuildUtils.getTypeDescriptor( fld.getTypeName() ) ) ); } mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getClassName() ), BuildUtils.getterName( fld.getName(), fld.getTypeName() ), "()" + BuildUtils.getTypeDescriptor( fld.getTypeName() ) ); if ( BuildUtils.isPrimitive( fld.getTypeName() ) ) { mv.visitMethodInsn( INVOKESTATIC, BuildUtils.getInternalType( BuildUtils.box( fld.getTypeName() ) ), "valueOf", "(" + BuildUtils.getTypeDescriptor( fld.getTypeName() ) + ")" + BuildUtils.getTypeDescriptor( BuildUtils.box( fld.getTypeName() ) ) ); } if ( fld.getInitExpr() != null ) { mv.visitLdcInsn( fld.getInitExpr() ); } else { mv.visitInsn( ACONST_NULL ); } mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( TraitFieldTMS.class ), "registerField", Type.getMethodDescriptor( Type.VOID_TYPE, new Type[] { Type.getType( Class.class ), Type.getType( String.class ), Type.getType( Class.class ), Type.getType( Object.class ), Type.getType( String.class ) } ) ); } } } /** * Creates a constructor that takes and assigns values to all * fields in the order they are declared. * * @param cw * @param classDef */ protected void buildConstructorWithFields(ClassVisitor cw, ClassDefinition classDef, Collection<FieldDefinition> fieldDefs) { Type[] params = new Type[fieldDefs.size()]; int index = 0; for ( FieldDefinition field : fieldDefs ) { params[index++] = Type.getType( BuildUtils.getTypeDescriptor( field.getTypeName() ) ); } MethodVisitor mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "<init>", Type.getMethodDescriptor( Type.VOID_TYPE, params ), null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } fieldConstructorStart( mv, classDef, fieldDefs ); mv.visitInsn( Opcodes.RETURN ); Label l1 = null; if ( this.debug ) { l1 = new Label(); mv.visitLabel( l1 ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, l1, 0 ); for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { Label l11 = new Label(); mv.visitLabel( l11 ); mv.visitLocalVariable( field.getName(), BuildUtils.getTypeDescriptor( field.getTypeName() ), null, l0, l1, 0 ); } } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } protected void fieldConstructorStart(MethodVisitor mv, ClassDefinition classDef, Collection<FieldDefinition> fieldDefs) { mv.visitVarInsn( Opcodes.ALOAD, 0 ); String sup = ""; try { sup = Type.getInternalName(Class.forName(classDef.getSuperClass())); } catch (ClassNotFoundException e) { sup = BuildUtils.getInternalType( classDef.getSuperClass() ); } mv.visitMethodInsn( Opcodes.INVOKESPECIAL, sup, "<init>", Type.getMethodDescriptor( Type.VOID_TYPE, new Type[]{} ) ); int index = 1; // local vars start at 1, as 0 is "this" for ( FieldDefinition field : fieldDefs ) { if ( this.debug ) { Label l11 = new Label(); mv.visitLabel( l11 ); } mv.visitVarInsn( Opcodes.ALOAD, 0 ); mv.visitVarInsn( Type.getType( BuildUtils.getTypeDescriptor( field.getTypeName() ) ).getOpcode( Opcodes.ILOAD ), index++ ); if ( field.getTypeName().equals( "long" ) || field.getTypeName().equals( "double" ) ) { // long and double variables use 2 words on the variables table index++; } if (! field.isInherited()) { mv.visitFieldInsn( Opcodes.PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), field.getName(), BuildUtils.getTypeDescriptor( field.getTypeName() ) ); } else { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, BuildUtils.getInternalType(classDef.getClassName()), field.getWriteMethod(), Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(BuildUtils.getTypeDescriptor(field.getTypeName()))} )); } } for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { if ( ! fieldDefs.contains( field ) && field.getInitExpr() != null && ! "".equals( field.getInitExpr().trim() ) ) { initFieldWithDefaultValue( mv, classDef, field ); } } if ( classDef.isTraitable() ) { initializeDynamicTypeStructures( mv, classDef ); } } /** * Creates the set method for the given field definition * * @param cw * @param classDef * @param fieldDef */ protected void buildSetMethod(ClassVisitor cw, ClassDefinition classDef, FieldDefinition fieldDef) { MethodVisitor mv; // set method { mv = cw.visitMethod( Opcodes.ACC_PUBLIC, fieldDef.getWriteMethod(), Type.getMethodDescriptor( Type.VOID_TYPE, new Type[]{Type.getType( BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ) )} ), null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } mv.visitVarInsn( Opcodes.ALOAD, 0 ); if ( classDef.isTraitable() && classDef.isFullTraiting() ) { updateTraitableField( mv, classDef, fieldDef ); } else { mv.visitVarInsn( Type.getType( BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ) ).getOpcode( Opcodes.ILOAD ), 1 ); } if ( ! fieldDef.hasOverride() ) { mv.visitFieldInsn( Opcodes.PUTFIELD, BuildUtils.getInternalType( classDef.getClassName() ), fieldDef.getName(), BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ) ); } else { mv.visitMethodInsn( INVOKESPECIAL, BuildUtils.getInternalType( classDef.getSuperClass() ), BuildUtils.setterName( fieldDef.getName(), fieldDef.getOverriding() ), Type.getMethodDescriptor( Type.VOID_TYPE, new Type[]{Type.getType( BuildUtils.getTypeDescriptor( fieldDef.getOverriding()) )} ), false ); } if (classDef.isReactive()) { mv.visitVarInsn( ALOAD, 0 ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getClassName() ), "notifyModification", "()V", false ); } mv.visitInsn( Opcodes.RETURN ); Label l1 = null; if ( this.debug ) { l1 = new Label(); mv.visitLabel( l1 ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, l1, 0 ); } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } /** * Creates the get method for the given field definition * * @param cw * @param classDef * @param fieldDef */ protected void buildGetMethod(ClassVisitor cw, ClassDefinition classDef, FieldDefinition fieldDef) { MethodVisitor mv; // Get method { mv = cw.visitMethod( Opcodes.ACC_PUBLIC, fieldDef.getReadMethod(), Type.getMethodDescriptor( Type.getType( BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ) ), new Type[]{} ), null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } mv.visitVarInsn( Opcodes.ALOAD, 0 ); if ( ! fieldDef.hasOverride() ) { mv.visitFieldInsn( Opcodes.GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), fieldDef.getName(), BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ) ); mv.visitInsn( Type.getType( BuildUtils.getTypeDescriptor( fieldDef.getTypeName() ) ).getOpcode( Opcodes.IRETURN ) ); } else { mv.visitMethodInsn( INVOKESPECIAL, BuildUtils.getInternalType( classDef.getSuperClass() ), BuildUtils.getterName( fieldDef.getName(), fieldDef.getOverriding() ), Type.getMethodDescriptor( Type.getType( BuildUtils.getTypeDescriptor( fieldDef.getOverriding() ) ), new Type[]{} ), false ); mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( fieldDef.getTypeName() ) ); mv.visitInsn( BuildUtils.returnType( fieldDef.getTypeName() ) ); } Label l1 = null; if ( this.debug ) { l1 = new Label(); mv.visitLabel( l1 ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, l1, 0 ); } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } protected void buildEquals(ClassVisitor cw, ClassDefinition classDef) { MethodVisitor mv; // Building equals method { mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } // if ( this == obj ) return true; mv.visitVarInsn( Opcodes.ALOAD, 0 ); mv.visitVarInsn( Opcodes.ALOAD, 1 ); Label l1 = new Label(); mv.visitJumpInsn( Opcodes.IF_ACMPNE, l1 ); mv.visitInsn( Opcodes.ICONST_1 ); mv.visitInsn( Opcodes.IRETURN ); // if ( obj == null ) return false; mv.visitLabel( l1 ); mv.visitVarInsn( Opcodes.ALOAD, 1 ); Label l2 = new Label(); mv.visitJumpInsn( Opcodes.IFNONNULL, l2 ); mv.visitInsn( Opcodes.ICONST_0 ); mv.visitInsn( Opcodes.IRETURN ); // if ( getClass() != obj.getClass() ) return false; mv.visitLabel( l2 ); mv.visitVarInsn( Opcodes.ALOAD, 0 ); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( Object.class ), "getClass", Type.getMethodDescriptor( Type.getType( Class.class ), new Type[]{} ) ); mv.visitVarInsn( Opcodes.ALOAD, 1 ); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( Object.class ), "getClass", Type.getMethodDescriptor( Type.getType( Class.class ), new Type[]{} ) ); Label l3 = new Label(); mv.visitJumpInsn( Opcodes.IF_ACMPEQ, l3 ); mv.visitInsn( Opcodes.ICONST_0 ); mv.visitInsn( Opcodes.IRETURN ); // final <classname> other = (<classname>) obj; mv.visitLabel( l3 ); mv.visitVarInsn( Opcodes.ALOAD, 1 ); mv.visitTypeInsn( Opcodes.CHECKCAST, BuildUtils.getInternalType( classDef.getClassName() ) ); mv.visitVarInsn( Opcodes.ASTORE, 2 ); // for each key field int count = 0; for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { if ( field.isKey() ) { count++; Label goNext = new Label(); if ( BuildUtils.isPrimitive(field.getTypeName()) ) { // if attr is primitive // if ( this.<attr> != other.<booleanAttr> ) return false; mv.visitVarInsn( Opcodes.ALOAD, 0 ); visitFieldOrGetter(mv, classDef, field); mv.visitVarInsn(Opcodes.ALOAD, 2); visitFieldOrGetter(mv, classDef, field); if ( field.getTypeName().equals( "long" ) ) { mv.visitInsn( Opcodes.LCMP ); mv.visitJumpInsn( Opcodes.IFEQ, goNext ); } else if ( field.getTypeName().equals( "double" ) ) { mv.visitInsn( Opcodes.DCMPL ); mv.visitJumpInsn( Opcodes.IFEQ, goNext ); } else if ( field.getTypeName().equals( "float" ) ) { mv.visitInsn( Opcodes.FCMPL ); mv.visitJumpInsn( Opcodes.IFEQ, goNext ); } else { // boolean, byte, char, short, int mv.visitJumpInsn( Opcodes.IF_ICMPEQ, goNext ); } mv.visitInsn( Opcodes.ICONST_0 ); mv.visitInsn( Opcodes.IRETURN ); } else { // if attr is not a primitive // if ( this.<attr> == null && other.<attr> != null || // this.<attr> != null && ! this.<attr>.equals( other.<attr> ) ) return false; mv.visitVarInsn( Opcodes.ALOAD, 0 ); visitFieldOrGetter(mv, classDef, field); Label secondIfPart = new Label(); mv.visitJumpInsn( Opcodes.IFNONNULL, secondIfPart ); // if ( other.objAttr != null ) return false; mv.visitVarInsn( Opcodes.ALOAD, 2 ); visitFieldOrGetter(mv, classDef, field); Label returnFalse = new Label(); mv.visitJumpInsn( Opcodes.IFNONNULL, returnFalse ); mv.visitLabel( secondIfPart ); mv.visitVarInsn( Opcodes.ALOAD, 0 ); visitFieldOrGetter(mv, classDef, field); mv.visitJumpInsn( Opcodes.IFNULL, goNext ); mv.visitVarInsn( Opcodes.ALOAD, 0 ); visitFieldOrGetter(mv, classDef, field); mv.visitVarInsn( Opcodes.ALOAD, 2 ); visitFieldOrGetter(mv, classDef, field); if ( ! BuildUtils.isArray( field.getTypeName() ) ) { mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z" ); } else { mv.visitMethodInsn( Opcodes.INVOKESTATIC, "java/util/Arrays", "equals", "(" + BuildUtils.arrayType( field.getTypeName() ) + BuildUtils.arrayType( field.getTypeName() ) + ")Z" ); } mv.visitJumpInsn( Opcodes.IFNE, goNext ); mv.visitLabel( returnFalse ); mv.visitInsn( Opcodes.ICONST_0 ); mv.visitInsn( Opcodes.IRETURN ); } mv.visitLabel( goNext ); } } if ( count > 0 ) { mv.visitInsn( Opcodes.ICONST_1 ); } else { mv.visitInsn( Opcodes.ICONST_0 ); } mv.visitInsn( Opcodes.IRETURN ); Label lastLabel = null; if ( this.debug ) { lastLabel = new Label(); mv.visitLabel( lastLabel ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, lastLabel, 0 ); mv.visitLocalVariable( "obj", Type.getDescriptor( Object.class ), null, l0, lastLabel, 1 ); mv.visitLocalVariable( "other", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, lastLabel, 2 ); } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } protected void buildSystemHashCode(ClassWriter cw) { { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "identityHashCode", "(Ljava/lang/Object;)I"); mv.visitInsn(IRETURN); mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } protected void buildHashCode(ClassVisitor cw, ClassDefinition classDef) { MethodVisitor mv; // Building hashCode() method { mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "hashCode", "()I", null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } // int result = 1; mv.visitInsn( Opcodes.ICONST_1 ); mv.visitVarInsn( Opcodes.ISTORE, 1 ); // for each key field for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { if ( field.isKey() ) { // result = result * 31 + <attr_hash> mv.visitVarInsn( Opcodes.ILOAD, 1 ); mv.visitIntInsn( Opcodes.BIPUSH, 31 ); mv.visitVarInsn( Opcodes.ILOAD, 1 ); mv.visitInsn( Opcodes.IMUL ); mv.visitVarInsn(Opcodes.ALOAD, 0); visitFieldOrGetter(mv, classDef, field); if ( "boolean".equals( field.getTypeName() ) ) { // attr_hash ::== <boolean_attr> ? 1231 : 1237; Label blabel1 = new Label(); mv.visitJumpInsn( Opcodes.IFEQ, blabel1 ); mv.visitIntInsn( Opcodes.SIPUSH, 1231 ); Label blabel2 = new Label(); mv.visitJumpInsn( Opcodes.GOTO, blabel2 ); mv.visitLabel( blabel1 ); mv.visitIntInsn( Opcodes.SIPUSH, 1237 ); mv.visitLabel( blabel2 ); } else if ( "long".equals( field.getTypeName() ) ) { // attr_hash ::== (int) (longAttr ^ (longAttr >>> 32)) mv.visitVarInsn( Opcodes.ALOAD, 0 ); visitFieldOrGetter(mv, classDef, field); mv.visitIntInsn( Opcodes.BIPUSH, 32 ); mv.visitInsn( Opcodes.LUSHR ); mv.visitInsn( Opcodes.LXOR ); mv.visitInsn( Opcodes.L2I ); } else if ( "float".equals( field.getTypeName() ) ) { // attr_hash ::== Float.floatToIntBits( floatAttr ); mv.visitMethodInsn( Opcodes.INVOKESTATIC, Type.getInternalName( Float.class ), "floatToIntBits", "(F)I" ); } else if ( "double".equals( field.getTypeName() ) ) { // attr_hash ::== (int) (Double.doubleToLongBits( doubleAttr ) ^ (Double.doubleToLongBits( doubleAttr ) >>> 32)); mv.visitMethodInsn( Opcodes.INVOKESTATIC, Type.getInternalName( Double.class ), "doubleToLongBits", "(D)J" ); mv.visitInsn( Opcodes.DUP2 ); mv.visitIntInsn( Opcodes.BIPUSH, 32 ); mv.visitInsn( Opcodes.LUSHR ); mv.visitInsn( Opcodes.LXOR ); mv.visitInsn( Opcodes.L2I ); } else if ( !BuildUtils.isPrimitive(field.getTypeName()) ) { // attr_hash ::== ((objAttr == null) ? 0 : objAttr.hashCode()); Label olabel1 = new Label(); mv.visitJumpInsn( Opcodes.IFNONNULL, olabel1 ); mv.visitInsn( Opcodes.ICONST_0 ); Label olabel2 = new Label(); mv.visitJumpInsn( Opcodes.GOTO, olabel2 ); mv.visitLabel( olabel1 ); mv.visitVarInsn( Opcodes.ALOAD, 0 ); visitFieldOrGetter(mv, classDef, field); if ( ! BuildUtils.isArray( field.getTypeName() ) ) { mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I" ); } else { mv.visitMethodInsn( INVOKESTATIC, "java/util/Arrays", "hashCode", "(" + BuildUtils.arrayType( field.getTypeName() ) + ")I"); } mv.visitLabel( olabel2 ); } mv.visitInsn( Opcodes.IADD ); mv.visitVarInsn( Opcodes.ISTORE, 1 ); } } mv.visitVarInsn( Opcodes.ILOAD, 1 ); mv.visitInsn( Opcodes.IRETURN ); Label lastLabel = null; if ( this.debug ) { lastLabel = new Label(); mv.visitLabel( lastLabel ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, lastLabel, 0 ); mv.visitLocalVariable( "hash", Type.getDescriptor( int.class ), null, l0, lastLabel, 1 ); } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } protected void buildToString(ClassVisitor cw, ClassDefinition classDef) { MethodVisitor mv; { mv = cw.visitMethod( Opcodes.ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null ); mv.visitCode(); Label l0 = null; if ( this.debug ) { l0 = new Label(); mv.visitLabel( l0 ); } // StringBuilder buf = new StringBuilder(); mv.visitTypeInsn( Opcodes.NEW, Type.getInternalName( StringBuilder.class ) ); mv.visitInsn( Opcodes.DUP ); mv.visitMethodInsn( Opcodes.INVOKESPECIAL, Type.getInternalName( StringBuilder.class ), "<init>", "()V" ); mv.visitVarInsn( Opcodes.ASTORE, 1 ); // buf.append(this.getClass().getSimpleName()) mv.visitVarInsn( Opcodes.ALOAD, 1 ); mv.visitVarInsn( Opcodes.ALOAD, 0 ); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getClassName() ), "getClass", "()Ljava/lang/Class;" ); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( Class.class ), "getSimpleName", "()Ljava/lang/String;" ); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( StringBuilder.class ), "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;" ); appendToStringBuilder(mv, "( "); buildFieldsToString( classDef, mv, false ); appendToStringBuilder(mv, " )"); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( StringBuilder.class ), "toString", "()Ljava/lang/String;" ); mv.visitInsn( Opcodes.ARETURN ); Label lastLabel = null; if ( this.debug ) { lastLabel = new Label(); mv.visitLabel( lastLabel ); mv.visitLocalVariable( "this", BuildUtils.getTypeDescriptor( classDef.getClassName() ), null, l0, lastLabel, 0 ); mv.visitLocalVariable( "buf", Type.getDescriptor( StringBuilder.class ), null, l0, lastLabel, 1 ); } mv.visitMaxs( 0, 0 ); mv.visitEnd(); } } protected boolean buildFieldsToString( ClassDefinition classDef, MethodVisitor mv, boolean previous ) { boolean first = true; for ( FieldDefinition field : classDef.getFieldsDefinitions() ) { buildFieldToString( field, classDef, mv, first ); first = false; } return previous; } protected void buildFieldToString(FieldDefinition field, ClassDefinition classDef, MethodVisitor mv, boolean first) { if ( !first ) { // buf.append(", "); appendToStringBuilder(mv, ", "); } // buf.append(attrName) appendToStringBuilder(mv, field.getName()); // buf.append("="); appendToStringBuilder(mv, "="); // buf.append(attrValue) if (field.isRecursive()) { appendToStringBuilder( mv, field.getTypeName() + " [recursive]"); } else { mv.visitVarInsn(Opcodes.ALOAD, 0); visitFieldOrGetter( mv, classDef, field ); if ( BuildUtils.isPrimitive(field.getTypeName()) ) { String type = field.getTypeName().matches( "(byte|short)" ) ? "int" : field.getTypeName(); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( StringBuilder.class ), "append", Type.getMethodDescriptor( Type.getType( StringBuilder.class ), Type.getType( BuildUtils.getTypeDescriptor( type ) ) ) ); } else if ( BuildUtils.isArray( field.getTypeName() ) && BuildUtils.arrayDimSize( field.getTypeName() ) == 1 ) { mv.visitMethodInsn( INVOKESTATIC, "java/util/Arrays", "toString", "(" + BuildUtils.getTypeDescriptor( BuildUtils.arrayType( field.getTypeName() ) ) + ")Ljava/lang/String;" ); mv.visitMethodInsn( INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;" ); } else { mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName( StringBuilder.class ), "append", Type.getMethodDescriptor( Type.getType( StringBuilder.class ), Type.getType( Object.class ) ) ); } } } private void appendToStringBuilder(MethodVisitor mv, String s) { mv.visitLdcInsn( s ); mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName(StringBuilder.class), "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;" ); } protected void buildClassAnnotations(ClassDefinition classDef, ClassVisitor cw) { for (AnnotationDefinition ad : classDef.getAnnotations()) { AnnotationVisitor av = cw.visitAnnotation("L"+BuildUtils.getInternalType(ad.getName())+";", true); addAnnotationAttribute( ad, av ); av.visitEnd(); } } protected void buildFieldAnnotations(FieldDefinition fieldDef, FieldVisitor fv) { if (fieldDef.getAnnotations() != null) { for (AnnotationDefinition ad : fieldDef.getAnnotations()) { AnnotationVisitor av = fv.visitAnnotation("L"+BuildUtils.getInternalType(ad.getName())+";", true); addAnnotationAttribute( ad, av ); av.visitEnd(); } } } public static void addAnnotationAttribute( AnnotationDefinition ad, AnnotationVisitor av ) { for (String key : ad.getValues().keySet()) { AnnotationDefinition.AnnotationPropertyVal apv = ad.getValues().get(key); switch (apv.getValType()) { case STRINGARRAY: AnnotationVisitor subAv = av.visitArray(apv.getProperty()); Object[] array = (Object[]) apv.getValue(); for (Object o : array) { subAv.visit(null,o); } subAv.visitEnd(); break; case PRIMARRAY: av.visit(apv.getProperty(),apv.getValue()); break; case ENUMARRAY: AnnotationVisitor subEnav = av.visitArray(apv.getProperty()); Enum[] enArray = (Enum[]) apv.getValue(); String aenumType = "L" + BuildUtils.getInternalType( enArray[0].getClass().getName() ) + ";"; for (Enum enumer : enArray) { subEnav.visitEnum(null,aenumType,enumer.name()); } subEnav.visitEnd(); break; case CLASSARRAY: AnnotationVisitor subKlav = av.visitArray(apv.getProperty()); Class[] klarray = (Class[]) apv.getValue(); for (Class klass : klarray) { subKlav.visit( null, Type.getType( "L" + BuildUtils.getInternalType( klass.getName() ) + ";" ) ); } subKlav.visitEnd(); break; case ENUMERATION: String enumType = "L" + BuildUtils.getInternalType(apv.getType().getName()) + ";"; av.visitEnum(apv.getProperty(),enumType,((Enum) apv.getValue()).name()); break; case KLASS: String klassName = BuildUtils.getInternalType(((Class) apv.getValue()).getName()); av.visit(apv.getProperty(),Type.getType("L"+klassName+";")); break; case PRIMITIVE: av.visit(apv.getProperty(),apv.getValue()); break; case STRING: av.visit(apv.getProperty(),apv.getValue()); break; } } } protected void visitFieldOrGetter(MethodVisitor mv, ClassDefinition classDef, FieldDefinition field) { if (! field.isInherited()) { mv.visitFieldInsn( Opcodes.GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), field.getName(), BuildUtils.getTypeDescriptor( field.getTypeName() ) ); } else { mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, BuildUtils.getInternalType( classDef.getClassName()), field.getReadMethod(), Type.getMethodDescriptor(Type.getType(BuildUtils.getTypeDescriptor(field.getTypeName())), new Type[]{}) ); } } protected void updateTraitableField( MethodVisitor mv, ClassDefinition classDef, FieldDefinition fieldDef ) { String fieldType = fieldDef.getTypeName(); mv.visitVarInsn( ALOAD, 0 ); mv.visitFieldInsn( GETFIELD, BuildUtils.getInternalType( classDef.getClassName() ), TraitableBean.FIELDTMS_FIELD_NAME, Type.getDescriptor( TraitFieldTMS.class ) ); mv.visitLdcInsn( fieldDef.resolveAlias() ); if ( BuildUtils.isPrimitive( fieldType ) ) { mv.visitVarInsn( BuildUtils.varType( fieldType ), 1 ); mv.visitMethodInsn( INVOKESTATIC, BuildUtils.getInternalType( BuildUtils.box( fieldType ) ), "valueOf", "(" + BuildUtils.getTypeDescriptor( fieldType ) + ")" + BuildUtils.getTypeDescriptor( BuildUtils.box( fieldType ) ) ); } else { mv.visitVarInsn( ALOAD, 1 ); } if ( BuildUtils.isPrimitive( fieldType ) ) { // mv.visitFieldInsn( GETSTATIC, BuildUtils.getInternalType( BuildUtils.box( fieldType ) ), "TYPE", Type.getDescriptor( Class.class ) ); mv.visitLdcInsn( Type.getType( BuildUtils.getTypeDescriptor( BuildUtils.box( fieldDef.getTypeName() ) ) ) ); } else { mv.visitLdcInsn( Type.getType( BuildUtils.getTypeDescriptor( fieldType ) ) ); } mv.visitMethodInsn( INVOKEINTERFACE, Type.getInternalName( TraitFieldTMS.class ), "set", Type.getMethodDescriptor( Type.getType( Object.class ), new Type[] { Type.getType( String.class ), Type.getType( Object.class ), Type.getType( Class.class ) } ) ); if ( BuildUtils.isPrimitive( fieldType ) ) { mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( BuildUtils.box( fieldType ) ) ); mv.visitMethodInsn( INVOKEVIRTUAL, BuildUtils.getInternalType( BuildUtils.box( fieldType ) ), BuildUtils.numericMorph( BuildUtils.box( fieldType ) ), "()" + BuildUtils.getTypeDescriptor( fieldType ) ); } else { mv.visitTypeInsn( CHECKCAST, BuildUtils.getInternalType( fieldType ) ); } } }