Merge
authorjgodinez
Thu May 14 09:53:35 2009 -0700 (10 months ago)
changeset 1285ec0a8acd4737
parent 12849cf4ef04d9a7
parent 12032a5a1b269e89
child 1286fb03586d68b6
Merge
--- a/.hgtags Wed May 06 14:14:54 2009 -0700
+++ b/.hgtags Thu May 14 09:53:35 2009 -0700
@@ -32,3 +32,4 @@ 522bb5aa17e0c0cff00b1ed7d1b51bc4db2cfef9
522bb5aa17e0c0cff00b1ed7d1b51bc4db2cfef9 jdk7-b55
7fd3bc37afe36f8f6165ba679db1229716db822a jdk7-b56
d5a1223e961891564de25c39fba6f2442d0fb045 jdk7-b57
+9ba256e2e5c161b89e638390f998baa175ec9abe jdk7-b58
--- a/make/common/Release.gmk Wed May 06 14:14:54 2009 -0700
+++ b/make/common/Release.gmk Thu May 14 09:53:35 2009 -0700
@@ -51,6 +51,9 @@ EXCLUDE_PROPWARN_PKGS = com.sun.java.swi
com.sun.java.swing.plaf.windows \
com.sun.java.swing.plaf.motif \
com.sun.java.swing.plaf.gtk
+
+# This is a stopgap until 6839872 is fixed.
+EXCLUDE_PROPWARN_PKGS += sun.dyn
# 64-bit solaris has a few special cases. We define the variable
# SOLARIS64 for use in this Makefile to easily test those cases
--- a/make/docs/CORE_PKGS.gmk Wed May 06 14:14:54 2009 -0700
+++ b/make/docs/CORE_PKGS.gmk Thu May 14 09:53:35 2009 -0700
@@ -1,5 +1,5 @@
#
-# Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,7 @@ EXCLUDE_PKGS = \
# This is a list of regular expressions. So foo.* matches "foo" and "foo.bar".
#
ACTIVE_JSR_PKGS= \
+ java.dyn \
java.sql \
javax.activation \
javax.annotation.* \
@@ -96,6 +97,7 @@ CORE_PKGS =
java.awt.print \
java.beans \
java.beans.beancontext \
+ java.dyn \
java.io \
java.lang \
java.lang.annotation \
--- a/make/java/Makefile Wed May 06 14:14:54 2009 -0700
+++ b/make/java/Makefile Thu May 14 09:53:35 2009 -0700
@@ -1,5 +1,5 @@
#
-# Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 1995-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,7 @@ SUBDIRS += hpi version jvm redist verify
# Others
# Note: java_crw_demo java_hprof_demo are demos but must be delivered built in sdk
SUBDIRS += security npt java_crw_demo java_hprof_demo \
- math awt util text applet net nio \
+ math awt util text applet net nio dyn \
sql rmi jar beans logging management instrument
--- a/src/share/classes/java/awt/GraphicsDevice.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/awt/GraphicsDevice.java Thu May 14 09:53:35 2009 -0700
@@ -282,7 +282,7 @@ public abstract class GraphicsDevice {
w.setOpacity(1.0f);
}
Color bgColor = w.getBackground();
- if (bgColor.getAlpha() < 255) {
+ if ((bgColor != null) && (bgColor.getAlpha() < 255)) {
bgColor = new Color(bgColor.getRed(), bgColor.getGreen(),
bgColor.getBlue(), 255);
w.setBackground(bgColor);
--- a/src/share/classes/java/io/Console.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/io/Console.java Thu May 14 09:53:35 2009 -0700
@@ -503,20 +503,25 @@ public final class Console implements Fl
// Set up JavaIOAccess in SharedSecrets
static {
-
- // Add a shutdown hook to restore console's echo state should
- // it be necessary.
- sun.misc.SharedSecrets.getJavaLangAccess()
- .registerShutdownHook(0 /* shutdown hook invocation order */,
- new Runnable() {
- public void run() {
- try {
- if (echoOff) {
- echo(true);
- }
- } catch (IOException x) { }
- }
- });
+ try {
+ // Add a shutdown hook to restore console's echo state should
+ // it be necessary.
+ sun.misc.SharedSecrets.getJavaLangAccess()
+ .registerShutdownHook(0 /* shutdown hook invocation order */,
+ false /* only register if shutdown is not in progress */,
+ new Runnable() {
+ public void run() {
+ try {
+ if (echoOff) {
+ echo(true);
+ }
+ } catch (IOException x) { }
+ }
+ });
+ } catch (IllegalStateException e) {
+ // shutdown is already in progress and console is first used
+ // by a shutdown hook
+ }
sun.misc.SharedSecrets.setJavaIOAccess(new sun.misc.JavaIOAccess() {
public Console console() {
--- a/src/share/classes/java/io/DeleteOnExitHook.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/io/DeleteOnExitHook.java Thu May 14 09:53:35 2009 -0700
@@ -34,23 +34,31 @@ import java.io.File;
*/
class DeleteOnExitHook {
+ private static LinkedHashSet<String> files = new LinkedHashSet<String>();
static {
- sun.misc.SharedSecrets.getJavaLangAccess()
- .registerShutdownHook(2 /* Shutdown hook invocation order */,
- new Runnable() {
- public void run() {
- runHooks();
- }
- });
+ // DeleteOnExitHook must be the last shutdown hook to be invoked.
+ // Application shutdown hooks may add the first file to the
+ // delete on exit list and cause the DeleteOnExitHook to be
+ // registered during shutdown in progress. So set the
+ // registerShutdownInProgress parameter to true.
+ sun.misc.SharedSecrets.getJavaLangAccess()
+ .registerShutdownHook(2 /* Shutdown hook invocation order */,
+ true /* register even if shutdown in progress */,
+ new Runnable() {
+ public void run() {
+ runHooks();
+ }
+ }
+ );
}
-
- private static LinkedHashSet<String> files = new LinkedHashSet<String>();
private DeleteOnExitHook() {}
static synchronized void add(String file) {
- if(files == null)
+ if(files == null) {
+ // DeleteOnExitHook is running. Too late to add a file
throw new IllegalStateException("Shutdown in progress");
+ }
files.add(file);
}
--- a/src/share/classes/java/lang/ApplicationShutdownHooks.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/lang/ApplicationShutdownHooks.java Thu May 14 09:53:35 2009 -0700
@@ -35,17 +35,26 @@ import java.util.*;
*/
class ApplicationShutdownHooks {
+ /* The set of registered hooks */
+ private static IdentityHashMap<Thread, Thread> hooks;
static {
- Shutdown.add(1 /* shutdown hook invocation order */,
- new Runnable() {
- public void run() {
- runHooks();
+ try {
+ Shutdown.add(1 /* shutdown hook invocation order */,
+ false /* not registered if shutdown in progress */,
+ new Runnable() {
+ public void run() {
+ runHooks();
+ }
}
- });
+ );
+ hooks = new IdentityHashMap<Thread, Thread>();
+ } catch (IllegalStateException e) {
+ // application shutdown hooks cannot be added if
+ // shutdown is in progress.
+ hooks = null;
+ }
}
- /* The set of registered hooks */
- private static IdentityHashMap<Thread, Thread> hooks = new IdentityHashMap<Thread, Thread>();
private ApplicationShutdownHooks() {}
--- a/src/share/classes/java/lang/Shutdown.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/lang/Shutdown.java Thu May 14 09:53:35 2009 -0700
@@ -53,6 +53,9 @@ class Shutdown {
private static final int MAX_SYSTEM_HOOKS = 10;
private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
+ // the index of the currently running shutdown hook to the hooks array
+ private static int currentRunningHook = 0;
+
/* The preceding static fields are protected by this lock */
private static class Lock { };
private static Object lock = new Lock();
@@ -68,17 +71,39 @@ class Shutdown {
}
- /* Add a new shutdown hook. Checks the shutdown state and the hook itself,
+ /**
+ * Add a new shutdown hook. Checks the shutdown state and the hook itself,
* but does not do any security checks.
- */
- static void add(int slot, Runnable hook) {
- synchronized (lock) {
- if (state > RUNNING)
- throw new IllegalStateException("Shutdown in progress");
-
+ *
+ * The registerShutdownInProgress parameter should be false except
+ * registering the DeleteOnExitHook since the first file may
+ * be added to the delete on exit list by the application shutdown
+ * hooks.
+ *
+ * @params slot the slot in the shutdown hook array, whose element
+ * will be invoked in order during shutdown
+ * @params registerShutdownInProgress true to allow the hook
+ * to be registered even if the shutdown is in progress.
+ * @params hook the hook to be registered
+ *
+ * @throw IllegalStateException
+ * if registerShutdownInProgress is false and shutdown is in progress; or
+ * if registerShutdownInProgress is true and the shutdown process
+ * already passes the given slot
+ */
+ static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
+ synchronized (lock) {
if (hooks[slot] != null)
throw new InternalError("Shutdown hook at slot " + slot + " already registered");
+ if (!registerShutdownInProgress) {
+ if (state > RUNNING)
+ throw new IllegalStateException("Shutdown in progress");
+ } else {
+ if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
+ throw new IllegalStateException("Shutdown in progress");
+ }
+
hooks[slot] = hook;
}
}
@@ -86,11 +111,15 @@ class Shutdown {
/* Run all registered shutdown hooks
*/
private static void runHooks() {
- /* We needn't bother acquiring the lock just to read the hooks field,
- * since the hooks can't be modified once shutdown is in progress
- */
- for (Runnable hook : hooks) {
+ for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
try {
+ Runnable hook;
+ synchronized (lock) {
+ // acquire the lock to make sure the hook registered during
+ // shutdown is visible here.
+ currentRunningHook = i;
+ hook = hooks[i];
+ }
if (hook != null) hook.run();
} catch(Throwable t) {
if (t instanceof ThreadDeath) {
--- a/src/share/classes/java/lang/System.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/lang/System.java Thu May 14 09:53:35 2009 -0700
@@ -1171,8 +1171,8 @@ public final class System {
public void blockedOn(Thread t, Interruptible b) {
t.blockedOn(b);
}
- public void registerShutdownHook(int slot, Runnable r) {
- Shutdown.add(slot, r);
+ public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
+ Shutdown.add(slot, registerShutdownInProgress, hook);
}
});
}
--- a/src/share/classes/java/util/zip/ZipFile.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/util/zip/ZipFile.java Thu May 14 09:53:35 2009 -0700
@@ -154,7 +154,7 @@ class ZipFile implements ZipConstants {
* @param file the ZIP file to be opened for reading
* @param mode the mode in which the file is to be opened
* @param charset
- * the {@link java.nio.charset.Charset {@code charset}} to
+ * the {@linkplain java.nio.charset.Charset charset} to
* be used to decode the ZIP entry name and comment that are not
* encoded by using UTF-8 encoding (indicated by entry's general
* purpose flag).
@@ -206,7 +206,7 @@ class ZipFile implements ZipConstants {
*
* @param name the name of the zip file
* @param charset
- * the {@link java.nio.charset.Charset {@code charset}} to
+ * the {@linkplain java.nio.charset.Charset charset} to
* be used to decode the ZIP entry name and comment that are not
* encoded by using UTF-8 encoding (indicated by entry's general
* purpose flag).
@@ -230,7 +230,7 @@ class ZipFile implements ZipConstants {
* Opens a ZIP file for reading given the specified File object.
* @param file the ZIP file to be opened for reading
* @param charset
- * The {@link java.nio.charset.Charset {@code charset}} to be
+ * The {@linkplain java.nio.charset.Charset charset} to be
* used to decode the ZIP entry name and comment (ignored if
* the <a href="package-summary.html#lang_encoding"> language
* encoding bit</a> of the ZIP entry's general purpose bit
--- a/src/share/classes/java/util/zip/ZipInputStream.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/util/zip/ZipInputStream.java Thu May 14 09:53:35 2009 -0700
@@ -84,7 +84,7 @@ class ZipInputStream extends InflaterInp
* @param in the actual input stream
*
* @param charset
- * The {@link java.nio.charset.Charset {@code charset}} to be
+ * The {@linkplain java.nio.charset.Charset charset} to be
* used to decode the ZIP entry name (ignored if the
* <a href="package-summary.html#lang_encoding"> language
* encoding bit</a> of the ZIP entry's general purpose bit
--- a/src/share/classes/java/util/zip/ZipOutputStream.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/java/util/zip/ZipOutputStream.java Thu May 14 09:53:35 2009 -0700
@@ -108,7 +108,7 @@ class ZipOutputStream extends DeflaterOu
*
* @param out the actual output stream
*
- * @param charset the {@link java.nio.charset.Charset </code>charset<code>}
+ * @param charset the {@linkplain java.nio.charset.Charset charset}
* to be used to encode the entry names and comments
*
* @since 1.7
--- a/src/share/classes/sun/misc/JavaLangAccess.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/sun/misc/JavaLangAccess.java Thu May 14 09:53:35 2009 -0700
@@ -55,6 +55,22 @@ public interface JavaLangAccess {
/** Set thread's blocker field. */
void blockedOn(Thread t, Interruptible b);
- /** register shutdown hook */
- void registerShutdownHook(int slot, Runnable r);
+ /**
+ * Registers a shutdown hook.
+ *
+ * It is expected that this method with registerShutdownInProgress=true
+ * is only used to register DeleteOnExitHook since the first file
+ * may be added to the delete on exit list by the application shutdown
+ * hooks.
+ *
+ * @params slot the slot in the shutdown hook array, whose element
+ * will be invoked in order during shutdown
+ * @params registerShutdownInProgress true to allow the hook
+ * to be registered even if the shutdown is in progress.
+ * @params hook the hook to be registered
+ *
+ * @throw IllegalStateException if shutdown is in progress and
+ * the slot is not valid to register.
+ */
+ void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
}
--- a/src/share/classes/sun/misc/Unsafe.java Wed May 06 14:14:54 2009 -0700
+++ b/src/share/classes/sun/misc/Unsafe.java Thu May 14 09:53:35 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -811,6 +811,25 @@ public final class Unsafe {
public native Class defineClass(String name, byte[] b, int off, int len);
+ /**
+ * Define a class but do not make it known to the class loader or system dictionary.
+ * <p>
+ * For each CP entry, the corresponding CP patch must either be null or have
+ * the a format that matches its tag:
+ * <ul>
+ * <li>Integer, Long, Float, Double: the corresponding wrapper object type from java.lang
+ * <li>Utf8: a string (must have suitable syntax if used as signature or name)
+ * <li>Class: any java.lang.Class object
+ * <li>String: any object (not just a java.lang.String)
+ * <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
+ * </ul>
+ * @params hostClass context for linkage, access control, protection domain, and class loader
+ * @params data bytes of a class file
+ * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data
+ */
+ public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);
+
+
/** Allocate an instance but do not run any constructor.
Initializes the class if it has not yet been. */
public native Object allocateInstance(Class cls)
--- a/src/share/javavm/export/classfile_constants.h Wed May 06 14:14:54 2009 -0700
+++ b/src/share/javavm/export/classfile_constants.h Thu May 14 09:53:35 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -306,7 +306,7 @@ enum {
JVM_OPC_invokespecial = 183,
JVM_OPC_invokestatic = 184,
JVM_OPC_invokeinterface = 185,
- JVM_OPC_xxxunusedxxx = 186,
+ JVM_OPC_invokedynamic = 186,
JVM_OPC_new = 187,
JVM_OPC_newarray = 188,
JVM_OPC_anewarray = 189,
@@ -515,7 +515,7 @@ enum {
3, /* invokespecial */ \
3, /* invokestatic */ \
5, /* invokeinterface */ \
- 0, /* xxxunusedxxx */ \
+ 5, /* invokedynamic */ \
3, /* new */ \
2, /* newarray */ \
3, /* anewarray */ \
--- a/src/share/native/common/check_code.c Wed May 06 14:14:54 2009 -0700
+++ b/src/share/native/common/check_code.c Thu May 14 09:53:35 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1223,16 +1223,20 @@ verify_opcode_operands(context_type *con
case JVM_OPC_invokevirtual:
case JVM_OPC_invokespecial:
case JVM_OPC_invokestatic:
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokeinterface: {
/* Make sure the constant pool item is the right type. */
int key = (code[offset + 1] << 8) + code[offset + 2];
const char *methodname;
jclass cb = context->class;
fullinfo_type clazz_info;
- int is_constructor, is_internal;
+ int is_constructor, is_internal, is_invokedynamic;
int kind = (opcode == JVM_OPC_invokeinterface
? 1 << JVM_CONSTANT_InterfaceMethodref
+ : opcode == JVM_OPC_invokedynamic
+ ? 1 << JVM_CONSTANT_NameAndType
: 1 << JVM_CONSTANT_Methodref);
+ is_invokedynamic = opcode == JVM_OPC_invokedynamic;
/* Make sure the constant pool item is the right type. */
verify_constant_pool_type(context, key, kind);
methodname = JVM_GetCPMethodNameUTF(env, cb, key);
@@ -1241,8 +1245,11 @@ verify_opcode_operands(context_type *con
is_internal = methodname[0] == '<';
pop_and_free(context);
- clazz_info = cp_index_to_class_fullinfo(context, key,
- JVM_CONSTANT_Methodref);
+ if (is_invokedynamic)
+ clazz_info = context->object_info; // anything will do
+ else
+ clazz_info = cp_index_to_class_fullinfo(context, key,
+ JVM_CONSTANT_Methodref);
this_idata->operand.i = key;
this_idata->operand2.fi = clazz_info;
if (is_constructor) {
@@ -1304,6 +1311,11 @@ verify_opcode_operands(context_type *con
"Fourth operand byte of invokeinterface must be zero");
}
pop_and_free(context);
+ } else if (opcode == JVM_OPC_invokedynamic) {
+ if (code[offset + 3] != 0 || code[offset + 4] != 0) {
+ CCerror(context,
+ "Third and fourth operand bytes of invokedynamic must be zero");
+ }
} else if (opcode == JVM_OPC_invokevirtual
|| opcode == JVM_OPC_invokespecial)
set_protected(context, inumber, key, opcode);
@@ -1990,6 +2002,7 @@ pop_stack(context_type *context, unsigne
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit: /* invokespecial call to <init> */
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
/* The top stuff on the stack depends on the method signature */
int operand = this_idata->operand.i;
@@ -2005,7 +2018,8 @@ pop_stack(context_type *context, unsigne
print_formatted_methodname(context, operand);
}
#endif
- if (opcode != JVM_OPC_invokestatic)
+ if (opcode != JVM_OPC_invokestatic &&
+ opcode != JVM_OPC_invokedynamic)
/* First, push the object */
*ip++ = (opcode == JVM_OPC_invokeinit ? '@' : 'A');
for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) {
@@ -2290,6 +2304,7 @@ pop_stack(context_type *context, unsigne
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit:
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokeinterface: case JVM_OPC_invokestatic: {
int operand = this_idata->operand.i;
const char *signature =
@@ -2299,7 +2314,8 @@ pop_stack(context_type *context, unsigne
int item;
const char *p;
check_and_push(context, signature, VM_STRING_UTF);
- if (opcode == JVM_OPC_invokestatic) {
+ if (opcode == JVM_OPC_invokestatic ||
+ opcode == JVM_OPC_invokedynamic) {
item = 0;
} else if (opcode == JVM_OPC_invokeinit) {
fullinfo_type init_type = this_idata->operand2.fi;
@@ -2680,6 +2696,7 @@ push_stack(context_type *context, unsign
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial:
case JVM_OPC_invokeinit:
+ case JVM_OPC_invokedynamic:
case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: {
/* Look to signature to determine correct result. */
int operand = this_idata->operand.i;
--- a/src/share/native/common/opcodes.in_out Wed May 06 14:14:54 2009 -0700
+++ b/src/share/native/common/opcodes.in_out Thu May 14 09:53:35 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -210,7 +210,7 @@ char * const opcode_in_out[][2] = {
{"?", "?"}, /* invokespecial */
{"?", "?"}, /* invokestatic */
{"?", "?"}, /* invokeinterface */
- {"?", "?"}, /* xxxunusedxxx */
+ {"?", "?"}, /* invokedynamic */
{"", "A"}, /* new */
{"I", "A"}, /* newarray */
{"I", "A"}, /* anewarray */
--- a/src/solaris/classes/sun/java2d/x11/X11SurfaceData.java Wed May 06 14:14:54 2009 -0700
+++ b/src/solaris/classes/sun/java2d/x11/X11SurfaceData.java Thu May 14 09:53:35 2009 -0700
@@ -530,6 +530,7 @@ public abstract class X11SurfaceData ext
sType = transparent ? X11SurfaceData.IntBgrX11_BM : X11SurfaceData.IntBgrX11;
}
} else {
+
throw new sun.java2d.InvalidPipeException("Unsupported bit " +
"depth/cm combo: " +
cm.getPixelSize() +
--- a/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Wed May 06 14:14:54 2009 -0700
+++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Thu May 14 09:53:35 2009 -0700
@@ -475,49 +475,40 @@ class WindowsAsynchronousSocketChannelIm
// get an OVERLAPPED structure (from the cache or allocate)
overlapped = ioCache.add(result);
- // synchronize on result to allow this thread handle the case
- // where the read completes immediately.
- synchronized (result) {
- int n = read0(handle, numBufs, readBufferArray, overlapped);
- if (n == IOStatus.UNAVAILABLE) {
- // I/O is pending
- pending = true;
- return;
+ // initiate read
+ int n = read0(handle, numBufs, readBufferArray, overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ pending = true;
+ return;
+ }
+ if (n == IOStatus.EOF) {
+ // input shutdown
+ enableReading();
+ if (scatteringRead) {
+ result.setResult((V)Long.valueOf(-1L));
+ } else {
+ result.setResult((V)Integer.valueOf(-1));
}
- // read completed immediately:
- // 1. update buffer position
- // 2. reset read flag
- // 3. release waiters
- if (n == 0) {
- n = -1;
- } else {
- updateBuffers(n);
- }
- enableReading();
-
- if (scatteringRead) {
- result.setResult((V)Long.valueOf(n));
- } else {
- result.setResult((V)Integer.valueOf(n));
- }
+ } else {
+ throw new InternalError("Read completed immediately");
}
} catch (Throwable x) {
- // failed to initiate read:
- // 1. reset read flag
- // 2. free resources
- // 3. release waiters
+ // failed to initiate read
+ // reset read flag before releasing waiters
enableReading();
- if (overlapped != 0L)
- ioCache.remove(overlapped);
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
if (!(x instanceof IOException))
x = new IOException(x);
result.setFailure(x);
} finally {
- if (prepared && !pending) {
- // return direct buffer(s) to cache if substituted
- releaseBuffers();
+ // release resources if I/O not pending
+ if (!pending) {
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ if (prepared)
+ releaseBuffers();
}
end();
}
@@ -721,7 +712,6 @@ class WindowsAsynchronousSocketChannelIm
@Override
@SuppressWarnings("unchecked")
public void run() {
- int n = -1;
long overlapped = 0L;
boolean prepared = false;
boolean pending = false;
@@ -736,56 +726,34 @@ class WindowsAsynchronousSocketChannelIm
// get an OVERLAPPED structure (from the cache or allocate)
overlapped = ioCache.add(result);
-
- // synchronize on result to allow this thread handle the case
- // where the read completes immediately.
- synchronized (result) {
- n = write0(handle, numBufs, writeBufferArray, overlapped);
- if (n == IOStatus.UNAVAILABLE) {
- // I/O is pending
- pending = true;
- return;
- }
-
- enableWriting();
-
- if (n == IOStatus.EOF) {
- // special case for shutdown output
- shutdown = true;
- throw new ClosedChannelException();
- }
-
- // write completed immediately:
- // 1. enable writing
- // 2. update buffer position
- // 3. release waiters
- updateBuffers(n);
-
- // result is a Long or Integer
- if (gatheringWrite) {
- result.setResult((V)Long.valueOf(n));
- } else {
- result.setResult((V)Integer.valueOf(n));
- }
- }
+ int n = write0(handle, numBufs, writeBufferArray, overlapped);
+ if (n == IOStatus.UNAVAILABLE) {
+ // I/O is pending
+ pending = true;
+ return;
+ }
+ if (n == IOStatus.EOF) {
+ // special case for shutdown output
+ shutdown = true;
+ throw new ClosedChannelException();
+ }
+ // write completed immediately
+ throw new InternalError("Write completed immediately");
} catch (Throwable x) {
+ // write failed. Enable writing before releasing waiters.
enableWriting();
-
- // failed to initiate read:
if (!shutdown && (x instanceof ClosedChannelException))
x = new AsynchronousCloseException();
if (!(x instanceof IOException))
x = new IOException(x);
result.setFailure(x);
-
- // release resources
- if (overlapped != 0L)
- ioCache.remove(overlapped);
-
} finally {
- if (prepared && !pending) {
- // return direct buffer(s) to cache if substituted
- releaseBuffers();
+ // release resources if I/O not pending
+ if (!pending) {
+ if (overlapped != 0L)
+ ioCache.remove(overlapped);
+ if (prepared)
+ releaseBuffers();
}
end();
}
--- a/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c Wed May 06 14:14:54 2009 -0700
+++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c Thu May 14 09:53:35 2009 -0700
@@ -157,14 +157,13 @@ Java_sun_nio_ch_WindowsAsynchronousSocke
WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address);
OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov);
BOOL res;
- DWORD nread = 0;
DWORD flags = 0;
ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));
res = WSARecv(s,
lpWsaBuf,
(DWORD)count,
- &nread,
+ NULL,
&flags,
lpOverlapped,
NULL);
@@ -175,17 +174,12 @@ Java_sun_nio_ch_WindowsAsynchronousSocke
return IOS_UNAVAILABLE;
}
if (error == WSAESHUTDOWN) {
- return 0; // input shutdown
+ return IOS_EOF; // input shutdown
}
JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed");
return IOS_THROWN;
}
- if (nread == 0) {
- // Handle graceful close or bytes not yet available cases
- // via completion port notification.
- return IOS_UNAVAILABLE;
- }
- return (jint)nread;
+ return IOS_UNAVAILABLE;
}
JNIEXPORT jint JNICALL
@@ -196,13 +190,12 @@ Java_sun_nio_ch_WindowsAsynchronousSocke
WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address);
OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov);
BOOL res;
- DWORD nwritten;
ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));
res = WSASend(s,
lpWsaBuf,
(DWORD)count,
- &nwritten,
+ NULL,
0,
lpOverlapped,
NULL);
@@ -218,5 +211,5 @@ Java_sun_nio_ch_WindowsAsynchronousSocke
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
return IOS_THROWN;
}
- return (jint)nwritten;
-}
+ return IOS_UNAVAILABLE;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/java/dyn/Makefile Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,44 @@
+#
+# Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+BUILDDIR = ../..
+
+PACKAGE = java.dyn
+PRODUCT = java
+include $(BUILDDIR)/common/Defs.gmk
+
+AUTO_FILES_JAVA_DIRS = java/dyn sun/dyn
+
+# The sources built here use new language syntax to generate
+# method handle calls. Let's be sure we are using that format.
+#LANGUAGE_VERSION = -source 7
+#CLASS_VERSION = -target 7
+
+# Actually, it will be less disruptive to compile with the same
+# -target option as the rest of the system, and just turn on
+# the specific compiler option we need here:
+OTHER_JAVACFLAGS = -XDinvokedynamic
+
+include $(BUILDDIR)/common/Classes.gmk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/CallSite.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import sun.dyn.util.BytecodeName;
+
+/**
+ * An {@code invokedynamic} call site, as reified by the
+ * containing class's bootstrap method.
+ * Every call site object corresponds to a distinct instance
+ * of the <code>invokedynamic</code> instruction, and vice versa.
+ * Every call site has one state variable, called the {@code target}.
+ * It is typed as a {@link MethodHandle}. This state is never null, and
+ * it is the responsibility of the bootstrap method to produce call sites
+ * which have been pre-linked to an initial target method.
+ * <p>
+ * (Note: The bootstrap method may elect to produce call sites of a
+ * language-specific subclass of {@code CallSite}. In such a case,
+ * the subclass may claim responsibility for initializing its target to
+ * a non-null value, by overriding {@link #initialTarget}.)
+ * <p>
+ * An {@code invokedynamic} instruction which has not yet been executed
+ * is said to be <em>unlinked</em>. When an unlinked call site is executed,
+ * the containing class's bootstrap method is called to manufacture a call site,
+ * for the instruction. If the bootstrap method does not assign a non-null
+ * value to the new call site's target variable, the method {@link #initialTarget}
+ * is called to produce the new call site's first target method.
+ * <p>
+ * @see Linkage#registerBootstrapMethod(java.lang.Class, java.dyn.MethodHandle)
+ * @author John Rose, JSR 292 EG
+ */
+public class CallSite {
+ // Fields used only by the JVM. Do not use or change.
+ private Object vmmethod;
+ int callerMID, callerBCI; // supplied by the JVM
+
+ MethodHandle target;
+ final Object caller; // usually a class
+ final String name;
+ final MethodType type;
+
+ /**
+ * Make a call site given the parameters from a call to the bootstrap method.
+ * The resulting call site is in an unlinked state, which means that before
+ * it is returned from a bootstrap method call it must be provided with
+ * a target method via a call to {@link CallSite#setTarget}.
+ * @param caller the class in which the relevant {@code invokedynamic} instruction occurs
+ * @param name the name specified by the {@code invokedynamic} instruction
+ * @param type the method handle type derived from descriptor of the {@code invokedynamic} instruction
+ */
+ public CallSite(Object caller, String name, MethodType type) {
+ this.caller = caller;
+ this.name = name;
+ this.type = type;
+ }
+
+ private static void privateInitializeCallSite(CallSite site, int callerMID, int callerBCI) {
+ site.callerMID = callerMID;
+ site.callerBCI = callerBCI;
+ if (site.target == null)
+ site.setTarget(site.initialTarget());
+ }
+
+ /**
+ * Just after a call site is created by a bootstrap method handle,
+ * if the target has not been initialized by the factory method itself,
+ * the method {@code initialTarget} is called to produce an initial
+ * non-null target. (Live call sites must never have null targets.)
+ * <p>
+ * If the bootstrap method itself does not initialize the call site,
+ * this method must be overridden, because it just raises an
+ * {@code InvokeDynamicBootstrapError}, which in turn causes the
+ * linkage of the {@code invokedynamic} instruction to terminate
+ * abnormally.
+ */
+ protected MethodHandle initialTarget() {
+ throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+this);
+ }
+
+ /**
+ * Report the current linkage state of the call site. (This is mutable.)
+ * The value maybe null only if the call site is currently unlinked.
+ * When a linked call site is invoked, the target method is used directly.
+ * When an unlinked call site is invoked, its bootstrap method receives
+ * the call, as if via {@link Linkage#bootstrapInvokeDynamic}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, the current thread may choose to reuse the result
+ * of a previous read of the target from memory, and may fail to see
+ * a recent update to the target by another thread.
+ * @return the current linkage state of the call site
+ * @see #setTarget
+ */
+ public MethodHandle getTarget() {
+ return target;
+ }
+
+ /**
+ * Link or relink the call site, by setting its target method.
+ * <p>
+ * The interactions of {@code setTarget} with memory are the same
+ * as of a write to an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, unrelated threads may fail to see the updated target
+ * until they perform a read from memory.
+ * Stronger guarantees can be created by putting appropriate operations
+ * into the bootstrap method and/or the target methods used
+ * at any given call site.
+ * @param target the new target, or null if it is to be unlinked
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the call site's {@link #type()}
+ */
+ public void setTarget(MethodHandle target) {
+ checkTarget(target);
+ this.target = target;
+ }
+
+ protected void checkTarget(MethodHandle target) {
+ target.type(); // provoke NPE
+ if (!canSetTarget(target))
+ throw new WrongMethodTypeException(String.valueOf(target));
+ }
+
+ protected boolean canSetTarget(MethodHandle target) {
+ return (target != null && target.type() == type());
+ }
+
+ /**
+ * Report the class containing the call site.
+ * This is an immutable property of the call site, set from the first argument to the constructor.
+ * @return class containing the call site
+ */
+ public Class<?> callerClass() {
+ return (Class) caller;
+ }
+
+ /**
+ * Report the method name specified in the {@code invokedynamic} instruction.
+ * This is an immutable property of the call site, set from the second argument to the constructor.
+ * <p>
+ * Note that the name is a JVM bytecode name, and as such can be any
+ * non-empty string, as long as it does not contain certain "dangerous"
+ * characters such as slash {@code '/'} and dot {@code '.'}.
+ * See the Java Virtual Machine specification for more details.
+ * <p>
+ * Application such as a language runtimes may need to encode
+ * arbitrary program element names and other configuration information
+ * into the name. A standard convention for doing this is
+ * <a href="http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm">specified here</a>.
+ * @return method name specified by the call site
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Report the method name specified in the {@code invokedynamic} instruction,
+ * as a series of components, individually demangled according to
+ * the standard convention
+ * <a href="http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm">specified here</a>.
+ * <p>
+ * Non-empty runs of characters between dangerous characters are demangled.
+ * Each component is either a completely arbitrary demangled string,
+ * or else a character constant for a punctuation character, typically ':'.
+ * (In principle, the character can be any dangerous character that the
+ * JVM lets through in a method name, such as '$' or ']'.
+ * Runtime implementors are encouraged to use colon ':' for building
+ * structured names.)
+ * <p>
+ * In the common case where the name contains no dangerous characters,
+ * the result is an array whose only element array is the demangled
+ * name at the call site. Such a demangled name can be any sequence
+ * of any number of any unicode characters.
+ * @return method name components specified by the call site
+ */
+ public Object[] nameComponents() {
+ return BytecodeName.parseBytecodeName(name);
+ }
+
+ /**
+ * Report the resolved result and parameter types of this call site,
+ * which are derived from its bytecode-level invocation descriptor.
+ * The types are packaged into a {@link MethodType}.
+ * Any linked target of this call site must be exactly this method type.
+ * This is an immutable property of the call site, set from the third argument to the constructor.
+ * @return method type specified by the call site
+ */
+ public MethodType type() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/InvokeDynamic.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * Syntactic marker to request javac to emit an {@code invokedynamic} instruction.
+ * An {@code invokedynamic} instruction is a 5-byte bytecoded instruction
+ * which begins with an opcode byte of value 186 ({@code 0xBA}),
+ * and is followed by a two-byte index of a {@code NameAndType} constant
+ * pool entry, then by two zero bytes. The constant pool reference gives
+ * the method name and argument and return types of the call site; there
+ * is no other information provided at the call site.
+ * <p>
+ * The {@code invokedynamic} instruction is incomplete without a target method.
+ * The target method is a property of the reified call site object
+ * (of type {@link CallSite}) which is in a one-to-one association with each
+ * corresponding {@code invokedynamic} instruction. The call site object
+ * is initially produced by a <em>bootstrap method</em> associated with
+ * the call site, via the various overloadings of {@link Linkage#registerBootstrapMethod}.
+ * <p>
+ * The type {@code InvokeDynamic} has no particular meaning as a
+ * class or interface supertype, or an object type; it can never be instantiated.
+ * Logically, it denotes a source of all dynamically typed methods.
+ * It may be viewed as a pure syntactic marker (an importable one) of static calls.
+ * @author John Rose, JSR 292 EG
+ */
+public final class InvokeDynamic {
+ private InvokeDynamic() { throw new InternalError(); } // do not instantiate
+
+ // no statically defined static methods
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * Thrown to indicate that an {@code invokedynamic} instruction has
+ * failed to find its bootstrap method, or the bootstrap method has
+ * failed to provide a call site with a non-null target.
+ * <p>
+ * The boostrap method must have been declared during a class's initialization
+ * by a call to {@link Linkage#registerBootstrapMethod}.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public class InvokeDynamicBootstrapError extends LinkageError {
+ /**
+ * Constructs a {@code InvokeDynamicBootstrapError} with no detail message.
+ */
+ public InvokeDynamicBootstrapError() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code InvokeDynamicBootstrapError} with the specified
+ * detail message.
+ *
+ * @param s the detail message.
+ */
+ public InvokeDynamicBootstrapError(String s) {
+ super(s);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/JavaMethodHandle.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * A Java method handle extends the basic method handle type with additional
+ * programmer defined methods and fields.
+ * Its behavior as a method handle is determined at instance creation time,
+ * by providing the new instance with an "entry point" method handle
+ * to handle calls. This entry point must accept a leading argument
+ * whose type is the Java method handle itself or a supertype, and the
+ * entry point is always called with the Java method handle itself as
+ * the first argument. This is similar to ordinary virtual methods, which also
+ * accept the receiver object {@code this} as an implicit leading argument.
+ * The {@code MethodType} of the Java method handle is the same as that
+ * of the entry point method handle, with the leading parameter type
+ * omitted.
+ * <p>
+ * Here is an example of usage:
+ * <p><blockquote><pre>
+ * class Greeter extends JavaMethodHandle {
+ * public void run() { System.out.println("hello, "+greetee); }
+ * private final String greetee;
+ * Greeter(String greetee) {
+ * super(RUN);
+ * this.greetee = greetee;
+ * }
+ * // the entry point function is computed once:
+ * private static final MethodHandle RUN
+ * = MethodHandles.findVirtual(MyMethodHandle.class, "run",
+ * MethodType.make(void.class));
+ * }
+ * Greeter greeter = new Greeter("world");
+ * greeter.run(); // prints "hello, world"
+ * MethodHandle mh = greeter;
+ * mh.invoke(); // also prints "hello, world"
+ * </pre></blockquote>
+ * <p>
+ * In this example, the method {@code run} provides the entry point.
+ * The entry point need not be a constant value; it may be independently
+ * computed in each call to the constructor. The entry point does not
+ * even need to be a method on the Java method handle class, though
+ * that is the typical case.
+ * @see MethodHandle
+ * @author John Rose, JSR 292 EG
+ */
+public abstract class JavaMethodHandle
+ // Note: This is an implementation inheritance hack, and will be removed
+ // with a JVM change which moves the required hidden behavior onto this class.
+ extends sun.dyn.BoundMethodHandle
+{
+ /**
+ * When creating a, pass in {@code entryPoint}, any method handle which
+ * can take the current object
+ * @param entryPoint
+ */
+ protected JavaMethodHandle(MethodHandle entryPoint) {
+ super(entryPoint, 0);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/Linkage.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.util.WeakHashMap;
+import sun.reflect.Reflection;
+import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege;
+
+/**
+ * Static methods which control the linkage of invokedynamic call sites.
+ * @author John Rose, JSR 292 EG
+ */
+public class Linkage {
+ private Linkage() {} // do not instantiate
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Register a <em>bootstrap method</em> to use when linking a given caller class.
+ * It must be a method handle of a type equivalent to {@link CallSite#CallSite}.
+ * In other words, it must act as a factory method which accepts the arguments
+ * to {@code CallSite}'s constructor (a class, a string, and a method type),
+ * and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}).
+ * <p>
+ * The registration will fail with an {@code IllegalStateException} if any of the following conditions hold:
+ * <ul>
+ * <li>The caller of this method is in a different package than the {@code callerClass},
+ * and there is a security manager, and its {@code checkPermission} call throws
+ * when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass).
+ * <li>The given class already has a bootstrap method from a previous
+ * call to this method.
+ * <li>The given class is already fully initialized.
+ * <li>The given class is in the process of initialization, in another thread.
+ * </ul>
+ * Because of these rules, a class may install its own bootstrap method in
+ * a static initializer.
+ */
+ public static
+ void registerBootstrapMethod(Class callerClass, MethodHandle mh) {
+ Class callc = Reflection.getCallerClass(2);
+ checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
+ checkBSM(mh);
+ synchronized (bootstrapMethods) {
+ if (bootstrapMethods.containsKey(callerClass))
+ throw new IllegalStateException("bootstrap method already declared in "+callerClass);
+ bootstrapMethods.put(callerClass, mh);
+ }
+ }
+
+ static void checkBSM(MethodHandle mh) {
+ if (mh == null) throw new IllegalArgumentException("null bootstrap method");
+ if (mh.type() == OLD_BOOTSTRAP_METHOD_TYPE) // FIXME: delete at EDR/PFD
+ throw new WrongMethodTypeException("bootstrap method must be a CallSite factory");
+ if (mh.type() != BOOTSTRAP_METHOD_TYPE)
+ throw new WrongMethodTypeException(mh.toString());
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Simplified version of registerBootstrapMethod for self-registration,
+ * to be called from a static initializer.
+ * Finds a static method of the required type in the
+ * given class, and installs it on the caller.
+ * @throws IllegalArgumentException if there is no such method
+ */
+ public static
+ void registerBootstrapMethod(Class<?> runtime, String name) {
+ Class callc = Reflection.getCallerClass(2);
+ MethodHandle bootstrapMethod =
+ MethodHandles.findStaticFrom(callc, runtime, name, BOOTSTRAP_METHOD_TYPE);
+ // FIXME: exception processing wrong here
+ checkBSM(bootstrapMethod);
+ Linkage.registerBootstrapMethod(callc, bootstrapMethod);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Simplified version of registerBootstrapMethod for self-registration,
+ * to be called from a static initializer.
+ * Finds a static method of the required type in the
+ * caller's class, and installs it on the caller.
+ * @throws IllegalArgumentException if there is no such method
+ */
+ public static
+ void registerBootstrapMethod(String name) {
+ Class callc = Reflection.getCallerClass(2);
+ MethodHandle bootstrapMethod =
+ MethodHandles.findStaticFrom(callc, callc, name, BOOTSTRAP_METHOD_TYPE);
+ // FIXME: exception processing wrong here
+ checkBSM(bootstrapMethod);
+ Linkage.registerBootstrapMethod(callc, bootstrapMethod);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Report the bootstrap method registered for a given class.
+ * Returns null if the class has never yet registered a bootstrap method,
+ * or if the class has explicitly registered a null bootstrap method.
+ * Only callers privileged to set the bootstrap method may inquire
+ * about it, because a bootstrap method is potentially a back-door entry
+ * point into its class.
+ */
+ public static
+ MethodHandle getBootstrapMethod(Class callerClass) {
+ Class callc = Reflection.getCallerClass(2);
+ checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod");
+ synchronized (bootstrapMethods) {
+ return bootstrapMethods.get(callerClass);
+ }
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * The type of any bootstrap method is a three-argument method
+ * {@code (Class, String, MethodType)} returning a {@code CallSite}.
+ */
+ public static final MethodType BOOTSTRAP_METHOD_TYPE
+ = MethodType.make(CallSite.class,
+ Class.class, String.class, MethodType.class);
+
+ private static final MethodType OLD_BOOTSTRAP_METHOD_TYPE
+ = MethodType.make(Object.class,
+ CallSite.class, Object[].class);
+
+ private static final WeakHashMap<Class, MethodHandle> bootstrapMethods =
+ new WeakHashMap<Class, MethodHandle>();
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Invalidate all <code>invokedynamic</code> call sites everywhere.
+ * <p>
+ * When this method returns, every <code>invokedynamic</code> instruction
+ * will invoke its bootstrap method on next call.
+ * <p>
+ * It is unspecified whether call sites already known to the Java
+ * code will continue to be associated with <code>invokedynamic</code>
+ * instructions. If any call site is still so associated, its
+ * {@link CallSite#getTarget()} method is guaranteed to return null
+ * the invalidation operation completes.
+ * <p>
+ * Invalidation operations are likely to be slow. Use them sparingly.
+ */
+ public static
+ Object invalidateAll() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(new LinkagePermission("invalidateAll"));
+ }
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Invalidate all <code>invokedynamic</code> call sites associated
+ * with the given class.
+ * (These are exactly those sites which report the given class
+ * via the {@link CallSite#callerClass()} method.)
+ * <p>
+ * When this method returns, every matching <code>invokedynamic</code>
+ * instruction will invoke its bootstrap method on next call.
+ * <p>
+ * For additional semantics of call site invalidation,
+ * see {@link #invalidateAll()}.
+ */
+ public static
+ Object invalidateCallerClass(Class<?> callerClass) {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(new LinkagePermission("invalidateAll", callerClass));
+ }
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ private static Object doNotBootstrap(CallSite site, Object... arguments) {
+ throw new UnsupportedOperationException("call site must not have null target: "+site);
+ }
+
+ private static final MethodHandle DO_NOT_BOOTSTRAP =
+ MethodHandles.Lookup.IMPL_LOOKUP.findStatic(Linkage.class, "doNotBootstrap",
+ OLD_BOOTSTRAP_METHOD_TYPE);
+
+ // Up-call from the JVM. Obsolete. FIXME: Delete from VM then from here.
+ static
+ MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) {
+ return DO_NOT_BOOTSTRAP;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/LinkagePermission.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.security.*;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+/**
+ * This class is for runtime permissions. A RuntimePermission
+ * contains a name (also referred to as a "target name") but
+ * no actions list; you either have the named permission
+ * or you don't.
+ *
+ * <P>
+ * The target name is the name of the runtime permission (see below). The
+ * naming convention follows the hierarchical property naming convention.
+ * Also, an asterisk
+ * may appear at the end of the name, following a ".", or by itself, to
+ * signify a wildcard match. For example: "loadLibrary.*" or "*" is valid,
+ * "*loadLibrary" or "a*b" is not valid.
+ * <P>
+ * The following table lists all the possible RuntimePermission target names,
+ * and for each provides a description of what the permission allows
+ * and a discussion of the risks of granting code the permission.
+ * <P>
+ *
+ * <table border=1 cellpadding=5 summary="permission target name,
+ * what the target allows,and associated risks">
+ * <tr>
+ * <th>Permission Target Name</th>
+ * <th>What the Permission Allows</th>
+ * <th>Risks of Allowing this Permission</th>
+ * </tr>
+ *
+ * <tr>
+ * <td>registerBootstrapMethod.{class name}</td>
+ * <td>Specifying a bootstrap method for invokedynamic, within a class of the given name</td>
+ * <td>An attacker could attempt to attach a bootstrap method to a class which
+ * has just been loaded, thus gaining control of its invokedynamic calls.</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>invalidateAll</td>
+ * <td>Force the relinking of invokedynamic call sites everywhere.</td>
+ * <td>This could allow an attacker to slow down the system, or perhaps surface timing bugs in a dynamic language implementations, by forcing redundant relinking operations.</td>
+ * </tr>
+ *
+ *
+ * <tr>
+ * <td>invalidateCallerClass.{class name}</td>
+ * <td>Force the relinking of invokedynamic call sites in the given class.</td>
+ * <td>See {@code invalidateAll}.</td>
+ * </tr>
+ * </table>
+ *
+ * @see java.security.BasicPermission
+ * @see java.lang.SecurityManager
+ *
+ * @author John Rose, JSR 292 EG
+ */
+
+public final class LinkagePermission extends BasicPermission {
+ /**
+ * Create a new LinkagePermission with the given name.
+ * The name is the symbolic name of the LinkagePermission, such as
+ * "registerBootstrapMethod", "invalidateClass.*", etc. An asterisk
+ * may appear at the end of the name, following a ".", or by itself, to
+ * signify a wildcard match.
+ *
+ * @param name the name of the LinkagePermission
+ */
+ public LinkagePermission(String name) {
+ super(name);
+ }
+
+ /**
+ * Create a new LinkagePermission with the given name on the given class.
+ * Equivalent to {@code LinkagePermission(name+"."+clazz.getName())}.
+ *
+ * @param name the name of the LinkagePermission
+ * @param clazz the class affected by the permission
+ */
+ public LinkagePermission(String name, Class<?> clazz) {
+ super(name + "." + clazz.getName());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/MethodHandle.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+//import sun.dyn.*;
+
+import sun.dyn.Access;
+import sun.dyn.MethodHandleImpl;
+
+/**
+ * A method handle is a typed reference to the entry point of a method.
+ * <p>
+ * Method handles are strongly typed according to signature.
+ * They are not distinguished by method name or enclosing class.
+ * A method handle must be invoked under a signature which exactly matches
+ * the method handle's own type.
+ * <p>
+ * Every method handle confesses its type via the <code>type</code> accessor.
+ * The structure of this type is a series of classes, one of which is
+ * the return type of the method (or <code>void.class</code> if none).
+ * <p>
+ * Every method handle appears as an object containing a method named
+ * <code>invoke</code>, whose signature exactly matches
+ * the method handle's type.
+ * A normal Java method call (using the <code>invokevirtual</code> instruction)
+ * can invoke this method from Java source code (if language support is present).
+ * <p>
+ * Every call to a method handle specifies an intended method type,
+ * which must exactly match the type of the method handle.
+ * (The type is specified in the <code>invokevirtual</code> instruction,
+ * via a {@code CONSTANT_NameAndType} constant pool entry.)
+ * The call looks within the receiver object for a method
+ * named <code>invoke</code> of the intended method type.
+ * The call fails with a {@link WrongMethodTypeException}
+ * if the method does not exist, even if there is an <code>invoke</code>
+ * method of a closely similar signature.
+ * <p>
+ * A method handle is an unrestricted capability to call a method.
+ * A method handle can be formed on a non-public method by a class
+ * that has access to that method; the resulting handle can be used
+ * in any place by any caller who receives a reference to it. Thus, access
+ * checking is performed when the method handle is created, not
+ * (as in reflection) every time it is called. Handles to non-public
+ * methods, or in non-public classes, should generally be kept secret.
+ * They should not be passed to untrusted code.
+ * <p>
+ * Bytecode in an extended JVM can directly call a method handle's
+ * <code>invoke</code> from an <code>invokevirtual</code> instruction.
+ * The receiver class type must be <code>MethodHandle</code> and the method name
+ * must be <code>invoke</code>. The signature of the invocation
+ * (after resolving symbolic type names) must exactly match the method type
+ * of the target method.
+ * <p>
+ * Bytecode in an extended JVM can directly obtain a method handle
+ * for any accessible method from a <code>ldc</code> instruction
+ * which refers to a <code>CONSTANT_Methodref</code> or
+ * <code>CONSTANT_InterfaceMethodref</code> constant pool entry.
+ * <p>
+ * All JVMs can also use a reflective API called <code>MethodHandles</code>
+ * for creating and calling method handles.
+ * <p>
+ * A method reference may refer either to a static or non-static method.
+ * In the non-static case, the method handle type includes an explicit
+ * receiver argument, prepended before any other arguments.
+ * In the method handle's type, the initial receiver argument is typed
+ * according to the class under which the method was initially requested.
+ * (E.g., if a non-static method handle is obtained via <code>ldc</code>,
+ * the type of the receiver is the class named in the constant pool entry.)
+ * <p>
+ * When a method handle to a virtual method is invoked, the method is
+ * always looked up in the receiver (that is, the first argument).
+ * <p>
+ * A non-virtual method handles to a specific virtual method implementation
+ * can also be created. These do not perform virtual lookup based on
+ * receiver type. Such a method handle simulates the effect of
+ * an <code>invokespecial</code> instruction to the same method.
+ *
+ * @see MethodType
+ * @see MethodHandles
+ * @author John Rose, JSR 292 EG
+ */
+public abstract class MethodHandle
+ // Note: This is an implementation inheritance hack, and will be removed
+ // with a JVM change which moves the required hidden state onto this class.
+ extends MethodHandleImpl
+{
+ // interface MethodHandle<T extends MethodType<R,A...>>
+ // { T type(); <R,A...> public R invoke(A...); }
+
+ final private MethodType type;
+
+ /**
+ * Report the type of this method handle.
+ * Every invocation of this method handle must exactly match this type.
+ * @return the method handle type
+ */
+ public MethodType type() {
+ return type;
+ }
+
+ /**
+ * The constructor for MethodHandle may only be called by privileged code.
+ * Subclasses may be in other packages, but must possess
+ * a token which they obtained from MH with a security check.
+ * @param token non-null object which proves access permission
+ * @param type type (permanently assigned) of the new method handle
+ */
+ protected MethodHandle(Access token, MethodType type) {
+ super(token);
+ this.type = type;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/MethodHandles.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,1104 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.lang.reflect.Constructor;
+import sun.dyn.Access;
+import sun.dyn.MemberName;
+import sun.dyn.MethodHandleImpl;
+import sun.dyn.util.VerifyAccess;
+import sun.dyn.util.Wrapper;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import sun.dyn.Invokers;
+import sun.dyn.MethodTypeImpl;
+import sun.reflect.Reflection;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+import static sun.dyn.MemberName.newNoAccessException;
+
+/**
+ * Fundamental operations and utilities for MethodHandle.
+ * <p>
+ * <em>API Note:</em> The matching of method types in this API cannot
+ * be completely checked by Java's generic type system for three reasons:
+ * <ol>
+ * <li>Method types range over all possible arities,
+ * from no arguments to an arbitrary number of arguments.
+ * Generics are not variadic, and so cannot represent this.</li>
+ * <li>Method types can specify arguments of primitive types,
+ * which Java generic types cannot range over.</li>
+ * <li>Method types can optionally specify varargs (ellipsis).</li>
+ * </ol>
+ * @author John Rose, JSR 292 EG
+ */
+public class MethodHandles {
+
+ private MethodHandles() { } // do not instantiate
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+ private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
+ static { MethodHandleImpl.initStatics(); }
+ // See IMPL_LOOKUP below.
+
+ //// Method handle creation from ordinary methods.
+
+ public static Lookup lookup() {
+ return new Lookup();
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * A factory object for creating method handles, when the creation
+ * requires access checking. Method handles do not perform
+ * access checks when they are called; this is a major difference
+ * from reflective {@link Method}, which performs access checking
+ * against every caller, on every call. Method handle access
+ * restrictions are enforced when a method handle is created.
+ * The caller class against which those restrictions are enforced
+ * is known as the "lookup class". {@link Lookup} embodies an
+ * authenticated lookup class, and can be used to create any number
+ * of access-checked method handles, all checked against a single
+ * lookup class.
+ * <p>
+ * A class which needs to create method handles will call
+ * {@code MethodHandles.lookup()} to create a factory for itself.
+ * It may then use this factory to create method handles on
+ * all of its methods, including private ones.
+ * It may also delegate the lookup (e.g., to a metaobject protocol)
+ * by passing the {@code Lookup} object to other code.
+ * If this other code creates method handles, they will be access
+ * checked against the original lookup class, and not with any higher
+ * privileges.
+ * <p>
+ * Note that access checks only apply to named and reflected methods.
+ * Other method handle creation methods, such as {@link #convertArguments},
+ * do not require any access checks, and can be done independently
+ * of any lookup class.
+ * <p>
+ * <em>A note about error conditions:<em> A lookup can fail, because
+ * the containing class is not accessible to the lookup class, or
+ * because the desired class member is missing, or because the
+ * desired class member is not accessible to the lookup class.
+ * It can also fail if a security manager is installed and refuses
+ * access. In any of these cases, an exception will be
+ * thrown from the attempted lookup.
+ * In general, the conditions under which a method handle may be
+ * created for a method {@code M} are exactly as restrictive as the conditions
+ * under which the lookup class could have compiled a call to {@code M}.
+ * At least some of these error conditions are likely to be
+ * represented by checked exceptions in the final version of this API.
+ */
+ public static final
+ class Lookup {
+ private final Class<?> lookupClass;
+
+ /** Which class is performing the lookup? It is this class against
+ * which checks are performed for visibility and access permissions.
+ * <p>
+ * This value is null if and only if this lookup is {@link #PUBLIC_LOOKUP}.
+ */
+ public Class<?> lookupClass() {
+ return lookupClass;
+ }
+
+ /** Embody the current class (the lookupClass) as a lookup class
+ * for method handle creation.
+ * Must be called by from a method in this package,
+ * which in turn is called by a method not in this package.
+ * Also, don't make it private, lest javac interpose
+ * an access$N method.
+ */
+ Lookup() {
+ Class caller = getCallerClassAtEntryPoint();
+ // make sure we haven't accidentally picked up this class:
+ checkUnprivilegedlookupClass(caller);
+ this.lookupClass = caller;
+ }
+
+ private Lookup(Class<?> lookupClass) {
+ this.lookupClass = lookupClass;
+ }
+
+ private static final Class<?> PUBLIC_ONLY = sun.dyn.empty.Empty.class;
+
+ /** Version of lookup which is trusted minimally.
+ * It can only be used to create method handles to
+ * publicly accessible members.
+ */
+ public static final Lookup PUBLIC_LOOKUP = new Lookup(PUBLIC_ONLY);
+
+ /** Package-private version of lookup which is trusted. */
+ static final Lookup IMPL_LOOKUP = new Lookup(null);
+ static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); }
+
+ private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
+ String name = lookupClass.getName();
+ if (name.startsWith("java.dyn.") || name.startsWith("sun.dyn."))
+ throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
+ }
+
+ @Override
+ public String toString() {
+ if (lookupClass == PUBLIC_ONLY)
+ return "public";
+ if (lookupClass == null)
+ return "privileged";
+ return lookupClass.getName();
+ }
+
+ // call this from an entry point method in Lookup with extraFrames=0.
+ private static Class<?> getCallerClassAtEntryPoint() {
+ final int CALLER_DEPTH = 4;
+ // 0: Reflection.getCC, 1: getCallerClassAtEntryPoint,
+ // 2: Lookup.<init>, 3: MethodHandles.*, 4: caller
+ // Note: This should be the only use of getCallerClass in this file.
+ return Reflection.getCallerClass(CALLER_DEPTH);
+ }
+
+ /**
+ * Produce a method handle for a static method.
+ * The type of the method handle will be that of the method.
+ * The method and all its argument types must be accessible to the lookup class.
+ * If the method's class has not yet been initialized, that is done
+ * immediately, before the method handle is returned.
+ * @param defc the class from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method
+ * @return the desired method handle
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public
+ MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
+ checkStatic(true, method, lookupClass);
+ //throw NoSuchMethodException
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
+ }
+
+ /**
+ * Produce a method handle for a virtual method.
+ * The type of the method handle will be that of the method,
+ * with the receiver type ({@code defc}) prepended.
+ * The method and all its argument types must be accessible to the lookup class.
+ * <p>
+ * (<em>BUG NOTE:</em> The type {@code Object} may be prepended instead
+ * of the receiver type, if the receiver type is not on the boot class path.
+ * This is due to a temporary JVM limitation, in which MethodHandle
+ * claims to be unable to access such classes. To work around this
+ * bug, use {@code convertArguments} to normalize the type of the leading
+ * argument to a type on the boot class path, such as {@code Object}.)
+ * <p>
+ * When called, the handle will treat the first argument as a receiver
+ * and dispatch on the receiver's type to determine which method
+ * implementation to enter.
+ * (The dispatching action is identical with that performed by an
+ * {@code invokevirtual} or {@code invokeinterface} instruction.)
+ * @param defc the class or interface from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method, with the receiver argument omitted
+ * @return the desired method handle
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), true, lookupClass);
+ checkStatic(false, method, lookupClass);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
+ }
+
+ /**
+ * Produce an early-bound method handle for a virtual method,
+ * as if called from an {@code invokespecial}
+ * instruction from {@code caller}.
+ * The type of the method handle will be that of the method,
+ * with a suitably restricted receiver type (such as {@code caller}) prepended.
+ * The method and all its argument types must be accessible
+ * to the caller.
+ * <p>
+ * When called, the handle will treat the first argument as a receiver,
+ * but will not dispatch on the receiver's type.
+ * (This direct invocation action is identical with that performed by an
+ * {@code invokespecial} instruction.)
+ * <p>
+ * If the explicitly specified caller class is not identical with the
+ * lookup class, a security check TBD is performed.
+ * @param defc the class or interface from which the method is accessed
+ * @param name the name of the method, or "<init>" for a constructor
+ * @param type the type of the method, with the receiver argument omitted
+ * @param specialCaller the proposed calling class to perform the {@code invokespecial}
+ * @return the desired method handle
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public MethodHandle findSpecial(Class<?> defc, String name, MethodType type,
+ Class<?> specialCaller) throws NoAccessException {
+ checkSpecialCaller(specialCaller, lookupClass);
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
+ checkStatic(false, method, lookupClass);
+ if (name.equals("<init>")) {
+ throw newNoAccessException("cannot directly invoke a constructor", method, null);
+ } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
+ throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass);
+ }
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller);
+ }
+
+ /**
+ * Produce an early-bound method handle for a non-static method.
+ * The receiver must have a supertype {@code defc} in which a method
+ * of the given name and type is accessible to the lookup class.
+ * The method and all its argument types must be accessible to the lookup class.
+ * The type of the method handle will be that of the method.
+ * The given receiver will be bound into the method handle.
+ * <p>
+ * Equivalent to the following expression:
+ * <code>
+ * {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver)
+ * </code>
+ * @param receiver the object from which the method is accessed
+ * @param name the name of the method
+ * @param type the type of the method, with the receiver argument omitted
+ * @return the desired method handle
+ * @exception SecurityException <em>TBD</em>
+ * @exception NoAccessException if the method does not exist or access checking fails
+ */
+ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
+ Class<? extends Object> rcvc = receiver.getClass(); // may get NPE
+ MemberName reference = new MemberName(rcvc, name, type);
+ MemberName method = IMPL_NAMES.resolveOrFail(reference, true, lookupClass);
+ checkStatic(false, method, lookupClass);
+ MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
+ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
+ if (bmh == null)
+ throw newNoAccessException(method, lookupClass);
+ return bmh;
+ }
+
+ /**
+ * Make a direct method handle to <i>m</i>, if the lookup class has permission.
+ * If <i>m</i> is non-static, the receiver argument is treated as an initial argument.
+ * If <i>m</i> is virtual, overriding is respected on every call.
+ * Unlike the Core Reflection API, exceptions are <em>not</em> wrapped.
+ * The type of the method handle will be that of the method,
+ * with the receiver type prepended (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
+ * @param m the reflected method
+ * @return a method handle which can invoke the reflected method
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflect(Method m) throws NoAccessException {
+ return unreflectImpl(new MemberName(m), m.isAccessible(), true, lookupClass);
+ }
+
+ /**
+ * Produce a method handle for a reflected method.
+ * It will bypass checks for overriding methods on the receiver,
+ * as if by the {@code invokespecial} instruction.
+ * The type of the method handle will be that of the method,
+ * with the receiver type prepended.
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class,
+ * as if {@code invokespecial} instruction were being linked.
+ * @param m the reflected method
+ * @return a method handle which can invoke the reflected method
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
+ checkSpecialCaller(specialCaller, lookupClass);
+ MemberName mname = new MemberName(m);
+ checkStatic(false, mname, lookupClass);
+ return unreflectImpl(mname, m.isAccessible(), false, specialCaller);
+ }
+
+ /**
+ * Produce a method handle for a reflected constructor.
+ * The type of the method handle will be that of the constructor.
+ * The method handle will perform a {@code newInstance} operation,
+ * creating a new instance of the constructor's class on the
+ * arguments passed to the method handle.
+ * <p>
+ * If the constructor's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class,
+ * as if {@code invokespecial} instruction were being linked.
+ * @param ctor the reflected constructor
+ * @return a method handle which can invoke the reflected constructor
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectConstructor(Constructor ctor) throws NoAccessException {
+ MemberName m = new MemberName(ctor);
+ return unreflectImpl(m, ctor.isAccessible(), false, lookupClass);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle giving read access to a reflected field.
+ * The type of the method handle will have a return type of the field's
+ * value type. Its sole argument will be the field's containing class
+ * (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * @param f the reflected field
+ * @return a method handle which can load values from the reflected field
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectGetter(Field f) throws NoAccessException {
+ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, lookupClass);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle giving write access to a reflected field.
+ * The type of the method handle will have a void return type.
+ * Its last argument will be the field's value type.
+ * Its other argument will be the field's containing class
+ * (but only if it is non-static).
+ * If the method's {@code accessible} flag is not set,
+ * access checking is performed immediately on behalf of the lookup class.
+ * @param f the reflected field
+ * @return a method handle which can store values into the reflected field
+ * @exception NoAccessException if access checking fails
+ */
+ public MethodHandle unreflectSetter(Field f) throws NoAccessException {
+ return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, lookupClass);
+ }
+
+ }
+
+ static /*must not be public*/
+ MethodHandle findStaticFrom(Class<?> lookupClass,
+ Class<?> defc, String name, MethodType type) throws NoAccessException {
+ MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
+ checkStatic(true, method, lookupClass);
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
+ }
+
+ static void checkStatic(boolean wantStatic, MemberName m, Class<?> lookupClass) {
+ if (wantStatic != m.isStatic()) {
+ String message = wantStatic ? "expected a static method" : "expected a non-static method";
+ throw newNoAccessException(message, m, lookupClass);
+ }
+ }
+
+ static void checkSpecialCaller(Class<?> specialCaller, Class<?> lookupClass) {
+ if (lookupClass == Lookup.IMPL_LOOKUP.lookupClass())
+ return; // privileged action
+ if (lookupClass == null || // public-only access
+ !VerifyAccess.isSamePackageMember(specialCaller, lookupClass))
+ throw newNoAccessException("no private access", new MemberName(specialCaller), lookupClass);
+ }
+
+ // Helper for creating handles on reflected methods and constructors.
+ static MethodHandle unreflectImpl(MemberName m, boolean isAccessible,
+ boolean doDispatch, Class<?> lookupClass) {
+ MethodType mtype = m.getInvocationType();
+ Class<?> defc = m.getDeclaringClass();
+ int mods = m.getModifiers();
+ if (m.isStatic()) {
+ if (!isAccessible &&
+ VerifyAccess.isAccessible(defc, mods, false, lookupClass) == null)
+ throw newNoAccessException(m, lookupClass);
+ } else {
+ Class<?> constraint;
+ if (isAccessible) {
+ // abbreviated access check for "unlocked" method
+ constraint = doDispatch ? defc : lookupClass;
+ } else {
+ constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, lookupClass);
+ }
+ if (constraint != defc && !constraint.isAssignableFrom(defc)) {
+ if (!defc.isAssignableFrom(constraint))
+ throw newNoAccessException("receiver must be in caller class", m, lookupClass);
+ mtype = mtype.changeParameterType(0, constraint);
+ }
+ }
+ return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, lookupClass);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle giving read access to elements of an array.
+ * The type of the method handle will have a return type of the array's
+ * element type. Its first argument will be the array type,
+ * and the second will be {@code int}.
+ * @param arrayClass an array type
+ * @return a method handle which can load values from the given array type
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
+ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle giving write access to elements of an array.
+ * The type of the method handle will have a void return type.
+ * Its last argument will be the array's element type.
+ * The first and second arguments will be the array type and int.
+ * @return a method handle which can store values into the array type
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
+ return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true);
+ }
+
+
+ /// method handle invocation (reflective style)
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Call the {@code invoke} method of a given method handle,
+ * with arguments that exactly match the parameter types of the method handle.
+ * The length of the arguments array must equal the parameter count
+ * of the target's type.
+ * The arguments array is spread into separate arguments, and
+ * basic reference and unboxing conversions are applied.
+ * <p>
+ * In order to match the type of the target, the following argument
+ * conversions are applied as necessary:
+ * <ul>
+ * <li>reference casting
+ * <li>unboxing
+ * </ul>
+ * The following conversions are not applied:
+ * <ul>
+ * <li>primitive conversions (e.g., {@code byte} to {@code int}
+ * <li>varargs conversions other than the initial spread
+ * <li>any application-specific conversions (e.g., string to number)
+ * </ul>
+ * The result returned by the call is boxed if it is a primitive,
+ * or forced to null if the return type is void.
+ * <p>
+ * This call is a convenience method for the following code:
+ * <pre>
+ * MethodHandle invoker = MethodHandles.genericInvoker(target.type(), 0, true);
+ * Object result = invoker.invoke(arguments);
+ * </pre>
+ * @param target the method handle to invoke
+ * @param arguments the arguments to pass to the target
+ * @return the result returned by the target
+ */
+ public static
+ Object invoke(MethodHandle target, Object... arguments) {
+ int argc = arguments == null ? 0 : arguments.length;
+ MethodType type = target.type();
+ if (argc <= 4) {
+ MethodHandle invoker = invokers(type).genericInvoker();
+ switch (argc) {
+ case 0: return invoker.<Object>invoke(target);
+ case 1: return invoker.<Object>invoke(target,
+ arguments[0]);
+ case 2: return invoker.<Object>invoke(target,
+ arguments[0], arguments[1]);
+ case 3: return invoker.<Object>invoke(target,
+ arguments[0], arguments[1], arguments[2]);
+ case 4: return invoker.<Object>invoke(target,
+ arguments[0], arguments[1], arguments[2], arguments[3]);
+ }
+ }
+ MethodHandle invoker = invokers(type).varargsInvoker();
+ return invoker.<Object>invoke(target, arguments);
+ }
+
+ public static
+ Object invoke_0(MethodHandle target) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.<Object>invoke(target);
+ }
+ public static
+ Object invoke_1(MethodHandle target, Object a0) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.<Object>invoke(target, a0);
+ }
+ public static
+ Object invoke_2(MethodHandle target, Object a0, Object a1) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.<Object>invoke(target, a0, a1);
+ }
+ public static
+ Object invoke_3(MethodHandle target, Object a0, Object a1, Object a2) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.<Object>invoke(target, a0, a1, a2);
+ }
+ public static
+ Object invoke_4(MethodHandle target, Object a0, Object a1, Object a2, Object a3) {
+ MethodHandle invoker = invokers(target.type()).genericInvoker();
+ return invoker.<Object>invoke(target, a0, a1, a2, a3);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Give a method handle which will invoke any method handle of the
+ * given type on a standard set of {@code Object} type arguments.
+ * The the resulting invoker will be a method handle with the following
+ * arguments:
+ * <ul>
+ * <li>a single {@code MethodHandle} target
+ * <li>zero or more {@code Object} values
+ * <li>an optional {@code Object[]} array containing more arguments
+ * </ul>
+ * The invoker will spread the varargs array (if present), apply
+ * reference casts as necessary, and unbox primitive arguments.
+ * The return value of the invoker will be an {@code Object} reference,
+ * boxing a primitive value if the original type returns a primitive,
+ * and always null if the original type returns void.
+ * <p>
+ * This is a convenience method equivalent to the following code:
+ * <pre>
+ * MethodHandle invoker = exactInvoker(type);
+ * MethodType genericType = MethodType.makeGeneric(objectArgCount, varargs);
+ * genericType = genericType.insertParameterType(0, MethodHandle.class);
+ * if (!varargs)
+ * return convertArguments(invoker, genericType);
+ * else
+ * return spreadArguments(invoker, genericType);
+ * </pre>
+ * @param type the desired target type
+ * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
+ * @param varargs if true, the invoker will accept a final {@code Object[]} argument
+ * @return a method handle suitable for invoking any method handle of the given type
+ */
+ static public
+ MethodHandle genericInvoker(MethodType type, int objectArgCount, boolean varargs) {
+ return invokers(type).genericInvoker();
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Give a method handle which will take a invoke any method handle of the
+ * given type. The resulting invoker will have a type which is
+ * exactly equal to the desired type, except that it will accept
+ * an additional leading argument of type {@code MethodHandle}.
+ * <p>
+ * This is a convenience method equivalent to the following code:
+ * <pre>
+ * MethodHandles.lookup().findVirtual(MethodHandle.class, "invoke", type);
+ * </pre>
+ * @param type the desired target type
+ * @return a method handle suitable for invoking any method handle of the given type
+ */
+ static public
+ MethodHandle exactInvoker(MethodType type) {
+ return invokers(type).exactInvoker();
+ }
+
+ static private Invokers invokers(MethodType type) {
+ return MethodTypeImpl.invokers(IMPL_TOKEN, type);
+ }
+
+ /**
+ * <em>WORK IN PROGRESS:</em>
+ * Perform value checking, exactly as if for an adapted method handle.
+ * It is assumed that the given value is either null, of type T0,
+ * or (if T0 is primitive) of the wrapper type corresponding to T0.
+ * The following checks and conversions are made:
+ * <ul>
+ * <li>If T0 and T1 are references, then a cast to T1 is applied.
+ * (The types do not need to be related in any particular way.)
+ * <li>If T0 and T1 are primitives, then a widening or narrowing
+ * conversion is applied, if one exists.
+ * <li>If T0 is a primitive and T1 a reference, and
+ * T0 has a wrapper type TW, a boxing conversion to TW is applied,
+ * possibly followed by a reference conversion.
+ * T1 must be TW or a supertype.
+ * <li>If T0 is a reference and T1 a primitive, and
+ * T1 has a wrapper type TW, an unboxing conversion is applied,
+ * possibly preceded by a reference conversion.
+ * T0 must be TW or a supertype.
+ * <li>If T1 is void, the return value is discarded
+ * <li>If T0 is void and T1 a reference, a null value is introduced.
+ * <li>If T0 is void and T1 a primitive, a zero value is introduced.
+ * </ul>
+ * If the value is discarded, null will be returned.
+ * @param valueType
+ * @param value
+ * @return the value, converted if necessary
+ * @throws java.lang.ClassCastException if a cast fails
+ */
+ static
+ <T0, T1> T1 checkValue(Class<T0> t0, Class<T1> t1, Object value)
+ throws ClassCastException
+ {
+ if (t0 == t1) {
+ // no conversion needed; just reassert the same type
+ if (t0.isPrimitive())
+ return Wrapper.asPrimitiveType(t1).cast(value);
+ else
+ return Wrapper.OBJECT.cast(value, t1);
+ }
+ boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive();
+ if (!prim0) {
+ // check contract with caller
+ Wrapper.OBJECT.cast(value, t0);
+ if (!prim1) {
+ return Wrapper.OBJECT.cast(value, t1);
+ }
+ // convert reference to primitive by unboxing
+ Wrapper w1 = Wrapper.forPrimitiveType(t1);
+ return w1.cast(value, t1);
+ }
+ // check contract with caller:
+ Wrapper.asWrapperType(t0).cast(value);
+ Wrapper w1 = Wrapper.forPrimitiveType(t1);
+ return w1.cast(value, t1);
+ }
+
+ static
+ Object checkValue(Class<?> T1, Object value)
+ throws ClassCastException
+ {
+ Class<?> T0;
+ if (value == null)
+ T0 = Object.class;
+ else
+ T0 = value.getClass();
+ return checkValue(T0, T1, value);
+ }
+
+ /// method handle modification (creation from other method handles)
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which adapts the type of the
+ * given method handle to a new type, by pairwise argument conversion,
+ * and/or varargs conversion.
+ * The original type and new type must have the same number of
+ * arguments, or else one or both them the must be varargs types.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type, with any varargs property erased.
+ * <p>
+ * If the original type and new type are equal, returns target.
+ * <p>
+ * The following conversions are applied as needed both to
+ * arguments and return types. Let T0 and T1 be the differing
+ * new and old parameter types (or old and new return types)
+ * for corresponding values passed by the new and old method types.
+ * <p>
+ * If an ordinary (non-varargs) parameter of the new type is
+ * to be boxed in a varargs parameter of the old type of type T1[],
+ * then T1 is the element type of the varargs array.
+ * Otherwise, if a varargs parameter of the new type of type T0[]
+ * is to be spread into one or more outgoing old type parameters,
+ * then T0 is the element type of the
+ * If the new type is varargs and the old type is not, the varargs
+ * argument will be checked and must be a non-null array of exactly
+ * the right length. If there are no parameters in the old type
+ * corresponding to the new varargs parameter, the varargs argument
+ * is also allowed to be null.
+ * <p>
+ * Given those types T0, T1, one of the following conversions is applied
+ * if possible:
+ * <ul>
+ * <li>If T0 and T1 are references, then a cast to T2 is applied,
+ * where T2 is Object if T1 is an interface, else T1.
+ * (The types do not need to be related in any particular way.
+ * The treatment of interfaces follows the usage of the bytecode verifier.)
+ * <li>If T0 and T1 are primitives, then a Java casting
+ * conversion (JLS 5.5) is applied, if one exists.
+ * <li>If T0 and T1 are primitives and one is boolean,
+ * the boolean is treated as a one-bit unsigned integer.
+ * (This treatment follows the usage of the bytecode verifier.)
+ * A conversion from another primitive type behaves as if
+ * it first converts to byte, and then masks all but the low bit.
+ * <li>If T0 is a primitive and T1 a reference, a boxing
+ * conversion is applied if one exists, possibly followed by
+ * an reference conversion to a superclass.
+ * T1 must be a wrapper class or a supertype of one.
+ * If T1 is a wrapper class, T0 is converted if necessary
+ * to T1's primitive type by one of the preceding conversions.
+ * Otherwise, T0 is boxed, and its wrapper converted to T1.
+ * <li>If T0 is a reference and T1 a primitive, an unboxing
+ * conversion is applied if one exists, possibly preceded by
+ * a reference conversion to a wrapper class.
+ * T0 must be a wrapper class or a supertype of one.
+ * If T0 is a wrapper class, its primitive value is converted
+ * if necessary to T1 by one of the preceding conversions.
+ * Otherwise, T0 is converted directly to the wrapper type for T1,
+ * which is then unboxed.
+ * <li>If T1 is void, any returned value is discarded
+ * <li>If T0 is void and T1 a reference, a null value is introduced.
+ * <li>If T0 is void and T1 a primitive, a zero value is introduced.
+ * </ul>
+ * @param target the method handle to invoke after arguments are retyped
+ * @param newType the expected type of the new method handle
+ * @return a method handle which delegates to {@code target} after performing
+ * any necessary argument conversions, and arranges for any
+ * necessary return value conversions
+ * @throws WrongMethodTypeException if the conversion cannot be made
+ */
+ public static
+ MethodHandle convertArguments(MethodHandle target, MethodType newType) {
+ MethodType oldType = target.type();
+ if (oldType.equals(newType))
+ return target;
+ MethodHandle res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
+ newType, oldType, null);
+ if (res == null)
+ throw newIllegalArgumentException("cannot convert to "+newType+": "+target);
+ return res;
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which adapts the calling sequence of the
+ * given method handle to a new type, by reordering the arguments.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ * <p>
+ * The given array controls the reordering.
+ * Call {@code #I} the number of incoming parameters (the value
+ * {@code newType.parameterCount()}, and call {@code #O} the number
+ * of outgoing parameters (the value {@code target.type().parameterCount()}).
+ * Then the length of the reordering array must be {@code #O},
+ * and each element must be a non-negative number less than {@code #I}.
+ * For every {@code N} less than {@code #O}, the {@code N}-th
+ * outgoing argument will be taken from the {@code I}-th incoming
+ * argument, where {@code I} is {@code reorder[N]}.
+ * <p>
+ * The reordering array need not specify an actual permutation.
+ * An incoming argument will be duplicated if its index appears
+ * more than once in the array, and an incoming argument will be dropped
+ * if its index does not appear in the array.
+ * <p>
+ * Pairwise conversions are applied as needed to arguments and return
+ * values, as with {@link #convertArguments}.
+ * @param target the method handle to invoke after arguments are reordered
+ * @param newType the expected type of the new method handle
+ * @param reorder a string which controls the reordering
+ * @return a method handle which delegates to {@code target} after performing
+ * any necessary argument motion and conversions, and arranges for any
+ * necessary return value conversions
+ */
+ public static
+ MethodHandle permuteArguments(MethodHandle target, MethodType newType, int[] reorder) {
+ MethodType oldType = target.type();
+ checkReorder(reorder, newType, oldType);
+ return MethodHandleImpl.convertArguments(IMPL_TOKEN, target,
+ newType, oldType,
+ reorder);
+ }
+
+ private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
+ if (reorder.length == oldType.parameterCount()) {
+ int limit = newType.parameterCount();
+ boolean bad = false;
+ for (int i : reorder) {
+ if (i < 0 || i >= limit) {
+ bad = true; break;
+ }
+ }
+ if (!bad) return;
+ }
+ throw newIllegalArgumentException("bad reorder array");
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which adapts the type of the
+ * given method handle to a new type, by spreading the final argument.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ * <p>
+ * The final parameter type of the new type must be an array type T[].
+ * This is the type of what is called the <i>spread</i> argument.
+ * All other arguments of the new type are called <i>ordinary</i> arguments.
+ * <p>
+ * The ordinary arguments of the new type are pairwise converted
+ * to the initial parameter types of the old type, according to the
+ * rules in {@link #convertArguments}.
+ * Any additional arguments in the old type
+ * are converted from the array element type T,
+ * again according to the rules in {@link #convertArguments}.
+ * The return value is converted according likewise.
+ * <p>
+ * The call verifies that the spread argument is in fact an array
+ * of exactly the type length, i.e., the excess number of
+ * arguments in the old type over the ordinary arguments in the new type.
+ * If there are no excess arguments, the spread argument is also
+ * allowed to be null.
+ * @param target the method handle to invoke after the argument is prepended
+ * @param newType the expected type of the new method handle
+ * @return a new method handle which spreads its final argument,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
+ MethodType oldType = target.type();
+ int inargs = newType.parameterCount();
+ int outargs = oldType.parameterCount();
+ int spreadPos = inargs - 1;
+ int numSpread = (outargs - spreadPos);
+ MethodHandle res = null;
+ if (spreadPos >= 0 && numSpread >= 0) {
+ res = MethodHandleImpl.spreadArguments(IMPL_TOKEN, target, newType, spreadPos);
+ }
+ if (res == null) {
+ throw newIllegalArgumentException("cannot spread "+newType+" to " +oldType);
+ }
+ return res;
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which adapts the type of the
+ * given method handle to a new type, by collecting a series of
+ * trailing arguments into an array.
+ * The resulting method handle is guaranteed to confess a type
+ * which is equal to the desired new type.
+ * <p>
+ * This method is inverse to {@link #spreadArguments}.
+ * The final parameter type of the old type must be an array type T[],
+ * which is the type of what is called the <i>spread</i> argument.
+ * The trailing arguments of the new type which correspond to
+ * the spread argument are all converted to type T and collected
+ * into an array before the original method is called.
+ * <p>
+ * ISSUE: Unify this with combineArguments. CollectArguments
+ * is combineArguments with (a) new Object[]{...} as a combiner,
+ * and (b) the combined arguments dropped, in favor of the combined result.
+ * @param target the method handle to invoke after the argument is prepended
+ * @param newType the expected type of the new method handle
+ * @return a new method handle which collects some trailings argument
+ * into an array, before calling the original method handle
+ */
+ public static
+ MethodHandle collectArguments(MethodHandle target, MethodType newType) {
+ MethodType oldType = target.type();
+ int inargs = newType.parameterCount();
+ int outargs = oldType.parameterCount();
+ int collectPos = outargs - 1;
+ int numCollect = (inargs - collectPos);
+ if (collectPos < 0 || numCollect < 0)
+ throw newIllegalArgumentException("wrong number of arguments");
+ return MethodHandleImpl.collectArguments(IMPL_TOKEN, target, newType, collectPos);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which calls the original method handle,
+ * after inserting the given argument at the given position.
+ * The type of the new method handle will drop the corresponding argument
+ * type from the original handle's type.
+ * <p>
+ * The given argument object must match the dropped argument type.
+ * If the dropped argument type is a primitive, the argument object
+ * must be a wrapper, and is unboxed to produce the primitive.
+ * <p>
+ * The <i>pos</i> may range between zero and <i>N</i> (inclusively),
+ * where <i>N</i> is the number of argument types in <i>target</i>,
+ * meaning to insert the new argument as the first or last (respectively),
+ * or somewhere in between.
+ * @param target the method handle to invoke after the argument is inserted
+ * @param pos where to insert the argument (zero for the first)
+ * @param value the argument to insert
+ * @return a new method handle which inserts an additional argument,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle insertArgument(MethodHandle target, int pos, Object value) {
+ MethodType oldType = target.type();
+ ArrayList<Class<?>> ptypes =
+ new ArrayList<Class<?>>(oldType.parameterList());
+ int outargs = oldType.parameterCount();
+ int inargs = outargs - 1;
+ if (pos < 0 || pos >= outargs)
+ throw newIllegalArgumentException("no argument type to append");
+ Class<?> valueType = ptypes.remove(pos);
+ value = checkValue(valueType, value);
+ if (pos == 0 && !valueType.isPrimitive()) {
+ // At least for now, make bound method handles a special case.
+ // This lets us get by with minimal JVM support, at the expense
+ // of generating signature-specific adapters as Java bytecodes.
+ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, target, value);
+ if (bmh != null) return bmh;
+ // else fall through to general adapter machinery
+ }
+ return MethodHandleImpl.bindArgument(IMPL_TOKEN, target, pos, value);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Produce a method handle which calls the original method handle,
+ * after dropping the given argument(s) at the given position.
+ * The type of the new method handle will insert the given argument
+ * type(s), at that position, into the original handle's type.
+ * <p>
+ * The <i>pos</i> may range between zero and <i>N-1</i>,
+ * where <i>N</i> is the number of argument types in <i>target</i>,
+ * meaning to drop the first or last argument (respectively),
+ * or an argument somewhere in between.
+ * @param target the method handle to invoke after the argument is dropped
+ * @param valueTypes the type(s) of the argument to drop
+ * @param pos which argument to drop (zero for the first)
+ * @return a new method handle which drops an argument of the given type,
+ * before calling the original method handle
+ */
+ public static
+ MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
+ if (valueTypes.length == 0) return target;
+ MethodType oldType = target.type();
+ int outargs = oldType.parameterCount();
+ int inargs = outargs + valueTypes.length;
+ if (pos < 0 || pos >= inargs)
+ throw newIllegalArgumentException("no argument type to remove");
+ ArrayList<Class<?>> ptypes =
+ new ArrayList<Class<?>>(oldType.parameterList());
+ ptypes.addAll(pos, Arrays.asList(valueTypes));
+ MethodType newType = MethodType.make(oldType.returnType(), ptypes);
+ return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Make a method handle which adapts a target method handle,
+ * by guarding it with a test, a boolean-valued method handle.
+ * If the guard fails, a fallback handle is called instead.
+ * All three method handles must have the same corresponding
+ * argument and return types, except that the return type
+ * of the test must be boolean.
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>
+ * signature T(A...);
+ * boolean test(A...);
+ * T target(A...);
+ * T fallback(A...);
+ * T adapter(A... a) {
+ * if (test(a...))
+ * return target(a...);
+ * else
+ * return fallback(a...);
+ * }
+ * </pre></blockquote>
+ * @param test method handle used for test, must return boolean
+ * @param target method handle to call if test passes
+ * @param fallback method handle to call if test fails
+ * @return method handle which incorporates the specified if/then/else logic
+ * @throws IllegalArgumentException if {@code test} does not return boolean,
+ * or if all three method types do not match (with the return
+ * type of {@code test} changed to match that of {@code target}).
+ */
+ public static
+ MethodHandle guardWithTest(MethodHandle test,
+ MethodHandle target,
+ MethodHandle fallback) {
+ if (target.type() != fallback.type())
+ throw newIllegalArgumentException("target and fallback types do not match");
+ if (target.type().changeReturnType(boolean.class) != test.type())
+ throw newIllegalArgumentException("target and test types do not match");
+ /* {
+ MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type());
+ static MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) {
+ return z ? t : f;
+ }
+ static MethodHandle compose(MethodHandle f, MethodHandle g) {
+ Class<?> initargs = g.type().parameterArray();
+ f = dropArguments(f, 1, initargs); // ignore 2nd copy of args
+ return combineArguments(f, g);
+ }
+ // choose = \z.(z ? target : fallback)
+ MethodHandle choose = findVirtual(MethodHandles.class, "choose",
+ MethodType.make(boolean.class, MethodHandle.class, MethodHandle.class));
+ choose = appendArgument(choose, target);
+ choose = appendArgument(choose, fallback);
+ MethodHandle dispatch = compose(choose, test);
+ // dispatch = \(a...).(test(a...) ? target : fallback)
+ return combineArguments(invoke, dispatch, 0);
+ // return \(a...).((test(a...) ? target : fallback).invoke(a...))
+ } */
+ return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback);
+ }
+
+ /**
+ * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
+ * Adapt a target method handle {@code target} by first processing
+ * its arguments, and then calling the target.
+ * The initial processing is performed by a second method handle, the {@code combiner}.
+ * After this, control passes to the {@code target}, with the same arguments.
+ * <p>
+ * The return value of the {@code combiner} is inserted into the argument list
+ * for the {@code target} at the indicated position {@code pos}, if it is non-negative.
+ * Except for this inserted argument (if any), the argument types of
+ * the target {@code target} and the {@code combiner} must be identical.
+ * <p>
+ * (Note that {@link #dropArguments} can be used to remove any arguments
+ * that either the {@code combiner} or {@code target} does not wish to receive.)
+ * <p>
+ * The combiner handle must have the same argument types as the
+ * target handle, but must return {@link MethodHandle} instead of
+ * the ultimate return type. The returned method handle, in turn,
+ * is required to have exactly the given final method type.
+ * <p> Here is pseudocode for the resulting adapter:
+ * <blockquote><pre>
+ * signature V(A[pos]..., B...);
+ * signature T(A[pos]..., V, B...);
+ * T target(A... a, V v, B... b);
+ * V combiner(A..., B...);
+ * T adapter(A... a, B... b) {
+ * V v = combiner(a..., b...);
+ * return target(a..., v, b...);
+ * }
+ * </pre></blockquote>
+ * @param target the method handle to invoke after arguments are combined
+ * @param pos where the return value of {@code combiner} is to
+ * be inserted as an argument to {@code target}
+ * @param combiner method handle to call initially on the incoming arguments
+ * @return method handle which incorporates the specified dispatch logic
+ * @throws IllegalArgumentException if {@code combiner} does not itself
+ * return either void or the {@code pos}-th argument of {@code target},
+ * or does not have the same argument types as {@code target}
+ * (minus the inserted argument)
+ */
+ public static
+ MethodHandle combineArguments(MethodHandle target, int pos, MethodHandle combiner) {
+ MethodType mhType = target.type();
+ Class<?> combineType = combiner.type().returnType();
+ MethodType incomingArgs;
+ if (pos < 0) {
+ // No inserted argument; target & combiner must have same argument types.
+ incomingArgs = mhType;
+ if (!incomingArgs.changeReturnType(combineType).equals(combiner.type()))
+ throw newIllegalArgumentException("target and combiner types do not match");
+ } else {
+ // Inserted argument.
+ if (pos >= mhType.parameterCount()
+ || mhType.parameterType(pos) != combineType)
+ throw newIllegalArgumentException("inserted combiner argument does not match target");
+ incomingArgs = mhType.dropParameterType(pos);
+ }
+ if (!incomingArgs.changeReturnType(combineType).equals(combiner.type())) {
+ throw newIllegalArgumentException("target and combiner types do not match");
+ }
+ return MethodHandleImpl.combineArguments(IMPL_TOKEN, target, combiner, pos);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/MethodType.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import sun.dyn.Access;
+import sun.dyn.Invokers;
+import sun.dyn.MethodTypeImpl;
+import sun.dyn.util.BytecodeSignature;
+import static sun.dyn.MemberName.newIllegalArgumentException;
+
+/**
+ * Run-time token used to match call sites with method handles.
+ * The structure is a return type accompanied by any number of parameter types.
+ * The types (primitive, void, and reference) are represented by Class objects.
+ * All instances of <code>MethodType</code> are immutable.
+ * Two instances are completely interchangeable if they compare equal.
+ * Equality depends exactly on the return and parameter types.
+ * <p>
+ * This type can be created only by factory methods, which manage interning.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public final
+class MethodType {
+ private final Class<?> rtype;
+ private final Class<?>[] ptypes;
+ private MethodTypeForm form; // erased form, plus cached data about primitives
+ private MethodType wrapAlt; // alternative wrapped/unwrapped version
+ private Invokers invokers; // cache of handy higher-order adapters
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+
+ // share a cache with a friend in this package
+ Invokers getInvokers() { return invokers; }
+ void setInvokers(Invokers inv) { invokers = inv; }
+
+ static {
+ // This hack allows the implementation package special access to
+ // the internals of MethodType. In particular, the Form has all sorts
+ // of cached information useful to the implementation code.
+ MethodTypeImpl.setMethodTypeFriend(IMPL_TOKEN, new MethodTypeImpl.MethodTypeFriend() {
+ public Class<?>[] ptypes(MethodType mt) { return mt.ptypes; }
+ public MethodTypeImpl form(MethodType mt) { return mt.form; }
+ public void setForm(MethodType mt, MethodTypeImpl form) {
+ assert(mt.form == null);
+ mt.form = (MethodTypeForm) form;
+ }
+ public MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
+ return MethodType.makeImpl(rtype, ptypes, trusted);
+ }
+ public MethodTypeImpl newMethodTypeForm(MethodType mt) {
+ return new MethodTypeForm(mt);
+ }
+ public Invokers getInvokers(MethodType mt) { return mt.invokers; }
+ public void setInvokers(MethodType mt, Invokers inv) { mt.invokers = inv; }
+ });
+ }
+
+ private MethodType(Class<?> rtype, Class<?>[] ptypes) {
+ checkRtype(rtype);
+ checkPtypes(ptypes);
+ this.rtype = rtype;
+ this.ptypes = ptypes;
+ }
+
+ private void checkRtype(Class<?> rtype) {
+ rtype.equals(rtype); // null check
+ }
+ private void checkPtypes(Class<?>[] ptypes) {
+ for (Class<?> ptype : ptypes) {
+ ptype.equals(ptype); // null check
+ if (ptype == void.class)
+ throw newIllegalArgumentException("void parameter: "+this);
+ }
+ }
+
+ static final HashMap<MethodType,MethodType> internTable
+ = new HashMap<MethodType, MethodType>();
+
+ static final Class<?>[] NO_PTYPES = {};
+
+ /** Find or create an instance of the given method type.
+ * @param rtype the return type
+ * @param ptypes the parameter types
+ * @return the interned method type with the given parts
+ * @throws NullPointerException if rtype or any ptype is null
+ * @throws IllegalArgumentException if any of the ptypes is void
+ */
+ public static
+ MethodType make(Class<?> rtype, Class<?>[] ptypes) {
+ return makeImpl(rtype, ptypes, false);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */
+ public static
+ MethodType make(Class<?> rtype, List<? extends Class<?>> ptypes) {
+ return makeImpl(rtype, ptypes.toArray(NO_PTYPES), true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The leading parameter type is prepended to the remaining array.
+ */
+ public static
+ MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
+ Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
+ ptypes1[0] = ptype0;
+ System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
+ return makeImpl(rtype, ptypes1, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The resulting method has no parameter types.
+ */
+ public static
+ MethodType make(Class<?> rtype) {
+ return makeImpl(rtype, NO_PTYPES, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The resulting method has the single given parameter type.
+ */
+ public static
+ MethodType make(Class<?> rtype, Class<?> ptype0) {
+ return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * The resulting method has the same parameter types as {@code ptypes},
+ * and the specified return type.
+ */
+ public static
+ MethodType make(Class<?> rtype, MethodType ptypes) {
+ return makeImpl(rtype, ptypes.ptypes, true);
+ }
+
+ /**
+ * Sole factory method to find or create an interned method type.
+ * @param rtype desired return type
+ * @param ptypes desired parameter types
+ * @param trusted whether the ptypes can be used without cloning
+ * @return the unique method type of the desired structure
+ */
+ private static
+ MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
+ if (ptypes == null || ptypes.length == 0) {
+ ptypes = NO_PTYPES; trusted = true;
+ }
+ MethodType mt1 = new MethodType(rtype, ptypes);
+ MethodType mt0;
+ synchronized (internTable) {
+ mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
+ }
+ if (!trusted)
+ // defensively copy the array passed in by the user
+ mt1 = new MethodType(rtype, ptypes.clone());
+ // promote the object to the Real Thing, and reprobe
+ MethodTypeImpl.initForm(IMPL_TOKEN, mt1);
+ synchronized (internTable) {
+ mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
+ internTable.put(mt1, mt1);
+ }
+ return mt1;
+ }
+
+ // Entry point from JVM. TODO: Change the name & signature.
+ private static MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes,
+ boolean ignore1, boolean ignore2) {
+ return makeImpl(rtype, ptypes, true);
+ }
+
+ private static final MethodType[] objectOnlyTypes = new MethodType[20];
+
+ /**
+ * Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * All parameters and the return type will be Object, except the final varargs parameter if any.
+ * @param objectArgCount number of parameters (excluding the varargs parameter if any)
+ * @param varargs whether there will be a varargs parameter, of type Object[]
+ * @return a totally generic method type, given only its count of parameters and varargs
+ * @see #makeGeneric(int)
+ */
+ public static
+ MethodType makeGeneric(int objectArgCount, boolean varargs) {
+ MethodType mt;
+ int ivarargs = (!varargs ? 0 : 1);
+ int ootIndex = objectArgCount*2 + ivarargs;
+ if (ootIndex < objectOnlyTypes.length) {
+ mt = objectOnlyTypes[ootIndex];
+ if (mt != null) return mt;
+ }
+ Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs];
+ Arrays.fill(ptypes, Object.class);
+ if (ivarargs != 0) ptypes[objectArgCount] = Object[].class;
+ mt = makeImpl(Object.class, ptypes, true);
+ if (ootIndex < objectOnlyTypes.length) {
+ objectOnlyTypes[ootIndex] = mt; // cache it here also!
+ }
+ return mt;
+ }
+
+ /**
+ * All parameters and the return type will be Object.
+ * @param objectArgCount number of parameters
+ * @return a totally generic method type, given only its count of parameters
+ * @see #makeGeneric(int, boolean)
+ */
+ public static
+ MethodType makeGeneric(int objectArgCount) {
+ return makeGeneric(objectArgCount, false);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param num the index (zero-based) of the parameter type to change
+ * @param nptype a new parameter type to replace the old one with
+ * @return the same type, except with the selected parameter changed
+ */
+ public MethodType changeParameterType(int num, Class<?> nptype) {
+ if (parameterType(num) == nptype) return this;
+ Class<?>[] nptypes = ptypes.clone();
+ nptypes[num] = nptype;
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param num the position (zero-based) of the inserted parameter type
+ * @param nptype a new parameter type to insert into the parameter list
+ * @return the same type, except with the selected parameter inserted
+ */
+ public MethodType insertParameterType(int num, Class<?> nptype) {
+ int len = ptypes.length;
+ Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1);
+ System.arraycopy(nptypes, num, nptypes, num+1, len-num);
+ nptypes[num] = nptype;
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param num the index (zero-based) of the parameter type to remove
+ * @return the same type, except with the selected parameter removed
+ */
+ public MethodType dropParameterType(int num) {
+ int len = ptypes.length;
+ Class<?>[] nptypes;
+ if (num == 0) {
+ nptypes = Arrays.copyOfRange(ptypes, 1, len);
+ } else {
+ nptypes = Arrays.copyOfRange(ptypes, 0, len-1);
+ System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num);
+ }
+ return makeImpl(rtype, nptypes, true);
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}.
+ * @param nrtype a return parameter type to replace the old one with
+ * @return the same type, except with the return type change
+ */
+ public MethodType changeReturnType(Class<?> nrtype) {
+ if (returnType() == nrtype) return this;
+ return makeImpl(nrtype, ptypes, true);
+ }
+
+ /** Convenience method.
+ * Report if this type contains a primitive argument or return value.
+ * @return true if any of the types are primitives
+ */
+ public boolean hasPrimitives() {
+ return form.hasPrimitives();
+ }
+
+ /** Convenience method.
+ * Report if this type contains a wrapper argument or return value.
+ * Wrappers are types which box primitive values, such as {@link Integer}.
+ * @return true if any of the types are wrappers
+ */
+ public boolean hasWrappers() {
+ return unwrap() != this;
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Erase all reference types to Object.
+ * @return a version of the original type with all reference types replaced
+ */
+ public MethodType erase() {
+ return form.erasedType();
+ }
+
+ /** Convenience method for {@link #makeGeneric(int)}.
+ * Convert all types, both reference and primitive, to Object.
+ * @return a version of the original type with all types replaced
+ */
+ public MethodType generic() {
+ return makeGeneric(parameterCount());
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Convert all primitive types to their corresponding wrapper types.
+ * A {@code void} return type is changed to the type {@code java.lang.Void}.
+ * @return a version of the original type with all primitive types replaced
+ */
+ public MethodType wrap() {
+ return hasPrimitives() ? wrapWithPrims(this) : this;
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Convert all wrapper types to their corresponding primitive types.
+ * A return type of {@code java.lang.Void} is changed to {@code void}.
+ * @return a version of the original type with all wrapper types replaced
+ */
+ public MethodType unwrap() {
+ MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this);
+ return unwrapWithNoPrims(noprims);
+ }
+
+ private static MethodType wrapWithPrims(MethodType pt) {
+ assert(pt.hasPrimitives());
+ MethodType wt = pt.wrapAlt;
+ if (wt == null) {
+ // fill in lazily
+ wt = MethodTypeImpl.canonicalize(pt, MethodTypeImpl.WRAP, MethodTypeImpl.WRAP);
+ assert(wt != null);
+ pt.wrapAlt = wt;
+ }
+ return wt;
+ }
+
+ private static MethodType unwrapWithNoPrims(MethodType wt) {
+ assert(!wt.hasPrimitives());
+ MethodType uwt = wt.wrapAlt;
+ if (uwt == null) {
+ // fill in lazily
+ uwt = MethodTypeImpl.canonicalize(wt, MethodTypeImpl.UNWRAP, MethodTypeImpl.UNWRAP);
+ if (uwt == null)
+ uwt = wt; // type has no wrappers or prims at all
+ wt.wrapAlt = uwt;
+ }
+ return uwt;
+ }
+
+ /** @param num the index (zero-based) of the desired parameter type
+ * @return the selected parameter type
+ */
+ public Class<?> parameterType(int num) {
+ return ptypes[num];
+ }
+ /** @return the number of parameter types */
+ public int parameterCount() {
+ return ptypes.length;
+ }
+ /** @return the return type */
+ public Class<?> returnType() {
+ return rtype;
+ }
+
+ /**
+ * Convenience method to present the arguments as a list.
+ * @return the parameter types (as an immutable list)
+ */
+ public List<Class<?>> parameterList() {
+ return Collections.unmodifiableList(Arrays.asList(ptypes));
+ }
+
+ /**
+ * Convenience method to present the arguments as an array.
+ * @return the parameter types (as a fresh copy if necessary)
+ */
+ public Class<?>[] parameterArray() {
+ return ptypes.clone();
+ }
+
+ /**
+ * Compares the specified object with this type for equality.
+ * That is, it returns <tt>true</tt> if and only if the specified object
+ * is also a method type with exactly the same parameters and return type.
+ * @param x object to compare
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object x) {
+ return this == x || x instanceof MethodType && equals((MethodType)x);
+ }
+
+ private boolean equals(MethodType that) {
+ return this.rtype == that.rtype
+ && Arrays.equals(this.ptypes, that.ptypes);
+ }
+
+ /**
+ * Returns the hash code value for this method type.
+ * It is defined to be the same as the hashcode of a List
+ * whose elements are the return type followed by the
+ * parameter types.
+ * @return the hash code value for this method type
+ * @see Object#hashCode()
+ * @see #equals(Object)
+ * @see List#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = 31 + rtype.hashCode();
+ for (Class<?> ptype : ptypes)
+ hashCode = 31*hashCode + ptype.hashCode();
+ return hashCode;
+ }
+
+ /**
+ * The string representation of a method type is a
+ * parenthesis enclosed, comma separated list of type names,
+ * followed immediately by the return type.
+ * <p>
+ * If a type name is array, it the base type followed
+ * by [], rather than the Class.getName of the array type.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (int i = 0; i < ptypes.length; i++) {
+ if (i > 0) sb.append(",");
+ putName(sb, ptypes[i]);
+ }
+ sb.append(")");
+ putName(sb, rtype);
+ return sb.toString();
+ }
+
+ static void putName(StringBuilder sb, Class<?> cls) {
+ int brackets = 0;
+ while (cls.isArray()) {
+ cls = cls.getComponentType();
+ brackets++;
+ }
+ String n = cls.getName();
+ /*
+ if (n.startsWith("java.lang.")) {
+ String nb = n.substring("java.lang.".length());
+ if (nb.indexOf('.') < 0) n = nb;
+ } else if (n.indexOf('.') < 0) {
+ n = "."+n; // anonymous package
+ }
+ */
+ sb.append(n);
+ while (brackets > 0) {
+ sb.append("[]");
+ brackets--;
+ }
+ }
+
+ /// Queries which have to do with the bytecode architecture
+
+ /** The number of JVM stack slots required to invoke a method
+ * of this type. Note that (for historic reasons) the JVM requires
+ * a second stack slot to pass long and double arguments.
+ * So this method returns {@link #parameterCount()} plus the
+ * number of long and double parameters (if any).
+ * <p>
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @return the number of JVM stack slots for this type's parameters
+ */
+ public int parameterSlotCount() {
+ return form.parameterSlotCount();
+ }
+
+ /** Number of JVM stack slots which carry all parameters after
+ * the given position, which must be in the range of 0 to
+ * {@code parameterCount} inclusive. Successive parameters are
+ * more shallowly stacked, and parameters are indexed in the bytecodes
+ * according to their trailing edge. Thus, to obtain the depth
+ * in the outgoing call stack of parameter {@code N}, obtain
+ * the {@code parameterSlotDepth} of its trailing edge
+ * at position {@code N+1}.
+ * <p>
+ * Parameters of type {@code long} and {@code double} occupy
+ * two stack slots (for historical reasons) and all others occupy one.
+ * Therefore, the number returned is the number of arguments
+ * <em>including</em> and <em>after</em> the given parameter,
+ * <em>plus</em> the number of long or double arguments
+ * at or after after the argument for the given parameter.
+ * <p>
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @param num an index (zero-based, inclusive) within the parameter types
+ * @return the index of the (shallowest) JVM stack slot transmitting the
+ * given parameter
+ */
+ public int parameterSlotDepth(int num) {
+ if (num < 0 || num > ptypes.length)
+ parameterType(num); // force a range check
+ return form.parameterToArgSlot(num-1);
+ }
+
+ /** The number of JVM stack slots required to receive a return value
+ * from a method of this type.
+ * If the {@link #returnType() return type} is void, it will be zero,
+ * else if the return type is long or double, it will be two, else one.
+ * <p>
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @return the number of JVM stack slots (0, 1, or 2) for this type's return value
+ */
+ public int returnSlotCount() {
+ return form.returnSlotCount();
+ }
+
+ /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}.
+ * Find or create an instance (interned) of the given method type.
+ * Any class or interface name embedded in the signature string
+ * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
+ * on the given loader (or if it is null, on the system class loader).
+ * <p>
+ * Note that it is possible to build method types which cannot be
+ * constructed by this method, because their component types are
+ * not all reachable from a common class loader.
+ * <p>
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * @param bytecodeSignature a bytecode-level signature string "(T...)T"
+ * @param loader the class loader in which to look up the types
+ * @return a method type matching the bytecode-level signature
+ * @throws IllegalArgumentException if the string is not well-formed
+ * @throws TypeNotPresentException if a named type cannot be found
+ */
+ public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader)
+ throws IllegalArgumentException, TypeNotPresentException
+ {
+ List<Class<?>> types = BytecodeSignature.parseMethod(bytecodeSignature, loader);
+ Class<?> rtype = types.remove(types.size() - 1);
+ Class<?>[] ptypes = types.toArray(NO_PTYPES);
+ return makeImpl(rtype, ptypes, true);
+ }
+
+ /**
+ * Create a bytecode signature representation of the type.
+ * Note that this is not a strict inverse of
+ * <p>
+ * This method is included for the benfit of applications that must
+ * generate bytecodes that process method handles and invokedynamic.
+ * {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)},
+ * because the latter requires a suitable class loader argument.
+ * @return the bytecode signature representation
+ */
+ public String toBytecodeString() {
+ return BytecodeSignature.unparse(this);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/MethodTypeForm.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * TO DO: Temporary shim; remove after refactoring effects are complete in JVM.
+ * @author John Rose
+ */
+import sun.dyn.MethodTypeImpl;
+
+class MethodTypeForm extends MethodTypeImpl {
+
+ MethodTypeForm(MethodType erasedType) {
+ super(erasedType);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/NoAccessException.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * Thrown to indicate that a caller has attempted to create a method handle
+ * which calls a method to which the caller does not have access.
+ * This unchecked exception is analogous to {@link IllegalAccessException},
+ * which is a checked exception thrown when reflective invocation fails
+ * because of an access check. With method handles, this same access
+ * checking is performed on behalf of the method handle creator,
+ * at the time of creation.
+ * @author John Rose, JSR 292 EG
+ */
+public class NoAccessException extends RuntimeException {
+ /**
+ * Constructs a {@code NoAccessException} with no detail message.
+ */
+ public NoAccessException() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code NoAccessException} with the specified
+ * detail message.
+ *
+ * @param s the detail message
+ */
+ public NoAccessException(String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs a {@code NoAccessException} with the specified cause.
+ *
+ * @param cause the underlying cause of the exception
+ */
+ public NoAccessException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs a {@code NoAccessException} with the specified
+ * detail message and cause.
+ *
+ * @param s the detail message
+ * @param cause the underlying cause of the exception
+ */
+ public NoAccessException(String s, Throwable cause) {
+ super(s, cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/WrongMethodTypeException.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.dyn;
+
+/**
+ * Thrown to indicate that code has attempted to call a method handle
+ * via the wrong method type. As with the bytecode representation of
+ * normal Java method calls, method handle calls are strongly typed
+ * to a specific signature associated with a call site.
+ * <p>
+ * This exception may also be thrown when two method handles are
+ * composed, and the system detects that their types cannot be
+ * matched up correctly. This amounts to an early evaluation
+ * of the type mismatch, at method handle construction time,
+ * instead of when the mismatched method handle is called.
+ *
+ * @author John Rose, JSR 292 EG
+ */
+public class WrongMethodTypeException extends RuntimeException {
+ /**
+ * Constructs a {@code WrongMethodTypeException} with no detail message.
+ */
+ public WrongMethodTypeException() {
+ super();
+ }
+
+ /**
+ * Constructs a {@code WrongMethodTypeException} with the specified
+ * detail message.
+ *
+ * @param s the detail message.
+ */
+ public WrongMethodTypeException(String s) {
+ super(s);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/dyn/package-info.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * This package contains dynamic language support provided directly by
+ * the Java core class libraries and virtual machine.
+ * @author John Rose, JSR 292 EG
+ */
+
+package java.dyn;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/Access.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.reflect.Reflection;
+
+/**
+ * Access control to this package.
+ * Classes in other packages can attempt to acquire the access token,
+ * but will fail if they are not recognized as friends.
+ * Certain methods in this package, although public, require a non-null
+ * access token in order to proceed; they act like package-private methods.
+ * @author jrose
+ */
+
+public class Access {
+
+ private Access() { }
+
+ /**
+ * The heart of this pattern: The list of classes which are
+ * permitted to acquire the access token, and become honorary
+ * members of this package.
+ */
+ private static final String[] FRIENDS = {
+ "java.dyn.", "sun.dyn."
+ };
+
+ /**
+ * The following object is NOT public. That's the point of the pattern.
+ * It is package-private, so that any member of this package
+ * can acquire the access token, and give it away to trusted friends.
+ */
+ static final Access TOKEN = new Access();
+
+ /**
+ * @return Access.TOKEN, if the caller is a friend of this package
+ */
+ public static Access getToken() {
+ Class<?> callc = Reflection.getCallerClass(2);
+ if (isFriend(callc))
+ return TOKEN;
+ else
+ throw new IllegalAccessError("bad caller: " + callc);
+ }
+
+ /** Is the given name the name of a class which could be our friend? */
+ public static boolean isFriendName(String name) {
+ for (String friend : FRIENDS) {
+ if (name.startsWith(friend))
+ return true;
+ }
+ return false;
+ }
+
+ /** Is the given class a friend? True if {@link #isFriendName},
+ * and the given class also shares a class loader with us.
+ */
+ public static boolean isFriend(Class<?> c) {
+ return isFriendName(c.getName()) && c.getClassLoader() == CLASS_LOADER;
+ }
+
+ private static final ClassLoader CLASS_LOADER = Access.class.getClassLoader();
+
+ /**
+ * Throw an IllegalAccessError if the caller does not possess
+ * the Access.TOKEN.
+ * @param must be Access.TOKEN
+ */
+ public static void check(Access token) {
+ if (token == null)
+ fail();
+ // else it must be the unique Access.TOKEN
+ assert(token == Access.TOKEN);
+ }
+ private static void fail() {
+ final int CALLER_DEPTH = 3;
+ // 0: Reflection.getCC, 1: this.fail, 2: Access.*, 3: caller
+ Class<?> callc = Reflection.getCallerClass(CALLER_DEPTH);
+ throw new IllegalAccessError("bad caller: " + callc);
+ }
+
+ static {
+ //sun.reflect.Reflection.registerMethodsToFilter(MH.class, "getToken");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.dyn.util.VerifyType;
+import sun.dyn.util.Wrapper;
+import java.dyn.*;
+import java.util.Arrays;
+import static sun.dyn.MethodHandleNatives.Constants.*;
+import static sun.dyn.MethodHandleImpl.newIllegalArgumentException;
+
+/**
+ * This method handle performs simple conversion or checking of a single argument.
+ * @author jrose
+ */
+public class AdapterMethodHandle extends BoundMethodHandle {
+
+ //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH
+ //Object argument; // parameter to the conversion if needed
+ //int vmargslot; // which argument slot is affected
+ private final int conversion; // the type of conversion: RETYPE_ONLY, etc.
+
+ // Constructors in this class *must* be package scoped or private.
+ private AdapterMethodHandle(MethodHandle target, MethodType newType,
+ long conv, Object convArg) {
+ super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv)));
+ this.conversion = convCode(conv);
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ // JVM might update VM-specific bits of conversion (ignore)
+ MethodHandleNatives.init(this, target, convArgPos(conv));
+ }
+ }
+ private AdapterMethodHandle(MethodHandle target, MethodType newType,
+ long conv) {
+ this(target, newType, conv, null);
+ }
+
+ private static final Access IMPL_TOKEN = Access.getToken();
+
+ // TO DO: When adapting another MH with a null conversion, clone
+ // the target and change its type, instead of adding another layer.
+
+ /** Can a JVM-level adapter directly implement the proposed
+ * argument conversions, as if by MethodHandles.convertArguments?
+ */
+ public static boolean canPairwiseConvert(MethodType newType, MethodType oldType) {
+ // same number of args, of course
+ int len = newType.parameterCount();
+ if (len != oldType.parameterCount())
+ return false;
+
+ // Check return type. (Not much can be done with it.)
+ Class<?> exp = newType.returnType();
+ Class<?> ret = oldType.returnType();
+ if (!VerifyType.isNullConversion(ret, exp))
+ return false;
+
+ // Check args pairwise.
+ for (int i = 0; i < len; i++) {
+ Class<?> src = newType.parameterType(i); // source type
+ Class<?> dst = oldType.parameterType(i); // destination type
+ if (!canConvertArgument(src, dst))
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Can a JVM-level adapter directly implement the proposed
+ * argument conversion, as if by MethodHandles.convertArguments?
+ */
+ public static boolean canConvertArgument(Class<?> src, Class<?> dst) {
+ // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
+ // so we don't need to repeat so much decision making.
+ if (VerifyType.isNullConversion(src, dst)) {
+ return true;
+ } else if (src.isPrimitive()) {
+ if (dst.isPrimitive())
+ return canPrimCast(src, dst);
+ else
+ return canBoxArgument(src, dst);
+ } else {
+ if (dst.isPrimitive())
+ return canUnboxArgument(src, dst);
+ else
+ return true; // any two refs can be interconverted
+ }
+ }
+
+ /**
+ * Create a JVM-level adapter method handle to conform the given method
+ * handle to the similar newType, using only pairwise argument conversions.
+ * For each argument, convert incoming argument to the exact type needed.
+ * Only null conversions are allowed on the return value (until
+ * the JVM supports ricochet adapters).
+ * The argument conversions allowed are casting, unboxing,
+ * integral widening or narrowing, and floating point widening or narrowing.
+ * @param token access check
+ * @param newType required call type
+ * @param target original method handle
+ * @return an adapter to the original handle with the desired new type,
+ * or the original target if the types are already identical
+ * or null if the adaptation cannot be made
+ */
+ public static MethodHandle makePairwiseConvert(Access token,
+ MethodType newType, MethodHandle target) {
+ Access.check(token);
+ MethodType oldType = target.type();
+ if (newType == oldType) return target;
+
+ if (!canPairwiseConvert(newType, oldType))
+ return null;
+ // (after this point, it is an assertion error to fail to convert)
+
+ // Find last non-trivial conversion (if any).
+ int lastConv = newType.parameterCount()-1;
+ while (lastConv >= 0) {
+ Class<?> src = newType.parameterType(lastConv); // source type
+ Class<?> dst = oldType.parameterType(lastConv); // destination type
+ if (VerifyType.isNullConversion(src, dst)) {
+ --lastConv;
+ } else {
+ break;
+ }
+ }
+ // Now build a chain of one or more adapters.
+ MethodHandle adapter = target;
+ MethodType midType = oldType.changeReturnType(newType.returnType());
+ for (int i = 0; i <= lastConv; i++) {
+ Class<?> src = newType.parameterType(i); // source type
+ Class<?> dst = midType.parameterType(i); // destination type
+ if (VerifyType.isNullConversion(src, dst)) {
+ // do nothing: difference is trivial
+ continue;
+ }
+ // Work the current type backward toward the desired caller type:
+ if (i != lastConv) {
+ midType = midType.changeParameterType(i, src);
+ } else {
+ // When doing the last (or only) real conversion,
+ // force all remaining null conversions to happen also.
+ assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
+ midType = newType;
+ }
+
+ // Tricky case analysis follows.
+ // It parallels canConvertArgument() above.
+ if (src.isPrimitive()) {
+ if (dst.isPrimitive()) {
+ adapter = makePrimCast(token, midType, adapter, i, dst);
+ } else {
+ adapter = makeBoxArgument(token, midType, adapter, i, dst);
+ }
+ } else {
+ if (dst.isPrimitive()) {
+ // Caller has boxed a primitive. Unbox it for the target.
+ // The box type must correspond exactly to the primitive type.
+ // This is simpler than the powerful set of widening
+ // conversions supported by reflect.Method.invoke.
+ // Those conversions require a big nest of if/then/else logic,
+ // which we prefer to make a user responsibility.
+ adapter = makeUnboxArgument(token, midType, adapter, i, dst);
+ } else {
+ // Simple reference conversion.
+ // Note: Do not check for a class hierarchy relation
+ // between src and dst. In all cases a 'null' argument
+ // will pass the cast conversion.
+ adapter = makeCheckCast(token, midType, adapter, i, dst);
+ }
+ }
+ assert(adapter != null);
+ assert(adapter.type() == midType);
+ }
+ if (adapter.type() != newType) {
+ // Only trivial conversions remain.
+ adapter = makeRetypeOnly(IMPL_TOKEN, newType, adapter);
+ assert(adapter != null);
+ // Actually, that's because there were no non-trivial ones:
+ assert(lastConv == -1);
+ }
+ assert(adapter.type() == newType);
+ return adapter;
+ }
+
+ /**
+ * Create a JVM-level adapter method handle to permute the arguments
+ * of the given method.
+ * @param token access check
+ * @param newType required call type
+ * @param target original method handle
+ * @param argumentMap for each target argument, position of its source in newType
+ * @return an adapter to the original handle with the desired new type,
+ * or the original target if the types are already identical
+ * and the permutation is null
+ * @throws IllegalArgumentException if the adaptation cannot be made
+ * directly by a JVM-level adapter, without help from Java code
+ */
+ public static MethodHandle makePermutation(Access token,
+ MethodType newType, MethodHandle target,
+ int[] argumentMap) {
+ MethodType oldType = target.type();
+ boolean nullPermutation = true;
+ for (int i = 0; i < argumentMap.length; i++) {
+ int pos = argumentMap[i];
+ if (pos != i)
+ nullPermutation = false;
+ if (pos < 0 || pos >= newType.parameterCount()) {
+ argumentMap = new int[0]; break;
+ }
+ }
+ if (argumentMap.length != oldType.parameterCount())
+ throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
+ if (nullPermutation) {
+ MethodHandle res = makePairwiseConvert(token, newType, target);
+ // well, that was easy
+ if (res == null)
+ throw newIllegalArgumentException("cannot convert pairwise: "+newType);
+ return res;
+ }
+
+ // Check return type. (Not much can be done with it.)
+ Class<?> exp = newType.returnType();
+ Class<?> ret = oldType.returnType();
+ if (!VerifyType.isNullConversion(ret, exp))
+ throw newIllegalArgumentException("bad return conversion for "+newType);
+
+ // See if the argument types match up.
+ for (int i = 0; i < argumentMap.length; i++) {
+ int j = argumentMap[i];
+ Class<?> src = newType.parameterType(j);
+ Class<?> dst = oldType.parameterType(i);
+ if (!VerifyType.isNullConversion(src, dst))
+ throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType);
+ }
+
+ // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters.
+ // A workable greedy algorithm is as follows:
+ // Drop unused outgoing arguments (right to left: shallowest first).
+ // Duplicate doubly-used outgoing arguments (left to right: deepest first).
+ // Then the remaining problem is a true argument permutation.
+ // Marshal the outgoing arguments as required from left to right.
+ // That is, find the deepest outgoing stack position that does not yet
+ // have the correct argument value, and correct at least that position
+ // by swapping or rotating in the misplaced value (from a shallower place).
+ // If the misplaced value is followed by one or more consecutive values
+ // (also misplaced) issue a rotation which brings as many as possible
+ // into position. Otherwise make progress with either a swap or a
+ // rotation. Prefer the swap as cheaper, but do not use it if it
+ // breaks a slot pair. Prefer the rotation over the swap if it would
+ // preserve more consecutive values shallower than the target position.
+ // When more than one rotation will work (because the required value
+ // is already adjacent to the target position), then use a rotation
+ // which moves the old value in the target position adjacent to
+ // one of its consecutive values. Also, prefer shorter rotation
+ // spans, since they use fewer memory cycles for shuffling.
+
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ private static byte basicType(Class<?> type) {
+ if (type == null) return T_VOID;
+ switch (Wrapper.forBasicType(type)) {
+ case BOOLEAN: return T_BOOLEAN;
+ case CHAR: return T_CHAR;
+ case FLOAT: return T_FLOAT;
+ case DOUBLE: return T_DOUBLE;
+ case BYTE: return T_BYTE;
+ case SHORT: return T_SHORT;
+ case INT: return T_INT;
+ case LONG: return T_LONG;
+ case OBJECT: return T_OBJECT;
+ case VOID: return T_VOID;
+ }
+ return 99; // T_ILLEGAL or some such
+ }
+
+ /** Number of stack slots for the given type.
+ * Two for T_DOUBLE and T_FLOAT, one for the rest.
+ */
+ private static int type2size(int type) {
+ assert(type >= T_BOOLEAN && type <= T_OBJECT);
+ return (type == T_FLOAT || type == T_DOUBLE) ? 2 : 1;
+ }
+
+ /** Construct an adapter conversion descriptor for a single-argument conversion. */
+ private static long makeConv(int convOp, int argnum, int src, int dest) {
+ assert(src == (src & 0xF));
+ assert(dest == (dest & 0xF));
+ assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
+ long stackMove = type2size(dest) - type2size(src);
+ return ((long) argnum << 32 |
+ (long) convOp << CONV_OP_SHIFT |
+ (int) src << CONV_SRC_TYPE_SHIFT |
+ (int) dest << CONV_DEST_TYPE_SHIFT |
+ stackMove << CONV_STACK_MOVE_SHIFT
+ );
+ }
+ private static long makeConv(int convOp, int argnum, int stackMove) {
+ assert(convOp >= OP_SWAP_ARGS && convOp <= OP_SPREAD_ARGS);
+ byte src = 0, dest = 0;
+ if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
+ src = dest = T_OBJECT;
+ return ((long) argnum << 32 |
+ (long) convOp << CONV_OP_SHIFT |
+ (int) src << CONV_SRC_TYPE_SHIFT |
+ (int) dest << CONV_DEST_TYPE_SHIFT |
+ stackMove << CONV_STACK_MOVE_SHIFT
+ );
+ }
+ private static long makeConv(int convOp) {
+ assert(convOp == OP_RETYPE_ONLY);
+ return (long) convOp << CONV_OP_SHIFT; // stackMove, src, dst, argnum all zero
+ }
+ private static int convCode(long conv) {
+ return (int)conv;
+ }
+ private static int convArgPos(long conv) {
+ return (int)(conv >>> 32);
+ }
+ private static boolean convOpSupported(int convOp) {
+ assert(convOp >= 0 && convOp <= CONV_OP_LIMIT);
+ return ((1<<convOp) & CONV_OP_IMPLEMENTED_MASK) != 0;
+ }
+
+ /** One of OP_RETYPE_ONLY, etc. */
+ int conversionOp() { return (conversion & CONV_OP_MASK) >> CONV_OP_SHIFT; }
+
+ @Override
+ public String toString() {
+ return addTypeString(this, "Adapted[" + basicToString(nonAdapter((MethodHandle)vmtarget)) + "]");
+ }
+
+ private static MethodHandle nonAdapter(MethodHandle mh) {
+ return (MethodHandle)
+ MethodHandleNatives.getTarget(mh, ETF_DIRECT_HANDLE);
+ }
+
+ /* Return one plus the position of the first non-trivial difference
+ * between the given types. This is not a symmetric operation;
+ * we are considering adapting the targetType to adapterType.
+ * Trivial differences are those which could be ignored by the JVM
+ * without subverting the verifier. Otherwise, adaptable differences
+ * are ones for which we could create an adapter to make the type change.
+ * Return zero if there are no differences (other than trivial ones).
+ * Return 1+N if N is the only adaptable argument difference.
+ * Return the -2-N where N is the first of several adaptable
+ * argument differences.
+ * Return -1 if there there are differences which are not adaptable.
+ */
+ private static int diffTypes(MethodType adapterType,
+ MethodType targetType,
+ boolean raw) {
+ int diff;
+ diff = diffReturnTypes(adapterType, targetType, raw);
+ if (diff != 0) return diff;
+ int nargs = adapterType.parameterCount();
+ if (nargs != targetType.parameterCount())
+ return -1;
+ diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw);
+ //System.out.println("diff "+adapterType);
+ //System.out.println(" "+diff+" "+targetType);
+ return diff;
+ }
+ private static int diffReturnTypes(MethodType adapterType,
+ MethodType targetType,
+ boolean raw) {
+ Class<?> src = targetType.returnType();
+ Class<?> dst = adapterType.returnType();
+ if ((!raw
+ ? VerifyType.canPassUnchecked(src, dst)
+ : VerifyType.canPassRaw(src, dst)
+ ) > 0)
+ return 0; // no significant difference
+ if (raw && !src.isPrimitive() && !dst.isPrimitive())
+ return 0; // can force a reference return (very carefully!)
+ //if (false) return 1; // never adaptable!
+ return -1; // some significant difference
+ }
+ private static int diffParamTypes(MethodType adapterType, int tstart,
+ MethodType targetType, int astart,
+ int nargs, boolean raw) {
+ assert(nargs >= 0);
+ int res = 0;
+ for (int i = 0; i < nargs; i++) {
+ Class<?> src = adapterType.parameterType(tstart+i);
+ Class<?> dest = targetType.parameterType(astart+i);
+ if ((!raw
+ ? VerifyType.canPassUnchecked(src, dest)
+ : VerifyType.canPassRaw(src, dest)
+ ) <= 0) {
+ // found a difference; is it the only one so far?
+ if (res != 0)
+ return -1-res; // return -2-i for prev. i
+ res = 1+i;
+ }
+ }
+ return res;
+ }
+
+ /** Can a retyping adapter (alone) validly convert the target to newType? */
+ public static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
+ return canRetypeOnly(newType, targetType, false);
+ }
+ /** Can a retyping adapter (alone) convert the target to newType?
+ * It is allowed to widen subword types and void to int, to make bitwise
+ * conversions between float/int and double/long, and to perform unchecked
+ * reference conversions on return. This last feature requires that the
+ * caller be trusted, and perform explicit cast conversions on return values.
+ */
+ static boolean canRawRetypeOnly(MethodType newType, MethodType targetType) {
+ return canRetypeOnly(newType, targetType, true);
+ }
+ static boolean canRetypeOnly(MethodType newType, MethodType targetType, boolean raw) {
+ if (!convOpSupported(OP_RETYPE_ONLY)) return false;
+ int diff = diffTypes(newType, targetType, raw);
+ // %%% This assert is too strong. Factor diff into VerifyType and reconcile.
+ assert((diff == 0) == VerifyType.isNullConversion(newType, targetType));
+ return diff == 0;
+ }
+
+ /** Factory method: Performs no conversions; simply retypes the adapter.
+ * Allows unchecked argument conversions pairwise, if they are safe.
+ * Returns null if not possible.
+ */
+ public static MethodHandle makeRetypeOnly(Access token,
+ MethodType newType, MethodHandle target) {
+ return makeRetypeOnly(token, newType, target, false);
+ }
+ public static MethodHandle makeRawRetypeOnly(Access token,
+ MethodType newType, MethodHandle target) {
+ return makeRetypeOnly(token, newType, target, true);
+ }
+ static MethodHandle makeRetypeOnly(Access token,
+ MethodType newType, MethodHandle target, boolean raw) {
+ Access.check(token);
+ if (!canRetypeOnly(newType, target.type(), raw))
+ return null;
+ // TO DO: clone the target guy, whatever he is, with new type.
+ return new AdapterMethodHandle(target, newType, makeConv(OP_RETYPE_ONLY));
+ }
+
+ /** Can a checkcast adapter validly convert the target to newType?
+ * The JVM supports all kind of reference casts, even silly ones.
+ */
+ public static boolean canCheckCast(MethodType newType, MethodType targetType,
+ int arg, Class<?> castType) {
+ if (!convOpSupported(OP_CHECK_CAST)) return false;
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = targetType.parameterType(arg);
+ if (!canCheckCast(src, castType)
+ || !VerifyType.isNullConversion(castType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+ /** Can an primitive conversion adapter validly convert src to dst? */
+ public static boolean canCheckCast(Class<?> src, Class<?> dst) {
+ return (!src.isPrimitive() && !dst.isPrimitive());
+ }
+
+ /** Factory method: Forces a cast at the given argument.
+ * The castType is the target of the cast, and can be any type
+ * with a null conversion to the corresponding target parameter.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makeCheckCast(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class<?> castType) {
+ Access.check(token);
+ if (!canCheckCast(newType, target.type(), arg, castType))
+ return null;
+ long conv = makeConv(OP_CHECK_CAST, arg, 0);
+ return new AdapterMethodHandle(target, newType, conv, castType);
+ }
+
+ /** Can an primitive conversion adapter validly convert the target to newType?
+ * The JVM currently supports all conversions except those between
+ * floating and integral types.
+ */
+ public static boolean canPrimCast(MethodType newType, MethodType targetType,
+ int arg, Class<?> convType) {
+ if (!convOpSupported(OP_PRIM_TO_PRIM)) return false;
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = targetType.parameterType(arg);
+ if (!canPrimCast(src, convType)
+ || !VerifyType.isNullConversion(convType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+ /** Can an primitive conversion adapter validly convert src to dst? */
+ public static boolean canPrimCast(Class<?> src, Class<?> dst) {
+ if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
+ return false;
+ } else if (Wrapper.forPrimitiveType(dst).isFloating()) {
+ // both must be floating types
+ return Wrapper.forPrimitiveType(src).isFloating();
+ } else {
+ // both are integral, and all combinations work fine
+ assert(Wrapper.forPrimitiveType(src).isIntegral() &&
+ Wrapper.forPrimitiveType(dst).isIntegral());
+ return true;
+ }
+ }
+
+ /** Factory method: Truncate the given argument with zero or sign extension,
+ * and/or convert between single and doubleword versions of integer or float.
+ * The convType is the target of the conversion, and can be any type
+ * with a null conversion to the corresponding target parameter.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makePrimCast(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class<?> convType) {
+ Access.check(token);
+ MethodType oldType = target.type();
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = oldType.parameterType(arg);
+ if (!canPrimCast(newType, oldType, arg, convType))
+ return null;
+ long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType));
+ return new AdapterMethodHandle(target, newType, conv);
+ }
+
+ /** Can an unboxing conversion validly convert src to dst?
+ * The JVM currently supports all kinds of casting and unboxing.
+ * The convType is the unboxed type; it can be either a primitive or wrapper.
+ */
+ public static boolean canUnboxArgument(MethodType newType, MethodType targetType,
+ int arg, Class<?> convType) {
+ if (!convOpSupported(OP_REF_TO_PRIM)) return false;
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = targetType.parameterType(arg);
+ Class<?> boxType = Wrapper.asWrapperType(convType);
+ convType = Wrapper.asPrimitiveType(convType);
+ if (!canCheckCast(src, boxType)
+ || boxType == convType
+ || !VerifyType.isNullConversion(convType, dst))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+ /** Can an primitive unboxing adapter validly convert src to dst? */
+ public static boolean canUnboxArgument(Class<?> src, Class<?> dst) {
+ return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
+ }
+
+ /** Factory method: Unbox the given argument.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makeUnboxArgument(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class<?> convType) {
+ MethodType oldType = target.type();
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = oldType.parameterType(arg);
+ Class<?> boxType = Wrapper.asWrapperType(convType);
+ Class<?> primType = Wrapper.asPrimitiveType(convType);
+ if (!canUnboxArgument(newType, oldType, arg, convType))
+ return null;
+ MethodType castDone = newType;
+ if (!VerifyType.isNullConversion(src, boxType))
+ castDone = newType.changeParameterType(arg, boxType);
+ long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
+ MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
+ if (castDone == newType)
+ return adapter;
+ return makeCheckCast(token, newType, adapter, arg, boxType);
+ }
+
+ /** Can an primitive boxing adapter validly convert src to dst? */
+ public static boolean canBoxArgument(Class<?> src, Class<?> dst) {
+ if (!convOpSupported(OP_PRIM_TO_REF)) return false;
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /** Factory method: Unbox the given argument.
+ * Return null if this cannot be done.
+ */
+ public static MethodHandle makeBoxArgument(Access token,
+ MethodType newType, MethodHandle target,
+ int arg, Class<?> convType) {
+ // this is difficult to do in the JVM because it must GC
+ return null;
+ }
+
+ // TO DO: makeSwapArguments, makeRotateArguments, makeDuplicateArguments
+
+ /** Can an adapter simply drop arguments to convert the target to newType? */
+ public static boolean canDropArguments(MethodType newType, MethodType targetType,
+ int dropArgPos, int dropArgCount) {
+ if (dropArgCount == 0)
+ return canRetypeOnly(newType, targetType);
+ if (!convOpSupported(OP_DROP_ARGS)) return false;
+ if (diffReturnTypes(newType, targetType, false) != 0)
+ return false;
+ int nptypes = newType.parameterCount();
+ // parameter types must be the same up to the drop point
+ if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0)
+ return false;
+ int afterPos = dropArgPos + dropArgCount;
+ int afterCount = nptypes - afterPos;
+ if (dropArgPos < 0 || dropArgPos >= nptypes ||
+ dropArgCount < 1 || afterPos > nptypes ||
+ targetType.parameterCount() != nptypes - dropArgCount)
+ return false;
+ // parameter types after the drop point must also be the same
+ if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0)
+ return false;
+ return true;
+ }
+
+ /** Factory method: Drop selected arguments.
+ * Allow unchecked retyping of remaining arguments, pairwise.
+ * Return null if this is not possible.
+ */
+ public static MethodHandle makeDropArguments(Access token,
+ MethodType newType, MethodHandle target,
+ int dropArgPos, int dropArgCount) {
+ Access.check(token);
+ if (dropArgCount == 0)
+ return makeRetypeOnly(IMPL_TOKEN, newType, target);
+ MethodType mt = target.type();
+ int argCount = mt.parameterCount();
+ if (!canDropArguments(newType, mt, dropArgPos, dropArgCount))
+ return null;
+ int dropSlotCount, dropSlotPos;
+ if (dropArgCount >= argCount) {
+ assert(dropArgPos == argCount-1);
+ dropSlotPos = 0;
+ dropSlotCount = mt.parameterSlotCount();
+ } else {
+ // arglist: [0: keep... | dpos: drop... | dpos+dcount: keep... ]
+ int lastDroppedArg = dropArgPos + dropArgCount - 1;
+ int lastKeptArg = dropArgPos - 1; // might be -1, which is OK
+ dropSlotPos = mt.parameterSlotDepth(1+lastDroppedArg);
+ int lastKeptSlot = mt.parameterSlotDepth(1+lastKeptArg);
+ dropSlotCount = lastKeptSlot - dropSlotPos;
+ assert(dropSlotCount >= dropArgCount);
+ }
+ long conv = makeConv(OP_DROP_ARGS, dropArgPos, +dropSlotCount);
+ return new AdapterMethodHandle(target, newType, dropSlotCount, conv);
+ }
+
+ /** Can an adapter spread an argument to convert the target to newType? */
+ public static boolean canSpreadArguments(MethodType newType, MethodType targetType,
+ Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ if (!convOpSupported(OP_SPREAD_ARGS)) return false;
+ if (diffReturnTypes(newType, targetType, false) != 0)
+ return false;
+ int nptypes = newType.parameterCount();
+ // parameter types must be the same up to the spread point
+ if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0)
+ return false;
+ int afterPos = spreadArgPos + spreadArgCount;
+ int afterCount = nptypes - afterPos;
+ if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
+ spreadArgCount < 0 ||
+ targetType.parameterCount() != nptypes - 1 + spreadArgCount)
+ return false;
+ // parameter types after the spread point must also be the same
+ if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0)
+ return false;
+ // match the array element type to the spread arg types
+ Class<?> rawSpreadArgType = newType.parameterType(spreadArgPos);
+ if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType))
+ return false;
+ for (int i = 0; i < spreadArgCount; i++) {
+ Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
+ Class<?> dst = targetType.parameterType(spreadArgPos + i);
+ if (src == null || !VerifyType.isNullConversion(src, dst))
+ return false;
+ }
+ return true;
+ }
+
+ /** Factory method: Spread selected argument. */
+ public static MethodHandle makeSpreadArguments(Access token,
+ MethodType newType, MethodHandle target,
+ Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ Access.check(token);
+ MethodType mt = target.type();
+ int argCount = mt.parameterCount();
+ if (!canSpreadArguments(newType, mt, spreadArgType, spreadArgPos, spreadArgCount))
+ return null;
+ int spreadSlotCount, spreadSlotPos;
+ if (spreadArgCount >= argCount) {
+ assert(spreadArgPos == argCount-1);
+ spreadSlotPos = 0;
+ spreadSlotCount = mt.parameterSlotCount();
+ } else {
+ // arglist: [0: keep... | dpos: spread... | dpos+dcount: keep... ]
+ int lastSpreadArg = spreadArgPos + spreadArgCount - 1;
+ int lastKeptArg = spreadArgPos - 1; // might be -1, which is OK
+ spreadSlotPos = mt.parameterSlotDepth(1+lastSpreadArg);
+ int lastKeptSlot = mt.parameterSlotDepth(1+lastKeptArg);
+ spreadSlotCount = lastKeptSlot - spreadSlotPos;
+ assert(spreadSlotCount >= spreadArgCount);
+ }
+ long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, spreadSlotCount);
+ return new AdapterMethodHandle(target, newType, conv, spreadArgType);
+ }
+
+ // TO DO: makeCollectArguments, makeFlyby, makeRicochet
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/BoundMethodHandle.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import sun.dyn.util.VerifyType;
+import sun.dyn.util.Wrapper;
+import java.dyn.*;
+
+/**
+ * The flavor of method handle which emulates an invoke instruction
+ * on a predetermined argument. The JVM dispatches to the correct method
+ * when the handle is created, not when it is invoked.
+ * @author jrose
+ */
+public class BoundMethodHandle extends MethodHandle {
+ //MethodHandle vmtarget; // next BMH or final DMH or methodOop
+ private final Object argument; // argument to insert
+ private final int vmargslot; // position at which it is inserted
+
+ // Constructors in this class *must* be package scoped or private.
+
+ /** Bind a direct MH to its receiver (or first ref. argument).
+ * The JVM will pre-dispatch the MH if it is not already static.
+ */
+ BoundMethodHandle(DirectMethodHandle mh, Object argument) {
+ super(Access.TOKEN, mh.type().dropParameterType(0));
+ // check the type now, once for all:
+ this.argument = checkReferenceArgument(argument, mh, 0);
+ this.vmargslot = this.type().parameterSlotCount();
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ this.vmtarget = null; // maybe updated by JVM
+ MethodHandleNatives.init(this, mh, 0);
+ } else {
+ this.vmtarget = mh;
+ }
+ }
+
+ private static final int REF_ARG = 0, PRIM_ARG = 1, SELF_ARG = 2;
+
+ /** Insert an argument into an arbitrary method handle.
+ * If argnum is zero, inserts the first argument, etc.
+ * The argument type must be a reference.
+ */
+ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
+ this(mh, argument, argnum, mh.type().parameterType(argnum).isPrimitive() ? PRIM_ARG : REF_ARG);
+ }
+
+ /** Insert an argument into an arbitrary method handle.
+ * If argnum is zero, inserts the first argument, etc.
+ */
+ BoundMethodHandle(MethodHandle mh, Object argument, int argnum, int whichArg) {
+ super(Access.TOKEN, mh.type().dropParameterType(argnum));
+ if (whichArg == PRIM_ARG)
+ this.argument = bindPrimitiveArgument(argument, mh, argnum);
+ else {
+ if (whichArg == SELF_ARG) argument = this;
+ this.argument = checkReferenceArgument(argument, mh, argnum);
+ }
+ this.vmargslot = this.type().parameterSlotDepth(argnum);
+ if (MethodHandleNatives.JVM_SUPPORT) {
+ this.vmtarget = null; // maybe updated by JVM
+ MethodHandleNatives.init(this, mh, argnum);
+ } else {
+ this.vmtarget = mh;
+ }
+ }
+
+ /** For the AdapterMethodHandle subclass.
+ */
+ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
+ super(Access.TOKEN, type);
+ this.argument = argument;
+ this.vmargslot = vmargslot;
+ assert(this.getClass() == AdapterMethodHandle.class);
+ }
+
+ /** Initialize the current object as a method handle, binding it
+ * as the {@code argnum}th argument of the method handle {@code entryPoint}.
+ * The invocation type of the resulting method handle will be the
+ * same as {@code entryPoint}, except that the {@code argnum}th argument
+ * type will be dropped.
+ */
+ public BoundMethodHandle(MethodHandle entryPoint, int argnum) {
+ this(entryPoint, null, argnum, SELF_ARG);
+
+ // Note: If the conversion fails, perhaps because of a bad entryPoint,
+ // the MethodHandle.type field will not be filled in, and therefore
+ // no MH.invoke call will ever succeed. The caller may retain a pointer
+ // to the broken method handle, but no harm can be done with it.
+ }
+
+ /** Initialize the current object as a method handle, binding it
+ * as the first argument of the method handle {@code entryPoint}.
+ * The invocation type of the resulting method handle will be the
+ * same as {@code entryPoint}, except that the first argument
+ * type will be dropped.
+ */
+ public BoundMethodHandle(MethodHandle entryPoint) {
+ this(entryPoint, null, 0, SELF_ARG);
+ }
+
+ /** Make sure the given {@code argument} can be used as {@code argnum}-th
+ * parameter of the given method handle {@code mh}, which must be a reference.
+ * <p>
+ * If this fails, throw a suitable {@code WrongMethodTypeException},
+ * which will prevent the creation of an illegally typed bound
+ * method handle.
+ */
+ final static Object checkReferenceArgument(Object argument, MethodHandle mh, int argnum) {
+ Class<?> ptype = mh.type().parameterType(argnum);
+ if (ptype.isPrimitive()) {
+ // fail
+ } else if (argument == null) {
+ return null;
+ } else if (VerifyType.isNullReferenceConversion(argument.getClass(), ptype)) {
+ return argument;
+ }
+ throw badBoundArgumentException(argument, mh, argnum);
+ }
+
+ /** Make sure the given {@code argument} can be used as {@code argnum}-th
+ * parameter of the given method handle {@code mh}, which must be a primitive.
+ * <p>
+ * If this fails, throw a suitable {@code WrongMethodTypeException},
+ * which will prevent the creation of an illegally typed bound
+ * method handle.
+ */
+ final static Object bindPrimitiveArgument(Object argument, MethodHandle mh, int argnum) {
+ Class<?> ptype = mh.type().parameterType(argnum);
+ Wrapper wrap = Wrapper.forPrimitiveType(ptype);
+ Object zero = wrap.zero();
+ if (zero == null) {
+ // fail
+ } else if (argument == null) {
+ if (ptype != int.class && wrap.isSubwordOrInt())
+ return Integer.valueOf(0);
+ else
+ return zero;
+ } else if (VerifyType.isNullReferenceConversion(argument.getClass(), zero.getClass())) {
+ if (ptype != int.class && wrap.isSubwordOrInt())
+ return Wrapper.INT.wrap(argument);
+ else
+ return argument;
+ }
+ throw badBoundArgumentException(argument, mh, argnum);
+ }
+
+ final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
+ String atype = (argument == null) ? "null" : argument.getClass().toString();
+ return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
+ }
+
+ @Override
+ public String toString() {
+ return "Bound[" + super.toString() + "]";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/CallSiteImpl.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.*;
+
+/**
+ * The CallSite privately created by the JVM at every invokedynamic instruction.
+ * @author jrose
+ */
+class CallSiteImpl extends CallSite {
+ // Fields used only by the JVM. Do not use or change.
+ private Object vmmethod;
+
+ // Values supplied by the JVM:
+ int callerMID, callerBCI;
+
+ private CallSiteImpl(Class<?> caller, String name, MethodType type) {
+ super(caller, name, type);
+ }
+
+ @Override
+ public void setTarget(MethodHandle mh) {
+ checkTarget(mh);
+ if (MethodHandleNatives.JVM_SUPPORT)
+ MethodHandleNatives.linkCallSite(this, (MethodHandle) mh);
+ else
+ super.setTarget(mh);
+ }
+
+ private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE =
+ MethodHandleImpl.IMPL_LOOKUP.findStatic(CallSite.class, "privateInitializeCallSite",
+ MethodType.make(void.class, CallSite.class, int.class, int.class));
+
+ // this is the up-call from the JVM:
+ static CallSite makeSite(Class<?> caller, String name, MethodType type,
+ int callerMID, int callerBCI) {
+ MethodHandle bsm = Linkage.getBootstrapMethod(caller);
+ if (bsm == null)
+ throw new InvokeDynamicBootstrapError("class has no bootstrap method: "+caller);
+ CallSite site = bsm.<CallSite>invoke(caller, name, type);
+ if (site == null)
+ throw new InvokeDynamicBootstrapError("class bootstrap method failed to create a call site: "+caller);
+ PRIVATE_INITIALIZE_CALL_SITE.<void>invoke(site, callerMID, callerBCI);
+ return site;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/DirectMethodHandle.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.*;
+import static sun.dyn.MethodHandleNatives.Constants.*;
+
+/**
+ * The flavor of method handle which emulates invokespecial or invokestatic.
+ * @author jrose
+ */
+class DirectMethodHandle extends MethodHandle {
+ //inherited oop vmtarget; // methodOop or virtual class/interface oop
+ private final int vmindex; // method index within class or interface
+ { vmindex = VM_INDEX_UNINITIALIZED; } // JVM may change this
+
+ // Constructors in this class *must* be package scoped or private.
+ DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class<?> lookupClass) {
+ super(Access.TOKEN, mtype);
+
+ assert(m.isMethod() || !doDispatch && m.isConstructor());
+ if (!m.isResolved())
+ throw new InternalError();
+
+ MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
+ }
+
+ boolean isValid() {
+ return (vmindex != VM_INDEX_UNINITIALIZED);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/FilterGeneric.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Sf, tifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodType;
+import java.dyn.NoAccessException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * "Flyby adapters" which apply arbitrary conversions to arguments
+ * on the way to a ultimate target.
+ * For simplicity, these are all generically typed.
+ * @author jrose
+ */
+class FilterGeneric {
+ // type for the outgoing call (will be generic)
+ private final MethodType targetType;
+ // position of (first) argument to participate in filtering
+ private final short argumentPosition;
+ // number of arguments to participate in filtering
+ private final short argumentCount;
+ // how the result interacts with the filtered arguments: Prepend, Append, Replace, Discard
+ private final char replaceMode;
+ // prototype adapter (clone and customize for each new target & conversion!)
+ private final Adapter adapter;
+ // entry point for adapter (Adapter mh, a...) => ...
+ private final MethodHandle entryPoint;
+ // more of them (loosely cached)
+ private FilterGeneric variations;
+
+ /** Compute and cache information common to all unboxing adapters
+ * that can call out to targets of the erasure-family of the given erased type.
+ */
+ // TO DO: Make this private.
+ FilterGeneric(MethodType targetType, short argumentPosition, short argumentCount, char replaceMode) {
+ if (argumentCount == 0) {
+ if (replaceMode == 'P' || replaceMode == 'A') replaceMode = 'R';
+ if (replaceMode == 'I') argumentPosition = 0;
+ }
+ this.targetType = targetType;
+ this.argumentPosition = argumentPosition;
+ this.argumentCount = argumentCount;
+ this.replaceMode = replaceMode;
+ validate(targetType, argumentPosition, argumentCount, replaceMode);
+ Adapter ad = findAdapter(targetType, argumentPosition, argumentCount, replaceMode, filterType());
+ if (ad == null)
+ ad = buildAdapterFromBytecodes(targetType, argumentPosition, argumentCount, replaceMode, filterType());
+ this.adapter = ad;
+ this.entryPoint = ad.prototypeEntryPoint();
+ }
+
+ Adapter makeInstance(MethodHandle filter, MethodHandle target) {
+ return adapter.makeInstance(entryPoint, filter, target);
+ }
+
+ /** Build an adapter of the given generic type, which invokes typedTarget
+ * on the incoming arguments, after unboxing as necessary.
+ * The return value is boxed if necessary.
+ * @param genericType the required type of the result
+ * @param typedTarget the target
+ * @return an adapter method handle
+ */
+ public static MethodHandle make(MethodHandle target, int pos, MethodHandle filter) {
+ return FilterGeneric.of(target.type(), (short)pos, (short)1, 'R').makeInstance(filter, target);
+ }
+
+ /** Return the adapter information for this type's erasure. */
+ static FilterGeneric of(MethodType type, short ap, short ac, char mode) {
+ if (type.generic() != type)
+ throw new IllegalArgumentException("must be generic: "+type);
+ validate(type, ap, ac, mode);
+ MethodTypeImpl form = MethodTypeImpl.of(type);
+ FilterGeneric filterGen = form.filterGeneric;
+ if (filterGen == null)
+ form.filterGeneric = filterGen = new FilterGeneric(type, (short)0, (short)1, 'R');
+ return find(filterGen, ap, ac, mode);
+ }
+
+ static FilterGeneric find(FilterGeneric gen, short ap, short ac, char mode) {
+ for (;;) {
+ if (gen.argumentPosition == ap &&
+ gen.argumentCount == ac &&
+ gen.replaceMode == mode) {
+ return gen;
+ }
+ FilterGeneric gen2 = gen.variations;
+ if (gen2 == null) break;
+ gen = gen2;
+ }
+ FilterGeneric gen2 = new FilterGeneric(gen.targetType, ap, ac, mode);
+ gen.variations = gen2; // OK if this smashes another cached chain
+ return gen2;
+ }
+
+ private static void validate(MethodType type, short ap, short ac, char mode) {
+ int endpos = ap + ac;
+ switch (mode) {
+ case 'P': case 'A': case 'R': case 'D':
+ if (ap >= 0 && ac >= 0 &&
+ endpos >= 0 && endpos <= type.parameterCount())
+ return;
+ default:
+ throw new InternalError("configuration "+patternName(ap, ac, mode));
+ }
+ }
+
+ public String toString() {
+ return "FilterGeneric/"+patternName()+targetType;
+ }
+
+ String patternName() {
+ return patternName(argumentPosition, argumentCount, replaceMode);
+ }
+
+ static String patternName(short ap, short ac, char mode) {
+ return ""+mode+ap+(ac>1?"_"+ac:"");
+ }
+
+ Class<?> filterType() {
+ return Object.class; // result of filter operation; an uninteresting type
+ }
+
+ static MethodType targetType(MethodType entryType, short ap, short ac, char mode,
+ Class<?> arg) {
+ MethodType type = entryType;
+ int pos = ap;
+ switch (mode) {
+ case 'A':
+ pos += ac;
+ case 'P':
+ type = type.insertParameterType(pos, arg);
+ break;
+ case 'I':
+ for (int i = 1; i < ac; i++)
+ type = type.dropParameterType(pos);
+ assert(type.parameterType(pos) == arg);
+ break;
+ case 'D':
+ break;
+ }
+ return type;
+ }
+
+ static MethodType entryType(MethodType targetType, short ap, short ac, char mode,
+ Class<?> arg) {
+ MethodType type = targetType;
+ int pos = ap;
+ switch (mode) {
+ case 'A':
+ pos += ac;
+ case 'P':
+ type = type.dropParameterType(pos);
+ break;
+ case 'I':
+ for (int i = 1; i < ac; i++)
+ type = type.insertParameterType(pos, arg);
+ assert(type.parameterType(pos) == arg);
+ break;
+ case 'D':
+ break;
+ }
+ return type;
+ }
+
+ /* Create an adapter that handles spreading calls for the given type. */
+ static Adapter findAdapter(MethodType targetType, short ap, short ac, char mode, Class<?> arg) {
+ MethodType entryType = entryType(targetType, ap, ac, mode, arg);
+ int argc = targetType.parameterCount();
+ String pname = patternName(ap, ac, mode);
+ String cname0 = "F"+argc;
+ String cname1 = "F"+argc+mode;
+ String cname2 = "F"+argc+pname;
+ String[] cnames = { cname0, cname1, cname1+"X", cname2 };
+ String iname = "invoke_"+pname;
+ // e.g., F5R; invoke_R3
+ for (String cname : cnames) {
+ Class<? extends Adapter> acls = Adapter.findSubClass(cname);
+ if (acls == null) continue;
+ // see if it has the required invoke method
+ MethodHandle entryPoint = null;
+ try {
+ entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
+ } catch (NoAccessException ex) {
+ }
+ if (entryPoint == null) continue;
+ Constructor<? extends Adapter> ctor = null;
+ try {
+ ctor = acls.getDeclaredConstructor(MethodHandle.class);
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ }
+ if (ctor == null) continue;
+ try {
+ // Produce an instance configured as a prototype.
+ return ctor.newInstance(entryPoint);
+ } catch (IllegalArgumentException ex) {
+ } catch (InvocationTargetException ex) {
+ } catch (InstantiationException ex) {
+ } catch (IllegalAccessException ex) {
+ }
+ }
+ return null;
+ }
+
+ static Adapter buildAdapterFromBytecodes(MethodType targetType, short ap, short ac, char mode, Class<?> arg) {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * This adapter takes some untyped arguments, and returns an untyped result.
+ * Internally, it applies the invoker to the target, which causes the
+ * objects to be unboxed; the result is a raw type in L/I/J/F/D.
+ * This result is passed to convert, which is responsible for
+ * converting the raw result into a boxed object.
+ * The invoker is kept separate from the target because it can be
+ * generated once per type erasure family, and reused across adapters.
+ */
+ static abstract class Adapter extends JavaMethodHandle {
+ protected final MethodHandle filter;
+ protected final MethodHandle target;
+
+ protected boolean isPrototype() { return target == null; }
+ protected Adapter(MethodHandle entryPoint) {
+ this(entryPoint, entryPoint, null);
+ assert(isPrototype());
+ }
+ protected MethodHandle prototypeEntryPoint() {
+ if (!isPrototype()) throw new InternalError();
+ return filter;
+ }
+
+ protected Adapter(MethodHandle entryPoint,
+ MethodHandle filter, MethodHandle target) {
+ super(entryPoint);
+ this.filter = filter;
+ this.target = target;
+ }
+
+ /** Make a copy of self, with new fields. */
+ protected abstract Adapter makeInstance(MethodHandle entryPoint,
+ MethodHandle filter, MethodHandle target);
+ // { return new ThisType(entryPoint, filter, target); }
+
+ static private final String CLASS_PREFIX; // "sun.dyn.FilterGeneric$"
+ static {
+ String aname = Adapter.class.getName();
+ String sname = Adapter.class.getSimpleName();
+ if (!aname.endsWith(sname)) throw new InternalError();
+ CLASS_PREFIX = aname.substring(0, aname.length() - sname.length());
+ }
+ /** Find a sibing class of Adapter. */
+ static Class<? extends Adapter> findSubClass(String name) {
+ String cname = Adapter.CLASS_PREFIX + name;
+ try {
+ return Class.forName(cname).asSubclass(Adapter.class);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (ClassCastException ex) {
+ return null;
+ }
+ }
+ }
+
+ //* generated classes follow this pattern:
+ static class F1RX extends Adapter {
+ protected F1RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F1RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F1RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F1RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.<Object>invoke(a0); }
+ protected Object target(Object a0) { return target.<Object>invoke(a0); }
+ protected Object invoke_R0(Object a0) { return target(filter(a0)); }
+ }
+ static class F2RX extends Adapter {
+ protected F2RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F2RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F2RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F2RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.<Object>invoke(a0); }
+ protected Object target(Object a0, Object a1) { return target.<Object>invoke(a0, a1); }
+ protected Object invoke_R0(Object a0, Object a1) { return target(filter(a0), a1); }
+ protected Object invoke_R1(Object a0, Object a1) { return target(a0, filter(a1)); }
+ }
+ static class F3RX extends Adapter {
+ protected F3RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F3RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F3RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F3RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.<Object>invoke(a0); }
+ protected Object target(Object a0, Object a1, Object a2) { return target.<Object>invoke(a0, a1, a2); }
+ protected Object invoke_R0(Object a0, Object a1, Object a2) { return target(filter(a0), a1, a2); }
+ protected Object invoke_R1(Object a0, Object a1, Object a2) { return target(a0, filter(a1), a2); }
+ protected Object invoke_R2(Object a0, Object a1, Object a2) { return target(a0, a1, filter(a2)); }
+ }
+ static class F4RX extends Adapter {
+ protected F4RX(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected F4RX(MethodHandle e, MethodHandle f, MethodHandle t)
+ { super(e, f, t); }
+ protected F4RX makeInstance(MethodHandle e, MethodHandle f, MethodHandle t)
+ { return new F4RX(e, f, t); }
+ protected Object filter(Object a0) { return filter.<Object>invoke(a0); }
+ protected Object target(Object a0, Object a1, Object a2, Object a3) { return target.<Object>invoke(a0, a1, a2, a3); }
+ protected Object invoke_R0(Object a0, Object a1, Object a2, Object a3) { return target(filter(a0), a1, a2, a3); }
+ protected Object invoke_R1(Object a0, Object a1, Object a2, Object a3) { return target(a0, filter(a1), a2, a3); }
+ protected Object invoke_R2(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, filter(a2), a3); }
+ protected Object invoke_R3(Object a0, Object a1, Object a2, Object a3) { return target(a0, a1, a2, filter(a3)); }
+ }
+ // */
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/FilterOneArgument.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+
+/**
+ * Unary function composition, useful for many small plumbing jobs.
+ * The invoke method takes a single reference argument, and returns a reference
+ * Internally, it first calls the {@code filter} method on the argument,
+ * Making up the difference between the raw method type and the
+ * final method type is the responsibility of a JVM-level adapter.
+ * @author jrose
+ */
+public class FilterOneArgument extends JavaMethodHandle {
+ protected final MethodHandle filter; // Object -> Object
+ protected final MethodHandle target; // Object -> Object
+
+ protected Object entryPoint(Object argument) {
+ Object filteredArgument = filter.<Object>invoke(argument);
+ return target.<Object>invoke(filteredArgument);
+ }
+
+ private static final MethodHandle entryPoint =
+ MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "entryPoint", MethodType.makeGeneric(1));
+
+ protected FilterOneArgument(MethodHandle filter, MethodHandle target) {
+ super(entryPoint);
+ this.filter = filter;
+ this.target = target;
+ }
+
+ public static MethodHandle make(MethodHandle filter, MethodHandle target) {
+ if (filter == null) return target;
+ if (target == null) return filter;
+ return new FilterOneArgument(filter, target);
+ }
+
+ public String toString() {
+ return filter + "|>" + target;
+ }
+
+// MethodHandle make(MethodHandle filter1, MethodHandle filter2, MethodHandle target) {
+// MethodHandle filter = make(filter1, filter2);
+// return make(filter, target);
+// }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/dyn/FromGeneric.java Thu May 14 09:53:35 2009 -0700
@@ -0,0 +1,627 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.JavaMethodHandle;
+import java.dyn.MethodHandle;
+import java.dyn.MethodHandles;
+import java.dyn.MethodType;
+import java.dyn.NoAccessException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import sun.dyn.util.ValueConversions;
+import sun.dyn.util.Wrapper;
+
+/**
+ * Adapters which mediate between incoming calls which are not generic
+ * and outgoing calls which are. Any call can be represented generically
+ * boxing up its arguments, and (on return) unboxing the return value.
+ * <p>
+ * A call is "generic" (in MethodHandle terms) if its MethodType features
+ * only Object arguments. A non-generic call therefore features
+ * primitives and/or reference types other than Object.
+ * An adapter has types for its incoming and outgoing calls.
+ * The incoming call type is simply determined by the adapter's type
+ * (the MethodType it presents to callers). The outgoing call type
+ * is determined by the adapter's target (a MethodHandle that the adapter
+ * either binds internally or else takes as a leading argument).
+ * (To stretch the term, adapter-like method handles may have multiple
+ * targets or be polymorphic across multiple call types.)
+ * <p>
+ * This adapter can sometimes be more directly implemented
+ * by the JVM's built-in OP_SPREAD_ARGS adapter.
+ * @author jrose
+ */
+class FromGeneric {
+ // type for the outgoing call (may have primitives, etc.)
+ private final MethodType targetType;
+ // type of the outgoing call internal to the adapter
+ private final MethodType internalType;
+ // prototype adapter (clone and customize for each new target!)
+ private final Adapter adapter;
+ // entry point for adapter (Adapter mh, a...) => ...
+ private final MethodHandle entryPoint;
+ // unboxing invoker of type (MH, Object**N) => raw return value
+ // it makes up the difference of internalType => targetType
+ private final MethodHandle unboxingInvoker;
+ // conversion which boxes a the target's raw return value
+ private final MethodHandle returnConversion;
+
+ /** Compute and cache information common to all unboxing adapters
+ * that can call out to targets of the erasure-family of the given erased type.
+ */
+ private FromGeneric(MethodType targetType) {
+ this.targetType = targetType;
+ MethodType internalType0;
+ // the target invoker will generally need casts on reference arguments
+ Adapter ad = findAdapter(internalType0 = targetType.erase());
+ if (ad != null) {
+ // Immediate hit to exactly the adapter we want,
+ // with no monkeying around with primitive types.
+ this.internalType = internalType0;
+ this.adapter = ad;
+ this.entryPoint = ad.prototypeEntryPoint();
+ this.returnConversion = computeReturnConversion(targetType, internalType0);
+ this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
+ return;
+ }
+
+ // outgoing primitive arguments will be wrapped; unwrap them
+ MethodType primsAsObj = MethodTypeImpl.of(targetType).primArgsAsBoxes();
+ MethodType objArgsRawRet = MethodTypeImpl.of(primsAsObj).primsAsInts();
+ if (objArgsRawRet != targetType)
+ ad = findAdapter(internalType0 = objArgsRawRet);
+ if (ad == null) {
+ ad = buildAdapterFromBytecodes(internalType0 = targetType);
+ }
+ this.internalType = internalType0;
+ this.adapter = ad;
+ MethodType tepType = targetType.insertParameterType(0, adapter.getClass());
+ this.entryPoint = ad.prototypeEntryPoint();
+ this.returnConversion = computeReturnConversion(targetType, internalType0);
+ this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
+ }
+
+ /**
+ * The typed target will be called according to targetType.
+ * The adapter code will in fact see the raw result from internalType,
+ * and must box it into an object. Produce a converter for this.
+ */
+ private static MethodHandle computeReturnConversion(
+ MethodType targetType, MethodType internalType) {
+ Class<?> tret = targetType.returnType();
+ Class<?> iret = internalType.returnType();
+ Wrapper wrap = Wrapper.forBasicType(tret);
+ if (!iret.isPrimitive()) {
+ assert(iret == Object.class);
+ return ValueConversions.identity();
+ } else if (wrap.primitiveType() == iret) {
+ return ValueConversions.box(wrap, false);
+ } else {
+ assert(tret == double.class ? iret == long.class : iret == int.class);
+ return ValueConversions.boxRaw(wrap, false);
+ }
+ }
+
+ /**
+ * The typed target will need an exact invocation point; provide it here.
+ * The adapter will possibly need to make a slightly different call,
+ * so adapt the invoker. This way, the logic for making up the
+ * difference between what the adapter can call and what the target
+ * needs can be cached once per type.
+ */
+ private static MethodHandle computeUnboxingInvoker(
+ MethodType targetType, MethodType internalType) {
+ // All the adapters we have here have reference-untyped internal calls.
+ assert(internalType == internalType.erase());
+ MethodHandle invoker = MethodHandles.exactInvoker(targetType);
+ // cast all narrow reference types, unbox all primitive arguments:
+ MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
+ MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN,
+ invoker, Invokers.invokerType(fixArgsType),
+ invoker.type(), null);
+ if (fixArgs == null)
+ throw new InternalError("bad fixArgs");
+ // reinterpret the calling sequence as raw:
+ MethodHandle retyper = AdapterMethodHandle.makeRawRetypeOnly(Access.TOKEN,
+ Invokers.invokerType(internalType), fixArgs);
+ if (retyper == null)
+ throw new InternalError("bad retyper");
+ return retyper;
+ }
+
+ Adapter makeInstance(MethodHandle typedTarget) {
+ MethodType type = typedTarget.type();
+ if (type == targetType) {
+ return adapter.makeInstance(entryPoint, unboxingInvoker, returnConversion, typedTarget);
+ }
+ // my erased-type is not exactly the same as the desired type
+ assert(type.erase() == targetType); // else we are busted
+ MethodHandle invoker = computeUnboxingInvoker(type, internalType);
+ return adapter.makeInstance(entryPoint, invoker, returnConversion, typedTarget);
+ }
+
+ /** Build an adapter of the given generic type, which invokes typedTarget
+ * on the incoming arguments, after unboxing as necessary.
+ * The return value is boxed if necessary.
+ * @param genericType the required type of the result
+ * @param typedTarget the target
+ * @return an adapter method handle
+ */
+ public static MethodHandle make(MethodHandle typedTarget) {
+ MethodType type = typedTarget.type();
+ if (type == type.generic()) return typedTarget;
+ return FromGeneric.of(type).makeInstance(typedTarget);
+ }
+
+ /** Return the adapter information for this type's erasure. */
+ static FromGeneric of(MethodType type) {
+ MethodTypeImpl form = MethodTypeImpl.of(type);
+ FromGeneric fromGen = form.fromGeneric;
+ if (fromGen == null)
+ form.fromGeneric = fromGen = new FromGeneric(form.erasedType());
+ return fromGen;
+ }
+
+ public String toString() {
+ return "FromGeneric"+targetType;
+ }
+
+ /* Create an adapter that handles spreading calls for the given type. */
+ static Adapter findAdapter(MethodType internalType) {
+ MethodType entryType = internalType.generic();
+ MethodTypeImpl form = MethodTypeImpl.of(internalType);
+ Class<?> rtype = internalType.returnType();
+ int argc = form.parameterCount();
+ int lac = form.longPrimitiveParameterCount();
+ int iac = form.primitiveParameterCount() - lac;
+ String intsAndLongs = (iac > 0 ? "I"+iac : "")+(lac > 0 ? "J"+lac : "");
+ String rawReturn = String.valueOf(Wrapper.forPrimitiveType(rtype).basicTypeChar());
+ String cname0 = rawReturn + argc;
+ String cname1 = "A" + argc;
+ String[] cnames = { cname0+intsAndLongs, cname0, cname1+intsAndLongs, cname1 };
+ String iname = "invoke_"+cname0+intsAndLongs;
+ // e.g., D5I2, D5, L5I2, L5; invoke_D5
+ for (String cname : cnames) {
+ Class<? extends Adapter> acls = Adapter.findSubClass(cname);
+ if (acls == null) continue;
+ // see if it has the required invoke method
+ MethodHandle entryPoint = null;
+ try {
+ entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
+ } catch (NoAccessException ex) {
+ }
+ if (entryPoint == null) continue;
+ Constructor<? extends Adapter> ctor = null;
+ try {
+ ctor = acls.getDeclaredConstructor(MethodHandle.class);
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ }
+ if (ctor == null) continue;
+ try {
+ // Produce an instance configured as a prototype.
+ return ctor.newInstance(entryPoint);
+ } catch (IllegalArgumentException ex) {
+ } catch (InvocationTargetException ex) {
+ } catch (InstantiationException ex) {
+ } catch (IllegalAccessException ex) {
+ }
+ }
+ return null;
+ }
+
+ static Adapter buildAdapterFromBytecodes(MethodType internalType) {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * This adapter takes some untyped arguments, and returns an untyped result.
+ * Internally, it applies the invoker to the target, which causes the
+ * objects to be unboxed; the result is a raw type in L/I/J/F/D.
+ * This result is passed to convert, which is responsible for
+ * converting the raw result into a boxed object.
+ * The invoker is kept separate from the target because it can be
+ * generated once per type erasure family, and reused across adapters.
+ */
+ static abstract class Adapter extends JavaMethodHandle {
+ /*
+ * class X<<R,int N>> extends Adapter {
+ * (MH, Object**N)=>raw(R) invoker;
+ * (any**N)=>R target;
+ * raw(R)=>Object convert;
+ * Object invoke(Object**N a) = convert(invoker(target, a...))
+ * }
+ */
+ protected final MethodHandle invoker; // (MH, Object**N) => raw(R)
+ protected final MethodHandle convert; // raw(R) => Object
+ protected final MethodHandle target; // (any**N) => R
+
+ protected boolean isPrototype() { return target == null; }
+ protected Adapter(MethodHandle entryPoint) {
+ this(entryPoint, null, entryPoint, null);
+ assert(isPrototype());
+ }
+ protected MethodHandle prototypeEntryPoint() {
+ if (!isPrototype()) throw new InternalError();
+ return convert;
+ }
+
+ protected Adapter(MethodHandle entryPoint,
+ MethodHandle invoker, MethodHandle convert, MethodHandle target) {
+ super(entryPoint);
+ this.invoker = invoker;
+ this.convert = convert;
+ this.target = target;
+ }
+
+ /** Make a copy of self, with new fields. */
+ protected abstract Adapter makeInstance(MethodHandle entryPoint,
+ MethodHandle invoker, MethodHandle convert, MethodHandle target);
+ // { return new ThisType(entryPoint, convert, target); }
+
+ /// Conversions on the value returned from the target.
+ protected Object convert_L(Object result) { return convert.<Object>invoke(result); }
+ protected Object convert_I(int result) { return convert.<Object>invoke(result); }
+ protected Object convert_J(long result) { return convert.<Object>invoke(result); }
+ protected Object convert_F(float result) { return convert.<Object>invoke(result); }
+ protected Object convert_D(double result) { return convert.<Object>invoke(result); }
+
+ static private final String CLASS_PREFIX; // "sun.dyn.FromGeneric$"
+ static {
+ String aname = Adapter.class.getName();
+ String sname = Adapter.class.getSimpleName();
+ if (!aname.endsWith(sname)) throw new InternalError();
+ CLASS_PREFIX = aname.substring(0, aname.length() - sname.length());
+ }
+ /** Find a sibing class of Adapter. */
+ static Class<? extends Adapter> findSubClass(String name) {
+ String cname = Adapter.CLASS_PREFIX + name;
+ try {
+ return Class.forName(cname).asSubclass(Adapter.class);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (ClassCastException ex) {
+ return null;
+ }
+ }
+ }
+
+ /* generated classes follow this pattern:
+ static class xA2 extends Adapter {
+ protected xA2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
+ protected xA2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { super(e, i, c, t); }
+ protected xA2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
+ { return new xA2(e, i, c, t); }
+ protected Object invoke_L2(Object a0, Object a1) { return convert_L(invoker.<Object>invoke(target, a0, a1)); }
+ protected Object invoke_I2(Object a0, Object a1) { return convert_I(invoker.<int >invoke(target, a0, a1)); }
+ protected Object invoke_J2(Object a0, Object a1) { return convert_J(invoker.<long >invoke(target, a0, a1)); }
+ protected Object invoke_F2(Object a0, Object a1) { return convert_F(invoker.<float >invoke(target, a0, a1)); }
+ protected Object invoke_D2(Object a0, Object a1) { return convert_D(invoker.<double>invoke(target, a0, a1)); }
+ }
+ // */
+
+/*
+: SHELL; n=FromGeneric; cp -p $n.java $n.java-; sed < $n.java- > $n.java+ -e '/{{*{{/,/}}*}}/w /tmp/genclasses.java' -e '/}}*}}/q'; (cd /tmp; javac -d . genclasses.java; java -cp . genclasses) >> $n.java+; echo '}' >> $n.java+; mv $n.java+ $n.java; mv $n.java- $n.java~
+//{{{
+import java.util.*;
+class genclasses {
+ static String[] TYPES = { "Object", "int ", "long ", "float ", "double" };
+ static String[] TCHARS = { "L", "I", "J", "F", "D", "A" };
+ static String[][] TEMPLATES = { {
+ "@for@ arity=0..10 rcat<=4 nrefs<=99 nints=0 nlongs=0",
+ " //@each-cat@",
+ " static class @cat@ extends Adapter {",
+ " protected @cat@(MethodHandle entryPoint) { super(entryPoint); } // to build prototype",
+ " protected @cat@(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)",
+ " { super(e, i, c, t); }",
+ " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)",
+ " { return new @cat@(e, i, c, t); }",
+ " //@each-R@",
+ " protected Object invoke_@catN@(@Tvav@) { return convert_@Rc@(invoker.<@R@>invoke(target@av@)); }",
+ " //@end-R@",
+ " }",
+ } };
+ static final String NEWLINE_INDENT = "\n ";
+ enum VAR {
+ cat, catN, R, Rc, av, Tvav, Ovav;
+ public final String pattern = "@"+toString().replace('_','.')+"@";
+ public String binding;
+ static void makeBindings(boolean topLevel, int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ if (topLevel)
+ VAR.cat.binding = catstr(ALL_RETURN_TYPES ? TYPES.length : rcat, nrefs, nints, nlongs);
+ VAR.catN.binding = catstr(rcat, nrefs, nints, nlongs);
+ VAR.R.binding = TYPES[rcat];
+ VAR.Rc.binding = TCHARS[rcat];
+ String[] Tv = new String[nargs];
+ String[] av = new String[nargs];
+ String[] Tvav = new String[nargs];
+ String[] Ovav = new String[nargs];
+ for (int i = 0; i < nargs; i++) {
+ int tcat = (i < nrefs) ? 0 : (i < nrefs + nints) ? 1 : 2;
+ Tv[i] = TYPES[tcat];
+ av[i] = arg(i);
+ Tvav[i] = param(Tv[i], av[i]);
+ Ovav[i] = param("Object", av[i]);
+ }
+ VAR.av.binding = comma(", ", av);
+ VAR.Tvav.binding = comma(Tvav);
+ VAR.Ovav.binding = comma(Ovav);
+ }
+ static String arg(int i) { return "a"+i; }
+ static String param(String t, String a) { return t+" "+a; }
+ static String comma(String[] v) { return comma("", v); }
+ static String comma(String sep, String[] v) {
+ if (v.length == 0) return "";
+ String res = sep+v[0];
+ for (int i = 1; i < v.length; i++) res += ", "+v[i];
+ return res;
+ }
+ static String transform(String string) {
+ for (VAR var : values())
+ string = string.replaceAll(var.pattern, var.binding);
+ return string;
+ }
+ }
+ static String[] stringsIn(String[] strings, int beg, int end) {
+ return Arrays.copyOfRange(strings, beg, Math.min(end, strings.length));
+ }
+ static String[] stringsBefore(String[] strings, int pos) {
+ return stringsIn(strings, 0, pos);
+ }
+ static String[] stringsAfter(String[] strings, int pos) {
+ return stringsIn(strings, pos, strings.length);
+ }
+ static int indexAfter(String[] strings, int pos, String tag) {
+ return Math.min(indexBefore(strings, pos, tag) + 1, strings.length);
+ }
+ static int indexBefore(String[] strings, int pos, String tag) {
+ for (int i = pos, end = strings.length; ; i++) {
+ if (i == end || strings[i].endsWith(tag)) return i;
+ }
+ }
+ static int MIN_ARITY, MAX_ARITY, MAX_RCAT, MAX_REFS, MAX_INTS, MAX_LONGS;
+ static boolean ALL_ARG_TYPES, ALL_RETURN_TYPES;
+ static HashSet<String> done = new HashSet<String>();
+ public static void main(String... av) {
+ for (String[] template : TEMPLATES) {
+ int forLinesLimit = indexBefore(template, 0, "@each-cat@");
+ String[] forLines = stringsBefore(template, forLinesLimit);
+ template = stringsAfter(template, forLinesLimit);
+ for (String forLine : forLines)
+ expandTemplate(forLine, template);
+ }
+ }
+ static void expandTemplate(String forLine, String[] template) {
+ String[] params = forLine.split("[^0-9]+");
+ if (params[0].length() == 0) params = stringsAfter(params, 1);
+ System.out.println("//params="+Arrays.asList(params));
+ int pcur = 0;
+ MIN_ARITY = Integer.valueOf(params[pcur++]);
+ MAX_ARITY = Integer.valueOf(params[pcur++]);
+ MAX_RCAT = Integer.valueOf(params[pcur++]);
+ MAX_REFS = Integer.valueOf(params[pcur++]);
+ MAX_INTS = Integer.valueOf(params[pcur++]);
+ MAX_LONGS = Integer.valueOf(params[pcur++]);
+ if (pcur != params.length) throw new RuntimeException("bad extra param: "+forLine);
+ if (MAX_RCAT >= TYPES.length) MAX_RCAT = TYPES.length - 1;
+ ALL_ARG_TYPES = (indexBefore(template, 0, "@each-Tv@") < template.length);
+ ALL_RETURN_TYPES = (indexBefore(template, 0, "@each-R@") < template.length);
+ for (int nargs = MIN_ARITY; nargs <= MAX_ARITY; nargs++) {
+ for (int rcat = 0; rcat <= MAX_RCAT; rcat++) {
+ expandTemplate(template, true, rcat, nargs, 0, 0);
+ if (ALL_ARG_TYPES) break;
+ expandTemplateForPrims(template, true, rcat, nargs, 1, 1);
+ if (ALL_RETURN_TYPES) break;
+ }
+ }
+ }
+ static String catstr(int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ String cat = TCHARS[rcat] + nargs;
+ if (!ALL_ARG_TYPES) cat += (nints==0?"":"I"+nints)+(nlongs==0?"":"J"+nlongs);
+ return cat;
+ }
+ static void expandTemplateForPrims(String[] template, boolean topLevel, int rcat, int nargs, int minints, int minlongs) {
+ for (int isLong = 0; isLong <= 1; isLong++) {
+ for (int nprims = 1; nprims <= nargs; nprims++) {
+ int nrefs = nargs - nprims;
+ int nints = ((1-isLong) * nprims);
+ int nlongs = (isLong * nprims);
+ expandTemplate(template, topLevel, rcat, nrefs, nints, nlongs);
+ }
+ }
+ }
+ static void expandTemplate(String[] template, boolean topLevel,
+ int rcat, int nrefs, int nints, int nlongs) {
+ int nargs = nrefs + nints + nlongs;
+ if (nrefs > MAX_REFS || nints > MAX_INTS || nlongs > MAX_LONGS) return;
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ if (topLevel && !done.add(VAR.cat.binding)) {
+ System.out.println(" //repeat "+VAR.cat.binding);
+ return;
+ }
+ for (int i = 0; i < template.length; i++) {
+ String line = template[i];
+ if (line.endsWith("@each-cat@")) {
+ // ignore
+ } else if (line.endsWith("@each-R@")) {
+ int blockEnd = indexAfter(template, i, "@end-R@");
+ String[] block = stringsIn(template, i+1, blockEnd-1);
+ for (int rcat1 = rcat; rcat1 <= MAX_RCAT; rcat1++)
+ expandTemplate(block, false, rcat1, nrefs, nints, nlongs);
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+ i = blockEnd-1; continue;
+ } else if (line.endsWith("@each-Tv@")) {
+ int blockEnd = indexAfter(template, i, "@end-Tv@");
+ String[] block = stringsIn(template, i+1, blockEnd-1);
+ expandTemplate(block, false, rcat, nrefs, nints, nlongs);
+ expandTemplateForPrims(block, false, rcat, nargs, nints+1, nlongs+1);
+ VAR.makeBindings(topLevel, rcat, nrefs, nints, nlongs);
+