> On Apr 8, 2020, at 14:43, John Rose <john.r.r...@oracle.com> wrote:
>
> I have a proposal for a translation strategy:
>
> 1. Translate casts to inline classes differently from “classic”
> casts. Add an extra step of null hostility. For very low-level
> reasons, I suggest using “ldc X” followed by Class::cast.
>
> Generally speaking, it’s a reasonable move to use reflective
> API points (like Class::cast) on constant metadata (like X.class)
> to implement language semantics.
There’s an alternative way to implement this:
Casts to inline classes C can be translated to
#ldc C
#checkcast C
with this new definition of checkast:
"If objectref is null then:
- if type C has not been loaded yet, the operand stack is unchanged,
- if type C has already been loaded:
- if type C is not an inline type, the operand stack is unchanged
- otherwise the checkcast instruction throws a ClassCastException
Otherwise, the named class, array, or interface type is resolved (§5.4.3.1). If
objectref can be cast to the resolved class, array, or interface type, the
operand stack is unchanged; otherwise, the checkcast instruction throws a
ClassCastException."
This new definition doesn’t change the behavior of checkcast for old class
files,
and doesn’t change the behavior nor the translation strategy for casts to
non-inline types.
In new class files, javac will use the ldc/checkcast sequence whenever a cast
to an
inline type is required. Note that in many cases, type C would have already be
loaded
before ldc is executed (by pre-loading or eager loading).
With migrated types, an old class file can still have a standalone checkcast
(without ldc)
referencing a type which is now an inline type, causing the null reference to
pass the checkcast
successfully. This is not a new issue. The same situation can be created by
reading a field
declared as ‘LC;’, getfield would simply read the field (only possible value is
null) and push
it on the stack without checking if C is an inline type or not. This ’null’
reference to an
invalid type can only be used by code that have the wrong information about
type C, and this
is in fact the only possible value for this type (any attempt to create a real
instance of
‘LC;’ would fail). In order to use this reference as a legitimate reference to
the real
type ‘QC;’, another checkcast, using the proper sequence above, would be
required and would
throw an exception.
Ill formed or malicious class files could be aware that C is an inline type,
but use a
single checkcast instruction (without preceding ldc) anyway. This is part of a
bigger
problem that has not been discussed yet: L/Q consistency inside a class file.
the SoV
document stipules that the value projection only exists in the Q-form and the
reference
projection only exists in the L-form. As of today, there’s no verification of
such kind
performed on class files. Nothing prevent a class file from declaring a field
of type
‘LC;’ and another of type ‘QC;’, and the same remark applies to method
arguments.
Going back to the modified specification of checkcast, new tests are easy to
implement
in the interpreter, and can easily be optimized by JIT compilers (most types
would be
loaded at compilation time), and there’s no bootstrapping issues.
Fred