Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8186006

DynamicConstantValue attribute to connect a named constant to DynamicConstant

    XMLWordPrintable

    Details

    • Type: Enhancement
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 11, 12, 13
    • Fix Version/s: tbd
    • Component/s: tools
    • Labels:
      None

      Description

      The new DynamicConstantValue attribute will have the same
      format as ConstantValue with similar semantics. Its constant
      pool reference will point to a DynamicConstant constant.
      The containing field must be static and final.

      At javac time, clients will ignore this attribute, treating the
      field as a normal lazily resolved static variable. At class
      load time, the field will be left uninitialized, but internally
      marked as unresolved.

      When a getstatic instruction is resolved against this
      field, and it is in the unresolved state, the JVM will
      resolve the constant pool entry. After successful
      resolution, the getstatic instruction will be linked
      to point at the constant pool entry (or a copy thereof,
      perhaps the otherwise-unused static field storage).

      The resolution of such a getstatic instruction will
      also entail the execution of the <clinit> method of
      the enclosing class. (Alternatively, the resolution
      will not entail the execution of the <clinit>; any
      such initialization action will take place independently.
      Either decision leads to a good user model.)

      Reflection of such a field will work by referring to
      the bytecode behavior of the corresponding getstatic.
      In no case will reflection be able to assign a new value.

      The static compiler will accept an annotation or keyword
      on a field declaration (static, final, and with an initializer
      expression) that will ensure that the field will be given
      a DynamicConstantValue attribute. Possibly, the initializer
      expression will be required to be a special form, such as
      the derivation of the initialization value from a Constable
      of the correct type. (This would make the translation to
      DynamicConstant more explicit.)

      A suggested keyword for these fields is "lazy". (This evaluation
      mode may also be useful in other contexts, such as instance fields.)

      (Alternatively, we could consider opting into the lazy behavior
      automatically on recompile; in that case the opt-out would consist
      of refactoring the initialization into an assignment in the <clinit> method.
      Such a change would potentially change initialization order in ways
      that could introduce bugs, but may be safe enough to be worth the
      gains in startup time. This requires empirical study.)

      Whether this behavior is enabled by an annotation or keyword,
      the Java Language Specification must take account of such
      constants, since their initialization takes place later than
      (or perhaps earlier than) the sequence of effects produced
      by the <clinit> method. The semantics of such fields is as
      if the fields were defined individually, in separate classes
      nested inside the target class, and as if the DynamicConstant
      expression were executed by an ldc instruction in the separate
      class initializer.

      By contrast, the ConstantValue attribute of a static final field can point
      to one of five constant pool types: String, Integer, Long, Float, Double.
      A reference to such a field is usually satisfied at static compile
      type, by folding the initializer value. Alternatively, it can be
      satisfied by executing a getstatic instruction against the field.
      In that case, the JVM arranges to install the constant in
      the field variable at class preparation time (and before the
      static initializer <clinit> is run).

      The DCV attribute interacts with "getstatic" to produce a fully
      resolved value later than the <clinit> block execution. (In fact
      it allows <clinit> blocks to become very rare.) A reflective
      API should be added also which permits one class to request
      (given suitable permissions) the constant pool structure behind
      a dynamic constant defined by that second class. The API
      for expressing such constant pool structures is out of scope
      for this RFE, but it would presumably include nodes which
      could represent not only primitive CP entries (string, int)
      but also DynamicConstant applications (including name,
      field type, BSM, and static argument constants). Such a
      reflective API would allow classes to serve as libraries not
      only of live values but also symbolic recipes for such values.
      As such, these recipes could not only be resolved under
      program control, but also modified and customized.

      Apart from the lazy-evaluation semantics, the DCV attribute
      also provides a way for a separately compiled classfile to
      export named references into its constant pool, which can
      be used later by other separately compiled classifies,
      by mentioning a field-ref to the named DCV. This suggests
      that metaprogramming on CP constants could make use
      of DCVs as a way for separately compiled classfiles to
      cooperatively build up complex constants (including
      behavioral ones).

      It may be useful to add to the DynamicConstantValue
      attribute a bit mask of mode bits, to individually select
      optional access modes:

      1. can this DCV be resolved via "getfield" and Field::get?
      2. can this DCV be reflected as a non-resolved symbolic constant in the CP?
      3. can this DCV be evaluated *after* the <clinit> of its classfile?
      4. can this DCV be evaluated (safely) *before* the <clinit> (including at compile time)?
      5. can this DCV be resolved, and then (safely) back-mapped from a live value to a symbolic reference?
      6. do any of the above modes (except 1) require additional access privileges, besides simple field-ref resolution (entailed by 1)?

      As noted by Remi Forax (comment section), a related use
      case for DCV could be *eager* symbolic evaluation at AOT
      time. This would seem to be the exact opposite of lazy
      evaluation, but the two can co-exist if the symbolic expression
      (a condy constant) of a lazy constant can be determined
      to be eagerly evaluable. This would be the case if all the
      constants and operations of the symbolic expression were
      either constant pool constants, or applications of methods
      that were suitably marked as "pure" (or "trackable", etc.).
      In that case, the AOT could confidently pre-evaluate the
      constant, and use the result, knowing that the result was
      not sensitive to evaluation ordering (neither dependent
      on nor producing side effects). Doing this correctly
      obviously requires additional attributes or annotations
      for the involved functions, but it would mesh well with
      DCV. Relative to AOT support, even if a DCV fails
      to be side-effect free, separating such a constant
      from another class's <clinit> is likely to enable some
      optimizations significant at AOT time. In particular,
      if a class exports 100 constants and an AOT compilation
      unit only uses 3 of them, the AOT code can trigger
      evaluation of just the 3 it needs, leaving the other 997
      untriggered. The AOT team has run into this use case
      already, and avoiding the 997 has made it difficult for
      them to optimize the 3.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              forax RĂ©mi Forax
              Reporter:
              jrose John Rose
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Dates

                Created:
                Updated: