PROPOSAL: language support for JSR 292
John Rose
John.Rose at Sun.COM
Mon Mar 30 01:41:36 PDT 2009
Here is a text form of the proposal, for the archives. The wiki form
should be equivalent at the moment, but as edits are made the wiki
form will be normative.
Best wishes,
-- John
AUTHOR(S):
John Rose
OVERVIEW
Create source-code syntaxes for using new JVM features from JSR 292.
These are invokedynamic instructions, method handle invocation,
certain relaxed conversions, and exotic identifiers.
BACKGROUND:
At the JVM level, an invokedynamic instruction is used to call methods
which have linkage and dispatch semantics defined by non-Java
languages. Again, a JVM-level invokevirtual instruction has slightly
altered linkage rules when the target class method is
java.dyn.MethodHandle.invoke: The change is that any type signature is
acceptable, and the JVM will make a type-safe method call, regardless
of the signature. In addition, the JVM already accepts (since Java 5)
any of a large set of strings as field, method, and class identifiers,
and many languages will use such identifiers beyond the limits of the
Java identifier syntax. Finally, the JVM verifier already accepts
(since the beginning) arbitrary reference values for any interface
type, and it is useful to allow at least one interface type to serve
as a wild-card type.
FEATURE SUMMARY:
We will make small, localized modifications the Java language to make
it easy to with with these JVM features (old and new). This will
allow Java code to interoperate with and/or implement libraries in non-
Java languages. The changes are as follows:
1. dynamic invocation, general case
The interface java.dyn.Dynamic may be used with the static call syntax
to form an invokedynamic call site. The method name may be any Java
identifier (including an exotic one; see point 3). The arguments may
be of any number and type. A type argument can optionally supply the
return type, in the existing <T> syntax. In effect, java.dyn.Dynamic
appears to have an infinite number of methods, of every possible name
and signature. More details are given below, but here is a first
example:
Object x = Dynamic.getMeSomething();
(As defined by JSR 292, an invokedynamic call site is linked to a
target method under the control of an application-defined bootstrap
method. The linkage state is determined by a method handle with the
same type descriptor as the call site itself.)
2. method handle invocation
Method handles (class java.dyn.MethodHandle) provide the "plumbing"
behind any invokedynamic instruction. There are library routines for
creating and adapting them, as specified by JSR 292. In addition, it
is necessary to provide a way of invoking a method handle as an
explicit target, rather than implicitly as the linked target of an
invokedynamic instruction. Since a method handle invocation (like
invokedynamic itself) can have any argument types and return value,
method handles also need a special extension for invocation. As for
type Dynamic, the type MethodHandle accepts an infinite variety of non-
static calls to the method named "invoke". The argument and return
types for the descriptor presented to the JVM are determined as for
invokedynamic calls. Here is a first example:
MethodHandle mh = ...;
mh.invoke();
3. exotic identifiers
The grammar for Java identifiers is extended to include "exotic
identifiers", whose spellings can be any sequence of characters, as
long as they avoid certain minor restrictions imposed by the JVM. An
exotic identifier is introduced by a hash mark, which is immediately
followed by a string literal. No special treatment is given to the
identifier, other than ensuring that its spelling contains exactly the
character sequence denoted by the string literal. Details are given
below; here is an example:
int #"strange variable name" = 42;
System.out.println(#"strange variable name"); // prints 42
4. conversion rules for interface Dynamic
The type Dynamic has another use which is synergistically related to
its role in encoding invokedynamic calls. It 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();
MAJOR ADVANTAGE:
These changes allow full access to invokedynamic and related new JVM
features from JSR 292. This allows Java to interoperate with new JVM
languages. It also enables Java to serve well as an language
implementation or systems programming language.
MAJOR BENEFIT:
Much greater ease of creating, for the JVM, with javac, new
programming languages and language runtimes.
(The potential opportunity cost is that language implementors who
presently use Java as a systems programming language will be forced to
stay down at the bytecode assembly level, making them slower to adopt
the JVM for their work.)
MAJOR DISADVANTAGE:
The JLS gets more complicated.
ALTERNATIVES:
The only viable alternative is assembly coding parts of the system
which must interoperate with new languages. This will discourage the
creation of common runtimes and libraries, and greatly reduce the
synergy between languages.
EXAMPLES:
See above and below (in the specification) for one-line examples
demonstrating each aspect of the syntax and type rules.
void test(MethodHandle mh) {
mh.invoke("world", 123);
// previous line generates invokevirtual
MethodHandle.invoke(String,int)Object
Dynamic.greet("hello", "world", 123);
// previous line generates invokedynamic
greet(Object,String,int)Object
// enclosing class must register a bootstrap method (handle)
to link Dynamic.greet
}
BEFORE/AFTER
There are no concise before/after examples for these language features
per se, because without the new syntax, dynamic language implementors
must resort to assembly code.
But, here is a mocked up example that shows how call site caches can
be created before and after JSR 292. This is for no particular
language; call it MyScript. Note the use of the proposed features to
form and manage dynamic call sites.
class Foo {
// compiled method for lambda (x) { print "Hello, "+x }
private static Object method1(Object x) {
System.out.println("Hello, "+x);
return null;
}
// function pointer, old style:
public static Method1 method1Ref() {
return new Method1() {
// there is a new classfile per expression reference
public Object apply(Object arg) { return method1(arg); }
}
}
// function pointer, new style:
public static MethodHandle method1Ref() {
// it all happens in one classfile
return MethodHandles.findStatic(Foo.class, "method1",
MethodTypes.makeGeneric(1));
}
}
class Bar {
// compiled method for lambda (x) { x.ready? }
// call-site cache, old style:
private static Method1 csc42 = null;
private static Object method2(Object x) {
Method1 tem = csc42;
// complex machinery with little hope of optimization
if (tem == null)
csc42 = tem = MOP.resolveCallSite(Foo.class, "ready?", x);
return tem.apply(x);
}
// call-site cache, new style:
private static Dynamic method2(Dynamic x) {
// native to the JVM and the JIT
return x.#"myscript:ready?"();
}
static { Linkage.registerBootstrapMethod("bootstrapDynamic"); }
private static Object bootstrapDynamic(java.dyn.CallSite site,
Object... args) {
return MOP.executeCallSite(site, args);
}
}
class MOP {
// shared logic for resolving call sites
public static executeCallSite(java.dyn.CallSite site, Object...
args) {
MethodHandle target = site.target();
if (target == null) {
target = resolveCallSite(site.callerClass(), site.name(),
args);
site.setTarget(target); // next time it will be a direct
call
}
return MethodHandles.invoke(target, args);
}
}
SIMPLE EXAMPLE:
This example greets the world using (a) normal static linkage, (b)
direct method handle invocation, and (c) a lazily linked call site
(invokedynamic). The output from the "bootstrap" routine appears only
once, after which the linked call site runs by directly calling the
target method, with no reflection.
import java.dyn.*;
public class Hello {
public static void main(String... av) {
if (av.length == 0) av = new String[] { "world" };
greeter(av[0] + " (from a statically linked call site)");
for (String whom : av) {
greeter.<void>invoke(whom); // strongly typed direct call
// previous line generates invokevirtual
MethodHandle.invoke(String)void
Dynamic x = whom;
x.hail(); // weakly typed invokedynamic
// previous line generates invokedynamic
MethodHandle.invoke(Dynamic)Dynamic
}
}
static void greeter(String x) { System.out.println("Hello, "+x); }
// intentionally pun between the method and its reified handle:
static MethodHandle greeter
= MethodHandles.findStatic(Hello.class, "greeter",
MethodType.make(void.class, String.class));
// Set up a class-local bootstrap method.
static { Linkage.registerBootstrapMethod("bootstrapDynamic"); }
private static Object bootstrapDynamic(CallSite site, Object... args) {
assert(args.length == 1 && site.name() == "hail"); // in lieu of
MOP
System.out.println("set target to adapt "+greeter);
MethodHandle target = MethodHandles.convertArguments(greeter,
site.type());
site.setTarget(target);
System.out.println("calling the slow path; this should be the
last time!");
return MethodHandles.invoke(target, args);
}
}
ADVANCED EXAMPLE:
(See before-and-after MOP example above.)
DETAILS
SPECIFICATION:
1.1 The type java.dyn.Dynamic shall be defined as an empty interface
with no supertypes. It is an error if Dynamic is defined as a class,
or if it has any supertypes, or if it defines any members. It does
not have the usual implicit supertype of Object.
package java.dyn;
public interface Dynamic /*must be empty*/ { /*must be empty*/ }
(The complete lack of supertypes makes it an unusual type, on a par
with Object. Of course the JVM verifier will allow free
interconversion between Object and Dynamic. This unusual "sterility"
allows Java complete access to all degrees of freedom in the JVM's
invokedynamic instruction.)
1.2 The type name Dynamic may be qualified with any method name
whatever, and invoked on any number and type of arguments. The
compiler generates an invokedynamic call site with the given name and
a descriptor (symbolic type signature) derived from the erasure of the
static types of all the arguments. In this way, an invokedynamic
instruction can be written in Java to use any of the full range of
calling sequences (i.e., descriptors) supported by the JVM. Neither
the JVM instruction nor the Java syntax is limited in its use of
argument types.
Dynamic.anyNameWhatever(); // no argument types
Dynamic.anotherName("foo", 42); // argument types (String, int)
1.3 Any call to a method in 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 java.dyn.Dynamic
itself. (See part 4 for conversion rules involving Dynamic.)
Dynamic x = Dynamic.myGetCurrentThing(); // type () -> Dynamic
Dynamic.<void>myPutCurrentThing(x); // type (Dynamic) -> void
int y = Dynamic.<int>myHashCode((Object)x); // type (Object) ->
int
boolean z = Dynamic.<boolean>myEquals(x, y); // type (Dynamic,
int) -> boolean
(Rationale: Although it is strange for non-references to appear in the
syntactic position of type arguments, this design is far simpler than
inventing a completely new syntax for specifying the return type of
the call site, as some early prototypes did.)
1.4 For the purposes of determining the descriptor of the
invokedynamic instruction, null arguments (unless casted to some other
type) are deemed to be of reference type java.lang.Void. This is a
pragmatic choice, compatible with the verifier and partially coherent
with the meaning of the type Void, since it happens to allow only null
reference values. Conversely, void method return values are reflected
as null values. The type Void will appear only to the bootstrap
method, and will serve notice that the call site contains a null,
rather than an explicitly typed reference.
Dynamic.myPrintLine(null); // type (Void) -> Dynamic
Dynamic.<void>foo((String)null, null); // type (String, Void) ->
void
1.5 No checked exceptions are produced by any call to Dynamic.
try { Dynamic.foo(); } catch (Exception ee) { } // a compile-
time error
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 = Dynamic.<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 As with any other expression of Dynamic type, qualifying it with a
method name has precisely the same effect as qualifying the type
Dynamic itself, and inserting the previously qualified expression as
the first method argument. In particular, there is no special check
for null references.
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.
1.9 Although it is perhaps not very useful, Dynamic can be used in
other Java constructs that accept interface types, with unchanged
meaning. A class or interface may specify Dynamic 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 Dynamic from disturbing static scoping
of any type other than Dynamic itself.) Java code may declare
variables, arrays, fields, arguments, and return values of type
Dynamic or its subtypes.
2.1 The class java.dyn.MethodHandle shall be defined (external to this
specification) without any method named "invoke". It is an error if
it or any of its supertypes define a method named "invoke". (In any
case, such supertypes are a fixed part of the Java APIs and/or
implementations. JSR 292 happens to define a method named "type" on
method handles.)
package java.dyn;
public class MethodHandle { ... }
2.2 Any reference of type MethodHandle may be qualified with the
method name "invoke" and invoked on any number and type of arguments.
Only the method named "invoke" is treated this new way. All other
expressions involving MethodHandle are unchanged in meaning, including
selection of members other than "invoke", casting, and instanceof.
MethodHandle mh = ...;
mh.invoke("foo", 42); // argument types (String, int)
MethodType mtype = mh.type(); // no new rules here; see JSR 292
javadocs
mh.neverBeforeSeenName(); // no new rules; must raise an error
In effect, java.dyn.MethodHandle appears to have an infinite number of
non-static methods named "invoke", of every possible signature.
(In fact, JSR 292 specifies that each individual method handle has a
unique type signature, and may be invoked only under that specific
type. This type is checked on every method handle call. JSR 292
guarantees runtime type safety by requiring that an exception be
thrown if a method handle caller and callee do not agree exactly on
the argument and return types. The details of this check are not part
of this specification, but rather of the MethodHandle API.)
2.3 A method handle call on "invoke" accepts an optional type
parameter to specify the return type. As with Dynamic, the type
parameter to MethodHandle.invoke may be any type, including void or a
primitive type. If it is omitted it defaults to Dynamic.
MethodHandle mh1, mh2, mh3, mh4; ...
Dynamic x = mh1.invoke(); // type () -> Dynamic
mh2.<void>invoke(x); // type (Dynamic) -> void
int y = mh3.<int>invoke((Object)x); // type (Object) -> int
boolean z = mh4.<boolean>invoke(x, y); // type (Dynamic, int) ->
boolean
2.4 As with Dynamic, otherwise untyped null argument values are
treated as if they were of type Void.
mh1.invoke(null); // type (Void) -> Dynamic
mh2.<void>invoke((String)null, null); // type (String, Void) ->
void
2.5 No checked exceptions are produced by the call expression.
try { mh.invoke(); } catch (Exception ee) { } // a compile-time
error
2.6 As usual, if a null value typed as a method handle is qualified
with the method name "invoke", the expression must terminate
abnormally with a NullPointerException.
MethodHandle nmh = null;
nmh.invoke(); // must produce a NullPointerException
2.7 If a class extends MethodHandle, it does not inherit any of the
implicitly defined "invoke" methods. To invoke a subclass of a method
handle, it must first be cast to MethodHandle per se. (Non-
inheritance of "invoke" methods prevents MethodHandle from disturbing
static scoping of any type other than MethodHandle itself. Note that
public method handle subclasses are not necessarily a feature of JSR
292.)
class DirectMethodHandle extends MethodHandle { ... }
DirectMethodHandle dmh = mh;
mh.invoke<void>(); // must be rejected: special MH.invoke not
visible here
2.8 The bytecode emitted for any call to MethodHandle.invoke is an
invokevirtual instruction, exactly as if a public virtual method of
the desired descriptor were already present in java.dyn.MethodHandle.
mh.<void>invoke(1); // produces an invokevirtual instruction
class MethodHandle { ... public abstract void invoke(int
x); ... } // hypothetical overloading of 'invoke'
mh.invoke(1); // would produce an identical invokevirtual,
if that overloading could exist
3.1 The two-character sequence '#' '"' (hash and string-quote, or
ASCII code points 35 and 24) introduces a new token similar in
structure to a Java string literal. The token is in fact an
identifier (JLS 3.8), which may be used for all the same syntactic
purposes as ordinary identifiers are used for.
int #"strange variable name" = 42;
System.out.println(#"strange variable name"); // prints 42
This is true whether or not the characters are alphanumeric, or
whether they happen (when unquoted) to spell any Java Java keyword or
token.
int #"+", #"\\", #"42" = 24;
System.out.println(#"42" * 100); // prints 2400
// another take on java.lang.Integer:
class #"int" extends Number {
final int #"int";
#"int"(int #"int") { this.#"int" = #"int"; }
static #"int" valueOf(int #"int") { return new #"int"(#"int"); }
public int intValue() { return #"int"; }
public long longValue() { return #"int"; }
public float floatValue() { return #"int"; }
public double doubleValue() { return #"int"; }
public String toString() { return String.valueOf(#"int"); }
}
3.2 The spelling of the identifier is obtained by collecting all the
characters between the string quotes. Every string escape sequence
(JLS 3.10.6) is replaced by the characters they refer to. As with
other tokens, this character collection occurs after Unicode escape
replacement is complete (JLS 3.3).
int #"\'\t" = 5; // a two-character identifier
System.out.println(#"'\u0009"); // prints 5
3.3 In particular, if the resulting sequence of characters happens to
be a previously valid Java identifier, both normal and exotic forms of
the same identifier token may be freely mixed.
int #"num" = 42, scale = 100;
System.out.println(num * #"scale"); // prints 4200
3.4 An exotic identifier may not be empty. That is, there must be at
least one character between the opening and closing quotes.
int #""; // must be rejected
3.5 Certain characters are treated specially within exotic identifiers
even though they are not specially treated in string or character
literals. The following so-called "dangerous characters" are illegal
in an exotic identifier unless preceded by a backslash: / . ; < >
[ ]. If a dangerous character is preceded by a backslash, the
backslash is elided and the character is collected anyway. Depending
on the ultimate use of the identifier, the program may be eventually
rejected with an error. This must happen if and only if the escaped
character would otherwise participate in a bytecode name forbidden by
the Java 5 JVM specification.
class #"foo/Bar" { } // not a package qualifier, must be rejected
class #"foo.Bar" { } // not a package qualifier, must be rejected
x.#"<init>"(); // not a method call; must be rejected
x.#"f(Ljava/lang/Long;)"(0); // not a method descriptor; must be
rejected
3.5.1 Specifically, the compiler must reject a program containing an
exotic identifier with an escaped dangerous character happen if any of
these is true: (a) the identifier is used as part or all of the
bytecode name of a class or interface, and it contains any of / . ; [,
or (b) the identifier is used as a part or all of the bytecode name of
a method, and it contains any of / . ; < >, or (c) the identifier is
used as a part or all of the bytecode name of a field, and it contains
any of / . ;. Note that close bracket ] will always pass through; it
is included in these rules simply for symmetry with open bracket ].
class #"java/io" { } // must be rejected
class #"java\/io" { } // must be rejected (perhaps in an
assembly phase)
class #"<foo>" { } // must be rejected
class #"\<foo\>" { } // legal (but probably a bad idea)
void f() { int #"¥" = '\u00A5'; } // must be rejected
void f() { int #"¥\;" = '\u00A5'; } // legal (but probably a
bad idea)
class #"]" { int #"]"; void #"]"() {} } // must be rejected
class #"\]" { int #"\]"; void #"\]"() {} } // legal (but
probably a bad idea)
These rules support the need for avoiding dangerous characters as a
general rule, while permitting occasional expert use of names known to
be legal to the JVM. However, there is no provision for uttering the
method names "<init>" or "<clinit>". Nor may package prefixes ever be
encoded within exotic identifiers.
3.6 Any ASCII punctuation character not otherwise affected by these
rules may serve as a so-called "exotic escape character". That is, it
may be preceded by a backslash; in this case both it and the backslash
is collected (as a pair of characters) into the exotic identifier.
Specifically, these characters are ! # $ % & ( ) * + , - : = ? @ ^ _
` { | } ~ and no others.
int #"=" = 42;
int #"\=" = 99;
System.out.println(#"="); // must print 42 not 99
These escapes are passed through to the bytecode level for further use
by reflective applications, such as a bootstrap linker for
invokedynamic. Such escapes are necessary at the level of bytecode
names in order to encode (mangle) the dangerous characters. By
sending both the backslash and the exotic escape character through to
the bytecode level, we avoid the problem of multiple escaping (as is
seen, for example, with regexp packages).
Although Java has not worked this way in the past, the need for
multiple phases of escaping motivates it here and now. Compare this
quoting behavior with that of the Unix shells, which perform delayed
escaping for similar reasons:
$ echo "test: \$NL = '\12'"
test: $NL = '\12'
(See http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm for a
proposal that manages bytecode-level mangling of exotic names. This
proposal is independent of the present specification.)
3.7 Here is the combined grammar for exotic identifiers, starting with
a new clause for Identifier (JLS 3.8):
Identifier: ...
ExoticIdentifier
ExoticIdentifier: # " ExoticIdentifierCharacters "
ExoticIdentifierCharacters:
ExoticIdentifierCharacter ExoticIdentifierCharacters
ExoticIdentifierCharacter:
StringCharacter but not DangerousCharacter
\ DangerousCharacter /* the backslash is elided and the
character is collected */
\ ExoticEscapeChar /* both the backslash and the character
are collected */
DangerousCharacter: one of / . ; < > [ ]
ExoticEscapeChar: one of ! # $ % & ( ) * + , - : = ? @ ^ _ `
{ | } ~
3.8 This construct does not conflict with any other existing or
proposed use of the hash character. In particular, if the hash
character were to be defined as a new sort of Java operator, it would
not conflict with this specification. Even if it were to be a
construct which could validly be followed by a normal Java string
literal, any ambiguity between the constructs could be resolved in
favor of the operator by inserting whitespace between the hash and the
opening quote of the string literal.
3.9 Exotic identifiers are occasionally useful for creating
dynamically linkable classes or methods whose names are determined by
naming scheme external to Java. (They may also be used for
occasionally avoiding Java keywords, although a leading underscore
will usually do just as well.) They are most crucially useful for
forming invokedynamic calls, when the method name must refer to an
entity in another language, or must contain structured information
relevant to a metaobject protocol.
package my.xml.tags;
class #"\<pre\>" { ... }
package my.sql.bindings;
interface Document { String title(); Text #"abstract"(); int
#"class"(); ... }
Dynamic mySchemeVector = ...;
Dynamic x = Dynamic.#"scheme:vector-ref"(mySchemeVector, 42);
4.1 As specified above, the interface java.dyn.Dynamic has no
supertypes or members. As such, it is a bare reference type. (As an
arbitrary relation, the superclass of Dynamic[] is Object[].) We
define a "dynamic expression" as an expression of static type
Dynamic. Dynamic 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.
Dynamic x = (any reference expression can go here);
4.2 The types Dynamic 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 Dynamic.) No runtime check is emitted when an Object is
converted to Dynamic. (That is, the compiler must never emit a
checkcast to java.dyn.Dynamic.)
Object x = (Object) "foo";
Dynamic 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 Dynamic 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 Dynamic, by first converting to Object. If the original
type is a primitive, it undergoes boxing conversion.
Dynamic x = 42; // (Dynamic) (Object) Integer.valueOf(42)
Dynamic x = "foo"; // (Dynamic) (Object) "foo"
(No bytecode need be emitted for such conversions, except for boxing.
The compiler must not emit a checkcast to Dynamic.)
4.4 Dynamic 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 Dynamic to generic
type instances.)
Dynamic x = ...;
String y = (String) x; // (String) (Object) x
(The compiler must not emit a checkcast to java.dyn.Dynamic.)
4.5 Dynamic expressions can be cast to any primitive type, by first
casting to the corresponding wrapper type then unboxing.
Dynamic 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. Dynamic expressions may also be
tested with instanceof. Also, Dynamic 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.)
COMPILATION:
See JSR 292 for the specification of invokedynamic instructions. In
brief, they begin with a new opcode, a CONSTANT_NameAndType index, and
end with two required zero bytes. Method handle invocation is just an
ordinary invokevirtual instruction, whose class is
java.dyn.MethodHandle, whose name is "invoke", and whose descriptor is
completely arbitrary; this requires no special compilation support
beyond putting a loophole in the method lookup logic. Exotic
identifiers require no compilation support beyond the lexer. (This
assumes Unicode-clean symbol tables all the way to the backend.)
There must be a final validity check in the class file assembler; this
can (and should) be copied from the JVM specification.
TESTING:
Testing will be done the usual way, via unit tests exercising a broad
variety of signatures and name spellings, and by early access
customers experimenting with the new facilities.
LIBRARY SUPPORT:
The JVM-level behavior of the types java.dyn.Dynamic and
java.dyn.MethodHandle are defined by JSR 292. Their language
integration should be defined by an expert group with language
expertise.
JSR 292 per se involves extensive libraries for the functionality it
defines, but they are not prerequisites to the features specified
here. Other than exotic identifiers, the features described here have
no impact except when the java.dyn types exist in the compilation
environment.
REFLECTIVE APIS:
The method java.lang.Class.getDeclaredMethod must be special-cased to
always succeed for MethodHandle.invoke and for Dynamic (any method
name), regardless of signature. The JSR 292 JVM has this logic
already, but it must be exposed out through the Class API.
Only single-result reflection lookups need to be changed. Multiple-
method lookups should *not* produce implicitly defined methods.
The javax.lang.model API (which is used internally by javac) does not
need specialization, because the implicitly defined methods of
MethodHandle and Dynamic do not ever need to mix with other more
normal methods. The static (compile-time) model of Dynamic may not
present any enclosed elemeents, while that of MethodHandle must not
present any methods named "invoke".
Facilities which compute type relations (such as
javax.lang.model.util.Types) may need to be updated to take Dynamic
into account. Generally speaking, the new Dynamic 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.
OTHER CHANGES:
Javap needs to disassemble invokedynamic instructions.
Javap needs to be robust about unusual identifier spellings. (It
already is, mostly.)
MIGRATION:
The feature is for new code only.
These language features, along with the related JVM extensions, will
make it possible for dynamic language implementations (a) to continue
to be coded in Java, but (b) to avoid the performance and complexity
overhead of the Core Reflection API.
COMPATIBILITY
BREAKING CHANGES:
None. All changes are associated with previously unused types and/or
syntaxes.
EXISTING PROGRAMS:
No special interaction. In earlier class files invokedynamic is an
illegal opcode, and java.dyn.Dynamic is a previously unused type name.
REFERENCES
EXISTING BUGS:
6754038: writing libraries in Java for non-Java languages requires
method handle invocation
6746458: writing libraries in Java for non-Java languages requires
support for exotic identifiers
00TBD00: writing libraries in Java for non-Java languages requires
permissive Dynamic type
URL FOR PROTOTYPE:
General:
http://hg.openjdk.java.net/mlvm/mlvm/langtools
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/nb-javac/
Invokedynamic and method handles:
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/meth.txt
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/meth.patch
Exotic identifiers:
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/quid.txt
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/quid.patch
Casting rules for Dynamic:
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/dyncast.txt
http://hg.openjdk.java.net/mlvm/mlvm/langtools/file/tip/dyncast.patch
More information about the coin-dev
mailing list