Carlos Filipe Costa
Carlos Filipe Costa

Reputation: 13

javassist Field check initialization

I am currently implementing an Annotation that forces the fields to respect a condition through javassist. I would like to check if a field is initialized when it is being read... so, currently, I am getting the classes by loading them when they are loaded by the VM through a Translator.onLoad(ClassPool pool, String className), and using an ExprEditor on each class through overriding the edit(FieldAccess arg) method. Right now, I managed to inject code to check the condition by running the following method inside onLoad :

    private void processFields(FieldsAndMethods data) {
        final FieldsAndMethods copy = data;
        Stack<CtClass> classes = data.getThisClass();
        for(CtClass cc : classes ){
            try {
                cc.instrument(new ExprEditor(){

                @Override
                public void edit(FieldAccess arg) throws CannotCompileException{
                   try{
                        CtField field = arg.getField();
                        if(copy.getFields().contains(field) &&
                           field.hasAnnotation(Assertion.class)){

                            Assertion a =
                                ((Assertion)field.getAnnotation(Assertion.class))   
                            String condition = assertion.value();
                            String fieldName = field.getName();
                            String processCondition = 
                                transformCondition(condition, fieldName);

                            if(arg.isWriter()){

                                String code = "{if(" + evaledCondition + ")" +                                         
                                              "$proceed($$) ;" +
                                              "else throw new " + 
                                              "RuntimeException(\"The assertion " + 
                                                   condition + " is false.\");}";                                                                               
                                arg.replace(code);

            }else if (arg.isReader()){
                                //Here is where I would like to check if the field 
                                //has been initialized... 
            }

                        }catch(ClassNotFoundException e){
                            System.out.println("could not find Annotation " + 
                            Assertion.class.getName() );
                        }catch(NotFoundException e){
                            System.out.println("could not find field " + 
                            arg.getFieldName() );
                        }                                       
                    }
                });
            } catch (CannotCompileException e) {                
                System.out.println("Could not interpret the expression");
                System.out.println(e);
            }
        }       
    }

    private String transformCondition(String condition, String fieldName){
        return condition.replace(fieldName, "$1");      
    }

Could you point me in the right direction for finding out if a field has been initialized? Notice that a field can be either a primitive or not.

Thanks in advance.

Upvotes: 0

Views: 1473

Answers (1)

pabrantes
pabrantes

Reputation: 2181

Assumptions

I'll assume the following:

  • By field initialized we are talking about fields that are null.

  • Primitive types cannot be null so no bother to check them.

The code

This example verification will work for both static and non static fields.

I've also created the code String in several lines for better readability. Being arg a FieldAccess object, you can write the following:

 if (arg.isReader() && !arg.getField().getType().isPrimitive()) {
  String code = "{ java.lang.Object var = $proceed();"
               +  "if(var == null) {"
                   + "java.lang.System.out.println(\"not initialized " + arg.getFieldName() + "\");"
               +  "}"
               + "$_=var;}";
            arg.replace(code);
        }

Code Explanation

As you can see, in this small example I've used a few javassist identifiers, for the complete reference about this please read the javassist official tutorial (I'm linking to the section about code modifications).

Here is what each identifier used means:

  • $proceed() : in the case of a field access this returns the value of the field.
  • $_ : this is an identifier that is mandatory when editing a FieldAccess in read mode. This token holds the value that will be used to set the field.

With this information it's easy to understand the code's idea:

  1. Put the field value into an auxiliary object named var
  2. Check if the field is null, if so print a warning with the field name
  3. Set the fieldname with the value (either it's null or not);

I guess this already points you to the right direction. But let me know if you need anything else.

Upvotes: 1

Related Questions