Generated dnProvideFields method appears to not handle case where fieldNumbers array is null (i.e. class has no attributes) in DN 5.1.6


Page bloom
 

In this class diagram you can see the abstract base class doesn't have (nor requires) any of it's own attributes. The FieldManager class could have been an interface apart from the fact that:
  • we wanted all classes in this hierarchy to be stored in a single database table
  • we plan to add some stats attributes later and these would definitely go into this base class



In DN 5.1.6 we get the following exception:

java.lang.IllegalArgumentException: argment is null
at com.sas.framework.filemgr.FileManager.dnProvideFields(FileManager.java)
at org.datanucleus.state.StateManagerImpl.provideFields(StateManagerImpl.java:2510)
at org.datanucleus.store.rdbms.RDBMSPersistenceHandler.checkForSchemaUpdatesForFieldsOfObject(RDBMSPersistenceHandler.java:667)
at org.datanucleus.store.rdbms.RDBMSPersistenceHandler.insertObject(RDBMSPersistenceHandler.java:118)
at org.datanucleus.state.StateManagerImpl.internalMakePersistent(StateManagerImpl.java:4535)
at org.datanucleus.state.StateManagerImpl.flush(StateManagerImpl.java:5735)
at org.datanucleus.store.rdbms.mapping.java.PersistableMapping.setObjectAsValue(PersistableMapping.java:490)
at org.datanucleus.store.rdbms.mapping.java.PersistableMapping.setObject(PersistableMapping.java:366)
at org.datanucleus.store.rdbms.fieldmanager.ParameterSetter.storeObjectField(ParameterSetter.java:191)
at org.datanucleus.state.StateManagerImpl.providedObjectField(StateManagerImpl.java:1823)

The DN 5 StateManagementImpl class is passing a null fieldNumbers array to the method: com.sas.framework.filemgr.FileManager.dnProvideFields, a generated method added during byte code enhancement.

           currFM = fm;
            try
            {
                // This will respond by calling this.providedXXXFields() with the value of the field
                myPC.dnProvideFields(fieldNumbers);       <------ fieldNumbers array is null because this base class has no attributes - which should be fine
            }
            finally
            {
                currFM = prevFM;
            }

The call is fine, however the problem is in the generated code in the PC's dnProvideFields method. It may not be handling the case where it passed in a fieldNumbers array which is null, however null seems like a valid case if the class has no attributes.

By adding an "int dummy" attribute to the base class the issue goes away which indicates that the issue is related to having no attributes in a base class.

Where would we find the code that is responsible for generating the dnProvideFields method in the DN code base?


Page bloom
 

I found the code that's adding the dnProvideFields methods during BCE:

org.datanucleus.enhancer.methods.ProvideFields.java

The execute method is doing the comparison with null here:

 

    public void execute()

    {

        visitor.visitCode();

 

        Label l0 = new Label();

        visitor.visitLabel(l0);

        visitor.visitVarInsn(Opcodes.ALOAD, 1);

        Label l1 = new Label();

        visitor.visitJumpInsn(Opcodes.IFNONNULL, l1);

        visitor.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException");

        visitor.visitInsn(Opcodes.DUP);

        visitor.visitLdcInsn("argment is null");

        visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/IllegalArgumentException",

            "<init>", "(Ljava/lang/String;)V");

        visitor.visitInsn(Opcodes.ATHROW);

It's all in Java VM machine language (obviously much more efficient than performing a compile phase during enhancement) which makes for interesting reading ;)

I'm wondering if it's better to just change StateManagerImpl so that it passes an empty Array into dnProvideFields rather than changing the enhancer...


Page bloom
 
Edited

Passing an empty fieldNumbers native array does prevent the dnProvideFields method from throwing an IllegalArgumentException 

I can raise a pull request for a fix like this if you think it's reasonable:

StateManagementImpl.java lines 2507-2517 
 
            try
            {
+                // In case class has no attributes pass empty array instead of null
+                if (fieldNumbers == null)
+                    fieldNumbers = new int[0];
+
                // This will respond by calling this.providedXXXFields() with the value of the field
                myPC.dnProvideFields(fieldNumbers);
            }
            finally
            {
                currFM = prevFM;
            }

It is probably a simpler fix than changing the generated dnProvideFields method but making the change there might be more "correct" - and it might even reduce the size of the generated method, possibly speeding up enhancement of large persistent models (but maybe not significantly)


Andy
 

The StateManager and enhancement contract are not the place to change anything.
You need to prevent calling dnProvideFields with crap. i.e checkForSchemaUpdatesForFieldsOfObject (i.e where you have interfaces and some discovered implementations).
This is only of relevance there.


Page bloom
 

This issue is independent of previous interface issues that we recently discussed.

In this scenario the base class implements Serializable but I removed that "implements" relationship as a test and the problem remains - i.e. simple 3 class hierarchy with no references to other classes or interfaces.

I'll look up checkForSchemaUpdatesForFieldsOfObject and see if I can attack the issue from in there.


Page bloom
 

I have attached a very simple, two class, test case which reproduces the issue of having no attributes in the base class.

A simple one class scenario with no attributes in the base class also reproduces the problem but that's a bit of a pointless model.