Re: Proposal: Static/final constructors for bucket-3 primitive classes.
> From: "John Rose" > To: "Brian Goetz" > Cc: "valhalla-spec-experts" , "clement > cherlin" > Sent: Thursday, December 9, 2021 5:30:50 AM > Subject: Re: Proposal: Static/final constructors for bucket-3 primitive > classes. > We have considered, at various points in the last six years or more, allowing > user-defined primitive types to define (under user control) their own default > values. The syntax is unimportant, but the concept is simple: Surely the user > who defines a primitive type can also define default initializer expressions > for each of the fields. > But this would be a trail of tears, which we have chosen to avoid, each time > the > suggestion comes up. > This feature is often visualized as a predefined bit pattern, which the JVM > would keep handy, and just stamp down wherever a default initializer is > needed. > It’s can’t really be that simple, but even such a bit pattern is problematic. > First of all is the problem of declaring the bit pattern. Java natively uses > the > side effects of to define constants using ad hoc bytecodes; it also > defines (for some types but not others) a concept of constant expression. > Neither of those fits well into a classfile that would define a primitive with > a default bit pattern. > If the bit pattern is defined using ad hoc bytecode, it must be defined in a > new > pseudo-method (not ), to execute not during the initialization of the > newly-declared primitive class, but before . (Surely not! a reader might > exclaim, but this is the sort of subtlety we have to deal with.) During > initialization of a class C, all fields of its own type C must be initialized > before the first bytecode of executes, so that the static initializer > code has something to write on. So there must be a “default value definition” > phase, call it , added after linking and before > initialization of C, so C’s method has something to work with. This > is really the body of a no-argument constructor of C, or > its > twin. A no-argument constructor of C is not a problem, but having it execute > before C’s block is a huge irregularity, which the JVM spec is not > organized to support, at present. > This would turn into both JVMS and JLS spec. complexity, and more odd corners > (and odd states) in the Java user experience. Sure, a user will say, “but I > promise not to do anything odd; I just want this field to be the value (int)1 > ”. Yes, but a spec. must define not only the expected usages, but all possible > usages, with no poorly-defined states. > OK, so if is not the place to define to define this elusive > bit pattern, what about something more declarative, like a ConstantValue > attribute? Surely we could put a similarly structured DefaultValue attribute > on > every non-static field of a value type, and that would give the JVM enough > information to synthesize the required bit pattern before it runs . > Consider the user model here: A primitive declaration would allow its fields > to > have non-zero default values, but only drawn from the restricted set of > constant expressions , because those are the ones which fit in the > ConstantValue attribute. (They are true bit patterns in the constant pool, > plus > String constants.) There is no previous place in Java where we make such a > restriction, except case labels. Can you hear the groans of users as we try to > explain why only constant expressions are allowed in that context? That’s the > muzak of the trail of tears I mentioned above. > But we have condy to fix that (someone will surely say). you read my mind :) > But that’s problematic, because the resolution of constant pool constants of a > class C requires C to be at least linked, and if the condy expression makes a > self-reference to C itself, that will trigger C’s initialization, at an > awkward > moment. Have you ever debugged a tangled initialization circularity, marked by > mysterious NPEs on variables you know you initialized? I have. It’s a stop on > the trail of tears I mentioned. > But if we really worked hard, and added a bunch of stuff to the JVMS and JLS, > and persuaded users not to bother us about the odd restrictions (to constant > expressions, or expressions which “don’t touch the class itself”), we could > define some sort of declarative default value initialization. > What then? Well, ask the JVM engineers how they initialize heap variables, > because those are the affected paths. Those parts of the JVM are among the > most > performance-sensitive. Currently, when a new object or array is created, its > whole body (except the header) is sprayed with a nice even coat of > all-zero-bit > machine words. This is pretty fast, and it’s important to keep it fast. What > if > creating an array required painting some beautifully crafted arabesque of a > bit > pattern defined by a creative user? Well, it’s doable, but much more > complicated. You need to load the bit pattern into live registers and (if it’s > an
Re: Proposal: Static/final constructors for bucket-3 primitive classes.
We have considered, at various points in the last six years or more, allowing user-defined primitive types to define (under user control) their own default values. The syntax is unimportant, but the concept is simple: Surely the user who defines a primitive type can also define default initializer expressions for each of the fields. But this would be a trail of tears, which we have chosen to avoid, each time the suggestion comes up. This feature is often visualized as a predefined bit pattern, which the JVM would keep handy, and just stamp down wherever a default initializer is needed. It’s can’t really be that simple, but even such a bit pattern is problematic. First of all is the problem of declaring the bit pattern. Java natively uses the side effects of `` to define constants using ad hoc bytecodes; it also defines (for some types but not others) a concept of constant expression. Neither of those fits well into a classfile that would define a primitive with a default bit pattern. If the bit pattern is defined using ad hoc bytecode, it must be defined in a new pseudo-method (not ``), to execute not *during* the initialization of the newly-declared primitive class, but *before*. (Surely not! a reader might exclaim, but this is the sort of subtlety we have to deal with.) During initialization of a class C, all fields of its own type C must be initialized *before* the first bytecode of `` executes, so that the static initializer code has something to write on. So there must be a “default value definition” phase, call it ``, added after linking and before initialization of C, so C’s `` method has something to work with. This `` is really the body of a no-argument constructor of C, or its twin. A no-argument constructor of C is not a problem, but having it execute before C’s `` block is a huge irregularity, which the JVM spec is not organized to support, at present. This would turn into both JVMS and JLS spec. complexity, and more odd corners (and odd states) in the Java user experience. Sure, a user will say, “but I promise not to do anything odd; I just want *this field* to be the value `(int)1`”. Yes, but a spec. must define not only the expected usages, but all possible usages, with no poorly-defined states. OK, so if `` is not the place to define to define this elusive bit pattern, what about something more declarative, like a `ConstantValue` attribute? Surely we could put a similarly structured `DefaultValue` attribute on every non-static field of a value type, and that would give the JVM enough information to synthesize the required bit pattern *before* it runs ``. Consider the user model here: A primitive declaration would allow its fields to have non-zero default values, *but only drawn from the restricted set of constant expressions*, because those are the ones which fit in the `ConstantValue` attribute. (They are true bit patterns in the constant pool, plus `String` constants.) There is no previous place in Java where we make such a restriction, except `case` labels. Can you hear the groans of users as we try to explain why only constant expressions are allowed in that context? That’s the muzak of the trail of tears I mentioned above. But we have condy to fix that (someone will surely say). But that’s problematic, because the resolution of constant pool constants of a class C requires C to be at least linked, and if the condy expression makes a self-reference to C itself, that will trigger C’s initialization, at an awkward moment. Have you ever debugged a tangled initialization circularity, marked by mysterious NPEs on variables you *know* you initialized? I have. It’s a stop on the trail of tears I mentioned. But if we really worked hard, and added a bunch of stuff to the JVMS and JLS, and persuaded users not to bother us about the odd restrictions (to constant expressions, or expressions which “don’t touch the class itself”), we *could* define some sort of declarative default value initialization. What then? Well, ask the JVM engineers how they initialize heap variables, because those are the affected paths. Those parts of the JVM are among the most performance-sensitive. Currently, when a new object or array is created, its whole body (except the header) is sprayed with a nice even coat of all-zero-bit machine words. This is pretty fast, and it’s important to keep it fast. What if creating an array required painting some beautifully crafted arabesque of a bit pattern defined by a creative user? Well, it’s doable, but much more complicated. You need to load the bit pattern into live registers and (if it’s an array of C) keep them live while you paint the whole array. That’s got to be more expensive than spraying zeroes. (There’s even hardware that’s good for spraying zeroes, on some machines.) Basically, if we generously allowed users even a limited set of pre-defined default