a new job for <init>: "static init factories"
Brian Goetz
brian.goetz at oracle.com
Thu Apr 25 14:40:34 UTC 2019
This all seems very sensible.
> On Apr 24, 2019, at 2:00 PM, John Rose <john.r.rose at oracle.com> wrote:
>
> We need a VM-level API point for running a constructor
> of an inline (by-value) type, which is distinct from the
> current way of running constructors. Currently, constructors
> are invoked by running invokespecial on a special internal
> method named <init>, passing a blank fresh instance of
> the required class. The blankness of the fresh instance
> is tracked by the verifier using very special rules which
> are triggered by the mention of the very special name <init>.
>
> We can't use this mechanism for constructing value types
> because there is no way to perform side effects on a
> value type instance, blank or not.
>
> The most natural way to create a value type instance,
> in the JVM, is to run invokestatic on an appropriately
> named static factory method. The name of this method
> is a convention which is known to various parties, notably
> the static compiler (javac) and reflection (jlr.Constructor).
>
> After some prototyping, I can say that it is a reasonable
> and simple thing to do to re-use the string "<init>" for
> the target of an invokestatic which translates a constructor
> of an inline type. For example:
>
> ```
> inline class Point { int x, y; Point(int x1, int y1) { x=x1; y=y1; } }
> // static Point.<init>(II)Point { vdefault[Point] … }
> class Client { static Point myPoint() { return new Point(3,4); } }
> // Client.myPoint() { … invokestatic[Point.<init>(II)Point] … }
> ```
>
> To do this, we will need to make some changes to the JVM
> specification (and implementations). Here are the changes
> I propose:
>
> * Relax constraints on CONSTANT_Methodref and CONSTANT_NameAndType
> allowing free use of <init> as if it were a regular method name.
> * Retain all restrictions on use of <init> via invokespecial.
> * Allow an invokestatic to mention <init> (but no other bytecodes).
> * Retain all restrictions on definition of <init> methods *in regular non-inline classfiles*
> * Allow an inline classfile to define a <init> method, only with ACC_STATIC
> * Require that the type returned by such an <init> method is the containing class.
> (Extra rider: If the class is non-denotable, aka. hidden, returned class must be Object.)
>
> These changes ensure that there are only two kinds of methods
> named <init>, classic by-reference object constructors, and
> new by-value "static init factories". The specification ensures
> that these two kinds of methods, both named <init>, can never
> be confused. The basic mechanism for ensuring this is that
> one kind of method is defined as non-static and the other is
> defined as static, and there is no way to accidentally invoke
> a static method via invokespecial, and vice versa for invokestatic.
>
> Note that CONSTANT_Methodrefs can refer to either kind of
> descriptor. This is not ambiguous, since every use of such a
> constant is coupled with an indicator (an opcode or ref-kind)
> which tells if the use is of a static method or not.
>
> Note also that there can be "crazy" references to the name
> <init> such as under the type "()I" (no args returning int).
> These references are harmless because they will never link
> to a definition of that signature. This is true not because there
> are limitations on the form of uses of <init>, but because there
> are strong limitations on the possible definitions of <init>.
> A "crazy" use has no impact, other than causing an eventual
> linkage error. We could try to add more limits against crazy
> uses of <init>, but that does not seem to be necessary, except
> perhaps as a "defense in depth" move.
>
> Additional changes are needed for reflection:
>
> * Allow new static init factories to be wrapped in `jlr.Constructor`s.
> * Ensure that `Constructor::newInstance` uses the right calling sequence
> for static init factories.
> * Ensure that `Lookup::findConstructor` can find static init factories.
> * Allow `Lookup::findStatic` to find static init factories.
> (This step simplifies mapping between invokestatic and method
> handle constants. It could be dropped.)
> * Ensure that resolution of `CONSTANT_MethodHandle` continues to
> use the reference-kind option correctly. (No actual spec change.)
>
> The low-level user model has a twist in it: At the level of bytecodes,
> a static init factory is just a vanilla static method, albeit with a funny
> name. But at the level of reflection, a static init factory appears to be
> a constructor. This is reasonable since a static init factory translates
> an actual constructor in source code. A `Constructor` which wraps
> a static init factory will have its `ACC_STATIC` method set, allowing
> users who care to distinguish the two uses of the name <init>.
>
> The query method `Class::getDeclaredMethods` will not expose
> static init factories. Only `Class::getDeclaredConstructors` will.
> A similar rule applies to related API points such as `getMethod`.
> The purpose of this restriction (which is optional) is to avoid
> having overlaps in the reflected lists of constructors and methods.
>
> Because the `java.lang.invoke` API is lower-level than `java.lang.reflect`,
> it should (probably) be willing to treat static init factories as vanilla
> static methods. Thus `findStatic` can return a handle to such a factory.
> Because `java.lang.invoke` also integrates (via "unreflection") with
> `java.lang.reflect`, `findConstructor` should be willing to treat
> static init factories as constructors. Perhaps one of these methods
> can be suppressed, but it seems reasonable to allow both in
> `java.lang.invoke`, because of the two-sided positioning of that
> API layer.
>
> I posted a HotSpot POC implementation here:
> http://cr.openjdk.java.net/~jrose/jvm/JDK-8222787/
>
> Comments, please?
>
> — John
More information about the valhalla-spec-observers
mailing list