-
Notifications
You must be signed in to change notification settings - Fork 130
ECJ Annotation‐based Null Analysis
How is annotation-based null analysis implemented in ECJ?
When using declaration annotations (i.e., not for target TYPE_USE), the semantics of null annotations will be encoded in bindings of the affected element using bitset tagBits
. This happens for:
- locals
- fields
- methods, meaning: their return type
A method, however, has no binding for the parameters it declares. MethodBinding.parameters
only holds the parameter-types.
For that reason, such information is encoded in MethodBinding.parameterFlowBits
.
During resolution we compute the specified or derived nullness into each ReferenceBinding
, which applies cloning to distinguish type bindings that differ only in annotations.
Different kinds of AST nodes are handled by different utility methods:
NullAnnotationMatching.checkAssignment()
handles various forms of assignment contexts:
- Assignment
- LocalDeclaration
- FieldDeclaration
- ForeachStatement
- The resources of a TryStatement are implicitly handled as those are encoded as LocalDeclaration
Statement.analyseArguments()
handles this invocation contexts:
- MessageSend
- AllocationExpression
- QualifiedAllocationExpression
- ExplicitContructorCall
Statement.checkAgainstNullTypeAnnotation()
is used for partially unclear purpose in:
- ArrayReference -- ??
- ArrayInitializer -- actually a kind of assignment context
- ReturnStatement -- actually a kind of assignment context
Each of the above methods is able to drill into complex expressions, to analyse all result expressions in:
- ConditionalExpression
- SwitchExpression
These expressions can also be freely nested. They transparently propagate the enclosing expression context.
Expression.checkNPE()
is used to check nullness of receivers or similar expressions of:
- MessageSend
- FieldReference
- ArrayReference
- ReferenceExpression
- ForeachStatement -- for its collection, as the receiver of the
iterator()
call - SwitchStatement -- if null-hostile for its switch expression (subject to conversions like unboxing)
- SynchronizedStatement -- for its expression which is required to be nonnull by the vm
- ThrowStatement -- cannot throw null
- CastExpression -- really?
- OperatorExpressions: UnaryExpression, BinaryExpression, CombinedBinaryExpression -- why?
- CompoundAssignment -- for its lhs, why?
What about QualifiedAllocationExpression?
Expression.checkNPEbyUnboxing()
checks expression for unboxing of a value that is not null-safe:
- Invocation arguments of
- MessageSend
- ExplicitConstructorCall
- AllocationExpression
- QualifiedAllocationExpression
- Dimensions / index of
- ArrayAllocationExpression
- ArrayReference
- Single expresion of
- CastExpression
- Assignment
- CompoundAssignment
- AssertStatement
- ReturnStatement
- YieldStatement
- Operands of
- ConditionalExpression
- OR_OR_Expression
- AND_AND_Expression
- check if EQUAL_EQUAL_Expression is safe, does it ever unbox?
- Condition of
- DoStatement
- ForStatement
- IfStatement
- WhileStatement
- Initialization of
- LocalDeclaration
- FieldDeclaration
T.B.C.