AUTHOR(S):

John Rose

OVERVIEW

Create additional source-code support for using new JVM features from JSR 292. The support consists of relaxed conversions and receiver-style dynamic invocations.

BACKGROUND

Since the JVM verifier already accepts (since the beginning) arbitrary reference values for any interface type, it is useful to allow at least one interface type to serve as a wild-card type. Given the ability to generate dynamic call sites in the previous proposal for JSR 292 support, this proposal endows that wild-card type with the ability to receive (what appear to be) non-static method calls.

FEATURE SUMMARY:

dynamic invocation of interface Dynamic receivers

The empty interface type Dynamic may be used, instead of InvokeDynamic, to encode invokedynamic calls. As a matter of pure syntactic sugar, if a method is invoked on a value of type Dynamic, it is the same as if a static call had been made to the same method via InvokeDynamic, with the receiver inserted before the other arguments as the new first argument.

conversion rules for interface Dynamic

Dynamic also serves as a dynamic version of a wildcard type. It is a bare reference with no regular methods, not even those of java.lang.Object. As a bare reference, it can be freely converted from any other type and to Object. With a cast, it can be converted to any other type. This works together with the invokedynamic syntax to allow dynamic method calls to be chained. (As a point of comparison, C# 4.0 has a similar integration between a new type "dynamic" and DLR call sites.) More details are below; here is an example:

Dynamic x = (any type of expression can go here);
Object  y = x.foo("ABC").bar(42).baz();

DETAILS

SPECIFICATION:

(Note: Numbering is retained from a previous proposal of the present proposal was a part.)

1.3 Any non-static call to Dynamic accepts an optional type parameter which specifies the return type of the call site's descriptor. The type parameter may any type whatever, including void or a primitive type. If it is omitted it defaults to the type Dynamic itself.

(Compare with invokedynamic calls expressed as static methods on InvokeDynamic, where the type parameter defaults to Object.)

Dynamic x = Dynamic.myGetCurrentThing();  // type () -> Dynamic
InvokeDynamic.<void>myPutCurrentThing(x);  // type (Dynamic) -> void
int y = InvokeDynamic.<int>myHashCode((Object)x);  // type (Object) -> int
boolean z = InvokeDynamic.<boolean>myEquals(x, y);  // type (Dynamic, int) -> boolean
Object p1 = InvokeDynamic.bothWays(x);  // type (Dynamic) -> Object
Object p2 = x.<Object>bothWays();  // same instruction as previous line
Object q1 = InvokeDynamic.<Dynamic>bothWays(x);  // type (Dynamic) -> Dynamic
Object q2 = x.bothWays();  // same instruction as previous line

1.6 In order to support chained invokedynamic expressions, any reference of type Dynamic may also be qualified with any name and invoked on any number and type of arguments. Since the invokedynamic instruction is receiverless (symmetrical in all arguments), the target value of the method invocation expression is pushed first on the stack before the other arguments, while its static type (which is always Dynamic) is prepended to the call descriptor presented to the JVM.

Dynamic x = ...;
boolean z = InvokeDynamic.<boolean>myEquals(x, 42);  // type (Dynamic, int) -> boolean
boolean z = x.<boolean>myEquals(y);  // identical meaning with previous line
x.foo().bar().baz();   // 3 invokedynamic calls of type (Dynamic) -> Dynamic

1.7 Except for the defaulting of a missing return type parameter, qualifying an expression of Dynamic type with a method name has precisely the same effect as qualifying the type InvokeDynamic, and inserting the previously qualified expression as the first method argument. In particular, there is no special check for null references.

InvokeDynamic.<Dynamic>looksBad((Dynamic)null, 42);  // type (Dynamic, int) -> Dynamic
((Dynamic)null).looksBad(42);  // identical meaning with previous line

1.8 No other member selection expressions involving Dynamic are allowed, including selection of fields, or of methods of java.lang.Object.

(Therefore, the meaning of a call to any Object method on a Dynamic receiver, is completely defined by the MOP, not by the JVM or by Java linkage rules. This means that there is no need in the MOP for special processing of names like "wait" or "equals". Those "hardwired" methods are always available after a dynamic value is cast explicitly to the type Object.)

1.9 Although it is perhaps not very useful, InvokeDynamic can be used in other Java constructs that accept interface types, with unchanged meaning. A class or interface may specify InvokeDynamic as a supertype, and the effect is similar to any other empty interface appearing as a supertype: No new methods are defined, and the instanceof and cast expressions work as they always have. (Non-inheritance of invokedynamic methods prevents InvokeDynamic from disturbing static scoping of any type other than InvokeDynamic itself.) Java code may declare variables, arrays, fields, arguments, and return values of type InvokeDynamic or its subtypes.

4.1 As specified above, the interface java.dyn.InvokeDynamic has no supertypes or members. As such, it is a bare reference type. (As an arbitrary relation, the superclass of InvokeDynamic[] is Object[].) We define a "dynamic expression" as an expression of static type InvokeDynamic. InvokeDynamic expressions can also be readily qualified to form invokedynamic calls. We extend the usefulness of dynamic expressions by allowing them to interact with other Java expressions and statements.

InvokeDynamic x = (any reference expression can go here);

4.2 The types InvokeDynamic and Object may be freely interconverted, even though neither is a supertype of the other. (The JVM verifier already allows this, for any interface and Object, or any two interfaces. We expose one specific instance of this general principle by special-casing InvokeDynamic.) No runtime check is emitted when an Object is converted to InvokeDynamic. (That is, the compiler must never emit a checkcast to java.dyn.InvokeDynamic.)

Object  x = (Object) "foo";
InvokeDynamic y = x;
Object  z = y;
System.out.println(x);    // prints "foo"
System.out.println(y);    // prints "foo"
System.out.println(z);    // prints "foo"

The middle line shows the limited way in which static overloading interacts with dynamic typing. The dynamic argument selects the Object overloading of "println" because InvokeDynamic implicitly converts to Object. It also converts to any of the other types accepted by overloading of "println", but those other types would require a cast. Specifically, although the reference happens to be of type String, the String overloading of "println" will not be selected. As it happens, "println" produces consistent results anyway.

4.3 Any type may be undergo argument, assignment, or casting conversion to InvokeDynamic, by first converting to Object. If the original type is a primitive, it undergoes boxing conversion.

InvokeDynamic x = 42;  // (InvokeDynamic) (Object) Integer.valueOf(42)
InvokeDynamic x = "foo";  // (InvokeDynamic) (Object) "foo"

(No bytecode need be emitted for such conversions, except for boxing. The compiler must not emit a checkcast to InvokeDynamic.)

4.4 InvokeDynamic expressions can be cast to any reference type, by first converting to Object, then casting, if that latter cast would be legal. (There is no special support for casting InvokeDynamic to generic type instances.)

InvokeDynamic x = ...;
String y = (String) x;  // (String) (Object) x

(The compiler must not emit a checkcast to java.dyn.InvokeDynamic.)

4.5 InvokeDynamic expressions can be cast to any primitive type, by first casting to the corresponding wrapper type then unboxing.

InvokeDynamic x = ...;
int y = (int) x;  // (int) (Integer) (Object) x

4.6 The expression syntaxes with predefined meaning for dynamic sub-expressions are those which perform the conversions described above. These are assignment "=" and casts. InvokeDynamic expressions may also be tested with instanceof. Also, InvokeDynamic values may be declared, assigned to variables, passed as method or constructor arguments, and returned from methods.

But, the Java operators == != + += on reference types are clarified to apply only to reference types which are java.lang.Object or one of its subtypes; they do not have a predefined meaning if either operand is dynamic. The "synchronized" and "throw" statements cannot be applied to dynamic expressions.

(In general, Java expressions which detects object reference identity will not work directly on dynamic expressions. Such expressions may be explicitly cast to Object, however.)

4.7 InvokeDynamic may by a generic type parameter to a type variable with a bound of Object.

List<InvokeDynamic> dl = ...;
dl.get(0).#"integer?"();  // an invokedynamic call

Although Dynamic may occur as a type variable bound, no generic type parameter other than InvokeDynamic itself or one of its proper subtypes is allowed as a generic type parameter. (This is not a change to the JLS.) For example, Object would not be allowed as a type parameter to a type variable bounded by InvokeDynamic.

REFLECTIVE APIS:

Facilities which compute type relations (such as javax.lang.model.util.Types) may need to be updated to take InvokeDynamic into account. Generally speaking, the new InvokeDynamic conversions operate in parallel to the implicit boxing conversions. That is, they add no new subtype or supertype relations, but they provide a few more ways for values to be implicitly converted or casted.

There may also need to be changes to programs that assume Object is the root of the reference type hierarchy. Joe Darcy recalls writing annotation processing code like that and such codes could required special-casing for Dynamic.

OTHER CHANGES:

(None.)

COMPATIBILITY

BREAKING CHANGES:

None. All changes are associated with previously unused types and/or syntaxes.

EXISTING PROGRAMS:

No special interaction. The type name java.dyn.Dynamic is previously unused.

MIGRATION:

The feature is for new code only.

REFERENCES

EXISTING BUGS:

URL FOR PROTOTYPE:

Casting rules for InvokeDynamic:

FAQ

Q: Is Dynamic is a valid type in a generic type parameter? (Neal Gafter)

A: The JLS rules as they stand today make it impossible to have List<Dynamic>, unless the type bound is itself Dynamic; then the rules would allow Dynamic itself as a GTP. What they won't allow (as written) is Dynamic to be the GTP for a type variable bound by Object (or something else other than Dynamic).

If it's useful to have a field or return value or array element of type Dynamic, it is equally useful to have List<Dynamic>. The specification now includes special pleading for GTP's should have special pleading to allow Dynamic to have a type upper bound (and to erase) to Object, analogous to the special pleading that Dynamic[] is a subtype of Object[]. (Incorporated above in 4.7.)