Merge
This commit is contained in:
commit
8f52d15f97
jdk
make
src
share
classes
java
dyn
CallSite.javaInvokeDynamic.javaInvokeDynamicBootstrapError.javaJavaMethodHandle.javaLinkage.javaLinkagePermission.javaMethodHandle.javaMethodHandles.javaMethodType.javaMethodTypeForm.javaNoAccessException.javaWrongMethodTypeException.javapackage-info.java
io
lang
util/zip
sun
dyn
Access.javaAdapterMethodHandle.javaBoundMethodHandle.javaCallSiteImpl.javaDirectMethodHandle.javaFilterGeneric.javaFilterOneArgument.javaFromGeneric.javaInvokers.javaMemberName.javaMethodHandleImpl.javaMethodHandleNatives.javaMethodTypeImpl.javaToGeneric.java
anon
AnonymousClassLoader.javaConstantPoolParser.javaConstantPoolPatch.javaConstantPoolVisitor.javaInvalidConstantPoolFormatException.java
empty
package-info.javautil
misc
javavm/export
native/common
windows
classes/sun/nio/ch
native/sun/nio/ch
test/java
lang/Runtime/shutdown
nio/channels/AsynchronousSocketChannel
@ -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.* \
|
||||
|
@ -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 fdlibm java sun_nio jli main zip
|
||||
# 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
|
||||
|
||||
|
||||
|
44
jdk/make/java/dyn/Makefile
Normal file
44
jdk/make/java/dyn/Makefile
Normal file
@ -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
|
201
jdk/src/share/classes/java/dyn/CallSite.java
Normal file
201
jdk/src/share/classes/java/dyn/CallSite.java
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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</code> call site, as reified to the bootstrap method.
|
||||
* Every instance of a call site corresponds to a distinct instance
|
||||
* of the <code>invokedynamic</code> instruction.
|
||||
* Call sites have state, one reference word, called the <code>target</code>,
|
||||
* and typed as a {@link MethodHandle}. When this state is null (as it is
|
||||
* initially) the call site is in the unlinked state. Otherwise, it is said
|
||||
* to be linked to its target.
|
||||
* <p>
|
||||
* When an unlinked call site is executed, a bootstrap routine is called
|
||||
* to finish the execution of the call site, and optionally to link
|
||||
* the call site.
|
||||
* <p>
|
||||
* @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;
|
||||
|
||||
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}.
|
||||
*/
|
||||
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 is null if and 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 WrongMethodTypeException if the new target is not null
|
||||
* and 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) {
|
||||
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 immutable static context.
|
||||
* @return class containing the call site
|
||||
*/
|
||||
public Class<?> callerClass() {
|
||||
return (Class) caller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the method name specified in the {@code invokedynamic} instruction.
|
||||
* This is immutable static context.
|
||||
* <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 immutable static context.
|
||||
* @return method type specified by the call site
|
||||
*/
|
||||
public MethodType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]";
|
||||
}
|
||||
}
|
39
jdk/src/share/classes/java/dyn/InvokeDynamic.java
Normal file
39
jdk/src/share/classes/java/dyn/InvokeDynamic.java
Normal file
@ -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;
|
||||
|
||||
/**
|
||||
* Syntactic marker interface to request javac to emit an {@code invokedynamic} instruction.
|
||||
* <p>
|
||||
* This type has no particular meaning as a class or interface supertype, and can never be instantiated.
|
||||
* Logically, it denotes a source of all dynamically typed methods.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public final class InvokeDynamic {
|
||||
private InvokeDynamic() { throw new InternalError(); } // do not instantiate
|
||||
|
||||
// no statically defined static methods
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
83
jdk/src/share/classes/java/dyn/JavaMethodHandle.java
Normal file
83
jdk/src/share/classes/java/dyn/JavaMethodHandle.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
199
jdk/src/share/classes/java/dyn/Linkage.java
Normal file
199
jdk/src/share/classes/java/dyn/Linkage.java
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Register a bootstrap method for use for a given caller class.
|
||||
* The method handle must be of a type equivalent to {@link Linkage#makeCallSite}.
|
||||
* <p>
|
||||
* The operation will fail with an exception 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, either from an embedded
|
||||
* {@code BootstrapInvokeDynamic} classfile attribute, or 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of registerBootstrapMethod for self-registration,
|
||||
* to be called from a static initializer.
|
||||
* Finds a static method of type (CallSite, Object[]) -> Object 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of registerBootstrapMethod for self-registration,
|
||||
* to be called from a static initializer.
|
||||
* Finds a static method of type (CallSite, Object[]) -> Object 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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>();
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
111
jdk/src/share/classes/java/dyn/LinkagePermission.java
Normal file
111
jdk/src/share/classes/java/dyn/LinkagePermission.java
Normal file
@ -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());
|
||||
}
|
||||
}
|
135
jdk/src/share/classes/java/dyn/MethodHandle.java
Normal file
135
jdk/src/share/classes/java/dyn/MethodHandle.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
1092
jdk/src/share/classes/java/dyn/MethodHandles.java
Normal file
1092
jdk/src/share/classes/java/dyn/MethodHandles.java
Normal file
File diff suppressed because it is too large
Load Diff
575
jdk/src/share/classes/java/dyn/MethodType.java
Normal file
575
jdk/src/share/classes/java/dyn/MethodType.java
Normal file
@ -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 {@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);
|
||||
}
|
||||
}
|
39
jdk/src/share/classes/java/dyn/MethodTypeForm.java
Normal file
39
jdk/src/share/classes/java/dyn/MethodTypeForm.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
75
jdk/src/share/classes/java/dyn/NoAccessException.java
Normal file
75
jdk/src/share/classes/java/dyn/NoAccessException.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
59
jdk/src/share/classes/java/dyn/WrongMethodTypeException.java
Normal file
59
jdk/src/share/classes/java/dyn/WrongMethodTypeException.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
32
jdk/src/share/classes/java/dyn/package-info.java
Normal file
32
jdk/src/share/classes/java/dyn/package-info.java
Normal file
@ -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;
|
@ -503,20 +503,25 @@ public final class Console implements Flushable
|
||||
|
||||
// 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() {
|
||||
|
@ -34,23 +34,31 @@ import java.io.File;
|
||||
*/
|
||||
|
||||
class DeleteOnExitHook {
|
||||
static {
|
||||
sun.misc.SharedSecrets.getJavaLangAccess()
|
||||
.registerShutdownHook(2 /* Shutdown hook invocation order */,
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
runHooks();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static LinkedHashSet<String> files = new LinkedHashSet<String>();
|
||||
static {
|
||||
// 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 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);
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* 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, Runnable hook) {
|
||||
static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
|
||||
synchronized (lock) {
|
||||
if (state > RUNNING)
|
||||
throw new IllegalStateException("Shutdown in progress");
|
||||
|
||||
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) {
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -84,7 +84,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
|
||||
* @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
|
||||
|
@ -108,7 +108,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
|
||||
*
|
||||
* @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
|
||||
|
109
jdk/src/share/classes/sun/dyn/Access.java
Normal file
109
jdk/src/share/classes/sun/dyn/Access.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
728
jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java
Normal file
728
jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java
Normal file
@ -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
|
||||
}
|
180
jdk/src/share/classes/sun/dyn/BoundMethodHandle.java
Normal file
180
jdk/src/share/classes/sun/dyn/BoundMethodHandle.java
Normal file
@ -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() + "]";
|
||||
}
|
||||
}
|
70
jdk/src/share/classes/sun/dyn/CallSiteImpl.java
Normal file
70
jdk/src/share/classes/sun/dyn/CallSiteImpl.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
56
jdk/src/share/classes/sun/dyn/DirectMethodHandle.java
Normal file
56
jdk/src/share/classes/sun/dyn/DirectMethodHandle.java
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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();
|
||||
|
||||
// Null check and replace privilege token (as passed to JVM) with null.
|
||||
if (lookupClass.equals(Access.class)) lookupClass = null;
|
||||
MethodHandleNatives.init(this, (Object) m, doDispatch, lookupClass);
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
return (vmindex != VM_INDEX_UNINITIALIZED);
|
||||
}
|
||||
}
|
338
jdk/src/share/classes/sun/dyn/FilterGeneric.java
Normal file
338
jdk/src/share/classes/sun/dyn/FilterGeneric.java
Normal file
@ -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)); }
|
||||
}
|
||||
// */
|
||||
}
|
73
jdk/src/share/classes/sun/dyn/FilterOneArgument.java
Normal file
73
jdk/src/share/classes/sun/dyn/FilterOneArgument.java
Normal file
@ -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);
|
||||
// }
|
||||
}
|
627
jdk/src/share/classes/sun/dyn/FromGeneric.java
Normal file
627
jdk/src/share/classes/sun/dyn/FromGeneric.java
Normal file
@ -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);
|
||||
i = blockEnd-1; continue;
|
||||
} else {
|
||||
System.out.println(VAR.transform(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//}}} */
|
||||
//params=[0, 10, 4, 99, 0, 0]
|
||||
static class A0 extends Adapter {
|
||||
protected A0(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A0(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A0(e, i, c, t); }
|
||||
protected Object invoke_L0() { return convert_L(invoker.<Object>invoke(target)); }
|
||||
protected Object invoke_I0() { return convert_I(invoker.<int >invoke(target)); }
|
||||
protected Object invoke_J0() { return convert_J(invoker.<long >invoke(target)); }
|
||||
protected Object invoke_F0() { return convert_F(invoker.<float >invoke(target)); }
|
||||
protected Object invoke_D0() { return convert_D(invoker.<double>invoke(target)); }
|
||||
}
|
||||
static class A1 extends Adapter {
|
||||
protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A1(e, i, c, t); }
|
||||
protected Object invoke_L1(Object a0) { return convert_L(invoker.<Object>invoke(target, a0)); }
|
||||
protected Object invoke_I1(Object a0) { return convert_I(invoker.<int >invoke(target, a0)); }
|
||||
protected Object invoke_J1(Object a0) { return convert_J(invoker.<long >invoke(target, a0)); }
|
||||
protected Object invoke_F1(Object a0) { return convert_F(invoker.<float >invoke(target, a0)); }
|
||||
protected Object invoke_D1(Object a0) { return convert_D(invoker.<double>invoke(target, a0)); }
|
||||
}
|
||||
static class A2 extends Adapter {
|
||||
protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A2(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)); }
|
||||
}
|
||||
static class A3 extends Adapter {
|
||||
protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A3(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A3(e, i, c, t); }
|
||||
protected Object invoke_L3(Object a0, Object a1, Object a2) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2)); }
|
||||
protected Object invoke_I3(Object a0, Object a1, Object a2) { return convert_I(invoker.<int >invoke(target, a0, a1, a2)); }
|
||||
protected Object invoke_J3(Object a0, Object a1, Object a2) { return convert_J(invoker.<long >invoke(target, a0, a1, a2)); }
|
||||
protected Object invoke_F3(Object a0, Object a1, Object a2) { return convert_F(invoker.<float >invoke(target, a0, a1, a2)); }
|
||||
protected Object invoke_D3(Object a0, Object a1, Object a2) { return convert_D(invoker.<double>invoke(target, a0, a1, a2)); }
|
||||
}
|
||||
static class A4 extends Adapter {
|
||||
protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A4(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A4(e, i, c, t); }
|
||||
protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3)); }
|
||||
protected Object invoke_I4(Object a0, Object a1, Object a2, Object a3) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3)); }
|
||||
protected Object invoke_J4(Object a0, Object a1, Object a2, Object a3) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3)); }
|
||||
protected Object invoke_F4(Object a0, Object a1, Object a2, Object a3) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3)); }
|
||||
protected Object invoke_D4(Object a0, Object a1, Object a2, Object a3) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3)); }
|
||||
}
|
||||
static class A5 extends Adapter {
|
||||
protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A5(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A5(e, i, c, t); }
|
||||
protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4)); }
|
||||
protected Object invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4)); }
|
||||
protected Object invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4)); }
|
||||
protected Object invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4)); }
|
||||
protected Object invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4)); }
|
||||
}
|
||||
static class A6 extends Adapter {
|
||||
protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A6(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A6(e, i, c, t); }
|
||||
protected Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5)); }
|
||||
protected Object invoke_I6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5)); }
|
||||
protected Object invoke_J6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5)); }
|
||||
protected Object invoke_F6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5)); }
|
||||
protected Object invoke_D6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5)); }
|
||||
}
|
||||
static class A7 extends Adapter {
|
||||
protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A7(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A7(e, i, c, t); }
|
||||
protected Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
|
||||
protected Object invoke_I7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
|
||||
protected Object invoke_J7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
|
||||
protected Object invoke_F7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
|
||||
protected Object invoke_D7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6)); }
|
||||
}
|
||||
static class A8 extends Adapter {
|
||||
protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A8(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A8(e, i, c, t); }
|
||||
protected Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
|
||||
protected Object invoke_I8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
|
||||
protected Object invoke_J8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
|
||||
protected Object invoke_F8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
|
||||
protected Object invoke_D8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7)); }
|
||||
}
|
||||
static class A9 extends Adapter {
|
||||
protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A9(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A9(e, i, c, t); }
|
||||
protected Object invoke_L9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
|
||||
protected Object invoke_I9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
|
||||
protected Object invoke_J9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
|
||||
protected Object invoke_F9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
|
||||
protected Object invoke_D9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); }
|
||||
}
|
||||
static class A10 extends Adapter {
|
||||
protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype
|
||||
protected A10(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ super(e, i, c, t); }
|
||||
protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)
|
||||
{ return new A10(e, i, c, t); }
|
||||
protected Object invoke_L10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_L(invoker.<Object>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
|
||||
protected Object invoke_I10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_I(invoker.<int >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
|
||||
protected Object invoke_J10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_J(invoker.<long >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
|
||||
protected Object invoke_F10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_F(invoker.<float >invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
|
||||
protected Object invoke_D10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) { return convert_D(invoker.<double>invoke(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); }
|
||||
}
|
||||
}
|
86
jdk/src/share/classes/sun/dyn/Invokers.java
Normal file
86
jdk/src/share/classes/sun/dyn/Invokers.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.MethodType;
|
||||
|
||||
|
||||
/**
|
||||
* Construction and caching of often-used invokers.
|
||||
* @author jrose
|
||||
*/
|
||||
public class Invokers {
|
||||
// exact type (sans leading taget MH) for the outgoing call
|
||||
private final MethodType targetType;
|
||||
|
||||
// exact invoker for the outgoing call
|
||||
private /*lazy*/ MethodHandle exactInvoker;
|
||||
|
||||
// generic (untyped) invoker for the outgoing call
|
||||
private /*lazy*/ MethodHandle genericInvoker;
|
||||
|
||||
/** Compute and cache information common to all collecting adapters
|
||||
* that implement members of the erasure-family of the given erased type.
|
||||
*/
|
||||
public Invokers(Access token, MethodType targetType) {
|
||||
Access.check(token);
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
public static MethodType invokerType(MethodType targetType) {
|
||||
return targetType.insertParameterType(0, MethodHandle.class);
|
||||
}
|
||||
|
||||
public MethodHandle exactInvoker() {
|
||||
MethodHandle invoker = exactInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
|
||||
if (invoker == null) throw new InternalError("JVM cannot find invoker for "+targetType);
|
||||
assert(invokerType(targetType) == invoker.type());
|
||||
exactInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public MethodHandle genericInvoker() {
|
||||
MethodHandle invoker1 = exactInvoker();
|
||||
MethodHandle invoker = genericInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
MethodType genericType = targetType.generic();
|
||||
invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType));
|
||||
genericInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public MethodHandle varargsInvoker() {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Invokers"+targetType;
|
||||
}
|
||||
}
|
552
jdk/src/share/classes/sun/dyn/MemberName.java
Normal file
552
jdk/src/share/classes/sun/dyn/MemberName.java
Normal file
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* 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.BytecodeSignature;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import static sun.dyn.MethodHandleNatives.Constants.*;
|
||||
|
||||
/**
|
||||
* Compact information which fully characterizes a method or field reference.
|
||||
* When resolved, it includes a direct pointer to JVM metadata.
|
||||
* This representation is stateless and only decriptive.
|
||||
* It provides no private information and no capability to use the member.
|
||||
* <p>
|
||||
* By contrast, a java.lang.reflect.Method contains fuller information
|
||||
* about the internals of a method (except its bytecodes) and also
|
||||
* allows invocation. A MemberName is much lighter than a reflect.Method,
|
||||
* since it contains about 7 fields to Method's 16 (plus its sub-arrays),
|
||||
* and those seven fields omit much of the information in Method.
|
||||
* @author jrose
|
||||
*/
|
||||
public final class MemberName implements Member, Cloneable {
|
||||
private Class<?> clazz; // class in which the method is defined
|
||||
private String name; // may be null if not yet materialized
|
||||
private Object type; // may be null if not yet materialized
|
||||
private int flags; // modifier bits; see reflect.Modifier
|
||||
|
||||
private Object vmtarget; // VM-specific target value
|
||||
private int vmindex; // method index within class or interface
|
||||
|
||||
{ vmindex = VM_INDEX_UNINITIALIZED; }
|
||||
|
||||
public Class<?> getDeclaringClass() {
|
||||
if (clazz == null && isResolved()) {
|
||||
expandFromVM();
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return clazz.getClassLoader();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
expandFromVM();
|
||||
if (name == null) return null;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public MethodType getMethodType() {
|
||||
if (type == null) {
|
||||
expandFromVM();
|
||||
if (type == null) return null;
|
||||
}
|
||||
if (!isInvocable())
|
||||
throw newIllegalArgumentException("not invocable, no method type");
|
||||
if (type instanceof MethodType) {
|
||||
return (MethodType) type;
|
||||
}
|
||||
if (type instanceof String) {
|
||||
String sig = (String) type;
|
||||
MethodType res = MethodType.fromBytecodeString(sig, getClassLoader());
|
||||
this.type = res;
|
||||
return res;
|
||||
}
|
||||
if (type instanceof Object[]) {
|
||||
Object[] typeInfo = (Object[]) type;
|
||||
Class<?>[] ptypes = (Class<?>[]) typeInfo[1];
|
||||
Class<?> rtype = (Class<?>) typeInfo[0];
|
||||
MethodType res = MethodType.make(rtype, ptypes);
|
||||
this.type = res;
|
||||
return res;
|
||||
}
|
||||
throw new InternalError("bad method type "+type);
|
||||
}
|
||||
|
||||
public MethodType getInvocationType() {
|
||||
MethodType itype = getMethodType();
|
||||
if (!isStatic())
|
||||
itype = itype.insertParameterType(0, clazz);
|
||||
return itype;
|
||||
}
|
||||
|
||||
public Class<?>[] getParameterTypes() {
|
||||
return getMethodType().parameterArray();
|
||||
}
|
||||
|
||||
public Class<?> getReturnType() {
|
||||
return getMethodType().returnType();
|
||||
}
|
||||
|
||||
public Class<?> getFieldType() {
|
||||
if (type == null) {
|
||||
expandFromVM();
|
||||
if (type == null) return null;
|
||||
}
|
||||
if (isInvocable())
|
||||
throw newIllegalArgumentException("not a field or nested class, no simple type");
|
||||
if (type instanceof Class<?>) {
|
||||
return (Class<?>) type;
|
||||
}
|
||||
if (type instanceof String) {
|
||||
String sig = (String) type;
|
||||
MethodType mtype = MethodType.fromBytecodeString("()"+sig, getClassLoader());
|
||||
Class<?> res = mtype.returnType();
|
||||
this.type = res;
|
||||
return res;
|
||||
}
|
||||
throw new InternalError("bad field type "+type);
|
||||
}
|
||||
|
||||
public Object getType() {
|
||||
return (isInvocable() ? getMethodType() : getFieldType());
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
if (type == null) {
|
||||
expandFromVM();
|
||||
if (type == null) return null;
|
||||
}
|
||||
if (type instanceof String)
|
||||
return (String) type;
|
||||
if (isInvocable())
|
||||
return BytecodeSignature.unparse(getMethodType());
|
||||
else
|
||||
return BytecodeSignature.unparse(getFieldType());
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return (flags & RECOGNIZED_MODIFIERS);
|
||||
}
|
||||
|
||||
private void setFlags(int flags) {
|
||||
this.flags = flags;
|
||||
assert(testAnyFlags(ALL_KINDS));
|
||||
}
|
||||
|
||||
private boolean testFlags(int mask, int value) {
|
||||
return (flags & mask) == value;
|
||||
}
|
||||
private boolean testAllFlags(int mask) {
|
||||
return testFlags(mask, mask);
|
||||
}
|
||||
private boolean testAnyFlags(int mask) {
|
||||
return !testFlags(mask, 0);
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return Modifier.isStatic(flags);
|
||||
}
|
||||
public boolean isPublic() {
|
||||
return Modifier.isPublic(flags);
|
||||
}
|
||||
public boolean isPrivate() {
|
||||
return Modifier.isPrivate(flags);
|
||||
}
|
||||
public boolean isProtected() {
|
||||
return Modifier.isProtected(flags);
|
||||
}
|
||||
public boolean isFinal() {
|
||||
return Modifier.isFinal(flags);
|
||||
}
|
||||
public boolean isAbstract() {
|
||||
return Modifier.isAbstract(flags);
|
||||
}
|
||||
// let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
|
||||
|
||||
// unofficial modifier flags, used by HotSpot:
|
||||
static final int BRIDGE = 0x00000040;
|
||||
static final int VARARGS = 0x00000080;
|
||||
static final int SYNTHETIC = 0x00001000;
|
||||
static final int ANNOTATION= 0x00002000;
|
||||
static final int ENUM = 0x00004000;
|
||||
public boolean isBridge() {
|
||||
return testAllFlags(IS_METHOD | BRIDGE);
|
||||
}
|
||||
public boolean isVarargs() {
|
||||
return testAllFlags(VARARGS) && isInvocable();
|
||||
}
|
||||
public boolean isSynthetic() {
|
||||
return testAllFlags(SYNTHETIC);
|
||||
}
|
||||
|
||||
static final String CONSTRUCTOR_NAME = "<init>"; // the ever-popular
|
||||
|
||||
// modifiers exported by the JVM:
|
||||
static final int RECOGNIZED_MODIFIERS = 0xFFFF;
|
||||
|
||||
// private flags, not part of RECOGNIZED_MODIFIERS:
|
||||
static final int
|
||||
IS_METHOD = MN_IS_METHOD, // method (not constructor)
|
||||
IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
|
||||
IS_FIELD = MN_IS_FIELD, // field
|
||||
IS_TYPE = MN_IS_TYPE; // nested type
|
||||
static final int // for MethodHandleNatives.getMembers
|
||||
SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES,
|
||||
SEARCH_INTERFACES = MN_SEARCH_INTERFACES;
|
||||
|
||||
static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
|
||||
static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
|
||||
static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
|
||||
static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD;
|
||||
static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES;
|
||||
|
||||
public boolean isInvocable() {
|
||||
return testAnyFlags(IS_INVOCABLE);
|
||||
}
|
||||
public boolean isFieldOrMethod() {
|
||||
return testAnyFlags(IS_FIELD_OR_METHOD);
|
||||
}
|
||||
public boolean isMethod() {
|
||||
return testAllFlags(IS_METHOD);
|
||||
}
|
||||
public boolean isConstructor() {
|
||||
return testAllFlags(IS_CONSTRUCTOR);
|
||||
}
|
||||
public boolean isField() {
|
||||
return testAllFlags(IS_FIELD);
|
||||
}
|
||||
public boolean isType() {
|
||||
return testAllFlags(IS_TYPE);
|
||||
}
|
||||
public boolean isPackage() {
|
||||
return !testAnyFlags(ALL_ACCESS);
|
||||
}
|
||||
|
||||
/** Initialize a query. It is not resolved. */
|
||||
private void init(Class<?> defClass, String name, Object type, int flags) {
|
||||
// defining class is allowed to be null (for a naked name/type pair)
|
||||
name.toString(); // null check
|
||||
type.equals(type); // null check
|
||||
// fill in fields:
|
||||
this.clazz = defClass;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
setFlags(flags);
|
||||
assert(!isResolved());
|
||||
}
|
||||
|
||||
private void expandFromVM() {
|
||||
if (!isResolved()) return;
|
||||
if (type instanceof Object[])
|
||||
type = null; // don't saddle JVM w/ typeInfo
|
||||
MethodHandleNatives.expand(this);
|
||||
}
|
||||
|
||||
// Capturing information from the Core Reflection API:
|
||||
private static int flagsMods(int flags, int mods) {
|
||||
assert((flags & RECOGNIZED_MODIFIERS) == 0);
|
||||
assert((mods & ~RECOGNIZED_MODIFIERS) == 0);
|
||||
return flags | mods;
|
||||
}
|
||||
public MemberName(Method m) {
|
||||
Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() };
|
||||
init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers()));
|
||||
// fill in vmtarget, vmindex while we have m in hand:
|
||||
MethodHandleNatives.init(this, m);
|
||||
assert(isResolved());
|
||||
}
|
||||
public MemberName(Constructor ctor) {
|
||||
Object[] typeInfo = { void.class, ctor.getParameterTypes() };
|
||||
init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers()));
|
||||
// fill in vmtarget, vmindex while we have ctor in hand:
|
||||
MethodHandleNatives.init(this, ctor);
|
||||
assert(isResolved());
|
||||
}
|
||||
public MemberName(Field fld) {
|
||||
init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers()));
|
||||
// fill in vmtarget, vmindex while we have fld in hand:
|
||||
MethodHandleNatives.init(this, fld);
|
||||
assert(isResolved());
|
||||
}
|
||||
public MemberName(Class<?> type) {
|
||||
init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers()));
|
||||
vmindex = 0; // isResolved
|
||||
assert(isResolved());
|
||||
}
|
||||
|
||||
// bare-bones constructor; the JVM will fill it in
|
||||
MemberName() { }
|
||||
|
||||
// locally useful cloner
|
||||
@Override protected MemberName clone() {
|
||||
try {
|
||||
return (MemberName) super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
// %%% define equals/hashcode?
|
||||
|
||||
// Construction from symbolic parts, for queries:
|
||||
public MemberName(Class<?> defClass, String name, Class<?> type, int modifiers) {
|
||||
init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS));
|
||||
}
|
||||
public MemberName(Class<?> defClass, String name, Class<?> type) {
|
||||
this(defClass, name, type, 0);
|
||||
}
|
||||
public MemberName(Class<?> defClass, String name, MethodType type, int modifiers) {
|
||||
int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
|
||||
init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS));
|
||||
}
|
||||
public MemberName(Class<?> defClass, String name, MethodType type) {
|
||||
this(defClass, name, type, 0);
|
||||
}
|
||||
|
||||
boolean isResolved() {
|
||||
return (vmindex != VM_INDEX_UNINITIALIZED);
|
||||
}
|
||||
|
||||
public boolean hasReceiverTypeDispatch() {
|
||||
return (isMethod() && getVMIndex(Access.TOKEN) >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isType())
|
||||
return type.toString(); // class java.lang.String
|
||||
// else it is a field, method, or constructor
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (getDeclaringClass() != null) {
|
||||
buf.append(getName(clazz));
|
||||
buf.append('.');
|
||||
}
|
||||
buf.append(getName());
|
||||
if (!isInvocable()) buf.append('/');
|
||||
buf.append(getName(getType()));
|
||||
/*
|
||||
buf.append('/');
|
||||
// key: Public, private, pRotected, sTatic, Final, sYnchronized,
|
||||
// transient/Varargs, native, (interface), abstract, sTrict, sYnthetic,
|
||||
// (annotation), Enum, (unused)
|
||||
final String FIELD_MOD_CHARS = "PprTF?vt????Y?E?";
|
||||
final String METHOD_MOD_CHARS = "PprTFybVn?atY???";
|
||||
String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS);
|
||||
for (int i = 0; i < modChars.length(); i++) {
|
||||
if ((flags & (1 << i)) != 0) {
|
||||
char mc = modChars.charAt(i);
|
||||
if (mc != '.')
|
||||
buf.append(mc);
|
||||
}
|
||||
}
|
||||
*/
|
||||
return buf.toString();
|
||||
}
|
||||
private static String getName(Object obj) {
|
||||
if (obj instanceof Class<?>)
|
||||
return ((Class<?>)obj).getName();
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
// Queries to the JVM:
|
||||
public int getVMIndex(Access token) {
|
||||
Access.check(token);
|
||||
if (!isResolved())
|
||||
throw newIllegalStateException("not resolved");
|
||||
return vmindex;
|
||||
}
|
||||
// public Object getVMTarget(Access token) {
|
||||
// Access.check(token);
|
||||
// if (!isResolved())
|
||||
// throw newIllegalStateException("not resolved");
|
||||
// return vmtarget;
|
||||
// }
|
||||
private RuntimeException newIllegalStateException(String message) {
|
||||
return new IllegalStateException(message+": "+this);
|
||||
}
|
||||
|
||||
// handy shared exception makers (they simplify the common case code)
|
||||
public static RuntimeException newIllegalArgumentException(String message) {
|
||||
return new IllegalArgumentException(message);
|
||||
}
|
||||
public static NoAccessException newNoAccessException(MemberName name, Class<?> lookupClass) {
|
||||
return newNoAccessException("cannot access", name, lookupClass);
|
||||
}
|
||||
public static NoAccessException newNoAccessException(String message,
|
||||
MemberName name, Class<?> lookupClass) {
|
||||
message += ": " + name;
|
||||
if (lookupClass != null) message += ", from " + lookupClass.getName();
|
||||
return new NoAccessException(message);
|
||||
}
|
||||
|
||||
/** Actually making a query requires an access check. */
|
||||
public static Factory getFactory(Access token) {
|
||||
Access.check(token);
|
||||
return Factory.INSTANCE;
|
||||
}
|
||||
public static Factory getFactory() {
|
||||
return getFactory(Access.getToken());
|
||||
}
|
||||
public static class Factory {
|
||||
private Factory() { } // singleton pattern
|
||||
static Factory INSTANCE = new Factory();
|
||||
|
||||
private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS;
|
||||
|
||||
/// Queries
|
||||
List<MemberName> getMembers(Class<?> defc,
|
||||
String matchName, Object matchType,
|
||||
int matchFlags, Class<?> lookupClass) {
|
||||
matchFlags &= ALLOWED_FLAGS;
|
||||
String matchSig = null;
|
||||
if (matchType != null) {
|
||||
matchSig = BytecodeSignature.unparse(matchType);
|
||||
if (matchSig.startsWith("("))
|
||||
matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE);
|
||||
else
|
||||
matchFlags &= ~(ALL_KINDS & ~IS_FIELD);
|
||||
}
|
||||
final int BUF_MAX = 0x2000;
|
||||
int len1 = matchName == null ? 10 : matchType == null ? 4 : 1;
|
||||
MemberName[] buf = newMemberBuffer(len1);
|
||||
int totalCount = 0;
|
||||
ArrayList<MemberName[]> bufs = null;
|
||||
for (;;) {
|
||||
int bufCount = MethodHandleNatives.getMembers(defc,
|
||||
matchName, matchSig, matchFlags,
|
||||
MethodHandleNatives.asNativeCaller(lookupClass),
|
||||
totalCount, buf);
|
||||
if (bufCount <= buf.length) {
|
||||
if (bufCount >= 0)
|
||||
totalCount += bufCount;
|
||||
break;
|
||||
}
|
||||
// JVM returned tp us with an intentional overflow!
|
||||
totalCount += buf.length;
|
||||
int excess = bufCount - buf.length;
|
||||
if (bufs == null) bufs = new ArrayList<MemberName[]>(1);
|
||||
bufs.add(buf);
|
||||
int len2 = buf.length;
|
||||
len2 = Math.max(len2, excess);
|
||||
len2 = Math.max(len2, totalCount / 4);
|
||||
buf = newMemberBuffer(Math.min(BUF_MAX, len2));
|
||||
}
|
||||
ArrayList<MemberName> result = new ArrayList<MemberName>(totalCount);
|
||||
if (bufs != null) {
|
||||
for (MemberName[] buf0 : bufs) {
|
||||
Collections.addAll(result, buf0);
|
||||
}
|
||||
}
|
||||
Collections.addAll(result, buf);
|
||||
// Signature matching is not the same as type matching, since
|
||||
// one signature might correspond to several types.
|
||||
// So if matchType is a Class or MethodType, refilter the results.
|
||||
if (matchType != null && matchType != matchSig) {
|
||||
for (Iterator<MemberName> it = result.iterator(); it.hasNext();) {
|
||||
MemberName m = it.next();
|
||||
if (!matchType.equals(m.getType()))
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
boolean resolveInPlace(MemberName m, boolean searchSupers, Class<?> lookupClass) {
|
||||
Class<?> caller = MethodHandleNatives.asNativeCaller(lookupClass);
|
||||
MethodHandleNatives.resolve(m, caller);
|
||||
if (m.isResolved()) return true;
|
||||
int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0);
|
||||
String matchSig = m.getSignature();
|
||||
MemberName[] buf = { m };
|
||||
int n = MethodHandleNatives.getMembers(m.getDeclaringClass(),
|
||||
m.getName(), matchSig, matchFlags, caller, 0, buf);
|
||||
if (n != 1) return false;
|
||||
return m.isResolved();
|
||||
}
|
||||
public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class<?> lookupClass) {
|
||||
MemberName result = m.clone();
|
||||
if (resolveInPlace(result, searchSupers, lookupClass))
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) {
|
||||
MemberName result = resolveOrNull(m, searchSupers, lookupClass);
|
||||
if (result != null)
|
||||
return result;
|
||||
throw newNoAccessException(m, lookupClass);
|
||||
}
|
||||
public List<MemberName> getMethods(Class<?> defc, boolean searchSupers,
|
||||
Class<?> lookupClass) {
|
||||
return getMethods(defc, searchSupers, null, null, lookupClass);
|
||||
}
|
||||
public List<MemberName> getMethods(Class<?> defc, boolean searchSupers,
|
||||
String name, MethodType type, Class<?> lookupClass) {
|
||||
int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
|
||||
return getMembers(defc, name, type, matchFlags, lookupClass);
|
||||
}
|
||||
public List<MemberName> getConstructors(Class<?> defc, Class<?> lookupClass) {
|
||||
return getMembers(defc, null, null, IS_CONSTRUCTOR, lookupClass);
|
||||
}
|
||||
public List<MemberName> getFields(Class<?> defc, boolean searchSupers,
|
||||
Class<?> lookupClass) {
|
||||
return getFields(defc, searchSupers, null, null, lookupClass);
|
||||
}
|
||||
public List<MemberName> getFields(Class<?> defc, boolean searchSupers,
|
||||
String name, Class<?> type, Class<?> lookupClass) {
|
||||
int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0);
|
||||
return getMembers(defc, name, type, matchFlags, lookupClass);
|
||||
}
|
||||
public List<MemberName> getNestedTypes(Class<?> defc, boolean searchSupers,
|
||||
Class<?> lookupClass) {
|
||||
int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0);
|
||||
return getMembers(defc, null, null, matchFlags, lookupClass);
|
||||
}
|
||||
private static MemberName[] newMemberBuffer(int length) {
|
||||
MemberName[] buf = new MemberName[length];
|
||||
// fill the buffer with dummy structs for the JVM to fill in
|
||||
for (int i = 0; i < length; i++)
|
||||
buf[i] = new MemberName();
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
// static {
|
||||
// System.out.println("Hello world! My methods are:");
|
||||
// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
|
||||
// }
|
||||
}
|
355
jdk/src/share/classes/sun/dyn/MethodHandleImpl.java
Normal file
355
jdk/src/share/classes/sun/dyn/MethodHandleImpl.java
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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.MethodHandle;
|
||||
import java.dyn.MethodHandles;
|
||||
import java.dyn.MethodHandles.Lookup;
|
||||
import java.dyn.MethodType;
|
||||
import sun.dyn.util.VerifyType;
|
||||
import java.dyn.NoAccessException;
|
||||
import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
import static sun.dyn.MemberName.newNoAccessException;
|
||||
|
||||
/**
|
||||
* Base class for method handles, containing JVM-specific fields and logic.
|
||||
* TO DO: It should not be a base class.
|
||||
* @author jrose
|
||||
*/
|
||||
public abstract class MethodHandleImpl {
|
||||
|
||||
// Fields which really belong in MethodHandle:
|
||||
private byte vmentry; // adapter stub or method entry point
|
||||
//private int vmslots; // optionally, hoist type.form.vmslots
|
||||
protected Object vmtarget; // VM-specific, class-specific target value
|
||||
//MethodType type; // defined in MethodHandle
|
||||
|
||||
// TO DO: vmtarget should be invisible to Java, since the JVM puts internal
|
||||
// managed pointers into it. Making it visible exposes it to debuggers,
|
||||
// which can cause errors when they treat the pointer as an Object.
|
||||
|
||||
// These two dummy fields are present to force 'I' and 'J' signatures
|
||||
// into this class's constant pool, so they can be transferred
|
||||
// to vmentry when this class is loaded.
|
||||
static final int INT_FIELD = 0;
|
||||
static final long LONG_FIELD = 0;
|
||||
|
||||
// type is defined in java.dyn.MethodHandle, which is platform-independent
|
||||
|
||||
// vmentry (a void* field) is used *only* by by the JVM.
|
||||
// The JVM adjusts its type to int or long depending on system wordsize.
|
||||
// Since it is statically typed as neither int nor long, it is impossible
|
||||
// to use this field from Java bytecode. (Please don't try to, either.)
|
||||
|
||||
// The vmentry is an assembly-language stub which is jumped to
|
||||
// immediately after the method type is verified.
|
||||
// For a direct MH, this stub loads the vmtarget's entry point
|
||||
// and jumps to it.
|
||||
|
||||
/**
|
||||
* VM-based method handles must have a security token.
|
||||
* This security token can only be obtained by trusted code.
|
||||
* Do not create method handles directly; use factory methods.
|
||||
*/
|
||||
public MethodHandleImpl(Access token) {
|
||||
Access.check(token);
|
||||
}
|
||||
|
||||
/** Initialize the method type form to participate in JVM calls.
|
||||
* This is done once for each erased type.
|
||||
*/
|
||||
public static void init(Access token, MethodType self) {
|
||||
Access.check(token);
|
||||
if (MethodHandleNatives.JVM_SUPPORT)
|
||||
MethodHandleNatives.init(self);
|
||||
}
|
||||
|
||||
/// Factory methods to create method handles:
|
||||
|
||||
private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
|
||||
|
||||
static private Lookup IMPL_LOOKUP_INIT;
|
||||
|
||||
public static void initLookup(Access token, Lookup lookup) {
|
||||
Access.check(token);
|
||||
if (IMPL_LOOKUP_INIT != null || lookup.lookupClass() != Access.class)
|
||||
throw new InternalError();
|
||||
IMPL_LOOKUP_INIT = lookup;
|
||||
}
|
||||
|
||||
public static Lookup getLookup(Access token) {
|
||||
Access.check(token);
|
||||
return IMPL_LOOKUP;
|
||||
}
|
||||
|
||||
static {
|
||||
// Force initialization:
|
||||
Lookup.PUBLIC_LOOKUP.lookupClass();
|
||||
if (IMPL_LOOKUP_INIT == null)
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
public static void initStatics() {
|
||||
// Trigger preceding sequence.
|
||||
}
|
||||
|
||||
/** Shared secret with MethodHandles.Lookup, a copy of Lookup.IMPL_LOOKUP. */
|
||||
static final Lookup IMPL_LOOKUP = IMPL_LOOKUP_INIT;
|
||||
|
||||
|
||||
/** Look up a given method.
|
||||
* Callable only from java.dyn and related packages.
|
||||
* <p>
|
||||
* The resulting method handle type will be of the given type,
|
||||
* with a receiver type {@code rcvc} prepended if the member is not static.
|
||||
* <p>
|
||||
* Access checks are made as of the given lookup class.
|
||||
* In particular, if the method is protected and {@code defc} is in a
|
||||
* different package from the lookup class, then {@code rcvc} must be
|
||||
* the lookup class or a subclass.
|
||||
* @param token Proof that the lookup class has access to this package.
|
||||
* @param member Resolved method or constructor to call.
|
||||
* @param name Name of the desired method.
|
||||
* @param rcvc Receiver type of desired non-static method (else null)
|
||||
* @param doDispatch whether the method handle will test the receiver type
|
||||
* @param lookupClass access-check relative to this class
|
||||
* @return a direct handle to the matching method
|
||||
* @throws NoAccessException if the given method cannot be accessed by the lookup class
|
||||
*/
|
||||
public static
|
||||
MethodHandle findMethod(Access token, MemberName method,
|
||||
boolean doDispatch, Class<?> lookupClass) {
|
||||
Access.check(token); // only trusted calls
|
||||
MethodType mtype = method.getMethodType();
|
||||
if (method.isStatic()) {
|
||||
doDispatch = false;
|
||||
} else {
|
||||
// adjust the advertised receiver type to be exactly the one requested
|
||||
// (in the case of invokespecial, this will be the calling class)
|
||||
mtype = mtype.insertParameterType(0, method.getDeclaringClass());
|
||||
if (method.isConstructor())
|
||||
doDispatch = true;
|
||||
}
|
||||
DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
|
||||
if (!mh.isValid())
|
||||
throw newNoAccessException(method, lookupClass);
|
||||
return mh;
|
||||
}
|
||||
|
||||
public static
|
||||
MethodHandle accessField(Access token,
|
||||
MemberName member, boolean isSetter,
|
||||
Class<?> lookupClass) {
|
||||
Access.check(token);
|
||||
// FIXME: Use sun.misc.Unsafe to dig up the dirt on the field.
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
public static
|
||||
MethodHandle accessArrayElement(Access token,
|
||||
Class<?> arrayClass, boolean isSetter) {
|
||||
Access.check(token);
|
||||
if (!arrayClass.isArray())
|
||||
throw newIllegalArgumentException("not an array: "+arrayClass);
|
||||
// FIXME: Use sun.misc.Unsafe to dig up the dirt on the array.
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
/** Bind a predetermined first argument to the given direct method handle.
|
||||
* Callable only from MethodHandles.
|
||||
* @param token Proof that the caller has access to this package.
|
||||
* @param target Any direct method handle.
|
||||
* @param receiver Receiver (or first static method argument) to pre-bind.
|
||||
* @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist
|
||||
*/
|
||||
public static
|
||||
MethodHandle bindReceiver(Access token,
|
||||
MethodHandle target, Object receiver) {
|
||||
Access.check(token);
|
||||
if (target instanceof DirectMethodHandle)
|
||||
return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0);
|
||||
return null; // let caller try something else
|
||||
}
|
||||
|
||||
/** Bind a predetermined argument to the given arbitrary method handle.
|
||||
* Callable only from MethodHandles.
|
||||
* @param token Proof that the caller has access to this package.
|
||||
* @param target Any method handle.
|
||||
* @param receiver Argument (which can be a boxed primitive) to pre-bind.
|
||||
* @return a suitable BoundMethodHandle
|
||||
*/
|
||||
public static
|
||||
MethodHandle bindArgument(Access token,
|
||||
MethodHandle target, int argnum, Object receiver) {
|
||||
Access.check(token);
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
public static MethodHandle convertArguments(Access token,
|
||||
MethodHandle target,
|
||||
MethodType newType,
|
||||
MethodType oldType,
|
||||
int[] permutationOrNull) {
|
||||
Access.check(token);
|
||||
MethodHandle res = AdapterMethodHandle.makePairwiseConvert(token, newType, target);
|
||||
if (res != null)
|
||||
return res;
|
||||
int argc = oldType.parameterCount();
|
||||
// The JVM can't do it directly, so fill in the gap with a Java adapter.
|
||||
// TO DO: figure out what to put here from case-by-case experience
|
||||
// Use a heavier method: Convert all the arguments to Object,
|
||||
// then back to the desired types. We might have to use Java-based
|
||||
// method handles to do this.
|
||||
MethodType objType = MethodType.makeGeneric(argc);
|
||||
MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(token, objType, target);
|
||||
if (objTarget == null)
|
||||
objTarget = FromGeneric.make(target);
|
||||
res = AdapterMethodHandle.makePairwiseConvert(token, newType, objTarget);
|
||||
if (res != null)
|
||||
return res;
|
||||
return ToGeneric.make(newType, objTarget);
|
||||
}
|
||||
|
||||
public static MethodHandle spreadArguments(Access token,
|
||||
MethodHandle target,
|
||||
MethodType newType,
|
||||
int spreadArg) {
|
||||
Access.check(token);
|
||||
// TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
|
||||
MethodType oldType = target.type();
|
||||
// spread the last argument of newType to oldType
|
||||
int spreadCount = oldType.parameterCount() - spreadArg;
|
||||
Class<Object[]> spreadArgType = Object[].class;
|
||||
MethodHandle res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount);
|
||||
if (res != null)
|
||||
return res;
|
||||
// try an intermediate adapter
|
||||
Class<?> spreadType = null;
|
||||
if (spreadArg < 0 || spreadArg >= newType.parameterCount()
|
||||
|| !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
|
||||
throw newIllegalArgumentException("no restarg in "+newType);
|
||||
Class<?>[] ptypes = oldType.parameterArray();
|
||||
for (int i = 0; i < spreadCount; i++)
|
||||
ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
|
||||
MethodType midType = MethodType.make(newType.returnType(), ptypes);
|
||||
// after spreading, some arguments may need further conversion
|
||||
target = convertArguments(token, target, midType, oldType, null);
|
||||
if (target == null)
|
||||
throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
|
||||
res = AdapterMethodHandle.makeSpreadArguments(token, newType, target, spreadArgType, spreadArg, spreadCount);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static MethodHandle collectArguments(Access token,
|
||||
MethodHandle target,
|
||||
MethodType newType,
|
||||
int collectArg) {
|
||||
if (collectArg > 0)
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
public static
|
||||
MethodHandle dropArguments(Access token, MethodHandle target,
|
||||
MethodType newType, int argnum) {
|
||||
Access.check(token);
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
public static
|
||||
MethodHandle makeGuardWithTest(Access token,
|
||||
final MethodHandle test,
|
||||
final MethodHandle target,
|
||||
final MethodHandle fallback) {
|
||||
Access.check(token);
|
||||
// %%% This is just a sketch. It needs to be de-boxed.
|
||||
// Adjust the handles to accept varargs lists.
|
||||
MethodType type = target.type();
|
||||
Class<?> rtype = type.returnType();
|
||||
if (type.parameterCount() != 1 || type.parameterType(0).isPrimitive()) {
|
||||
MethodType vatestType = MethodType.make(boolean.class, Object[].class);
|
||||
MethodType vatargetType = MethodType.make(rtype, Object[].class);
|
||||
MethodHandle vaguard = makeGuardWithTest(token,
|
||||
MethodHandles.spreadArguments(test, vatestType),
|
||||
MethodHandles.spreadArguments(target, vatargetType),
|
||||
MethodHandles.spreadArguments(fallback, vatargetType));
|
||||
return MethodHandles.collectArguments(vaguard, type);
|
||||
}
|
||||
if (rtype.isPrimitive()) {
|
||||
MethodType boxtype = type.changeReturnType(Object.class);
|
||||
MethodHandle boxguard = makeGuardWithTest(token,
|
||||
test,
|
||||
MethodHandles.convertArguments(target, boxtype),
|
||||
MethodHandles.convertArguments(fallback, boxtype));
|
||||
return MethodHandles.convertArguments(boxguard, type);
|
||||
}
|
||||
// Got here? Reduced calling sequence to Object(Object).
|
||||
class Guarder {
|
||||
Object invoke(Object x) {
|
||||
// If javac supports MethodHandle.invoke directly:
|
||||
//z = vatest.invoke<boolean>(arguments);
|
||||
// If javac does not support direct MH.invoke calls:
|
||||
boolean z = (Boolean) MethodHandles.invoke_1(test, x);
|
||||
MethodHandle mh = (z ? target : fallback);
|
||||
return MethodHandles.invoke_1(mh, x);
|
||||
}
|
||||
MethodHandle handle() {
|
||||
MethodType invokeType = MethodType.makeGeneric(0, true);
|
||||
MethodHandle vh = IMPL_LOOKUP.bind(this, "invoke", invokeType);
|
||||
return MethodHandles.collectArguments(vh, target.type());
|
||||
}
|
||||
}
|
||||
return new Guarder().handle();
|
||||
}
|
||||
|
||||
public static
|
||||
MethodHandle combineArguments(Access token, MethodHandle target, MethodHandle checker, int pos) {
|
||||
Access.check(token);
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
protected static String basicToString(MethodHandle target) {
|
||||
MemberName name = null;
|
||||
if (target != null)
|
||||
name = MethodHandleNatives.getMethodName(target);
|
||||
if (name == null)
|
||||
return "<unknown>";
|
||||
return name.getName();
|
||||
}
|
||||
|
||||
protected static String addTypeString(MethodHandle target, String name) {
|
||||
if (target == null) return name;
|
||||
return name+target.type();
|
||||
}
|
||||
static RuntimeException newIllegalArgumentException(String string) {
|
||||
return new IllegalArgumentException(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MethodHandle self = (MethodHandle) this;
|
||||
return addTypeString(self, basicToString(self));
|
||||
}
|
||||
}
|
271
jdk/src/share/classes/sun/dyn/MethodHandleNatives.java
Normal file
271
jdk/src/share/classes/sun/dyn/MethodHandleNatives.java
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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.MethodHandle;
|
||||
import java.dyn.MethodType;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import static sun.dyn.MethodHandleNatives.Constants.*;
|
||||
|
||||
/**
|
||||
* The JVM interface for the method handles package is all here.
|
||||
* @author jrose
|
||||
*/
|
||||
class MethodHandleNatives {
|
||||
|
||||
private MethodHandleNatives() { } // static only
|
||||
|
||||
/// MethodName support
|
||||
|
||||
static native void init(MemberName self, Object ref);
|
||||
static native void expand(MemberName self);
|
||||
static native void resolve(MemberName self, Class<?> caller);
|
||||
static native int getMembers(Class<?> defc, String matchName, String matchSig,
|
||||
int matchFlags, Class<?> caller, int skip, MemberName[] results);
|
||||
|
||||
static Class<?> asNativeCaller(Class<?> lookupClass) {
|
||||
if (lookupClass == null) // means "public only, non-privileged"
|
||||
return sun.dyn.empty.Empty.class;
|
||||
if (lookupClass == Access.class) // means "internal, privileged"
|
||||
return null; // to the JVM, null means completely privileged
|
||||
return lookupClass;
|
||||
}
|
||||
|
||||
/// MethodHandle support
|
||||
|
||||
/** Initialize the method handle to adapt the call. */
|
||||
static native void init(AdapterMethodHandle self, MethodHandle target, int argnum);
|
||||
/** Initialize the method handle to call the correct method, directly. */
|
||||
static native void init(BoundMethodHandle self, Object target, int argnum);
|
||||
/** Initialize the method handle to call as if by an invoke* instruction. */
|
||||
static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class<?> caller);
|
||||
|
||||
/** Initialize a method type, once per form. */
|
||||
static native void init(MethodType self);
|
||||
|
||||
/** Tell the JVM that we need to change the target of an invokedynamic. */
|
||||
static native void linkCallSite(CallSiteImpl site, MethodHandle target);
|
||||
|
||||
/** Fetch the vmtarget field.
|
||||
* It will be sanitized as necessary to avoid exposing non-Java references.
|
||||
* This routine is for debugging and reflection.
|
||||
*/
|
||||
static native Object getTarget(MethodHandle self, int format);
|
||||
|
||||
/** Fetch the name of the handled method, if available.
|
||||
* This routine is for debugging and reflection.
|
||||
*/
|
||||
static MemberName getMethodName(MethodHandle self) {
|
||||
if (!JVM_SUPPORT) return null;
|
||||
return (MemberName) getTarget(self, ETF_METHOD_NAME);
|
||||
}
|
||||
|
||||
/** Fetch the reflective version of the handled method, if available.
|
||||
*/
|
||||
static AccessibleObject getTargetMethod(MethodHandle self) {
|
||||
if (!JVM_SUPPORT) return null;
|
||||
return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD);
|
||||
}
|
||||
|
||||
/** Fetch the target of this method handle.
|
||||
* If it directly targets a method, return a tuple of method info.
|
||||
* The info is of the form new Object[]{defclass, name, sig, refclass}.
|
||||
* If it is chained to another method handle, return that handle.
|
||||
*/
|
||||
static Object getTargetInfo(MethodHandle self) {
|
||||
if (!JVM_SUPPORT) return null;
|
||||
return getTarget(self, ETF_HANDLE_OR_METHOD_NAME);
|
||||
}
|
||||
|
||||
static Object[] makeTarget(Class<?> defc, String name, String sig, int mods, Class<?> refc) {
|
||||
return new Object[] { defc, name, sig, mods, refc };
|
||||
}
|
||||
|
||||
/** Fetch MH-related JVM parameter.
|
||||
* which=0 retrieves MethodHandlePushLimit
|
||||
* which=1 retrieves stack slot push size (in address units)
|
||||
*/
|
||||
static native int getConstant(int which);
|
||||
|
||||
/** True iff this HotSpot JVM has built-in support for method handles.
|
||||
* If false, some test cases might run, but functionality will be missing.
|
||||
*/
|
||||
public static final boolean JVM_SUPPORT;
|
||||
|
||||
/** Java copy of MethodHandlePushLimit in range 2..255. */
|
||||
static final int JVM_PUSH_LIMIT;
|
||||
/** JVM stack motion (in words) after one slot is pushed, usually -1.
|
||||
*/
|
||||
static final int JVM_STACK_MOVE_UNIT;
|
||||
|
||||
private static native void registerNatives();
|
||||
static {
|
||||
boolean JVM_SUPPORT_;
|
||||
int JVM_PUSH_LIMIT_;
|
||||
int JVM_STACK_MOVE_UNIT_;
|
||||
try {
|
||||
registerNatives();
|
||||
JVM_SUPPORT_ = true;
|
||||
JVM_PUSH_LIMIT_ = getConstant(Constants.GC_JVM_PUSH_LIMIT);
|
||||
JVM_STACK_MOVE_UNIT_ = getConstant(Constants.GC_JVM_STACK_MOVE_LIMIT);
|
||||
//sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init");
|
||||
} catch (UnsatisfiedLinkError ee) {
|
||||
// ignore; if we use init() methods later we'll see linkage errors
|
||||
JVM_SUPPORT_ = false;
|
||||
JVM_PUSH_LIMIT_ = 3; // arbitrary
|
||||
JVM_STACK_MOVE_UNIT_ = -1; // arbitrary
|
||||
//System.out.println("Warning: Running with JVM_SUPPORT=false");
|
||||
//System.out.println(ee);
|
||||
JVM_SUPPORT = JVM_SUPPORT_;
|
||||
JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_;
|
||||
JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_;
|
||||
throw ee; // just die; hopeless to try to run with an older JVM
|
||||
}
|
||||
JVM_SUPPORT = JVM_SUPPORT_;
|
||||
JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_;
|
||||
JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_;
|
||||
}
|
||||
|
||||
// All compile-time constants go here.
|
||||
// There is an opportunity to check them against the JVM's idea of them.
|
||||
static class Constants {
|
||||
Constants() { } // static only
|
||||
// MethodHandleImpl
|
||||
static final int // for getConstant
|
||||
GC_JVM_PUSH_LIMIT = 0,
|
||||
GC_JVM_STACK_MOVE_LIMIT = 1;
|
||||
static final int
|
||||
ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method)
|
||||
ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self)
|
||||
ETF_METHOD_NAME = 2, // ultimate method as MemberName
|
||||
ETF_REFLECT_METHOD = 3; // ultimate method as java.lang.reflect object (sans refClass)
|
||||
|
||||
// MemberName
|
||||
// The JVM uses values of -2 and above for vtable indexes.
|
||||
// Field values are simple positive offsets.
|
||||
// Ref: src/share/vm/oops/methodOop.hpp
|
||||
// This value is negative enough to avoid such numbers,
|
||||
// but not too negative.
|
||||
static final int
|
||||
MN_IS_METHOD = 0x00010000, // method (not constructor)
|
||||
MN_IS_CONSTRUCTOR = 0x00020000, // constructor
|
||||
MN_IS_FIELD = 0x00040000, // field
|
||||
MN_IS_TYPE = 0x00080000, // nested type
|
||||
MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers
|
||||
MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers
|
||||
VM_INDEX_UNINITIALIZED = -99;
|
||||
|
||||
// AdapterMethodHandle
|
||||
/** Conversions recognized by the JVM.
|
||||
* They must align with the constants in sun.dyn_AdapterMethodHandle,
|
||||
* in the JVM file hotspot/src/share/vm/classfile/javaClasses.hpp.
|
||||
*/
|
||||
static final int
|
||||
OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
|
||||
OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument
|
||||
OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another
|
||||
OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive
|
||||
OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI)
|
||||
OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg)
|
||||
OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg)
|
||||
OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS)
|
||||
OP_DROP_ARGS = 0x8, // remove one or more argument slots
|
||||
OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI)
|
||||
OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size)
|
||||
OP_FLYBY = 0xB, // operate first on reified argument list (NYI)
|
||||
OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI)
|
||||
CONV_OP_LIMIT = 0xD; // limit of CONV_OP enumeration
|
||||
/** Shift and mask values for decoding the AMH.conversion field.
|
||||
* These numbers are shared with the JVM for creating AMHs.
|
||||
*/
|
||||
static final int
|
||||
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
|
||||
CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
|
||||
CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
|
||||
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
|
||||
CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed)
|
||||
CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed)
|
||||
CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change
|
||||
CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1;
|
||||
|
||||
/** Which conv-ops are implemented by the JVM? */
|
||||
static final int CONV_OP_IMPLEMENTED_MASK =
|
||||
// TODO: The following expression should be replaced by
|
||||
// a JVM query.
|
||||
((1<<OP_RETYPE_ONLY)
|
||||
|(1<<OP_CHECK_CAST)
|
||||
|(1<<OP_PRIM_TO_PRIM)
|
||||
|(1<<OP_REF_TO_PRIM)
|
||||
|(1<<OP_SWAP_ARGS)
|
||||
|(1<<OP_ROT_ARGS)
|
||||
|(1<<OP_DUP_ARGS)
|
||||
|(1<<OP_DROP_ARGS)
|
||||
);
|
||||
|
||||
/**
|
||||
* Basic types as encoded in the JVM. These code values are not
|
||||
* intended for use outside this class. They are used as part of
|
||||
* a private interface between the JVM and this class.
|
||||
*/
|
||||
static final int
|
||||
T_BOOLEAN = 4,
|
||||
T_CHAR = 5,
|
||||
T_FLOAT = 6,
|
||||
T_DOUBLE = 7,
|
||||
T_BYTE = 8,
|
||||
T_SHORT = 9,
|
||||
T_INT = 10,
|
||||
T_LONG = 11,
|
||||
T_OBJECT = 12,
|
||||
//T_ARRAY = 13
|
||||
T_VOID = 14;
|
||||
//T_ADDRESS = 15
|
||||
}
|
||||
|
||||
private static native int getNamedCon(int which, Object[] name);
|
||||
static boolean verifyConstants() {
|
||||
Object[] box = { null };
|
||||
for (int i = 0; ; i++) {
|
||||
box[0] = null;
|
||||
int vmval = getNamedCon(i, box);
|
||||
if (box[0] == null) break;
|
||||
String name = (String) box[0];
|
||||
try {
|
||||
Field con = Constants.class.getDeclaredField(name);
|
||||
int jval = con.getInt(null);
|
||||
if (jval != vmval)
|
||||
throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval);
|
||||
} catch (Exception ex) {
|
||||
throw new InternalError(name+": access failed, got "+ex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static {
|
||||
if (JVM_SUPPORT) verifyConstants();
|
||||
}
|
||||
}
|
502
jdk/src/share/classes/sun/dyn/MethodTypeImpl.java
Normal file
502
jdk/src/share/classes/sun/dyn/MethodTypeImpl.java
Normal file
@ -0,0 +1,502 @@
|
||||
/*
|
||||
* 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 sun.dyn.util.Wrapper;
|
||||
|
||||
/**
|
||||
* Shared information for a group of method types, which differ
|
||||
* only by reference types, and therefore share a common erasure
|
||||
* and wrapping.
|
||||
* <p>
|
||||
* For an empirical discussion of the structure of method types,
|
||||
* see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
|
||||
* the thread "Avoiding Boxing" on jvm-languages</a>.
|
||||
* There are approximately 2000 distinct erased method types in the JDK.
|
||||
* There are a little over 10 times that number of unerased types.
|
||||
* No more than half of these are likely to be loaded at once.
|
||||
* @author John Rose
|
||||
*/
|
||||
public class MethodTypeImpl {
|
||||
final int[] argToSlotTable, slotToArgTable;
|
||||
final long argCounts; // packed slot & value counts
|
||||
final long primCounts; // packed prim & double counts
|
||||
final int vmslots; // total number of parameter slots
|
||||
final MethodType erasedType; // the canonical erasure
|
||||
/*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
|
||||
/*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return
|
||||
/*lazy*/ MethodType primsAsInts; // replace prims by int/long
|
||||
/*lazy*/ MethodType primsAsLongs; // replace prims by long
|
||||
/*lazy*/ MethodType primsAtEnd; // reorder primitives to the end
|
||||
|
||||
// Cached adapter information:
|
||||
/*lazy*/ ToGeneric toGeneric; // convert cs. with prims to w/o
|
||||
/*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
|
||||
/*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
|
||||
///*lazy*/ Invokers invokers; // cache of handy higher-order adapters
|
||||
|
||||
public MethodType erasedType() {
|
||||
return erasedType;
|
||||
}
|
||||
|
||||
public static MethodTypeImpl of(MethodType type) {
|
||||
return METHOD_TYPE_FRIEND.form(type);
|
||||
}
|
||||
|
||||
/** Access methods for the internals of MethodType, supplied to
|
||||
* MethodTypeForm as a trusted agent.
|
||||
*/
|
||||
static public interface MethodTypeFriend {
|
||||
Class<?>[] ptypes(MethodType mt);
|
||||
MethodTypeImpl form(MethodType mt);
|
||||
void setForm(MethodType mt, MethodTypeImpl form);
|
||||
MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted);
|
||||
MethodTypeImpl newMethodTypeForm(MethodType mt);
|
||||
Invokers getInvokers(MethodType mt);
|
||||
void setInvokers(MethodType mt, Invokers inv);
|
||||
}
|
||||
public static void setMethodTypeFriend(Access token, MethodTypeFriend am) {
|
||||
Access.check(token);
|
||||
if (METHOD_TYPE_FRIEND != null)
|
||||
throw new InternalError(); // just once
|
||||
METHOD_TYPE_FRIEND = am;
|
||||
}
|
||||
static private MethodTypeFriend METHOD_TYPE_FRIEND;
|
||||
|
||||
protected MethodTypeImpl(MethodType erasedType) {
|
||||
this.erasedType = erasedType;
|
||||
|
||||
Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(erasedType);
|
||||
int ptypeCount = ptypes.length;
|
||||
int pslotCount = ptypeCount; // temp. estimate
|
||||
int rtypeCount = 1; // temp. estimate
|
||||
int rslotCount = 1; // temp. estimate
|
||||
|
||||
int[] argToSlotTab = null, slotToArgTab = null;
|
||||
|
||||
// Walk the argument types, looking for primitives.
|
||||
int pac = 0, lac = 0, prc = 0, lrc = 0;
|
||||
Class<?> epts[] = ptypes;
|
||||
for (int i = 0; i < epts.length; i++) {
|
||||
Class<?> pt = epts[i];
|
||||
if (pt != Object.class) {
|
||||
assert(pt.isPrimitive());
|
||||
++pac;
|
||||
if (hasTwoArgSlots(pt)) ++lac;
|
||||
}
|
||||
}
|
||||
pslotCount += lac; // #slots = #args + #longs
|
||||
Class<?> rt = erasedType.returnType();
|
||||
if (rt != Object.class) {
|
||||
++prc; // even void.class counts as a prim here
|
||||
if (hasTwoArgSlots(rt)) ++lrc;
|
||||
// adjust #slots, #args
|
||||
if (rt == void.class)
|
||||
rtypeCount = rslotCount = 0;
|
||||
else
|
||||
rslotCount += lrc;
|
||||
}
|
||||
if (lac != 0) {
|
||||
int slot = ptypeCount + lac;
|
||||
slotToArgTab = new int[slot+1];
|
||||
argToSlotTab = new int[1+ptypeCount];
|
||||
argToSlotTab[0] = slot; // argument "-1" is past end of slots
|
||||
for (int i = 0; i < epts.length; i++) {
|
||||
Class<?> pt = epts[i];
|
||||
if (hasTwoArgSlots(pt)) --slot;
|
||||
--slot;
|
||||
slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
assert(slot == 0); // filled the table
|
||||
}
|
||||
this.primCounts = pack(lrc, prc, lac, pac);
|
||||
this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
|
||||
if (slotToArgTab == null) {
|
||||
int slot = ptypeCount; // first arg is deepest in stack
|
||||
slotToArgTab = new int[slot+1];
|
||||
argToSlotTab = new int[1+ptypeCount];
|
||||
argToSlotTab[0] = slot; // argument "-1" is past end of slots
|
||||
for (int i = 0; i < ptypeCount; i++) {
|
||||
--slot;
|
||||
slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
|
||||
argToSlotTab[1+i] = slot;
|
||||
}
|
||||
}
|
||||
this.argToSlotTable = argToSlotTab;
|
||||
this.slotToArgTable = slotToArgTab;
|
||||
|
||||
if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments");
|
||||
|
||||
// send a few bits down to the JVM:
|
||||
this.vmslots = parameterSlotCount();
|
||||
|
||||
// short circuit some no-op canonicalizations:
|
||||
if (!hasPrimitives()) {
|
||||
primsAsBoxes = erasedType;
|
||||
primArgsAsBoxes = erasedType;
|
||||
primsAsInts = erasedType;
|
||||
primsAsLongs = erasedType;
|
||||
primsAtEnd = erasedType;
|
||||
}
|
||||
}
|
||||
|
||||
/** Turn all primitive types to corresponding wrapper types.
|
||||
*/
|
||||
public MethodType primsAsBoxes() {
|
||||
MethodType ct = primsAsBoxes;
|
||||
if (ct != null) return ct;
|
||||
MethodType t = erasedType;
|
||||
ct = canonicalize(erasedType, WRAP, WRAP);
|
||||
if (ct == null) ct = t; // no prims to box
|
||||
return primsAsBoxes = ct;
|
||||
}
|
||||
|
||||
/** Turn all primitive argument types to corresponding wrapper types.
|
||||
* Subword and void return types are promoted to int.
|
||||
*/
|
||||
public MethodType primArgsAsBoxes() {
|
||||
MethodType ct = primArgsAsBoxes;
|
||||
if (ct != null) return ct;
|
||||
MethodType t = erasedType;
|
||||
ct = canonicalize(erasedType, RAW_RETURN, WRAP);
|
||||
if (ct == null) ct = t; // no prims to box
|
||||
return primArgsAsBoxes = ct;
|
||||
}
|
||||
|
||||
/** Turn all primitive types to either int or long.
|
||||
* Floating point return types are not changed, because
|
||||
* they may require special calling sequences.
|
||||
* A void return value is turned to int.
|
||||
*/
|
||||
public MethodType primsAsInts() {
|
||||
MethodType ct = primsAsInts;
|
||||
if (ct != null) return ct;
|
||||
MethodType t = erasedType;
|
||||
ct = canonicalize(t, RAW_RETURN, INTS);
|
||||
if (ct == null) ct = t; // no prims to int-ify
|
||||
return primsAsInts = ct;
|
||||
}
|
||||
|
||||
/** Turn all primitive types to either int or long.
|
||||
* Floating point return types are not changed, because
|
||||
* they may require special calling sequences.
|
||||
* A void return value is turned to int.
|
||||
*/
|
||||
public MethodType primsAsLongs() {
|
||||
MethodType ct = primsAsLongs;
|
||||
if (ct != null) return ct;
|
||||
MethodType t = erasedType;
|
||||
ct = canonicalize(t, RAW_RETURN, LONGS);
|
||||
if (ct == null) ct = t; // no prims to int-ify
|
||||
return primsAsLongs = ct;
|
||||
}
|
||||
|
||||
/** Stably sort parameters into 3 buckets: ref, int, long. */
|
||||
public MethodType primsAtEnd() {
|
||||
MethodType ct = primsAtEnd;
|
||||
if (ct != null) return ct;
|
||||
MethodType t = erasedType;
|
||||
|
||||
int pac = primitiveParameterCount();
|
||||
if (pac == 0)
|
||||
return primsAtEnd = t;
|
||||
|
||||
int argc = parameterCount();
|
||||
int lac = longPrimitiveParameterCount();
|
||||
if (pac == argc && (lac == 0 || lac == argc))
|
||||
return primsAtEnd = t;
|
||||
|
||||
// known to have a mix of 2 or 3 of ref, int, long
|
||||
return primsAtEnd = reorderParameters(t, primsAtEndOrder(t), null);
|
||||
|
||||
}
|
||||
|
||||
/** Compute a new ordering of parameters so that all references
|
||||
* are before all ints or longs, and all ints are before all longs.
|
||||
* For this ordering, doubles count as longs, and all other primitive
|
||||
* values count as ints.
|
||||
* As a special case, if the parameters are already in the specified
|
||||
* order, this method returns a null reference, rather than an array
|
||||
* specifying a null permutation.
|
||||
* <p>
|
||||
* For example, the type {@code (int,boolean,int,Object,String)void}
|
||||
* produces the order {@code {3,4,0,1,2}}, the type
|
||||
* {@code (long,int,String)void} produces {@code {2,1,2}}, and
|
||||
* the type {@code (Object,int)Object} produces {@code null}.
|
||||
*/
|
||||
public static int[] primsAtEndOrder(MethodType mt) {
|
||||
MethodTypeImpl form = METHOD_TYPE_FRIEND.form(mt);
|
||||
if (form.primsAtEnd == form.erasedType)
|
||||
// quick check shows no reordering is necessary
|
||||
return null;
|
||||
|
||||
int argc = form.parameterCount();
|
||||
int[] paramOrder = new int[argc];
|
||||
|
||||
// 3-way bucket sort:
|
||||
int pac = form.primitiveParameterCount();
|
||||
int lac = form.longPrimitiveParameterCount();
|
||||
int rfill = 0, ifill = argc - pac, lfill = argc - lac;
|
||||
|
||||
Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < ptypes.length; i++) {
|
||||
Class<?> pt = ptypes[i];
|
||||
int ord;
|
||||
if (!pt.isPrimitive()) ord = rfill++;
|
||||
else if (!hasTwoArgSlots(pt)) ord = ifill++;
|
||||
else ord = lfill++;
|
||||
if (ord != i) changed = true;
|
||||
paramOrder[i] = ord;
|
||||
}
|
||||
assert(rfill == argc - pac && ifill == argc - lac && lfill == argc);
|
||||
if (!changed) {
|
||||
form.primsAtEnd = form.erasedType;
|
||||
return null;
|
||||
}
|
||||
return paramOrder;
|
||||
}
|
||||
|
||||
/** Put the existing parameters of mt into a new order, given by newParamOrder.
|
||||
* The third argument is logically appended to mt.parameterArray,
|
||||
* so that elements of newParamOrder can index either pre-existing or
|
||||
* new parameter types.
|
||||
*/
|
||||
public static MethodType reorderParameters(MethodType mt, int[] newParamOrder, Class<?>[] moreParams) {
|
||||
if (newParamOrder == null) return mt; // no-op reordering
|
||||
Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
|
||||
Class<?>[] ntypes = new Class<?>[newParamOrder.length];
|
||||
int ordMax = ptypes.length + (moreParams == null ? 0 : moreParams.length);
|
||||
boolean changed = (ntypes.length != ptypes.length);
|
||||
for (int i = 0; i < newParamOrder.length; i++) {
|
||||
int ord = newParamOrder[i];
|
||||
if (ord != i) changed = true;
|
||||
Class<?> nt;
|
||||
if (ord < ptypes.length) nt = ptypes[ord];
|
||||
else if (ord == ordMax) nt = mt.returnType();
|
||||
else nt = moreParams[ord - ptypes.length];
|
||||
ntypes[i] = nt;
|
||||
}
|
||||
if (!changed) return mt;
|
||||
return METHOD_TYPE_FRIEND.makeImpl(mt.returnType(), ntypes, true);
|
||||
}
|
||||
|
||||
private static boolean hasTwoArgSlots(Class<?> type) {
|
||||
return type == long.class || type == double.class;
|
||||
}
|
||||
|
||||
private static long pack(int a, int b, int c, int d) {
|
||||
assert(((a|b|c|d) & ~0xFFFF) == 0);
|
||||
long hw = ((a << 16) | b), lw = ((c << 16) | d);
|
||||
return (hw << 32) | lw;
|
||||
}
|
||||
private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
|
||||
assert(word <= 3);
|
||||
return (char)(packed >> ((3-word) * 16));
|
||||
}
|
||||
|
||||
public int parameterCount() { // # outgoing values
|
||||
return unpack(argCounts, 3);
|
||||
}
|
||||
public int parameterSlotCount() { // # outgoing interpreter slots
|
||||
return unpack(argCounts, 2);
|
||||
}
|
||||
public int returnCount() { // = 0 (V), or 1
|
||||
return unpack(argCounts, 1);
|
||||
}
|
||||
public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1
|
||||
return unpack(argCounts, 0);
|
||||
}
|
||||
public int primitiveParameterCount() {
|
||||
return unpack(primCounts, 3);
|
||||
}
|
||||
public int longPrimitiveParameterCount() {
|
||||
return unpack(primCounts, 2);
|
||||
}
|
||||
public int primitiveReturnCount() { // = 0 (obj), or 1
|
||||
return unpack(primCounts, 1);
|
||||
}
|
||||
public int longPrimitiveReturnCount() { // = 1 (J/D), or 0
|
||||
return unpack(primCounts, 0);
|
||||
}
|
||||
public boolean hasPrimitives() {
|
||||
return primCounts != 0;
|
||||
}
|
||||
// public boolean hasNonVoidPrimitives() {
|
||||
// if (primCounts == 0) return false;
|
||||
// if (primitiveParameterCount() != 0) return true;
|
||||
// return (primitiveReturnCount() != 0 && returnCount() != 0);
|
||||
// }
|
||||
public boolean hasLongPrimitives() {
|
||||
return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
|
||||
}
|
||||
public int parameterToArgSlot(int i) {
|
||||
return argToSlotTable[1+i];
|
||||
}
|
||||
public int argSlotToParameter(int argSlot) {
|
||||
// Note: Empty slots are represented by zero in this table.
|
||||
// Valid arguments slots contain incremented entries, so as to be non-zero.
|
||||
// We return -1 the caller to mean an empty slot.
|
||||
return slotToArgTable[argSlot] - 1;
|
||||
}
|
||||
|
||||
public static void initForm(Access token, MethodType mt) {
|
||||
Access.check(token);
|
||||
MethodTypeImpl form = findForm(mt);
|
||||
METHOD_TYPE_FRIEND.setForm(mt, form);
|
||||
if (form.erasedType == mt) {
|
||||
// This is a principal (erased) type; show it to the JVM.
|
||||
MethodHandleImpl.init(token, mt);
|
||||
}
|
||||
}
|
||||
|
||||
static MethodTypeImpl findForm(MethodType mt) {
|
||||
MethodType erased = canonicalize(mt, ERASE, ERASE);
|
||||
if (erased == null) {
|
||||
// It is already erased. Make a new MethodTypeForm.
|
||||
return METHOD_TYPE_FRIEND.newMethodTypeForm(mt);
|
||||
} else {
|
||||
// Share the MethodTypeForm with the erased version.
|
||||
return METHOD_TYPE_FRIEND.form(erased);
|
||||
}
|
||||
}
|
||||
|
||||
/** Codes for {@link #canonicalize(java.lang.Class, int).
|
||||
* ERASE means change every reference to {@code Object}.
|
||||
* WRAP means convert primitives (including {@code void} to their
|
||||
* corresponding wrapper types. UNWRAP means the reverse of WRAP.
|
||||
* INTS means convert all non-void primitive types to int or long,
|
||||
* according to size. LONGS means convert all non-void primitives
|
||||
* to long, regardless of size. RAW_RETURN means convert a type
|
||||
* (assumed to be a return type) to int if it is smaller than an int,
|
||||
* or if it is void.
|
||||
*/
|
||||
public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
|
||||
|
||||
/** Canonicalize the types in the given method type.
|
||||
* If any types change, intern the new type, and return it.
|
||||
* Otherwise return null.
|
||||
*/
|
||||
public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
|
||||
Class<?>[] ptypes = METHOD_TYPE_FRIEND.ptypes(mt);
|
||||
Class<?>[] ptc = MethodTypeImpl.canonicalizes(ptypes, howArgs);
|
||||
Class<?> rtype = mt.returnType();
|
||||
Class<?> rtc = MethodTypeImpl.canonicalize(rtype, howRet);
|
||||
if (ptc == null && rtc == null) {
|
||||
// It is already canonical.
|
||||
return null;
|
||||
}
|
||||
// Find the erased version of the method type:
|
||||
if (rtc == null) rtc = rtype;
|
||||
if (ptc == null) ptc = ptypes;
|
||||
return METHOD_TYPE_FRIEND.makeImpl(rtc, ptc, true);
|
||||
}
|
||||
|
||||
/** Canonicalize the given return or param type.
|
||||
* Return null if the type is already canonicalized.
|
||||
*/
|
||||
static Class<?> canonicalize(Class<?> t, int how) {
|
||||
Class<?> ct;
|
||||
if (t == Object.class) {
|
||||
// no change, ever
|
||||
} else if (!t.isPrimitive()) {
|
||||
switch (how) {
|
||||
case UNWRAP:
|
||||
ct = Wrapper.asPrimitiveType(t);
|
||||
if (ct != t) return ct;
|
||||
break;
|
||||
case RAW_RETURN:
|
||||
case ERASE:
|
||||
return Object.class;
|
||||
}
|
||||
} else if (t == void.class) {
|
||||
// no change, usually
|
||||
switch (how) {
|
||||
case RAW_RETURN:
|
||||
return int.class;
|
||||
case WRAP:
|
||||
return Void.class;
|
||||
}
|
||||
} else {
|
||||
// non-void primitive
|
||||
switch (how) {
|
||||
case WRAP:
|
||||
return Wrapper.asWrapperType(t);
|
||||
case INTS:
|
||||
if (t == int.class || t == long.class)
|
||||
return null; // no change
|
||||
if (t == double.class)
|
||||
return long.class;
|
||||
return int.class;
|
||||
case LONGS:
|
||||
if (t == long.class)
|
||||
return null; // no change
|
||||
return long.class;
|
||||
case RAW_RETURN:
|
||||
if (t == int.class || t == long.class ||
|
||||
t == float.class || t == double.class)
|
||||
return null; // no change
|
||||
// everything else returns as an int
|
||||
return int.class;
|
||||
}
|
||||
}
|
||||
// no change; return null to signify
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Canonicalize each param type in the given array.
|
||||
* Return null if all types are already canonicalized.
|
||||
*/
|
||||
static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
|
||||
Class<?>[] cs = null;
|
||||
for (int imax = ts.length, i = 0; i < imax; i++) {
|
||||
Class<?> c = canonicalize(ts[i], how);
|
||||
if (c != null) {
|
||||
if (cs == null)
|
||||
cs = ts.clone();
|
||||
cs[i] = c;
|
||||
}
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
public static Invokers invokers(Access token, MethodType type) {
|
||||
Access.check(token);
|
||||
Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
|
||||
if (inv != null) return inv;
|
||||
inv = new Invokers(token, type);
|
||||
METHOD_TYPE_FRIEND.setInvokers(type, inv);
|
||||
return inv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Form"+erasedType;
|
||||
}
|
||||
|
||||
}
|
1018
jdk/src/share/classes/sun/dyn/ToGeneric.java
Normal file
1018
jdk/src/share/classes/sun/dyn/ToGeneric.java
Normal file
File diff suppressed because it is too large
Load Diff
297
jdk/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
Normal file
297
jdk/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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.anon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Anonymous class loader. Will load any valid classfile, producing
|
||||
* a {@link Class} metaobject, without installing that class in the
|
||||
* system dictionary. Therefore, {@link Class#forName(String)} will never
|
||||
* produce a reference to an anonymous class.
|
||||
* <p>
|
||||
* The access permissions of the anonymous class are borrowed from
|
||||
* a <em>host class</em>. The new class behaves as if it were an
|
||||
* inner class of the host class. It can access the host's private
|
||||
* members, if the creator of the class loader has permission to
|
||||
* do so (or to create accessible reflective objects).
|
||||
* <p>
|
||||
* When the anonymous class is loaded, elements of its constant pool
|
||||
* can be patched to new values. This provides a hook to pre-resolve
|
||||
* named classes in the constant pool to other classes, including
|
||||
* anonymous ones. Also, string constants can be pre-resolved to
|
||||
* any reference. (The verifier treats non-string, non-class reference
|
||||
* constants as plain objects.)
|
||||
* <p>
|
||||
* Why include the patching function? It makes some use cases much easier.
|
||||
* Second, the constant pool needed some internal patching anyway,
|
||||
* to anonymize the loaded class itself. Finally, if you are going
|
||||
* to use this seriously, you'll want to build anonymous classes
|
||||
* on top of pre-existing anonymous classes, and that requires patching.
|
||||
*
|
||||
* <p>%%% TO-DO:
|
||||
* <ul>
|
||||
* <li>needs better documentation</li>
|
||||
* <li>needs more security work (for safe delegation)</li>
|
||||
* <li>needs a clearer story about error processing</li>
|
||||
* <li>patch member references also (use ';' as delimiter char)</li>
|
||||
* <li>patch method references to (conforming) method handles</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author jrose
|
||||
* @author Remi Forax
|
||||
* @see <a href="http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm">
|
||||
* http://blogs.sun.com/jrose/entry/anonymous_classes_in_the_vm</a>
|
||||
*/
|
||||
|
||||
public class AnonymousClassLoader {
|
||||
final Class<?> hostClass;
|
||||
|
||||
// Note: Do not refactor the calls to checkHostClass unless you
|
||||
// also adjust this constant:
|
||||
private static int CHC_CALLERS = 3;
|
||||
|
||||
public AnonymousClassLoader() {
|
||||
this.hostClass = checkHostClass(null);
|
||||
}
|
||||
public AnonymousClassLoader(Class<?> hostClass) {
|
||||
this.hostClass = checkHostClass(hostClass);
|
||||
}
|
||||
|
||||
private static Class<?> getTopLevelClass(Class<?> clazz) {
|
||||
for(Class<?> outer = clazz.getDeclaringClass(); outer != null;
|
||||
outer = outer.getDeclaringClass()) {
|
||||
clazz = outer;
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private static Class<?> checkHostClass(Class<?> hostClass) {
|
||||
// called only from the constructor
|
||||
// does a context-sensitive check on caller class
|
||||
// CC[0..3] = {Reflection, this.checkHostClass, this.<init>, caller}
|
||||
Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
|
||||
|
||||
if (caller == null) {
|
||||
// called from the JVM directly
|
||||
if (hostClass == null)
|
||||
return AnonymousClassLoader.class; // anything central will do
|
||||
return hostClass;
|
||||
}
|
||||
|
||||
if (hostClass == null)
|
||||
hostClass = caller; // default value is caller itself
|
||||
|
||||
// anonymous class will access hostClass on behalf of caller
|
||||
Class<?> callee = hostClass;
|
||||
|
||||
if (caller == callee)
|
||||
// caller can always nominate itself to grant caller's own access rights
|
||||
return hostClass;
|
||||
|
||||
// normalize caller and callee to their top-level classes:
|
||||
caller = getTopLevelClass(caller);
|
||||
callee = getTopLevelClass(callee);
|
||||
if (caller == callee)
|
||||
return caller;
|
||||
|
||||
ClassLoader callerCL = caller.getClassLoader();
|
||||
if (callerCL == null) {
|
||||
// caller is trusted code, so accept the proposed hostClass
|
||||
return hostClass;
|
||||
}
|
||||
|
||||
// %%% should do something with doPrivileged, because trusted
|
||||
// code should have a way to execute on behalf of
|
||||
// partially-trusted clients
|
||||
|
||||
// Does the caller have the right to access the private
|
||||
// members of the callee? If not, raise an error.
|
||||
final int ACC_PRIVATE = 2;
|
||||
try {
|
||||
sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE);
|
||||
} catch (IllegalAccessException ee) {
|
||||
throw new IllegalArgumentException(ee);
|
||||
}
|
||||
|
||||
return hostClass;
|
||||
}
|
||||
|
||||
public Class<?> loadClass(byte[] classFile) {
|
||||
if (defineAnonymousClass == null) {
|
||||
// no JVM support; try to fake an approximation
|
||||
try {
|
||||
return fakeLoadClass(new ConstantPoolParser(classFile).createPatch());
|
||||
} catch (InvalidConstantPoolFormatException ee) {
|
||||
throw new IllegalArgumentException(ee);
|
||||
}
|
||||
}
|
||||
return loadClass(classFile, null);
|
||||
}
|
||||
|
||||
public Class<?> loadClass(ConstantPoolPatch classPatch) {
|
||||
if (defineAnonymousClass == null) {
|
||||
// no JVM support; try to fake an approximation
|
||||
return fakeLoadClass(classPatch);
|
||||
}
|
||||
Object[] patches = classPatch.patchArray;
|
||||
// Convert class names (this late in the game)
|
||||
// to use slash '/' instead of dot '.'.
|
||||
// Java likes dots, but the JVM likes slashes.
|
||||
for (int i = 0; i < patches.length; i++) {
|
||||
Object value = patches[i];
|
||||
if (value != null) {
|
||||
byte tag = classPatch.getTag(i);
|
||||
switch (tag) {
|
||||
case ConstantPoolVisitor.CONSTANT_Class:
|
||||
if (value instanceof String) {
|
||||
if (patches == classPatch.patchArray)
|
||||
patches = patches.clone();
|
||||
patches[i] = ((String)value).replace('.', '/');
|
||||
}
|
||||
break;
|
||||
case ConstantPoolVisitor.CONSTANT_Fieldref:
|
||||
case ConstantPoolVisitor.CONSTANT_Methodref:
|
||||
case ConstantPoolVisitor.CONSTANT_InterfaceMethodref:
|
||||
case ConstantPoolVisitor.CONSTANT_NameAndType:
|
||||
// When/if the JVM supports these patches,
|
||||
// we'll probably need to reformat them also.
|
||||
// Meanwhile, let the class loader create the error.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return loadClass(classPatch.outer.classFile, classPatch.patchArray);
|
||||
}
|
||||
|
||||
private Class<?> loadClass(byte[] classFile, Object[] patchArray) {
|
||||
try {
|
||||
return (Class<?>)
|
||||
defineAnonymousClass.invoke(unsafe,
|
||||
hostClass, classFile, patchArray);
|
||||
} catch (Exception ex) {
|
||||
throwReflectedException(ex);
|
||||
throw new RuntimeException("error loading into "+hostClass, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwReflectedException(Exception ex) {
|
||||
if (ex instanceof InvocationTargetException) {
|
||||
Throwable tex = ((InvocationTargetException)ex).getTargetException();
|
||||
if (tex instanceof Error)
|
||||
throw (Error) tex;
|
||||
ex = (Exception) tex;
|
||||
}
|
||||
if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> fakeLoadClass(ConstantPoolPatch classPatch) {
|
||||
// Implementation:
|
||||
// 1. Make up a new name nobody has used yet.
|
||||
// 2. Inspect the tail-header of the class to find the this_class index.
|
||||
// 3. Patch the CONSTANT_Class for this_class to the new name.
|
||||
// 4. Add other CP entries required by (e.g.) string patches.
|
||||
// 5. Flatten Class constants down to their names, making sure that
|
||||
// the host class loader can pick them up again accurately.
|
||||
// 6. Generate the edited class file bytes.
|
||||
//
|
||||
// Potential limitations:
|
||||
// * The class won't be truly anonymous, and may interfere with others.
|
||||
// * Flattened class constants might not work, because of loader issues.
|
||||
// * Pseudo-string constants will not flatten down to real strings.
|
||||
// * Method handles will (of course) fail to flatten to linkage strings.
|
||||
if (true) throw new UnsupportedOperationException("NYI");
|
||||
Object[] cpArray;
|
||||
try {
|
||||
cpArray = classPatch.getOriginalCP();
|
||||
} catch (InvalidConstantPoolFormatException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
int thisClassIndex = classPatch.getParser().getThisClassIndex();
|
||||
String thisClassName = (String) cpArray[thisClassIndex];
|
||||
synchronized (AnonymousClassLoader.class) {
|
||||
thisClassName = thisClassName+"\\|"+(++fakeNameCounter);
|
||||
}
|
||||
classPatch.putUTF8(thisClassIndex, thisClassName);
|
||||
byte[] classFile = null;
|
||||
return unsafe.defineClass(null, classFile, 0, classFile.length,
|
||||
hostClass.getClassLoader(),
|
||||
hostClass.getProtectionDomain());
|
||||
}
|
||||
private static int fakeNameCounter = 99999;
|
||||
|
||||
// ignore two warnings on this line:
|
||||
static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
|
||||
// preceding line requires that this class be on the boot class path
|
||||
|
||||
static private final Method defineAnonymousClass;
|
||||
static {
|
||||
Method dac = null;
|
||||
Class<? extends sun.misc.Unsafe> unsafeClass = unsafe.getClass();
|
||||
try {
|
||||
dac = unsafeClass.getMethod("defineAnonymousClass",
|
||||
Class.class,
|
||||
byte[].class,
|
||||
Object[].class);
|
||||
} catch (Exception ee) {
|
||||
dac = null;
|
||||
}
|
||||
defineAnonymousClass = dac;
|
||||
}
|
||||
|
||||
private static void noJVMSupport() {
|
||||
throw new UnsupportedOperationException("no JVM support for anonymous classes");
|
||||
}
|
||||
|
||||
|
||||
private static native Class<?> loadClassInternal(Class<?> hostClass,
|
||||
byte[] classFile,
|
||||
Object[] patchArray);
|
||||
|
||||
public static byte[] readClassFile(Class<?> templateClass) throws IOException {
|
||||
String templateName = templateClass.getName();
|
||||
int lastDot = templateName.lastIndexOf('.');
|
||||
java.net.URL url = templateClass.getResource(templateName.substring(lastDot+1)+".class");
|
||||
java.net.URLConnection connection = url.openConnection();
|
||||
int contentLength = connection.getContentLength();
|
||||
if (contentLength < 0)
|
||||
throw new IOException("invalid content length "+contentLength);
|
||||
|
||||
byte[] classFile = new byte[contentLength];
|
||||
InputStream tcs = connection.getInputStream();
|
||||
for (int fill = 0, nr; fill < classFile.length; fill += nr) {
|
||||
nr = tcs.read(classFile, fill, classFile.length - fill);
|
||||
if (nr < 0)
|
||||
throw new IOException("premature end of file");
|
||||
}
|
||||
return classFile;
|
||||
}
|
||||
}
|
368
jdk/src/share/classes/sun/dyn/anon/ConstantPoolParser.java
Normal file
368
jdk/src/share/classes/sun/dyn/anon/ConstantPoolParser.java
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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.anon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static sun.dyn.anon.ConstantPoolVisitor.*;
|
||||
|
||||
/** A constant pool parser.
|
||||
*/
|
||||
public class ConstantPoolParser {
|
||||
final byte[] classFile;
|
||||
final byte[] tags;
|
||||
final char[] firstHeader; // maghi, maglo, minor, major, cplen
|
||||
|
||||
// these are filled in on first parse:
|
||||
int endOffset;
|
||||
char[] secondHeader; // flags, this_class, super_class, intlen
|
||||
|
||||
// used to decode UTF8 array
|
||||
private char[] charArray = new char[80];
|
||||
|
||||
/** Creates a constant pool parser.
|
||||
* @param classFile an array of bytes containing a class.
|
||||
* @throws InvalidConstantPoolFormatException if the header of the class has errors.
|
||||
*/
|
||||
public ConstantPoolParser(byte[] classFile) throws InvalidConstantPoolFormatException {
|
||||
this.classFile = classFile;
|
||||
this.firstHeader = parseHeader(classFile);
|
||||
this.tags = new byte[firstHeader[4]];
|
||||
}
|
||||
|
||||
/** Create a constant pool parser by loading the bytecodes of the
|
||||
* class taken as argument.
|
||||
*
|
||||
* @param templateClass the class to parse.
|
||||
*
|
||||
* @throws IOException raised if an I/O occurs when loading
|
||||
* the bytecode of the template class.
|
||||
* @throws InvalidConstantPoolFormatException if the header of the class has errors.
|
||||
*
|
||||
* @see #ConstantPoolParser(byte[])
|
||||
* @see AnonymousClassLoader#readClassFile(Class)
|
||||
*/
|
||||
public ConstantPoolParser(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException {
|
||||
this(AnonymousClassLoader.readClassFile(templateClass));
|
||||
}
|
||||
|
||||
/** Creates an empty patch to patch the class file
|
||||
* used by the current parser.
|
||||
* @return a new class patch.
|
||||
*/
|
||||
public ConstantPoolPatch createPatch() {
|
||||
return new ConstantPoolPatch(this);
|
||||
}
|
||||
|
||||
/** Report the tag of the indicated CP entry.
|
||||
* @param index
|
||||
* @return one of {@link ConstantPoolVisitor#CONSTANT_Utf8}, etc.
|
||||
*/
|
||||
public byte getTag(int index) {
|
||||
getEndOffset(); // trigger an exception if we haven't parsed yet
|
||||
return tags[index];
|
||||
}
|
||||
|
||||
/** Report the length of the constant pool. */
|
||||
public int getLength() {
|
||||
return firstHeader[4];
|
||||
}
|
||||
|
||||
/** Report the offset, within the class file, of the start of the constant pool. */
|
||||
public int getStartOffset() {
|
||||
return firstHeader.length * 2;
|
||||
}
|
||||
|
||||
/** Report the offset, within the class file, of the end of the constant pool. */
|
||||
public int getEndOffset() {
|
||||
if (endOffset == 0)
|
||||
throw new IllegalStateException("class file has not yet been parsed");
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
/** Report the CP index of this class's own name. */
|
||||
public int getThisClassIndex() {
|
||||
getEndOffset(); // provoke exception if not yet parsed
|
||||
return secondHeader[1];
|
||||
}
|
||||
|
||||
/** Report the total size of the class file. */
|
||||
public int getTailLength() {
|
||||
return classFile.length - getEndOffset();
|
||||
}
|
||||
|
||||
/** Write the head (header plus constant pool)
|
||||
* of the class file to the indicated stream.
|
||||
*/
|
||||
public void writeHead(OutputStream out) throws IOException {
|
||||
out.write(classFile, 0, getEndOffset());
|
||||
}
|
||||
|
||||
/** Write the head (header plus constant pool)
|
||||
* of the class file to the indicated stream,
|
||||
* incorporating the non-null entries of the given array
|
||||
* as patches.
|
||||
*/
|
||||
void writePatchedHead(OutputStream out, Object[] patchArray) {
|
||||
// this will be useful to partially emulate the class loader on old JVMs
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
/** Write the tail (everything after the constant pool)
|
||||
* of the class file to the indicated stream.
|
||||
*/
|
||||
public void writeTail(OutputStream out) throws IOException {
|
||||
out.write(classFile, getEndOffset(), getTailLength());
|
||||
}
|
||||
|
||||
private static char[] parseHeader(byte[] classFile) throws InvalidConstantPoolFormatException {
|
||||
char[] result = new char[5];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(classFile);
|
||||
for (int i = 0; i < result.length; i++)
|
||||
result[i] = (char) getUnsignedShort(buffer);
|
||||
int magic = result[0] << 16 | result[1] << 0;
|
||||
if (magic != 0xCAFEBABE)
|
||||
throw new InvalidConstantPoolFormatException("invalid magic number "+magic);
|
||||
// skip major, minor version
|
||||
int len = result[4];
|
||||
if (len < 1)
|
||||
throw new InvalidConstantPoolFormatException("constant pool length < 1");
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Parse the constant pool of the class
|
||||
* calling a method visit* each time a constant pool entry is parsed.
|
||||
*
|
||||
* The order of the calls to visit* is not guaranteed to be the same
|
||||
* than the order of the constant pool entry in the bytecode array.
|
||||
*
|
||||
* @param visitor
|
||||
* @throws InvalidConstantPoolFormatException
|
||||
*/
|
||||
public void parse(ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(classFile);
|
||||
buffer.position(getStartOffset()); //skip header
|
||||
|
||||
Object[] values = new Object[getLength()];
|
||||
try {
|
||||
parseConstantPool(buffer, values, visitor);
|
||||
} catch(BufferUnderflowException e) {
|
||||
throw new InvalidConstantPoolFormatException(e);
|
||||
}
|
||||
if (endOffset == 0) {
|
||||
endOffset = buffer.position();
|
||||
secondHeader = new char[4];
|
||||
for (int i = 0; i < secondHeader.length; i++) {
|
||||
secondHeader[i] = (char) getUnsignedShort(buffer);
|
||||
}
|
||||
}
|
||||
resolveConstantPool(values, visitor);
|
||||
}
|
||||
|
||||
private char[] getCharArray(int utfLength) {
|
||||
if (utfLength <= charArray.length)
|
||||
return charArray;
|
||||
return charArray = new char[utfLength];
|
||||
}
|
||||
|
||||
private void parseConstantPool(ByteBuffer buffer, Object[] values, ConstantPoolVisitor visitor) throws InvalidConstantPoolFormatException {
|
||||
for (int i = 1; i < tags.length; ) {
|
||||
byte tag = (byte) getUnsignedByte(buffer);
|
||||
assert(tags[i] == 0 || tags[i] == tag);
|
||||
tags[i] = tag;
|
||||
switch (tag) {
|
||||
case CONSTANT_Utf8:
|
||||
int utfLen = getUnsignedShort(buffer);
|
||||
String value = getUTF8(buffer, utfLen, getCharArray(utfLen));
|
||||
visitor.visitUTF8(i, CONSTANT_Utf8, value);
|
||||
tags[i] = tag;
|
||||
values[i++] = value;
|
||||
break;
|
||||
case CONSTANT_Integer:
|
||||
visitor.visitConstantValue(i, tag, buffer.getInt());
|
||||
i++;
|
||||
break;
|
||||
case CONSTANT_Float:
|
||||
visitor.visitConstantValue(i, tag, buffer.getFloat());
|
||||
i++;
|
||||
break;
|
||||
case CONSTANT_Long:
|
||||
visitor.visitConstantValue(i, tag, buffer.getLong());
|
||||
i+=2;
|
||||
break;
|
||||
case CONSTANT_Double:
|
||||
visitor.visitConstantValue(i, tag, buffer.getDouble());
|
||||
i+=2;
|
||||
break;
|
||||
|
||||
case CONSTANT_Class: // fall through:
|
||||
case CONSTANT_String:
|
||||
tags[i] = tag;
|
||||
values[i++] = new int[] { getUnsignedShort(buffer) };
|
||||
break;
|
||||
|
||||
case CONSTANT_Fieldref: // fall through:
|
||||
case CONSTANT_Methodref: // fall through:
|
||||
case CONSTANT_InterfaceMethodref: // fall through:
|
||||
case CONSTANT_NameAndType:
|
||||
tags[i] = tag;
|
||||
values[i++] = new int[] { getUnsignedShort(buffer), getUnsignedShort(buffer) };
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("invalid constant "+tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveConstantPool(Object[] values, ConstantPoolVisitor visitor) {
|
||||
// clean out the int[] values, which are temporary
|
||||
for (int beg = 1, end = values.length-1, beg2, end2;
|
||||
beg <= end;
|
||||
beg = beg2, end = end2) {
|
||||
beg2 = end; end2 = beg-1;
|
||||
//System.out.println("CP resolve pass: "+beg+".."+end);
|
||||
for (int i = beg; i <= end; i++) {
|
||||
Object value = values[i];
|
||||
if (!(value instanceof int[]))
|
||||
continue;
|
||||
int[] array = (int[]) value;
|
||||
byte tag = tags[i];
|
||||
switch (tag) {
|
||||
case CONSTANT_String:
|
||||
String stringBody = (String) values[array[0]];
|
||||
visitor.visitConstantString(i, tag, stringBody, array[0]);
|
||||
values[i] = null;
|
||||
break;
|
||||
case CONSTANT_Class: {
|
||||
String className = (String) values[array[0]];
|
||||
// use the external form favored by Class.forName:
|
||||
className = className.replace('/', '.');
|
||||
visitor.visitConstantString(i, tag, className, array[0]);
|
||||
values[i] = className;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_NameAndType: {
|
||||
String memberName = (String) values[array[0]];
|
||||
String signature = (String) values[array[1]];
|
||||
visitor.visitDescriptor(i, tag, memberName, signature,
|
||||
array[0], array[1]);
|
||||
values[i] = new String[] {memberName, signature};
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Fieldref: // fall through:
|
||||
case CONSTANT_Methodref: // fall through:
|
||||
case CONSTANT_InterfaceMethodref: {
|
||||
Object className = values[array[0]];
|
||||
Object nameAndType = values[array[1]];
|
||||
if (!(className instanceof String) ||
|
||||
!(nameAndType instanceof String[])) {
|
||||
// one more pass is needed
|
||||
if (beg2 > i) beg2 = i;
|
||||
if (end2 < i) end2 = i;
|
||||
continue;
|
||||
}
|
||||
String[] nameAndTypeArray = (String[]) nameAndType;
|
||||
visitor.visitMemberRef(i, tag,
|
||||
(String)className,
|
||||
nameAndTypeArray[0],
|
||||
nameAndTypeArray[1],
|
||||
array[0], array[1]);
|
||||
values[i] = null;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getUnsignedByte(ByteBuffer buffer) {
|
||||
return buffer.get() & 0xFF;
|
||||
}
|
||||
|
||||
private static int getUnsignedShort(ByteBuffer buffer) {
|
||||
int b1 = getUnsignedByte(buffer);
|
||||
int b2 = getUnsignedByte(buffer);
|
||||
return (b1 << 8) + (b2 << 0);
|
||||
}
|
||||
|
||||
private static String getUTF8(ByteBuffer buffer, int utfLen, char[] charArray) throws InvalidConstantPoolFormatException {
|
||||
int utfLimit = buffer.position() + utfLen;
|
||||
int index = 0;
|
||||
while (buffer.position() < utfLimit) {
|
||||
int c = buffer.get() & 0xff;
|
||||
if (c > 127) {
|
||||
buffer.position(buffer.position() - 1);
|
||||
return getUTF8Extended(buffer, utfLimit, charArray, index);
|
||||
}
|
||||
charArray[index++] = (char)c;
|
||||
}
|
||||
return new String(charArray, 0, index);
|
||||
}
|
||||
|
||||
private static String getUTF8Extended(ByteBuffer buffer, int utfLimit, char[] charArray, int index) throws InvalidConstantPoolFormatException {
|
||||
int c, c2, c3;
|
||||
while (buffer.position() < utfLimit) {
|
||||
c = buffer.get() & 0xff;
|
||||
switch (c >> 4) {
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||
/* 0xxxxxxx*/
|
||||
charArray[index++] = (char)c;
|
||||
break;
|
||||
case 12: case 13:
|
||||
/* 110x xxxx 10xx xxxx*/
|
||||
c2 = buffer.get();
|
||||
if ((c2 & 0xC0) != 0x80)
|
||||
throw new InvalidConstantPoolFormatException(
|
||||
"malformed input around byte " + buffer.position());
|
||||
charArray[index++] = (char)(((c & 0x1F) << 6) |
|
||||
(c2 & 0x3F));
|
||||
break;
|
||||
case 14:
|
||||
/* 1110 xxxx 10xx xxxx 10xx xxxx */
|
||||
c2 = buffer.get();
|
||||
c3 = buffer.get();
|
||||
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80))
|
||||
throw new InvalidConstantPoolFormatException(
|
||||
"malformed input around byte " + (buffer.position()));
|
||||
charArray[index++] = (char)(((c & 0x0F) << 12) |
|
||||
((c2 & 0x3F) << 6) |
|
||||
((c3 & 0x3F) << 0));
|
||||
break;
|
||||
default:
|
||||
/* 10xx xxxx, 1111 xxxx */
|
||||
throw new InvalidConstantPoolFormatException(
|
||||
"malformed input around byte " + buffer.position());
|
||||
}
|
||||
}
|
||||
// The number of chars produced may be less than utflen
|
||||
return new String(charArray, 0, index);
|
||||
}
|
||||
}
|
503
jdk/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java
Normal file
503
jdk/src/share/classes/sun/dyn/anon/ConstantPoolPatch.java
Normal file
@ -0,0 +1,503 @@
|
||||
/*
|
||||
* 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.anon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static sun.dyn.anon.ConstantPoolVisitor.*;
|
||||
|
||||
/** A class and its patched constant pool.
|
||||
*
|
||||
* This class allow to modify (patch) a constant pool
|
||||
* by changing the value of its entry.
|
||||
* Entry are referenced using index that can be get
|
||||
* by parsing the constant pool using
|
||||
* {@link ConstantPoolParser#parse(ConstantPoolVisitor)}.
|
||||
*
|
||||
* @see ConstantPoolVisitor
|
||||
* @see ConstantPoolParser#createPatch()
|
||||
*/
|
||||
public class ConstantPoolPatch {
|
||||
final ConstantPoolParser outer;
|
||||
final Object[] patchArray;
|
||||
|
||||
ConstantPoolPatch(ConstantPoolParser outer) {
|
||||
this.outer = outer;
|
||||
this.patchArray = new Object[outer.getLength()];
|
||||
}
|
||||
|
||||
/** Create a {@link ConstantPoolParser} and
|
||||
* a {@link ConstantPoolPatch} in one step.
|
||||
* Equivalent to {@code new ConstantPoolParser(classFile).createPatch()}.
|
||||
*
|
||||
* @param classFile an array of bytes containing a class.
|
||||
* @see #ConstantPoolParser(Class)
|
||||
*/
|
||||
public ConstantPoolPatch(byte[] classFile) throws InvalidConstantPoolFormatException {
|
||||
this(new ConstantPoolParser(classFile));
|
||||
}
|
||||
|
||||
/** Create a {@link ConstantPoolParser} and
|
||||
* a {@link ConstantPoolPatch} in one step.
|
||||
* Equivalent to {@code new ConstantPoolParser(templateClass).createPatch()}.
|
||||
*
|
||||
* @param templateClass the class to parse.
|
||||
* @see #ConstantPoolParser(Class)
|
||||
*/
|
||||
public ConstantPoolPatch(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException {
|
||||
this(new ConstantPoolParser(templateClass));
|
||||
}
|
||||
|
||||
|
||||
/** Creates a patch from an existing patch.
|
||||
* All changes are copied from that patch.
|
||||
* @param patch a patch
|
||||
*
|
||||
* @see ConstantPoolParser#createPatch()
|
||||
*/
|
||||
public ConstantPoolPatch(ConstantPoolPatch patch) {
|
||||
outer = patch.outer;
|
||||
patchArray = patch.patchArray.clone();
|
||||
}
|
||||
|
||||
/** Which parser built this patch? */
|
||||
public ConstantPoolParser getParser() {
|
||||
return outer;
|
||||
}
|
||||
|
||||
/** Report the tag at the given index in the constant pool. */
|
||||
public byte getTag(int index) {
|
||||
return outer.getTag(index);
|
||||
}
|
||||
|
||||
/** Report the current patch at the given index of the constant pool.
|
||||
* Null means no patch will be made.
|
||||
* To observe the unpatched entry at the given index, use
|
||||
* {@link #getParser()}{@code .}@link ConstantPoolParser#parse(ConstantPoolVisitor)}
|
||||
*/
|
||||
public Object getPatch(int index) {
|
||||
Object value = patchArray[index];
|
||||
if (value == null) return null;
|
||||
switch (getTag(index)) {
|
||||
case CONSTANT_Fieldref:
|
||||
case CONSTANT_Methodref:
|
||||
case CONSTANT_InterfaceMethodref:
|
||||
if (value instanceof String)
|
||||
value = stripSemis(2, (String) value);
|
||||
break;
|
||||
case CONSTANT_NameAndType:
|
||||
if (value instanceof String)
|
||||
value = stripSemis(1, (String) value);
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Clear all patches. */
|
||||
public void clear() {
|
||||
Arrays.fill(patchArray, null);
|
||||
}
|
||||
|
||||
/** Clear one patch. */
|
||||
public void clear(int index) {
|
||||
patchArray[index] = null;
|
||||
}
|
||||
|
||||
/** Produce the patches as an array. */
|
||||
public Object[] getPatches() {
|
||||
return patchArray.clone();
|
||||
}
|
||||
|
||||
/** Produce the original constant pool as an array. */
|
||||
public Object[] getOriginalCP() throws InvalidConstantPoolFormatException {
|
||||
return getOriginalCP(0, patchArray.length, -1);
|
||||
}
|
||||
|
||||
/** Walk the constant pool, applying patches using the given map.
|
||||
*
|
||||
* @param utf8Map Utf8 strings to modify, if encountered
|
||||
* @param classMap Classes (or their names) to modify, if encountered
|
||||
* @param valueMap Constant values to modify, if encountered
|
||||
* @param deleteUsedEntries if true, delete map entries that are used
|
||||
*/
|
||||
public void putPatches(final Map<String,String> utf8Map,
|
||||
final Map<String,Object> classMap,
|
||||
final Map<Object,Object> valueMap,
|
||||
boolean deleteUsedEntries) throws InvalidConstantPoolFormatException {
|
||||
final HashSet<String> usedUtf8Keys;
|
||||
final HashSet<String> usedClassKeys;
|
||||
final HashSet<Object> usedValueKeys;
|
||||
if (deleteUsedEntries) {
|
||||
usedUtf8Keys = (utf8Map == null) ? null : new HashSet<String>();
|
||||
usedClassKeys = (classMap == null) ? null : new HashSet<String>();
|
||||
usedValueKeys = (valueMap == null) ? null : new HashSet<Object>();
|
||||
} else {
|
||||
usedUtf8Keys = null;
|
||||
usedClassKeys = null;
|
||||
usedValueKeys = null;
|
||||
}
|
||||
|
||||
outer.parse(new ConstantPoolVisitor() {
|
||||
|
||||
@Override
|
||||
public void visitUTF8(int index, byte tag, String utf8) {
|
||||
putUTF8(index, utf8Map.get(utf8));
|
||||
if (usedUtf8Keys != null) usedUtf8Keys.add(utf8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantValue(int index, byte tag, Object value) {
|
||||
putConstantValue(index, tag, valueMap.get(value));
|
||||
if (usedValueKeys != null) usedValueKeys.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantString(int index, byte tag, String name, int nameIndex) {
|
||||
if (tag == CONSTANT_Class) {
|
||||
putConstantValue(index, tag, classMap.get(name));
|
||||
if (usedClassKeys != null) usedClassKeys.add(name);
|
||||
} else {
|
||||
assert(tag == CONSTANT_String);
|
||||
visitConstantValue(index, tag, name);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (usedUtf8Keys != null) utf8Map.keySet().removeAll(usedUtf8Keys);
|
||||
if (usedClassKeys != null) classMap.keySet().removeAll(usedClassKeys);
|
||||
if (usedValueKeys != null) valueMap.keySet().removeAll(usedValueKeys);
|
||||
}
|
||||
|
||||
Object[] getOriginalCP(final int startIndex,
|
||||
final int endIndex,
|
||||
final int tagMask) throws InvalidConstantPoolFormatException {
|
||||
final Object[] cpArray = new Object[endIndex - startIndex];
|
||||
outer.parse(new ConstantPoolVisitor() {
|
||||
|
||||
void show(int index, byte tag, Object value) {
|
||||
if (index < startIndex || index >= endIndex) return;
|
||||
if (((1 << tag) & tagMask) == 0) return;
|
||||
cpArray[index - startIndex] = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUTF8(int index, byte tag, String utf8) {
|
||||
show(index, tag, utf8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantValue(int index, byte tag, Object value) {
|
||||
assert(tag != CONSTANT_String);
|
||||
show(index, tag, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantString(int index, byte tag,
|
||||
String value, int j) {
|
||||
show(index, tag, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMemberRef(int index, byte tag,
|
||||
String className, String memberName,
|
||||
String signature,
|
||||
int j, int k) {
|
||||
show(index, tag, new String[]{ className, memberName, signature });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDescriptor(int index, byte tag,
|
||||
String memberName, String signature,
|
||||
int j, int k) {
|
||||
show(index, tag, new String[]{ memberName, signature });
|
||||
}
|
||||
});
|
||||
return cpArray;
|
||||
}
|
||||
|
||||
/** Write the head (header plus constant pool)
|
||||
* of the patched class file to the indicated stream.
|
||||
*/
|
||||
void writeHead(OutputStream out) throws IOException {
|
||||
outer.writePatchedHead(out, patchArray);
|
||||
}
|
||||
|
||||
/** Write the tail (everything after the constant pool)
|
||||
* of the patched class file to the indicated stream.
|
||||
*/
|
||||
void writeTail(OutputStream out) throws IOException {
|
||||
outer.writeTail(out);
|
||||
}
|
||||
|
||||
private void checkConstantTag(byte tag, Object value) {
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException(
|
||||
"invalid null constant value");
|
||||
if (classForTag(tag) != value.getClass())
|
||||
throw new IllegalArgumentException(
|
||||
"invalid constant value"
|
||||
+ (tag == CONSTANT_None ? ""
|
||||
: " for tag "+tagName(tag))
|
||||
+ " of class "+value.getClass());
|
||||
}
|
||||
|
||||
private void checkTag(int index, byte putTag) {
|
||||
byte tag = outer.tags[index];
|
||||
if (tag != putTag)
|
||||
throw new IllegalArgumentException(
|
||||
"invalid put operation"
|
||||
+ " for " + tagName(putTag)
|
||||
+ " at index " + index + " found " + tagName(tag));
|
||||
}
|
||||
|
||||
private void checkTagMask(int index, int tagBitMask) {
|
||||
byte tag = outer.tags[index];
|
||||
int tagBit = ((tag & 0x1F) == tag) ? (1 << tag) : 0;
|
||||
if ((tagBit & tagBitMask) == 0)
|
||||
throw new IllegalArgumentException(
|
||||
"invalid put operation"
|
||||
+ " at index " + index + " found " + tagName(tag));
|
||||
}
|
||||
|
||||
private static void checkMemberName(String memberName) {
|
||||
if (memberName.indexOf(';') >= 0)
|
||||
throw new IllegalArgumentException("memberName " + memberName + " contains a ';'");
|
||||
}
|
||||
|
||||
/** Set the entry of the constant pool indexed by index to
|
||||
* a new string.
|
||||
*
|
||||
* @param index an index to a constant pool entry containing a
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Utf8} value.
|
||||
* @param utf8 a string
|
||||
*
|
||||
* @see ConstantPoolVisitor#visitUTF8(int, byte, String)
|
||||
*/
|
||||
public void putUTF8(int index, String utf8) {
|
||||
if (utf8 == null) { clear(index); return; }
|
||||
checkTag(index, CONSTANT_Utf8);
|
||||
patchArray[index] = utf8;
|
||||
}
|
||||
|
||||
/** Set the entry of the constant pool indexed by index to
|
||||
* a new value, depending on its dynamic type.
|
||||
*
|
||||
* @param index an index to a constant pool entry containing a
|
||||
* one of the following structures:
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Integer},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Float},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Long},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Double},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_String}, or
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Class}
|
||||
* @param value a boxed int, float, long or double; or a string or class object
|
||||
* @throws IllegalArgumentException if the type of the constant does not
|
||||
* match the constant pool entry type,
|
||||
* as reported by {@link #getTag(int)}
|
||||
*
|
||||
* @see #putConstantValue(int, byte, Object)
|
||||
* @see ConstantPoolVisitor#visitConstantValue(int, byte, Object)
|
||||
* @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
|
||||
*/
|
||||
public void putConstantValue(int index, Object value) {
|
||||
if (value == null) { clear(index); return; }
|
||||
byte tag = tagForConstant(value.getClass());
|
||||
checkConstantTag(tag, value);
|
||||
checkTag(index, tag);
|
||||
patchArray[index] = value;
|
||||
}
|
||||
|
||||
/** Set the entry of the constant pool indexed by index to
|
||||
* a new value.
|
||||
*
|
||||
* @param index an index to a constant pool entry matching the given tag
|
||||
* @param tag one of the following values:
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Integer},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Float},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Long},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Double},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_String}, or
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Class}
|
||||
* @param value a boxed number, string, or class object
|
||||
* @throws IllegalArgumentException if the type of the constant does not
|
||||
* match the constant pool entry type, or if a class name contains
|
||||
* '/' or ';'
|
||||
*
|
||||
* @see #putConstantValue(int, Object)
|
||||
* @see ConstantPoolVisitor#visitConstantValue(int, byte, Object)
|
||||
* @see ConstantPoolVisitor#visitConstantString(int, byte, String, int)
|
||||
*/
|
||||
public void putConstantValue(int index, byte tag, Object value) {
|
||||
if (value == null) { clear(index); return; }
|
||||
checkTag(index, tag);
|
||||
if (tag == CONSTANT_Class && value instanceof String) {
|
||||
checkClassName((String) value);
|
||||
} else if (tag == CONSTANT_String) {
|
||||
// the JVM accepts any object as a patch for a string
|
||||
} else {
|
||||
// make sure the incoming value is the right type
|
||||
checkConstantTag(tag, value);
|
||||
}
|
||||
checkTag(index, tag);
|
||||
patchArray[index] = value;
|
||||
}
|
||||
|
||||
/** Set the entry of the constant pool indexed by index to
|
||||
* a new {@link ConstantPoolVisitor#CONSTANT_NameAndType} value.
|
||||
*
|
||||
* @param index an index to a constant pool entry containing a
|
||||
* {@link ConstantPoolVisitor#CONSTANT_NameAndType} value.
|
||||
* @param memberName a memberName
|
||||
* @param signature a signature
|
||||
* @throws IllegalArgumentException if memberName contains the character ';'
|
||||
*
|
||||
* @see ConstantPoolVisitor#visitDescriptor(int, byte, String, String, int, int)
|
||||
*/
|
||||
public void putDescriptor(int index, String memberName, String signature) {
|
||||
checkTag(index, CONSTANT_NameAndType);
|
||||
checkMemberName(memberName);
|
||||
patchArray[index] = addSemis(memberName, signature);
|
||||
}
|
||||
|
||||
/** Set the entry of the constant pool indexed by index to
|
||||
* a new {@link ConstantPoolVisitor#CONSTANT_Fieldref},
|
||||
* {@link ConstantPoolVisitor#CONSTANT_Methodref}, or
|
||||
* {@link ConstantPoolVisitor#CONSTANT_InterfaceMethodref} value.
|
||||
*
|
||||
* @param index an index to a constant pool entry containing a member reference
|
||||
* @param className a class name
|
||||
* @param memberName a field or method name
|
||||
* @param signature a field or method signature
|
||||
* @throws IllegalArgumentException if memberName contains the character ';'
|
||||
* or signature is not a correct signature
|
||||
*
|
||||
* @see ConstantPoolVisitor#visitMemberRef(int, byte, String, String, String, int, int)
|
||||
*/
|
||||
public void putMemberRef(int index, byte tag,
|
||||
String className, String memberName, String signature) {
|
||||
checkTagMask(tag, CONSTANT_MemberRef_MASK);
|
||||
checkTag(index, tag);
|
||||
checkClassName(className);
|
||||
checkMemberName(memberName);
|
||||
if (signature.startsWith("(") == (tag == CONSTANT_Fieldref))
|
||||
throw new IllegalArgumentException("bad signature: "+signature);
|
||||
patchArray[index] = addSemis(className, memberName, signature);
|
||||
}
|
||||
|
||||
static private final int CONSTANT_MemberRef_MASK =
|
||||
CONSTANT_Fieldref
|
||||
| CONSTANT_Methodref
|
||||
| CONSTANT_InterfaceMethodref;
|
||||
|
||||
private static final Map<Class<?>, Byte> CONSTANT_VALUE_CLASS_TAG
|
||||
= new IdentityHashMap<Class<?>, Byte>();
|
||||
private static final Class[] CONSTANT_VALUE_CLASS = new Class[16];
|
||||
static {
|
||||
Object[][] values = {
|
||||
{Integer.class, CONSTANT_Integer},
|
||||
{Long.class, CONSTANT_Long},
|
||||
{Float.class, CONSTANT_Float},
|
||||
{Double.class, CONSTANT_Double},
|
||||
{String.class, CONSTANT_String},
|
||||
{Class.class, CONSTANT_Class}
|
||||
};
|
||||
for (Object[] value : values) {
|
||||
Class<?> cls = (Class<?>)value[0];
|
||||
Byte tag = (Byte) value[1];
|
||||
CONSTANT_VALUE_CLASS_TAG.put(cls, tag);
|
||||
CONSTANT_VALUE_CLASS[(byte)tag] = cls;
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> classForTag(byte tag) {
|
||||
if ((tag & 0xFF) >= CONSTANT_VALUE_CLASS.length)
|
||||
return null;
|
||||
return CONSTANT_VALUE_CLASS[tag];
|
||||
}
|
||||
|
||||
static byte tagForConstant(Class<?> cls) {
|
||||
Byte tag = CONSTANT_VALUE_CLASS_TAG.get(cls);
|
||||
return (tag == null) ? CONSTANT_None : (byte)tag;
|
||||
}
|
||||
|
||||
private static void checkClassName(String className) {
|
||||
if (className.indexOf('/') >= 0 || className.indexOf(';') >= 0)
|
||||
throw new IllegalArgumentException("invalid class name " + className);
|
||||
}
|
||||
|
||||
static String addSemis(String name, String... names) {
|
||||
StringBuilder buf = new StringBuilder(name.length() * 5);
|
||||
buf.append(name);
|
||||
for (String name2 : names) {
|
||||
buf.append(';').append(name2);
|
||||
}
|
||||
String res = buf.toString();
|
||||
assert(stripSemis(names.length, res)[0].equals(name));
|
||||
assert(stripSemis(names.length, res)[1].equals(names[0]));
|
||||
assert(names.length == 1 ||
|
||||
stripSemis(names.length, res)[2].equals(names[1]));
|
||||
return res;
|
||||
}
|
||||
|
||||
static String[] stripSemis(int count, String string) {
|
||||
String[] res = new String[count+1];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
int pos2 = string.indexOf(';', pos);
|
||||
if (pos2 < 0) pos2 = string.length(); // yuck
|
||||
res[i] = string.substring(pos, pos2);
|
||||
pos = pos2;
|
||||
}
|
||||
res[count] = string.substring(pos);
|
||||
return res;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(this.getClass().getName());
|
||||
buf.append("{");
|
||||
Object[] origCP = null;
|
||||
for (int i = 0; i < patchArray.length; i++) {
|
||||
if (patchArray[i] == null) continue;
|
||||
if (origCP != null) {
|
||||
buf.append(", ");
|
||||
} else {
|
||||
try {
|
||||
origCP = getOriginalCP();
|
||||
} catch (InvalidConstantPoolFormatException ee) {
|
||||
origCP = new Object[0];
|
||||
}
|
||||
}
|
||||
Object orig = (i < origCP.length) ? origCP[i] : "?";
|
||||
buf.append(orig).append("=").append(patchArray[i]);
|
||||
}
|
||||
buf.append("}");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
192
jdk/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java
Normal file
192
jdk/src/share/classes/sun/dyn/anon/ConstantPoolVisitor.java
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.anon;
|
||||
|
||||
/**
|
||||
* A visitor called by {@link ConstantPoolParser#parse(ConstantPoolVisitor)}
|
||||
* when a constant pool entry is parsed.
|
||||
* <p>
|
||||
* A visit* method is called when a constant pool entry is parsed.
|
||||
* The first argument is always the constant pool index.
|
||||
* The second argument is always the constant pool tag,
|
||||
* even for methods like {@link #visitUTF8(int, byte, String)} which only apply to one tag.
|
||||
* String arguments refer to Utf8 or NameAndType entries declared elsewhere,
|
||||
* and are always accompanied by the indexes of those entries.
|
||||
* <p>
|
||||
* The order of the calls to the visit* methods is not necessarily related
|
||||
* to the order of the entries in the constant pool.
|
||||
* If one entry has a reference to another entry, the latter (lower-level)
|
||||
* entry will be visited first.
|
||||
* <p>
|
||||
* The following table shows the relation between constant pool entry
|
||||
* types and the corresponding visit* methods:
|
||||
*
|
||||
* <table border=1 cellpadding=5 summary="constant pool visitor methods">
|
||||
* <tr><th>Tag(s)</th><th>Method</th></tr>
|
||||
* <tr>
|
||||
* <td>{@link #CONSTANT_Utf8}</td>
|
||||
* <td>{@link #visitUTF8(int, byte, String)}</td>
|
||||
* </tr><tr>
|
||||
* <td>{@link #CONSTANT_Integer}, {@link #CONSTANT_Float},
|
||||
* {@link #CONSTANT_Long}, {@link #CONSTANT_Double}</td>
|
||||
* <td>{@link #visitConstantValue(int, byte, Object)}</td>
|
||||
* </tr><tr>
|
||||
* <td>{@link #CONSTANT_String}, {@link #CONSTANT_Class}</td>
|
||||
* <td>{@link #visitConstantString(int, byte, String, int)}</td>
|
||||
* </tr><tr>
|
||||
* <td>{@link #CONSTANT_NameAndType}</td>
|
||||
* <td>{@link #visitDescriptor(int, byte, String, String, int, int)}</td>
|
||||
* </tr><tr>
|
||||
* <td>{@link #CONSTANT_Fieldref},
|
||||
* {@link #CONSTANT_Methodref},
|
||||
* {@link #CONSTANT_InterfaceMethodref}</td>
|
||||
* <td>{@link #visitMemberRef(int, byte, String, String, String, int, int)}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @see ConstantPoolPatch
|
||||
* @author Remi Forax
|
||||
* @author jrose
|
||||
*/
|
||||
public class ConstantPoolVisitor {
|
||||
/** Called each time an UTF8 constant pool entry is found.
|
||||
* @param index the constant pool index
|
||||
* @param tag always {@link #CONSTANT_Utf8}
|
||||
* @param utf8 string encoded in modified UTF-8 format passed as a {@code String}
|
||||
*
|
||||
* @see ConstantPoolPatch#putUTF8(int, String)
|
||||
*/
|
||||
public void visitUTF8(int index, byte tag, String utf8) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/** Called for each constant pool entry that encodes an integer,
|
||||
* a float, a long, or a double.
|
||||
* Constant strings and classes are not managed by this method but
|
||||
* by {@link #visitConstantString(int, byte, String, int)}.
|
||||
*
|
||||
* @param index the constant pool index
|
||||
* @param tag one of {@link #CONSTANT_Integer},
|
||||
* {@link #CONSTANT_Float},
|
||||
* {@link #CONSTANT_Long},
|
||||
* or {@link #CONSTANT_Double}
|
||||
* @param value encoded value
|
||||
*
|
||||
* @see ConstantPoolPatch#putConstantValue(int, Object)
|
||||
*/
|
||||
public void visitConstantValue(int index, byte tag, Object value) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/** Called for each constant pool entry that encodes a string or a class.
|
||||
* @param index the constant pool index
|
||||
* @param tag one of {@link #CONSTANT_String},
|
||||
* {@link #CONSTANT_Class},
|
||||
* @param name string body or class name (using dot separator)
|
||||
* @param nameIndex the index of the Utf8 string for the name
|
||||
*
|
||||
* @see ConstantPoolPatch#putConstantValue(int, byte, Object)
|
||||
*/
|
||||
public void visitConstantString(int index, byte tag,
|
||||
String name, int nameIndex) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/** Called for each constant pool entry that encodes a name and type.
|
||||
* @param index the constant pool index
|
||||
* @param tag always {@link #CONSTANT_NameAndType}
|
||||
* @param memberName a field or method name
|
||||
* @param signature the member signature
|
||||
* @param memberNameIndex index of the Utf8 string for the member name
|
||||
* @param signatureIndex index of the Utf8 string for the signature
|
||||
*
|
||||
* @see ConstantPoolPatch#putDescriptor(int, String, String)
|
||||
*/
|
||||
public void visitDescriptor(int index, byte tag,
|
||||
String memberName, String signature,
|
||||
int memberNameIndex, int signatureIndex) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/** Called for each constant pool entry that encodes a field or method.
|
||||
* @param index the constant pool index
|
||||
* @param tag one of {@link #CONSTANT_Fieldref},
|
||||
* or {@link #CONSTANT_Methodref},
|
||||
* or {@link #CONSTANT_InterfaceMethodref}
|
||||
* @param className the class name (using dot separator)
|
||||
* @param memberName name of the field or method
|
||||
* @param signature the field or method signature
|
||||
* @param classNameIndex index of the Utf8 string for the class name
|
||||
* @param descriptorIndex index of the NameAndType descriptor constant
|
||||
*
|
||||
* @see ConstantPoolPatch#putMemberRef(int, byte, String, String, String)
|
||||
*/
|
||||
public void visitMemberRef(int index, byte tag,
|
||||
String className, String memberName, String signature,
|
||||
int classNameIndex, int descriptorIndex) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public static final byte
|
||||
CONSTANT_None = 0,
|
||||
CONSTANT_Utf8 = 1,
|
||||
//CONSTANT_Unicode = 2, /* unused */
|
||||
CONSTANT_Integer = 3,
|
||||
CONSTANT_Float = 4,
|
||||
CONSTANT_Long = 5,
|
||||
CONSTANT_Double = 6,
|
||||
CONSTANT_Class = 7,
|
||||
CONSTANT_String = 8,
|
||||
CONSTANT_Fieldref = 9,
|
||||
CONSTANT_Methodref = 10,
|
||||
CONSTANT_InterfaceMethodref = 11,
|
||||
CONSTANT_NameAndType = 12;
|
||||
|
||||
private static String[] TAG_NAMES = {
|
||||
"Empty",
|
||||
"Utf8",
|
||||
null, //"Unicode",
|
||||
"Integer",
|
||||
"Float",
|
||||
"Long",
|
||||
"Double",
|
||||
"Class",
|
||||
"String",
|
||||
"Fieldref",
|
||||
"Methodref",
|
||||
"InterfaceMethodref",
|
||||
"NameAndType"
|
||||
};
|
||||
|
||||
public static String tagName(byte tag) {
|
||||
String name = null;
|
||||
if ((tag & 0xFF) < TAG_NAMES.length)
|
||||
name = TAG_NAMES[tag];
|
||||
if (name == null)
|
||||
name = "Unknown#"+(tag&0xFF);
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.anon;
|
||||
|
||||
/** Exception used when there is an error in the constant pool
|
||||
* format.
|
||||
*/
|
||||
public class InvalidConstantPoolFormatException extends Exception {
|
||||
private static final long serialVersionUID=-6103888330523770949L;
|
||||
|
||||
public InvalidConstantPoolFormatException(String message,Throwable cause) {
|
||||
super(message,cause);
|
||||
}
|
||||
|
||||
public InvalidConstantPoolFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidConstantPoolFormatException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
36
jdk/src/share/classes/sun/dyn/empty/Empty.java
Normal file
36
jdk/src/share/classes/sun/dyn/empty/Empty.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 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.empty;
|
||||
|
||||
/**
|
||||
* An empty class in an empty package.
|
||||
* Used as a proxy for unprivileged code, since making access checks
|
||||
* against it will only succeed against public methods in public types.
|
||||
* @author jrose
|
||||
*/
|
||||
public class Empty {
|
||||
private Empty() { throw new InternalError(); }
|
||||
}
|
35
jdk/src/share/classes/sun/dyn/package-info.java
Normal file
35
jdk/src/share/classes/sun/dyn/package-info.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation details for JSR 292 RI, package java.dyn.
|
||||
* This particular version is specific to Hotspot.
|
||||
* There is also a backport version of this sub-package which uses reflection,
|
||||
* and can therefore run (slowly) on older versions of Java.
|
||||
* Other JVM vendors may create their own versions of this sub-package.
|
||||
* @author jrose
|
||||
*/
|
||||
|
||||
package sun.dyn;
|
711
jdk/src/share/classes/sun/dyn/util/BytecodeName.java
Normal file
711
jdk/src/share/classes/sun/dyn/util/BytecodeName.java
Normal file
@ -0,0 +1,711 @@
|
||||
/*
|
||||
* Copyright 2007-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.util;
|
||||
|
||||
/**
|
||||
* Utility routines for dealing with bytecode-level names.
|
||||
* Includes universal mangling rules for the JVM.
|
||||
*
|
||||
* <h3>Avoiding Dangerous Characters </h3>
|
||||
*
|
||||
* <p>
|
||||
* The JVM defines a very small set of characters which are illegal
|
||||
* in name spellings. We will slightly extend and regularize this set
|
||||
* into a group of <cite>dangerous characters</cite>.
|
||||
* These characters will then be replaced, in mangled names, by escape sequences.
|
||||
* In addition, accidental escape sequences must be further escaped.
|
||||
* Finally, a special prefix will be applied if and only if
|
||||
* the mangling would otherwise fail to begin with the escape character.
|
||||
* This happens to cover the corner case of the null string,
|
||||
* and also clearly marks symbols which need demangling.
|
||||
* </p>
|
||||
* <p>
|
||||
* Dangerous characters are the union of all characters forbidden
|
||||
* or otherwise restricted by the JVM specification,
|
||||
* plus their mates, if they are brackets
|
||||
* (<code><big><b>[</b></big></code> and <code><big><b>]</b></big></code>,
|
||||
* <code><big><b><</b></big></code> and <code><big><b>></b></big></code>),
|
||||
* plus, arbitrarily, the colon character <code><big><b>:</b></big></code>.
|
||||
* There is no distinction between type, method, and field names.
|
||||
* This makes it easier to convert between mangled names of different
|
||||
* types, since they do not need to be decoded (demangled).
|
||||
* </p>
|
||||
* <p>
|
||||
* The escape character is backslash <code><big><b>\</b></big></code>
|
||||
* (also known as reverse solidus).
|
||||
* This character is, until now, unheard of in bytecode names,
|
||||
* but traditional in the proposed role.
|
||||
*
|
||||
* </p>
|
||||
* <h3> Replacement Characters </h3>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Every escape sequence is two characters
|
||||
* (in fact, two UTF8 bytes) beginning with
|
||||
* the escape character and followed by a
|
||||
* <cite>replacement character</cite>.
|
||||
* (Since the replacement character is never a backslash,
|
||||
* iterated manglings do not double in size.)
|
||||
* </p>
|
||||
* <p>
|
||||
* Each dangerous character has some rough visual similarity
|
||||
* to its corresponding replacement character.
|
||||
* This makes mangled symbols easier to recognize by sight.
|
||||
* </p>
|
||||
* <p>
|
||||
* The dangerous characters are
|
||||
* <code><big><b>/</b></big></code> (forward slash, used to delimit package components),
|
||||
* <code><big><b>.</b></big></code> (dot, also a package delimiter),
|
||||
* <code><big><b>;</b></big></code> (semicolon, used in signatures),
|
||||
* <code><big><b>$</b></big></code> (dollar, used in inner classes and synthetic members),
|
||||
* <code><big><b><</b></big></code> (left angle),
|
||||
* <code><big><b>></b></big></code> (right angle),
|
||||
* <code><big><b>[</b></big></code> (left square bracket, used in array types),
|
||||
* <code><big><b>]</b></big></code> (right square bracket, reserved in this scheme for language use),
|
||||
* and <code><big><b>:</b></big></code> (colon, reserved in this scheme for language use).
|
||||
* Their replacements are, respectively,
|
||||
* <code><big><b>|</b></big></code> (vertical bar),
|
||||
* <code><big><b>,</b></big></code> (comma),
|
||||
* <code><big><b>?</b></big></code> (question mark),
|
||||
* <code><big><b>%</b></big></code> (percent),
|
||||
* <code><big><b>^</b></big></code> (caret),
|
||||
* <code><big><b>_</b></big></code> (underscore), and
|
||||
* <code><big><b>{</b></big></code> (left curly bracket),
|
||||
* <code><big><b>}</b></big></code> (right curly bracket),
|
||||
* <code><big><b>!</b></big></code> (exclamation mark).
|
||||
* In addition, the replacement character for the escape character itself is
|
||||
* <code><big><b>-</b></big></code> (hyphen),
|
||||
* and the replacement character for the null prefix is
|
||||
* <code><big><b>=</b></big></code> (equal sign).
|
||||
* </p>
|
||||
* <p>
|
||||
* An escape character <code><big><b>\</b></big></code>
|
||||
* followed by any of these replacement characters
|
||||
* is an escape sequence, and there are no other escape sequences.
|
||||
* An equal sign is only part of an escape sequence
|
||||
* if it is the second character in the whole string, following a backslash.
|
||||
* Two consecutive backslashes do <em>not</em> form an escape sequence.
|
||||
* </p>
|
||||
* <p>
|
||||
* Each escape sequence replaces a so-called <cite>original character</cite>
|
||||
* which is either one of the dangerous characters or the escape character.
|
||||
* A null prefix replaces an initial null string, not a character.
|
||||
* </p>
|
||||
* <p>
|
||||
* All this implies that escape sequences cannot overlap and may be
|
||||
* determined all at once for a whole string. Note that a spelling
|
||||
* string can contain <cite>accidental escapes</cite>, apparent escape
|
||||
* sequences which must not be interpreted as manglings.
|
||||
* These are disabled by replacing their leading backslash with an
|
||||
* escape sequence (<code><big><b>\-</b></big></code>). To mangle a string, three logical steps
|
||||
* are required, though they may be carried out in one pass:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>In each accidental escape, replace the backslash with an escape sequence
|
||||
* (<code><big><b>\-</b></big></code>).</li>
|
||||
* <li>Replace each dangerous character with an escape sequence
|
||||
* (<code><big><b>\|</b></big></code> for <code><big><b>/</b></big></code>, etc.).</li>
|
||||
* <li>If the first two steps introduced any change, <em>and</em>
|
||||
* if the string does not already begin with a backslash, prepend a null prefix (<code><big><b>\=</b></big></code>).</li>
|
||||
* </ol>
|
||||
*
|
||||
* To demangle a mangled string that begins with an escape,
|
||||
* remove any null prefix, and then replace (in parallel)
|
||||
* each escape sequence by its original character.
|
||||
* <p>Spelling strings which contain accidental
|
||||
* escapes <em>must</em> have them replaced, even if those
|
||||
* strings do not contain dangerous characters.
|
||||
* This restriction means that mangling a string always
|
||||
* requires a scan of the string for escapes.
|
||||
* But then, a scan would be required anyway,
|
||||
* to check for dangerous characters.
|
||||
*
|
||||
* </p>
|
||||
* <h3> Nice Properties </h3>
|
||||
*
|
||||
* <p>
|
||||
* If a bytecode name does not contain any escape sequence,
|
||||
* demangling is a no-op: The string demangles to itself.
|
||||
* Such a string is called <cite>self-mangling</cite>.
|
||||
* Almost all strings are self-mangling.
|
||||
* In practice, to demangle almost any name “found in nature”,
|
||||
* simply verify that it does not begin with a backslash.
|
||||
* </p>
|
||||
* <p>
|
||||
* Mangling is a one-to-one function, while demangling
|
||||
* is a many-to-one function.
|
||||
* A mangled string is defined as <cite>validly mangled</cite> if
|
||||
* it is in fact the unique mangling of its spelling string.
|
||||
* Three examples of invalidly mangled strings are <code><big><b>\=foo</b></big></code>,
|
||||
* <code><big><b>\-bar</b></big></code>, and <code><big><b>baz\!</b></big></code>, which demangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and
|
||||
* <code><big><b>baz\!</b></big></code>, but then remangle to <code><big><b>foo</b></big></code>, <code><big><b>\bar</b></big></code>, and <code><big><b>\=baz\-!</b></big></code>.
|
||||
* If a language back-end or runtime is using mangled names,
|
||||
* it should never present an invalidly mangled bytecode
|
||||
* name to the JVM. If the runtime encounters one,
|
||||
* it should also report an error, since such an occurrence
|
||||
* probably indicates a bug in name encoding which
|
||||
* will lead to errors in linkage.
|
||||
* However, this note does not propose that the JVM verifier
|
||||
* detect invalidly mangled names.
|
||||
* </p>
|
||||
* <p>
|
||||
* As a result of these rules, it is a simple matter to
|
||||
* compute validly mangled substrings and concatenations
|
||||
* of validly mangled strings, and (with a little care)
|
||||
* these correspond to corresponding operations on their
|
||||
* spelling strings.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Any prefix of a validly mangled string is also validly mangled,
|
||||
* although a null prefix may need to be removed.</li>
|
||||
* <li>Any suffix of a validly mangled string is also validly mangled,
|
||||
* although a null prefix may need to be added.</li>
|
||||
* <li>Two validly mangled strings, when concatenated,
|
||||
* are also validly mangled, although any null prefix
|
||||
* must be removed from the second string,
|
||||
* and a trailing backslash on the first string may need escaping,
|
||||
* if it would participate in an accidental escape when followed
|
||||
* by the first character of the second string.</li>
|
||||
* </ul>
|
||||
* <p>If languages that include non-Java symbol spellings use this
|
||||
* mangling convention, they will enjoy the following advantages:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>They can interoperate via symbols they share in common.</li>
|
||||
* <li>Low-level tools, such as backtrace printers, will have readable displays.</li>
|
||||
* <li>Future JVM and language extensions can safely use the dangerous characters
|
||||
* for structuring symbols, but will never interfere with valid spellings.</li>
|
||||
* <li>Runtimes and compilers can use standard libraries for mangling and demangling.</li>
|
||||
* <li>Occasional transliterations and name composition will be simple and regular,
|
||||
* for classes, methods, and fields.</li>
|
||||
* <li>Bytecode names will continue to be compact.
|
||||
* When mangled, spellings will at most double in length, either in
|
||||
* UTF8 or UTF16 format, and most will not change at all.</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <h3> Suggestions for Human Readable Presentations </h3>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* For human readable displays of symbols,
|
||||
* it will be better to present a string-like quoted
|
||||
* representation of the spelling, because JVM users
|
||||
* are generally familiar with such tokens.
|
||||
* We suggest using single or double quotes before and after
|
||||
* mangled symbols which are not valid Java identifiers,
|
||||
* with quotes, backslashes, and non-printing characters
|
||||
* escaped as if for literals in the Java language.
|
||||
* </p>
|
||||
* <p>
|
||||
* For example, an HTML-like spelling
|
||||
* <code><big><b><pre></b></big></code> mangles to
|
||||
* <code><big><b>\^pre\_</b></big></code> and could
|
||||
* display more cleanly as
|
||||
* <code><big><b>'<pre>'</b></big></code>,
|
||||
* with the quotes included.
|
||||
* Such string-like conventions are <em>not</em> suitable
|
||||
* for mangled bytecode names, in part because
|
||||
* dangerous characters must be eliminated, rather
|
||||
* than just quoted. Otherwise internally structured
|
||||
* strings like package prefixes and method signatures
|
||||
* could not be reliably parsed.
|
||||
* </p>
|
||||
* <p>
|
||||
* In such human-readable displays, invalidly mangled
|
||||
* names should <em>not</em> be demangled and quoted,
|
||||
* for this would be misleading. Likewise, JVM symbols
|
||||
* which contain dangerous characters (like dots in field
|
||||
* names or brackets in method names) should not be
|
||||
* simply quoted. The bytecode names
|
||||
* <code><big><b>\=phase\,1</b></big></code> and
|
||||
* <code><big><b>phase.1</b></big></code> are distinct,
|
||||
* and in demangled displays they should be presented as
|
||||
* <code><big><b>'phase.1'</b></big></code> and something like
|
||||
* <code><big><b>'phase'.1</b></big></code>, respectively.
|
||||
* </p>
|
||||
*
|
||||
* @author John Rose
|
||||
* @version 1.2, 02/06/2008
|
||||
* @see http://blogs.sun.com/jrose/entry/symbolic_freedom_in_the_vm
|
||||
*/
|
||||
public class BytecodeName {
|
||||
private BytecodeName() { } // static only class
|
||||
|
||||
/** Given a source name, produce the corresponding bytecode name.
|
||||
* The source name should not be qualified, because any syntactic
|
||||
* markers (dots, slashes, dollar signs, colons, etc.) will be mangled.
|
||||
* @param s the source name
|
||||
* @return a valid bytecode name which represents the source name
|
||||
*/
|
||||
public static String toBytecodeName(String s) {
|
||||
String bn = mangle(s);
|
||||
assert((Object)bn == s || looksMangled(bn)) : bn;
|
||||
assert(s.equals(toSourceName(bn))) : s;
|
||||
return bn;
|
||||
}
|
||||
|
||||
/** Given an unqualified bytecode name, produce the corresponding source name.
|
||||
* The bytecode name must not contain dangerous characters.
|
||||
* In particular, it must not be qualified or segmented by colon {@code ':'}.
|
||||
* @param s the bytecode name
|
||||
* @return the source name, which may possibly have unsafe characters
|
||||
* @throws IllegalArgumentException if the bytecode name is not {@link #isSafeBytecodeName safe}
|
||||
* @see #isSafeBytecodeName(java.lang.String)
|
||||
*/
|
||||
public static String toSourceName(String s) {
|
||||
checkSafeBytecodeName(s);
|
||||
String sn = s;
|
||||
if (looksMangled(s)) {
|
||||
sn = demangle(s);
|
||||
assert(s.equals(mangle(sn))) : s+" => "+sn+" => "+mangle(sn);
|
||||
}
|
||||
return sn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a bytecode name from a classfile, separate it into
|
||||
* components delimited by dangerous characters.
|
||||
* Each resulting array element will be either a dangerous character,
|
||||
* or else a safe bytecode name.
|
||||
* (The safe name might possibly be mangled to hide further dangerous characters.)
|
||||
* For example, the qualified class name {@code java/lang/String}
|
||||
* will be parsed into the array {@code {"java", '/', "lang", '/', "String"}}.
|
||||
* The name {@code <init>} will be parsed into { '<', "init", '>'}}
|
||||
* The name {@code foo/bar$:baz} will be parsed into
|
||||
* {@code {"foo", '/', "bar", '$', ':', "baz"}}.
|
||||
*/
|
||||
public static Object[] parseBytecodeName(String s) {
|
||||
int slen = s.length();
|
||||
Object[] res = null;
|
||||
for (int pass = 0; pass <= 1; pass++) {
|
||||
int fillp = 0;
|
||||
int lasti = 0;
|
||||
for (int i = 0; i <= slen; i++) {
|
||||
int whichDC = -1;
|
||||
if (i < slen) {
|
||||
whichDC = DANGEROUS_CHARS.indexOf(s.charAt(i));
|
||||
if (whichDC < DANGEROUS_CHAR_FIRST_INDEX) continue;
|
||||
}
|
||||
// got to end of string or next dangerous char
|
||||
if (lasti < i) {
|
||||
// normal component
|
||||
if (pass != 0)
|
||||
res[fillp] = s.substring(lasti, i);
|
||||
fillp++;
|
||||
lasti = i+1;
|
||||
}
|
||||
if (whichDC >= DANGEROUS_CHAR_FIRST_INDEX) {
|
||||
if (pass != 0)
|
||||
res[fillp] = DANGEROUS_CHARS_CA[whichDC];
|
||||
fillp++;
|
||||
}
|
||||
}
|
||||
if (pass != 0) break;
|
||||
// between passes, build the result array
|
||||
res = new String[fillp];
|
||||
if (fillp <= 1) {
|
||||
if (fillp != 0) res[0] = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a series of components, create a bytecode name for a classfile.
|
||||
* This is the inverse of {@link #parseBytecodeName(java.lang.String)}.
|
||||
* Each component must either be an interned one-character string of
|
||||
* a dangerous character, or else a safe bytecode name.
|
||||
* @param components a series of name components
|
||||
* @return the concatenation of all components
|
||||
* @throws IllegalArgumentException if any component contains an unsafe
|
||||
* character, and is not an interned one-character string
|
||||
* @throws NullPointerException if any component is null
|
||||
*/
|
||||
public static String unparseBytecodeName(Object[] components) {
|
||||
for (Object c : components) {
|
||||
if (c instanceof String)
|
||||
checkSafeBytecodeName((String) c); // may fail
|
||||
}
|
||||
return appendAll(components);
|
||||
}
|
||||
private static String appendAll(Object[] components) {
|
||||
if (components.length <= 1) {
|
||||
if (components.length == 1) {
|
||||
return String.valueOf(components[0]);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
int slen = 0;
|
||||
for (Object c : components) {
|
||||
if (c instanceof String)
|
||||
slen += String.valueOf(c).length();
|
||||
else
|
||||
slen += 1;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(slen);
|
||||
for (Object c : components) {
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a bytecode name, produce the corresponding display name.
|
||||
* This is the source name, plus quotes if needed.
|
||||
* If the bytecode name contains dangerous characters,
|
||||
* assume that they are being used as punctuation,
|
||||
* and pass them through unchanged.
|
||||
* @param s the original bytecode name (which may be qualified)
|
||||
* @return a human-readable presentation
|
||||
*/
|
||||
public static String toDisplayName(String s) {
|
||||
Object[] components = parseBytecodeName(s);
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
if (!(components[i] instanceof String))
|
||||
continue;
|
||||
String c = (String) components[i];
|
||||
// pretty up the name by demangling it
|
||||
String sn = toSourceName(c);
|
||||
if ((Object)sn != c || !isJavaIdent(sn)) {
|
||||
components[i] = quoteDisplay(sn);
|
||||
}
|
||||
}
|
||||
return appendAll(components);
|
||||
}
|
||||
private static boolean isJavaIdent(String s) {
|
||||
int slen = s.length();
|
||||
if (slen == 0) return false;
|
||||
if (!Character.isUnicodeIdentifierStart(s.charAt(0)))
|
||||
return false;
|
||||
for (int i = 1; i < slen; i++) {
|
||||
if (!Character.isUnicodeIdentifierPart(s.charAt(0)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static String quoteDisplay(String s) {
|
||||
// TO DO: Replace wierd characters in s by C-style escapes.
|
||||
return "'"+s.replaceAll("['\\\\]", "\\\\$0")+"'";
|
||||
}
|
||||
|
||||
private static void checkSafeBytecodeName(String s)
|
||||
throws IllegalArgumentException {
|
||||
if (!isSafeBytecodeName(s)) {
|
||||
throw new IllegalArgumentException(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report whether a simple name is safe as a bytecode name.
|
||||
* Such names are acceptable in class files as class, method, and field names.
|
||||
* Additionally, they are free of "dangerous" characters, even if those
|
||||
* characters are legal in some (or all) names in class files.
|
||||
* @param s the proposed bytecode name
|
||||
* @return true if the name is non-empty and all of its characters are safe
|
||||
*/
|
||||
public static boolean isSafeBytecodeName(String s) {
|
||||
if (s.length() == 0) return false;
|
||||
// check occurrences of each DANGEROUS char
|
||||
for (char xc : DANGEROUS_CHARS_A) {
|
||||
if (xc == ESCAPE_C) continue; // not really that dangerous
|
||||
if (s.indexOf(xc) >= 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report whether a character is safe in a bytecode name.
|
||||
* This is true of any unicode character except the following
|
||||
* <em>dangerous characters</em>: {@code ".;:$[]<>/"}.
|
||||
* @param s the proposed character
|
||||
* @return true if the character is safe to use in classfiles
|
||||
*/
|
||||
public static boolean isSafeBytecodeChar(char c) {
|
||||
return DANGEROUS_CHARS.indexOf(c) < DANGEROUS_CHAR_FIRST_INDEX;
|
||||
}
|
||||
|
||||
private static boolean looksMangled(String s) {
|
||||
return s.charAt(0) == ESCAPE_C;
|
||||
}
|
||||
|
||||
private static String mangle(String s) {
|
||||
if (s.length() == 0)
|
||||
return NULL_ESCAPE;
|
||||
|
||||
// build this lazily, when we first need an escape:
|
||||
StringBuilder sb = null;
|
||||
|
||||
for (int i = 0, slen = s.length(); i < slen; i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
boolean needEscape = false;
|
||||
if (c == ESCAPE_C) {
|
||||
if (i+1 < slen) {
|
||||
char c1 = s.charAt(i+1);
|
||||
if ((i == 0 && c1 == NULL_ESCAPE_C)
|
||||
|| c1 != originalOfReplacement(c1)) {
|
||||
// an accidental escape
|
||||
needEscape = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
needEscape = isDangerous(c);
|
||||
}
|
||||
|
||||
if (!needEscape) {
|
||||
if (sb != null) sb.append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// build sb if this is the first escape
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder(s.length()+10);
|
||||
// mangled names must begin with a backslash:
|
||||
if (s.charAt(0) != ESCAPE_C && i > 0)
|
||||
sb.append(NULL_ESCAPE);
|
||||
// append the string so far, which is unremarkable:
|
||||
sb.append(s.substring(0, i));
|
||||
}
|
||||
|
||||
// rewrite \ to \-, / to \|, etc.
|
||||
sb.append(ESCAPE_C);
|
||||
sb.append(replacementOf(c));
|
||||
}
|
||||
|
||||
if (sb != null) return sb.toString();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static String demangle(String s) {
|
||||
// build this lazily, when we first meet an escape:
|
||||
StringBuilder sb = null;
|
||||
|
||||
int stringStart = 0;
|
||||
if (s.startsWith(NULL_ESCAPE))
|
||||
stringStart = 2;
|
||||
|
||||
for (int i = stringStart, slen = s.length(); i < slen; i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (c == ESCAPE_C && i+1 < slen) {
|
||||
// might be an escape sequence
|
||||
char rc = s.charAt(i+1);
|
||||
char oc = originalOfReplacement(rc);
|
||||
if (oc != rc) {
|
||||
// build sb if this is the first escape
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder(s.length());
|
||||
// append the string so far, which is unremarkable:
|
||||
sb.append(s.substring(stringStart, i));
|
||||
}
|
||||
++i; // skip both characters
|
||||
c = oc;
|
||||
}
|
||||
}
|
||||
|
||||
if (sb != null)
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
if (sb != null) return sb.toString();
|
||||
|
||||
return s.substring(stringStart);
|
||||
}
|
||||
|
||||
static char ESCAPE_C = '\\';
|
||||
// empty escape sequence to avoid a null name or illegal prefix
|
||||
static char NULL_ESCAPE_C = '=';
|
||||
static String NULL_ESCAPE = ESCAPE_C+""+NULL_ESCAPE_C;
|
||||
|
||||
static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; // \\ must be first
|
||||
static final String REPLACEMENT_CHARS = "-|,?!%{}^_";
|
||||
static final int DANGEROUS_CHAR_FIRST_INDEX = 1; // index after \\
|
||||
static char[] DANGEROUS_CHARS_A = DANGEROUS_CHARS.toCharArray();
|
||||
static char[] REPLACEMENT_CHARS_A = REPLACEMENT_CHARS.toCharArray();
|
||||
static final Character[] DANGEROUS_CHARS_CA;
|
||||
static {
|
||||
Character[] dcca = new Character[DANGEROUS_CHARS.length()];
|
||||
for (int i = 0; i < dcca.length; i++)
|
||||
dcca[i] = Character.valueOf(DANGEROUS_CHARS.charAt(i));
|
||||
DANGEROUS_CHARS_CA = dcca;
|
||||
}
|
||||
|
||||
static final long[] SPECIAL_BITMAP = new long[2]; // 128 bits
|
||||
static {
|
||||
String SPECIAL = DANGEROUS_CHARS + REPLACEMENT_CHARS;
|
||||
//System.out.println("SPECIAL = "+SPECIAL);
|
||||
for (char c : SPECIAL.toCharArray()) {
|
||||
SPECIAL_BITMAP[c >>> 6] |= 1L << c;
|
||||
}
|
||||
}
|
||||
static boolean isSpecial(char c) {
|
||||
if ((c >>> 6) < SPECIAL_BITMAP.length)
|
||||
return ((SPECIAL_BITMAP[c >>> 6] >> c) & 1) != 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
static char replacementOf(char c) {
|
||||
if (!isSpecial(c)) return c;
|
||||
int i = DANGEROUS_CHARS.indexOf(c);
|
||||
if (i < 0) return c;
|
||||
return REPLACEMENT_CHARS.charAt(i);
|
||||
}
|
||||
static char originalOfReplacement(char c) {
|
||||
if (!isSpecial(c)) return c;
|
||||
int i = REPLACEMENT_CHARS.indexOf(c);
|
||||
if (i < 0) return c;
|
||||
return DANGEROUS_CHARS.charAt(i);
|
||||
}
|
||||
static boolean isDangerous(char c) {
|
||||
if (!isSpecial(c)) return false;
|
||||
return (DANGEROUS_CHARS.indexOf(c) >= DANGEROUS_CHAR_FIRST_INDEX);
|
||||
}
|
||||
static int indexOfDangerousChar(String s, int from) {
|
||||
for (int i = from, slen = s.length(); i < slen; i++) {
|
||||
if (isDangerous(s.charAt(i)))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int lastIndexOfDangerousChar(String s, int from) {
|
||||
for (int i = Math.min(from, s.length()-1); i >= 0; i--) {
|
||||
if (isDangerous(s.charAt(i)))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// test driver
|
||||
static void main(String[] av) {
|
||||
// If verbose is enabled, quietly check everything.
|
||||
// Otherwise, print the output for the user to check.
|
||||
boolean verbose = false;
|
||||
|
||||
int maxlen = 0;
|
||||
|
||||
while (av.length > 0 && av[0].startsWith("-")) {
|
||||
String flag = av[0].intern();
|
||||
av = java.util.Arrays.copyOfRange(av, 1, av.length); // Java 1.6 or later
|
||||
if (flag == "-" || flag == "--") break;
|
||||
else if (flag == "-q")
|
||||
verbose = false;
|
||||
else if (flag == "-v")
|
||||
verbose = true;
|
||||
else if (flag.startsWith("-l"))
|
||||
maxlen = Integer.valueOf(flag.substring(2));
|
||||
else
|
||||
throw new Error("Illegal flag argument: "+flag);
|
||||
}
|
||||
|
||||
if (maxlen == 0)
|
||||
maxlen = (verbose ? 2 : 4);
|
||||
if (verbose) System.out.println("Note: maxlen = "+maxlen);
|
||||
|
||||
switch (av.length) {
|
||||
case 0: av = new String[] {
|
||||
DANGEROUS_CHARS.substring(0) +
|
||||
REPLACEMENT_CHARS.substring(0, 1) +
|
||||
NULL_ESCAPE + "x"
|
||||
}; // and fall through:
|
||||
case 1:
|
||||
char[] cv = av[0].toCharArray();
|
||||
av = new String[cv.length];
|
||||
int avp = 0;
|
||||
for (char c : cv) {
|
||||
String s = String.valueOf(c);
|
||||
if (c == 'x') s = "foo"; // tradition...
|
||||
av[avp++] = s;
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
System.out.println("Note: Verbose output mode enabled. Use '-q' to suppress.");
|
||||
Tester t = new Tester();
|
||||
t.maxlen = maxlen;
|
||||
t.verbose = verbose;
|
||||
t.tokens = av;
|
||||
t.test("", 0);
|
||||
}
|
||||
|
||||
static class Tester {
|
||||
boolean verbose;
|
||||
int maxlen;
|
||||
java.util.Map<String,String> map = new java.util.HashMap<String,String>();
|
||||
String[] tokens;
|
||||
|
||||
void test(String stringSoFar, int tokensSoFar) {
|
||||
test(stringSoFar);
|
||||
if (tokensSoFar <= maxlen) {
|
||||
for (String token : tokens) {
|
||||
if (token.length() == 0) continue; // skip empty tokens
|
||||
if (stringSoFar.indexOf(token) != stringSoFar.lastIndexOf(token))
|
||||
continue; // there are already two occs. of this token
|
||||
if (token.charAt(0) == ESCAPE_C && token.length() == 1 && maxlen < 4)
|
||||
test(stringSoFar+token, tokensSoFar); // want lots of \'s
|
||||
else if (tokensSoFar < maxlen)
|
||||
test(stringSoFar+token, tokensSoFar+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test(String s) {
|
||||
// for small batches, do not test the null string
|
||||
if (s.length() == 0 && maxlen >=1 && maxlen <= 2) return;
|
||||
String bn = testSourceName(s);
|
||||
if (bn == null) return;
|
||||
if (bn == s) {
|
||||
//if (verbose) System.out.println(s+" == id");
|
||||
} else {
|
||||
if (verbose) System.out.println(s+" => "+bn+" "+toDisplayName(bn));
|
||||
String bnbn = testSourceName(bn);
|
||||
if (bnbn == null) return;
|
||||
if (verbose) System.out.println(bn+" => "+bnbn+" "+toDisplayName(bnbn));
|
||||
/*
|
||||
String bn3 = testSourceName(bnbn);
|
||||
if (bn3 == null) return;
|
||||
if (verbose) System.out.println(bnbn+" => "+bn3);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
String testSourceName(String s) {
|
||||
if (map.containsKey(s)) return null;
|
||||
String bn = toBytecodeName(s);
|
||||
map.put(s, bn);
|
||||
String sn = toSourceName(bn);
|
||||
if (!sn.equals(s)) {
|
||||
String bad = (s+" => "+bn+" != "+sn);
|
||||
if (!verbose) throw new Error("Bad mangling: "+bad);
|
||||
System.out.println("*** "+bad);
|
||||
return null;
|
||||
}
|
||||
return bn;
|
||||
}
|
||||
}
|
||||
}
|
137
jdk/src/share/classes/sun/dyn/util/BytecodeSignature.java
Normal file
137
jdk/src/share/classes/sun/dyn/util/BytecodeSignature.java
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.dyn.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility routines for dealing with bytecode-level signatures.
|
||||
* @author jrose
|
||||
*/
|
||||
public class BytecodeSignature {
|
||||
|
||||
private BytecodeSignature() { } // cannot instantiate
|
||||
|
||||
public static List<Class<?>> parseMethod(String bytecodeSignature, ClassLoader loader) {
|
||||
return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader);
|
||||
}
|
||||
|
||||
static List<Class<?>> parseMethod(String bytecodeSignature,
|
||||
int start, int end, ClassLoader loader) {
|
||||
if (loader == null)
|
||||
loader = ClassLoader.getSystemClassLoader();
|
||||
String str = bytecodeSignature;
|
||||
int[] i = {start};
|
||||
ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>();
|
||||
if (i[0] < end && str.charAt(i[0]) == '(') {
|
||||
++i[0]; // skip '('
|
||||
while (i[0] < end && str.charAt(i[0]) != ')') {
|
||||
Class<?> pt = parseSig(str, i, end, loader);
|
||||
if (pt == null || pt == void.class)
|
||||
parseError(str, "bad argument type");
|
||||
ptypes.add(pt);
|
||||
}
|
||||
++i[0]; // skip ')'
|
||||
} else {
|
||||
parseError(str, "not a method type");
|
||||
}
|
||||
Class<?> rtype = parseSig(str, i, end, loader);
|
||||
if (rtype == null || i[0] != end)
|
||||
parseError(str, "bad return type");
|
||||
ptypes.add(rtype);
|
||||
return ptypes;
|
||||
}
|
||||
|
||||
static private void parseError(String str, String msg) {
|
||||
throw new IllegalArgumentException("bad signature: "+str+": "+msg);
|
||||
}
|
||||
|
||||
static private Class<?> parseSig(String str, int[] i, int end, ClassLoader loader) {
|
||||
if (i[0] == end) return null;
|
||||
char c = str.charAt(i[0]++);
|
||||
if (c == 'L') {
|
||||
int begc = i[0], endc = str.indexOf(';', begc);
|
||||
if (endc < 0) return null;
|
||||
i[0] = endc+1;
|
||||
String name = str.substring(begc, endc).replace('/', '.');
|
||||
try {
|
||||
return loader.loadClass(name);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new TypeNotPresentException(name, ex);
|
||||
}
|
||||
} else if (c == '[') {
|
||||
Class<?> t = parseSig(str, i, end, loader);
|
||||
if (t != null)
|
||||
t = java.lang.reflect.Array.newInstance(t, 0).getClass();
|
||||
return t;
|
||||
} else {
|
||||
return Wrapper.forBasicType(c).primitiveType();
|
||||
}
|
||||
}
|
||||
|
||||
public static String unparse(Class<?> type) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
unparseSig(type, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String unparse(MethodType type) {
|
||||
return unparseMethod(type.returnType(), type.parameterList());
|
||||
}
|
||||
|
||||
public static String unparse(Object type) {
|
||||
if (type instanceof Class<?>)
|
||||
return unparse((Class<?>) type);
|
||||
if (type instanceof MethodType)
|
||||
return unparse((MethodType) type);
|
||||
return (String) type;
|
||||
}
|
||||
|
||||
public static String unparseMethod(Class<?> rtype, List<Class<?>> ptypes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
for (Class<?> pt : ptypes)
|
||||
unparseSig(pt, sb);
|
||||
sb.append(')');
|
||||
unparseSig(rtype, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static private void unparseSig(Class<?> t, StringBuilder sb) {
|
||||
char c = Wrapper.forBasicType(t).basicTypeChar();
|
||||
if (c != 'L') {
|
||||
sb.append(c);
|
||||
} else {
|
||||
boolean lsemi = (!t.isArray());
|
||||
if (lsemi) sb.append('L');
|
||||
sb.append(t.getName().replace('.', '/'));
|
||||
if (lsemi) sb.append(';');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
563
jdk/src/share/classes/sun/dyn/util/ValueConversions.java
Normal file
563
jdk/src/share/classes/sun/dyn/util/ValueConversions.java
Normal file
@ -0,0 +1,563 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.dyn.*;
|
||||
import java.dyn.MethodHandles.Lookup;
|
||||
import java.util.EnumMap;
|
||||
import sun.dyn.Access;
|
||||
import sun.dyn.AdapterMethodHandle;
|
||||
import sun.dyn.MethodHandleImpl;
|
||||
|
||||
public class ValueConversions {
|
||||
private static final Access IMPL_TOKEN = Access.getToken();
|
||||
private static final Lookup IMPL_LOOKUP = MethodHandleImpl.getLookup(IMPL_TOKEN);
|
||||
|
||||
private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
|
||||
EnumMap<Wrapper, MethodHandle>[] caches
|
||||
= (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n]; // unchecked warning expected here
|
||||
for (int i = 0; i < n; i++)
|
||||
caches[i] = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
|
||||
return caches;
|
||||
}
|
||||
|
||||
/// Converting references to values.
|
||||
|
||||
static int unboxInteger(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Integer) x).intValue();
|
||||
}
|
||||
|
||||
static byte unboxByte(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Byte) x).byteValue();
|
||||
}
|
||||
|
||||
static short unboxShort(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Short) x).shortValue();
|
||||
}
|
||||
|
||||
static boolean unboxBoolean(Object x) {
|
||||
if (x == null) return false; // never NPE
|
||||
return ((Boolean) x).booleanValue();
|
||||
}
|
||||
|
||||
static char unboxCharacter(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Character) x).charValue();
|
||||
}
|
||||
|
||||
static long unboxLong(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Long) x).longValue();
|
||||
}
|
||||
|
||||
static float unboxFloat(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Float) x).floatValue();
|
||||
}
|
||||
|
||||
static double unboxDouble(Object x) {
|
||||
if (x == null) return 0; // never NPE
|
||||
return ((Double) x).doubleValue();
|
||||
}
|
||||
|
||||
/// Converting references to "raw" values.
|
||||
/// A raw primitive value is always an int or long.
|
||||
|
||||
static int unboxByteRaw(Object x) {
|
||||
return unboxByte(x);
|
||||
}
|
||||
|
||||
static int unboxShortRaw(Object x) {
|
||||
return unboxShort(x);
|
||||
}
|
||||
|
||||
static int unboxBooleanRaw(Object x) {
|
||||
return unboxBoolean(x) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int unboxCharacterRaw(Object x) {
|
||||
return unboxCharacter(x);
|
||||
}
|
||||
|
||||
static int unboxFloatRaw(Object x) {
|
||||
return Float.floatToIntBits(unboxFloat(x));
|
||||
}
|
||||
|
||||
static long unboxDoubleRaw(Object x) {
|
||||
return Double.doubleToRawLongBits(unboxDouble(x));
|
||||
}
|
||||
|
||||
private static MethodType unboxType(Wrapper wrap, boolean raw) {
|
||||
return MethodType.make(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType());
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
UNBOX_CONVERSIONS = newWrapperCaches(4);
|
||||
|
||||
private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
switch (wrap) {
|
||||
case OBJECT:
|
||||
mh = IDENTITY; break;
|
||||
case VOID:
|
||||
mh = raw ? ALWAYS_ZERO : IGNORE; break;
|
||||
case INT: case LONG:
|
||||
// these guys don't need separate raw channels
|
||||
if (raw) mh = unbox(wrap, exact, false);
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
// look up the method
|
||||
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
|
||||
MethodType type = unboxType(wrap, raw);
|
||||
if (!exact)
|
||||
// actually, type is wrong; the Java method takes Object
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
|
||||
else
|
||||
mh = retype(type, unbox(wrap, !exact, raw));
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : ""));
|
||||
}
|
||||
|
||||
public static MethodHandle unbox(Wrapper type, boolean exact) {
|
||||
return unbox(type, exact, false);
|
||||
}
|
||||
|
||||
public static MethodHandle unboxRaw(Wrapper type, boolean exact) {
|
||||
return unbox(type, exact, true);
|
||||
}
|
||||
|
||||
public static MethodHandle unbox(Class<?> type, boolean exact) {
|
||||
return unbox(Wrapper.forPrimitiveType(type), exact, false);
|
||||
}
|
||||
|
||||
public static MethodHandle unboxRaw(Class<?> type, boolean exact) {
|
||||
return unbox(Wrapper.forPrimitiveType(type), exact, true);
|
||||
}
|
||||
|
||||
/// Converting primitives to references
|
||||
|
||||
static Integer boxInteger(int x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Byte boxByte(byte x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Short boxShort(short x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Boolean boxBoolean(boolean x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Character boxCharacter(char x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Long boxLong(long x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Float boxFloat(float x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static Double boxDouble(double x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/// Converting raw primitives to references
|
||||
|
||||
static Byte boxByteRaw(int x) {
|
||||
return boxByte((byte)x);
|
||||
}
|
||||
|
||||
static Short boxShortRaw(int x) {
|
||||
return boxShort((short)x);
|
||||
}
|
||||
|
||||
static Boolean boxBooleanRaw(int x) {
|
||||
return boxBoolean(x != 0);
|
||||
}
|
||||
|
||||
static Character boxCharacterRaw(int x) {
|
||||
return boxCharacter((char)x);
|
||||
}
|
||||
|
||||
static Float boxFloatRaw(int x) {
|
||||
return boxFloat(Float.intBitsToFloat(x));
|
||||
}
|
||||
|
||||
static Double boxDoubleRaw(long x) {
|
||||
return boxDouble(Double.longBitsToDouble(x));
|
||||
}
|
||||
|
||||
// a raw void value is (arbitrarily) a garbage int
|
||||
static Void boxVoidRaw(int x) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MethodType boxType(Wrapper wrap, boolean raw) {
|
||||
// be exact, since return casts are hard to compose
|
||||
Class<?> boxType = wrap.wrapperType();
|
||||
return MethodType.make(boxType, rawWrapper(wrap, raw).primitiveType());
|
||||
}
|
||||
|
||||
private static Wrapper rawWrapper(Wrapper wrap, boolean raw) {
|
||||
if (raw) return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT;
|
||||
return wrap;
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
BOX_CONVERSIONS = newWrapperCaches(4);
|
||||
|
||||
private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
switch (wrap) {
|
||||
case OBJECT:
|
||||
mh = IDENTITY; break;
|
||||
case VOID:
|
||||
if (!raw) mh = ZERO_OBJECT;
|
||||
break;
|
||||
case INT: case LONG:
|
||||
// these guys don't need separate raw channels
|
||||
if (raw) mh = box(wrap, exact, false);
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
// look up the method
|
||||
String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
|
||||
MethodType type = boxType(wrap, raw);
|
||||
if (exact)
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
||||
else
|
||||
mh = retype(type.erase(), box(wrap, !exact, raw));
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : ""));
|
||||
}
|
||||
|
||||
public static MethodHandle box(Class<?> type, boolean exact) {
|
||||
return box(Wrapper.forPrimitiveType(type), exact, false);
|
||||
}
|
||||
|
||||
public static MethodHandle boxRaw(Class<?> type, boolean exact) {
|
||||
return box(Wrapper.forPrimitiveType(type), exact, true);
|
||||
}
|
||||
|
||||
public static MethodHandle box(Wrapper type, boolean exact) {
|
||||
return box(type, exact, false);
|
||||
}
|
||||
|
||||
public static MethodHandle boxRaw(Wrapper type, boolean exact) {
|
||||
return box(type, exact, true);
|
||||
}
|
||||
|
||||
/// Kludges for when raw values get accidentally boxed.
|
||||
|
||||
static Byte reboxRawByte(Object x) {
|
||||
if (x instanceof Byte) return (Byte) x;
|
||||
return boxByteRaw(unboxInteger(x));
|
||||
}
|
||||
|
||||
static Short reboxRawShort(Object x) {
|
||||
if (x instanceof Short) return (Short) x;
|
||||
return boxShortRaw(unboxInteger(x));
|
||||
}
|
||||
|
||||
static Boolean reboxRawBoolean(Object x) {
|
||||
if (x instanceof Boolean) return (Boolean) x;
|
||||
return boxBooleanRaw(unboxInteger(x));
|
||||
}
|
||||
|
||||
static Character reboxRawCharacter(Object x) {
|
||||
if (x instanceof Character) return (Character) x;
|
||||
return boxCharacterRaw(unboxInteger(x));
|
||||
}
|
||||
|
||||
static Float reboxRawFloat(Object x) {
|
||||
if (x instanceof Float) return (Float) x;
|
||||
return boxFloatRaw(unboxInteger(x));
|
||||
}
|
||||
|
||||
static Double reboxRawDouble(Object x) {
|
||||
if (x instanceof Double) return (Double) x;
|
||||
return boxDoubleRaw(unboxLong(x));
|
||||
}
|
||||
|
||||
private static MethodType reboxType(Wrapper wrap) {
|
||||
Class<?> boxType = wrap.wrapperType();
|
||||
return MethodType.make(boxType, Object.class);
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
REBOX_CONVERSIONS = newWrapperCaches(2);
|
||||
|
||||
public static MethodHandle rebox(Wrapper wrap, boolean exact) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[exact?1:0];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
switch (wrap) {
|
||||
case OBJECT:
|
||||
mh = IDENTITY; break;
|
||||
case VOID:
|
||||
throw new IllegalArgumentException("cannot rebox a void");
|
||||
case INT: case LONG:
|
||||
mh = cast(wrap.wrapperType(), exact);
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
// look up the method
|
||||
String name = "reboxRaw" + wrap.simpleName();
|
||||
MethodType type = reboxType(wrap);
|
||||
if (exact)
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
|
||||
else
|
||||
mh = retype(IDENTITY.type(), rebox(wrap, !exact));
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
|
||||
}
|
||||
|
||||
public static MethodHandle rebox(Class<?> type, boolean exact) {
|
||||
return rebox(Wrapper.forPrimitiveType(type), exact);
|
||||
}
|
||||
|
||||
/// Width-changing conversions between int and long.
|
||||
|
||||
static long widenInt(int x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static int narrowLong(long x) {
|
||||
return (int) x;
|
||||
}
|
||||
|
||||
/// Constant functions
|
||||
|
||||
static void ignore(Object x) {
|
||||
// no value to return; this is an unbox of null
|
||||
return;
|
||||
}
|
||||
|
||||
static void empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
static Object zeroObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
static int zeroInteger() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long zeroLong() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static float zeroFloat() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static double zeroDouble() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle>[]
|
||||
ZERO_CONSTANT_FUNCTIONS = newWrapperCaches(1);
|
||||
|
||||
public static MethodHandle zeroConstantFunction(Wrapper wrap) {
|
||||
EnumMap<Wrapper, MethodHandle> cache = ZERO_CONSTANT_FUNCTIONS[0];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
MethodType type = MethodType.make(wrap.primitiveType());
|
||||
switch (wrap) {
|
||||
case VOID:
|
||||
mh = EMPTY;
|
||||
break;
|
||||
case INT: case LONG: case FLOAT: case DOUBLE:
|
||||
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
|
||||
// use the raw method
|
||||
Wrapper rawWrap = wrap.rawPrimitive();
|
||||
if (rawWrap != wrap) {
|
||||
mh = retype(type, zeroConstantFunction(rawWrap));
|
||||
}
|
||||
if (mh != null) {
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find zero constant for " + wrap);
|
||||
}
|
||||
|
||||
/// Converting references to references.
|
||||
|
||||
/**
|
||||
* Value-killing function.
|
||||
* @param x an arbitrary reference value
|
||||
* @return a null
|
||||
*/
|
||||
static Object alwaysNull(Object x) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value-killing function.
|
||||
* @param x an arbitrary reference value
|
||||
* @return a zero
|
||||
*/
|
||||
static int alwaysZero(Object x) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function.
|
||||
* @param x an arbitrary reference value
|
||||
* @return the same value x
|
||||
*/
|
||||
static <T> T identity(T x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity function, with reference cast.
|
||||
* @param t an arbitrary reference type
|
||||
* @param x an arbitrary reference value
|
||||
* @return the same value x
|
||||
*/
|
||||
static <T,U> T castReference(Class<? extends T> t, U x) {
|
||||
return t.cast(x);
|
||||
}
|
||||
|
||||
private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY;
|
||||
static {
|
||||
try {
|
||||
MethodType idType = MethodType.makeGeneric(1);
|
||||
MethodType castType = idType.insertParameterType(0, Class.class);
|
||||
MethodType alwaysZeroType = idType.changeReturnType(int.class);
|
||||
MethodType ignoreType = idType.changeReturnType(void.class);
|
||||
MethodType zeroObjectType = MethodType.makeGeneric(0);
|
||||
IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType);
|
||||
//CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
|
||||
CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType);
|
||||
ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType);
|
||||
ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType);
|
||||
ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
|
||||
IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
|
||||
EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterType(0));
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle> WRAPPER_CASTS
|
||||
= new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
|
||||
|
||||
private static final EnumMap<Wrapper, MethodHandle> EXACT_WRAPPER_CASTS
|
||||
= new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
|
||||
|
||||
/** Return a method that casts its sole argument (an Object) to the given type
|
||||
* and returns it as the given type (if exact is true), or as plain Object (if erase is true).
|
||||
*/
|
||||
public static MethodHandle cast(Class<?> type, boolean exact) {
|
||||
if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
|
||||
MethodHandle mh = null;
|
||||
Wrapper wrap = null;
|
||||
EnumMap<Wrapper, MethodHandle> cache = null;
|
||||
if (Wrapper.isWrapperType(type)) {
|
||||
wrap = Wrapper.forWrapperType(type);
|
||||
cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS);
|
||||
mh = cache.get(wrap);
|
||||
if (mh != null) return mh;
|
||||
}
|
||||
if (VerifyType.isNullReferenceConversion(Object.class, type))
|
||||
mh = IDENTITY;
|
||||
else if (VerifyType.isNullType(type))
|
||||
mh = ALWAYS_NULL;
|
||||
else
|
||||
mh = MethodHandles.insertArgument(CAST_REFERENCE, 0, type);
|
||||
if (exact) {
|
||||
MethodType xmt = MethodType.make(type, Object.class);
|
||||
mh = AdapterMethodHandle.makeRawRetypeOnly(IMPL_TOKEN, xmt, mh);
|
||||
}
|
||||
if (cache != null)
|
||||
cache.put(wrap, mh);
|
||||
return mh;
|
||||
}
|
||||
|
||||
public static MethodHandle identity() {
|
||||
return IDENTITY;
|
||||
}
|
||||
|
||||
private static MethodHandle retype(MethodType type, MethodHandle mh) {
|
||||
return AdapterMethodHandle.makeRetypeOnly(IMPL_TOKEN, type, mh);
|
||||
}
|
||||
}
|
169
jdk/src/share/classes/sun/dyn/util/VerifyAccess.java
Normal file
169
jdk/src/share/classes/sun/dyn/util/VerifyAccess.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.dyn.LinkagePermission;
|
||||
import java.lang.reflect.Modifier;
|
||||
import sun.dyn.Access;
|
||||
|
||||
/**
|
||||
* This class centralizes information about the JVM's linkage access control.
|
||||
* @author jrose
|
||||
*/
|
||||
public class VerifyAccess {
|
||||
|
||||
private VerifyAccess() { } // cannot instantiate
|
||||
|
||||
/**
|
||||
* Evaluate the JVM linkage rules for access to the given method on behalf of caller.
|
||||
* Return non-null if and only if the given accessing class has at least partial
|
||||
* privileges to invoke the given method. The return value {@code Object.class}
|
||||
* denotes unlimited privileges.
|
||||
* <p>
|
||||
* Some circumstances require an additional check on the
|
||||
* leading parameter (the receiver) of the method, if it is non-static.
|
||||
* In the case of {@code invokespecial} ({@code doDispatch} is false),
|
||||
* the leading parameter must be the accessing class or a subclass.
|
||||
* In the case of a call to a {@code protected} method outside the same
|
||||
* package, the same constraint applies.
|
||||
* @param m the proposed callee
|
||||
* @param doDispatch if false, a non-static m will be invoked as if by {@code invokespecial}
|
||||
* @param lookupClass the class for which the access check is being made
|
||||
* @return null if the method is not accessible, else a receiver type constraint, else {@code Object.class}
|
||||
*/
|
||||
public static Class<?> isAccessible(Class<?> defc, int mods,
|
||||
boolean doDispatch, Class<?> lookupClass) {
|
||||
if (!isAccessible(defc, lookupClass))
|
||||
return null;
|
||||
Class<?> constraint = Object.class;
|
||||
if (!doDispatch && !Modifier.isStatic(mods)) {
|
||||
constraint = lookupClass;
|
||||
}
|
||||
if (Modifier.isPublic(mods))
|
||||
return constraint;
|
||||
if (Modifier.isPrivate(mods))
|
||||
return isSamePackageMember(defc, lookupClass) ? constraint : null;
|
||||
if (isSamePackage(defc, lookupClass))
|
||||
return constraint;
|
||||
if (Modifier.isProtected(mods) && defc.isAssignableFrom(lookupClass))
|
||||
return constraint;
|
||||
// else it is private or package scoped, and not close enough
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the JVM linkage rules for access to the given class on behalf of caller.
|
||||
*/
|
||||
public static boolean isAccessible(Class<?> refc, Class<?> lookupClass) {
|
||||
int mods = refc.getModifiers();
|
||||
if (Modifier.isPublic(mods))
|
||||
return true;
|
||||
if (isSamePackage(lookupClass, refc))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if two classes have the same class loader and package qualifier.
|
||||
* @param class1
|
||||
* @param class2
|
||||
* @return whether they are in the same package
|
||||
*/
|
||||
public static boolean isSamePackage(Class<?> class1, Class<?> class2) {
|
||||
if (class1 == class2)
|
||||
return true;
|
||||
if (loadersAreRelated(class1.getClassLoader(), class2.getClassLoader()))
|
||||
return false;
|
||||
String name1 = class1.getName(), name2 = class2.getName();
|
||||
int dot = name1.lastIndexOf('.');
|
||||
if (dot != name2.lastIndexOf('.'))
|
||||
return false;
|
||||
for (int i = 0; i < dot; i++) {
|
||||
if (name1.charAt(i) != name2.charAt(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if two classes are defined as part of the same package member (top-level class).
|
||||
* If this is true, they can share private access with each other.
|
||||
* @param class1
|
||||
* @param class2
|
||||
* @return whether they are identical or nested together
|
||||
*/
|
||||
public static boolean isSamePackageMember(Class<?> class1, Class<?> class2) {
|
||||
if (class1 == class2)
|
||||
return true;
|
||||
if (!isSamePackage(class1, class2))
|
||||
return false;
|
||||
if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Class<?> getOutermostEnclosingClass(Class<?> c) {
|
||||
Class<?> pkgmem = c;
|
||||
for (Class<?> enc = c; (enc = enc.getEnclosingClass()) != null; )
|
||||
pkgmem = enc;
|
||||
return pkgmem;
|
||||
}
|
||||
|
||||
private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2) {
|
||||
if (loader1 == loader2 || loader1 == null || loader2 == null) {
|
||||
return true;
|
||||
}
|
||||
for (ClassLoader scan1 = loader1;
|
||||
scan1 != null; scan1 = scan1.getParent()) {
|
||||
if (scan1 == loader2) return true;
|
||||
}
|
||||
for (ClassLoader scan2 = loader2;
|
||||
scan2 != null; scan2 = scan2.getParent()) {
|
||||
if (scan2 == loader1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the requesting class have privileges to perform invokedynamic
|
||||
* linkage operations on subjectClass. True if requestingClass is
|
||||
* Access.class (meaning the request originates from the JVM) or if the
|
||||
* classes are in the same package and have consistent class loaders.
|
||||
* (The subject class loader must be identical with or be a child of
|
||||
* the requesting class loader.)
|
||||
* @param requestingClass
|
||||
* @param subjectClass
|
||||
*/
|
||||
public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass,
|
||||
String permissionName) {
|
||||
if (requestingClass == Access.class) return;
|
||||
if (requestingClass == subjectClass) return;
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security == null) return; // open season
|
||||
if (isSamePackage(requestingClass, subjectClass)) return;
|
||||
security.checkPermission(new LinkagePermission(permissionName, requestingClass));
|
||||
}
|
||||
}
|
219
jdk/src/share/classes/sun/dyn/util/VerifyType.java
Normal file
219
jdk/src/share/classes/sun/dyn/util/VerifyType.java
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.dyn.MethodType;
|
||||
|
||||
/**
|
||||
* This class centralizes information about the JVM verifier
|
||||
* and its requirements about type correctness.
|
||||
* @author jrose
|
||||
*/
|
||||
public class VerifyType {
|
||||
|
||||
private VerifyType() { } // cannot instantiate
|
||||
|
||||
/**
|
||||
* True if a value can be stacked as the source type and unstacked as the
|
||||
* destination type, without violating the JVM's type consistency.
|
||||
*
|
||||
* @param call the type of a stacked value
|
||||
* @param recv the type by which we'd like to treat it
|
||||
* @return whether the retyping can be done without motion or reformatting
|
||||
*/
|
||||
public static boolean isNullConversion(Class<?> src, Class<?> dst) {
|
||||
if (src == dst) return true;
|
||||
// Verifier allows any interface to be treated as Object:
|
||||
if (dst.isInterface()) dst = Object.class;
|
||||
if (src.isInterface()) src = Object.class;
|
||||
if (src == dst) return true; // check again
|
||||
if (dst == void.class) return true; // drop any return value
|
||||
if (isNullType(src)) return !dst.isPrimitive();
|
||||
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
|
||||
// Verifier allows an int to carry byte, short, char, or even boolean:
|
||||
if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of isNullConversion to reference types.
|
||||
|
||||
* @param call the type of a stacked value
|
||||
* @param recv the reference type by which we'd like to treat it
|
||||
* @return whether the retyping can be done without a cast
|
||||
*/
|
||||
public static boolean isNullReferenceConversion(Class<?> src, Class<?> dst) {
|
||||
assert(!dst.isPrimitive());
|
||||
if (dst.isInterface()) return true; // verifier allows this
|
||||
if (isNullType(src)) return true;
|
||||
return dst.isAssignableFrom(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given type either java.lang.Void or java.lang.Null?
|
||||
* These types serve as markers for bare nulls and therefore
|
||||
* may be promoted to any type. This is secure, since
|
||||
*/
|
||||
public static boolean isNullType(Class<?> type) {
|
||||
if (type == null) return false;
|
||||
return type == NULL_CLASS_1 || type == NULL_CLASS_2;
|
||||
}
|
||||
private static final Class<?> NULL_CLASS_1, NULL_CLASS_2;
|
||||
static {
|
||||
Class<?> nullClass1 = null, nullClass2 = null;
|
||||
try {
|
||||
nullClass1 = Class.forName("java.lang.Null");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
// OK, we'll cope
|
||||
}
|
||||
NULL_CLASS_1 = nullClass1;
|
||||
|
||||
// This one may also be used as a null type.
|
||||
// TO DO: Decide if we really want to legitimize it here.
|
||||
// Probably we do, unless java.lang.Null really makes it into Java 7
|
||||
nullClass2 = Void.class;
|
||||
NULL_CLASS_2 = nullClass2;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if a method handle can receive a call under a slightly different
|
||||
* method type, without moving or reformatting any stack elements.
|
||||
*
|
||||
* @param call the type of call being made
|
||||
* @param recv the type of the method handle receiving the call
|
||||
* @return whether the retyping can be done without motion or reformatting
|
||||
*/
|
||||
public static boolean isNullConversion(MethodType call, MethodType recv) {
|
||||
if (call == recv) return true;
|
||||
int len = call.parameterCount();
|
||||
if (len != recv.parameterCount()) return false;
|
||||
for (int i = 0; i < len; i++)
|
||||
if (!isNullConversion(call.parameterType(i), recv.parameterType(i)))
|
||||
return false;
|
||||
return isNullConversion(recv.returnType(), call.returnType());
|
||||
}
|
||||
|
||||
//TO DO: isRawConversion
|
||||
|
||||
/**
|
||||
* Determine if the JVM verifier allows a value of type call to be
|
||||
* passed to a formal parameter (or return variable) of type recv.
|
||||
* Returns 1 if the verifier allows the types to match without conversion.
|
||||
* Returns -1 if the types can be made to match by a JVM-supported adapter.
|
||||
* Cases supported are:
|
||||
* <ul><li>checkcast
|
||||
* </li><li>conversion between any two integral types (but not floats)
|
||||
* </li><li>unboxing from a wrapper to its corresponding primitive type
|
||||
* </li><li>conversion in either direction between float and double
|
||||
* </li></ul>
|
||||
* (Autoboxing is not supported here; it must be done via Java code.)
|
||||
* Returns 0 otherwise.
|
||||
*/
|
||||
public static int canPassUnchecked(Class<?> src, Class<?> dst) {
|
||||
if (src == dst)
|
||||
return 1;
|
||||
|
||||
if (dst.isPrimitive()) {
|
||||
if (dst == void.class)
|
||||
// Return anything to a caller expecting void.
|
||||
// This is a property of the implementation, which links
|
||||
// return values via a register rather than via a stack push.
|
||||
// This makes it possible to ignore cleanly.
|
||||
return 1;
|
||||
if (src == void.class)
|
||||
return 0; // void-to-something?
|
||||
if (!src.isPrimitive())
|
||||
// Cannot pass a reference to any primitive type (exc. void).
|
||||
return 0;
|
||||
Wrapper sw = Wrapper.forPrimitiveType(src);
|
||||
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||
if (sw.isSubwordOrInt() && dw.isSubwordOrInt()) {
|
||||
if (sw.bitWidth() >= dw.bitWidth())
|
||||
return -1; // truncation may be required
|
||||
if (!dw.isSigned() && sw.isSigned())
|
||||
return -1; // sign elimination may be required
|
||||
}
|
||||
if (src == float.class || dst == float.class) {
|
||||
if (src == double.class || dst == double.class)
|
||||
return -1; // floating conversion may be required
|
||||
else
|
||||
return 0; // other primitive conversions NYI
|
||||
} else {
|
||||
// all fixed-point conversions are supported
|
||||
return 0;
|
||||
}
|
||||
} else if (src.isPrimitive()) {
|
||||
// Cannot pass a primitive to any reference type.
|
||||
// (Maybe allow null.class?)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle reference types in the rest of the block:
|
||||
|
||||
// The verifier treats interfaces exactly like Object.
|
||||
if (isNullReferenceConversion(src, dst))
|
||||
// pass any reference to object or an arb. interface
|
||||
return 1;
|
||||
// else it's a definite "maybe" (cast is required)
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int canPassRaw(Class<?> src, Class<?> dst) {
|
||||
if (dst.isPrimitive()) {
|
||||
if (dst == void.class)
|
||||
// As above, return anything to a caller expecting void.
|
||||
return 1;
|
||||
if (src == void.class)
|
||||
// Special permission for raw conversions: allow a void
|
||||
// to be captured as a garbage int.
|
||||
// Caller promises that the actual value will be disregarded.
|
||||
return dst == int.class ? 1 : 0;
|
||||
if (!src.isPrimitive())
|
||||
return 0;
|
||||
Wrapper sw = Wrapper.forPrimitiveType(src);
|
||||
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||
if (sw.stackSlots() == dw.stackSlots())
|
||||
return 1; // can do a reinterpret-cast on a stacked primitive
|
||||
if (sw.isSubwordOrInt() && dw == Wrapper.VOID)
|
||||
return 1; // can drop an outgoing int value
|
||||
return 0;
|
||||
} else if (src.isPrimitive()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Both references.
|
||||
if (isNullReferenceConversion(src, dst))
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean isSpreadArgType(Class<?> spreadArg) {
|
||||
return spreadArg.isArray();
|
||||
}
|
||||
public static Class<?> spreadArgElementType(Class<?> spreadArg, int i) {
|
||||
return spreadArg.getComponentType();
|
||||
}
|
||||
}
|
467
jdk/src/share/classes/sun/dyn/util/Wrapper.java
Normal file
467
jdk/src/share/classes/sun/dyn/util/Wrapper.java
Normal file
@ -0,0 +1,467 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
public enum Wrapper {
|
||||
INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
|
||||
LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
|
||||
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
|
||||
SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
|
||||
CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
|
||||
BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)),
|
||||
FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
|
||||
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
|
||||
VOID(Void.class, void.class, 'V', null, Format.other(0)),
|
||||
//NULL(Null.class, null.class, 'N', null, Format.other(1)),
|
||||
OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
|
||||
;
|
||||
|
||||
private final Class<?> wrapperType;
|
||||
private final Class<?> primitiveType;
|
||||
private final char basicTypeChar;
|
||||
private final Object zero;
|
||||
private final int format;
|
||||
private final String simpleName;
|
||||
|
||||
private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, int format) {
|
||||
this.wrapperType = wtype;
|
||||
this.primitiveType = ptype;
|
||||
this.basicTypeChar = tchar;
|
||||
this.zero = zero;
|
||||
this.format = format;
|
||||
this.simpleName = wtype.getSimpleName();
|
||||
}
|
||||
|
||||
private static abstract class Format {
|
||||
static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
|
||||
static final int
|
||||
SIGNED = (-1) << KIND_SHIFT,
|
||||
UNSIGNED = 0 << KIND_SHIFT,
|
||||
FLOATING = 1 << KIND_SHIFT;
|
||||
static final int
|
||||
SLOT_MASK = ((1<<(SIZE_SHIFT-SLOT_SHIFT))-1),
|
||||
SIZE_MASK = ((1<<(KIND_SHIFT-SIZE_SHIFT))-1);
|
||||
static int format(int kind, int size, int slots) {
|
||||
assert(((kind >> KIND_SHIFT) << KIND_SHIFT) == kind);
|
||||
assert((size & (size-1)) == 0); // power of two
|
||||
assert((kind == SIGNED) ? (size > 0) :
|
||||
(kind == UNSIGNED) ? (size > 0) :
|
||||
(kind == FLOATING) ? (size == 32 || size == 64) :
|
||||
false);
|
||||
assert((slots == 2) ? (size == 64) :
|
||||
(slots == 1) ? (size <= 32) :
|
||||
false);
|
||||
return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT);
|
||||
}
|
||||
static int
|
||||
INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
|
||||
BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
|
||||
FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
|
||||
VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT),
|
||||
NUM_MASK = (-1) << SIZE_SHIFT;
|
||||
static int signed(int size) { return format(SIGNED, size, (size > 32 ? 2 : 1)); }
|
||||
static int unsigned(int size) { return format(UNSIGNED, size, (size > 32 ? 2 : 1)); }
|
||||
static int floating(int size) { return format(FLOATING, size, (size > 32 ? 2 : 1)); }
|
||||
static int other(int slots) { return slots << SLOT_SHIFT; }
|
||||
}
|
||||
|
||||
/// format queries:
|
||||
|
||||
/** How many bits are in the wrapped value? Returns 0 for OBJECT or VOID. */
|
||||
public int bitWidth() { return (format >> Format.SIZE_SHIFT) & Format.SIZE_MASK; }
|
||||
/** How many JVM stack slots occupied by the wrapped value? Returns 0 for VOID. */
|
||||
public int stackSlots() { return (format >> Format.SLOT_SHIFT) & Format.SLOT_MASK; }
|
||||
/** Does the wrapped value occupy a single JVM stack slot? */
|
||||
public boolean isSingleWord() { return (format & (1 << Format.SLOT_SHIFT)) != 0; }
|
||||
/** Does the wrapped value occupy two JVM stack slots? */
|
||||
public boolean isDoubleWord() { return (format & (2 << Format.SLOT_SHIFT)) != 0; }
|
||||
/** Is the wrapped type numeric (not void or object)? */
|
||||
public boolean isNumeric() { return (format & Format.NUM_MASK) != 0; }
|
||||
/** Is the wrapped type a primitive other than float, double, or void? */
|
||||
public boolean isIntegral() { return isNumeric() && format < Format.FLOAT; }
|
||||
/** Is the wrapped type one of int, boolean, byte, char, or short? */
|
||||
public boolean isSubwordOrInt() { return isIntegral() && isSingleWord(); }
|
||||
/* Is the wrapped value a signed integral type (one of byte, short, int, or long)? */
|
||||
public boolean isSigned() { return format < Format.VOID; }
|
||||
/* Is the wrapped value an unsigned integral type (one of boolean or char)? */
|
||||
public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
|
||||
/** Is the wrapped type either float or double? */
|
||||
public boolean isFloating() { return format >= Format.FLOAT; }
|
||||
|
||||
/** Produce a zero value for the given wrapper type.
|
||||
* This will be a numeric zero for a number or character,
|
||||
* false for a boolean, and null for a reference or void.
|
||||
* The common thread is that this is what is contained
|
||||
* in a default-initialized variable of the given primitive
|
||||
* type. (For void, it is what a reflective method returns
|
||||
* instead of no value at all.)
|
||||
*/
|
||||
public Object zero() { return zero; }
|
||||
|
||||
/** Produce a zero value for the given wrapper type T.
|
||||
* The optinoal argument must a type compatible with this wrapper.
|
||||
* Equivalent to {@code this.cast(this.zero(), type)}.
|
||||
*/
|
||||
public <T> T zero(Class<T> type) { return cast(zero, type); }
|
||||
|
||||
// /** Produce a wrapper for the given wrapper or primitive type. */
|
||||
// public static Wrapper valueOf(Class<?> type) {
|
||||
// if (isPrimitiveType(type))
|
||||
// return forPrimitiveType(type);
|
||||
// else
|
||||
// return forWrapperType(type);
|
||||
// }
|
||||
|
||||
/** Return the wrapper that wraps values of the given type.
|
||||
* The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
|
||||
* Otherwise, the type must be a primitive.
|
||||
* @throws IllegalArgumentException for unexpected types
|
||||
*/
|
||||
public static Wrapper forPrimitiveType(Class<?> type) {
|
||||
Wrapper w = FROM_PRIM[hashPrim(type)];
|
||||
if (w != null && w.primitiveType == type) {
|
||||
return w;
|
||||
}
|
||||
if (type.isPrimitive())
|
||||
throw new InternalError(); // redo hash function
|
||||
throw newIllegalArgumentException("not primitive: "+type);
|
||||
}
|
||||
|
||||
/** Return the wrapper that wraps values into the given wrapper type.
|
||||
* If it is {@code Object} or an interface, return {@code OBJECT}.
|
||||
* Otherwise, it must be a wrapper type.
|
||||
* The type must not be a primitive type.
|
||||
* @throws IllegalArgumentException for unexpected types
|
||||
*/
|
||||
public static Wrapper forWrapperType(Class<?> type) {
|
||||
Wrapper w = findWrapperType(type);
|
||||
if (w != null) return w;
|
||||
for (Wrapper x : values())
|
||||
if (w.wrapperType == type)
|
||||
throw new InternalError(); // redo hash function
|
||||
throw newIllegalArgumentException("not wrapper: "+type);
|
||||
}
|
||||
|
||||
static Wrapper findWrapperType(Class<?> type) {
|
||||
Wrapper w = FROM_WRAP[hashWrap(type)];
|
||||
if (w != null && w.wrapperType == type) {
|
||||
return w;
|
||||
}
|
||||
if (type.isInterface())
|
||||
return OBJECT;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Return the wrapper that corresponds to the given bytecode
|
||||
* signature character. Return {@code OBJECT} for the character 'L'.
|
||||
* @throws IllegalArgumentException for any non-signature character or {@code '['}.
|
||||
*/
|
||||
public static Wrapper forBasicType(char type) {
|
||||
Wrapper w = FROM_CHAR[hashChar(type)];
|
||||
if (w != null && w.basicTypeChar == type) {
|
||||
return w;
|
||||
}
|
||||
for (Wrapper x : values())
|
||||
if (w.basicTypeChar == type)
|
||||
throw new InternalError(); // redo hash function
|
||||
throw newIllegalArgumentException("not basic type char: "+type);
|
||||
}
|
||||
|
||||
/** Return the wrapper for the given type, if it is
|
||||
* a primitive type, else return {@code OBJECT}.
|
||||
*/
|
||||
public static Wrapper forBasicType(Class<?> type) {
|
||||
if (type.isPrimitive())
|
||||
return forPrimitiveType(type);
|
||||
return OBJECT; // any reference, including wrappers or arrays
|
||||
}
|
||||
|
||||
// Note on perfect hashes:
|
||||
// for signature chars c, do (c + (c >> 1)) % 16
|
||||
// for primitive type names n, do (n[0] + n[2]) % 16
|
||||
// The type name hash works for both primitive and wrapper names.
|
||||
// You can add "java/lang/Object" to the primitive names.
|
||||
// But you add the wrapper name Object, use (n[2] + (3*n[1])) % 16.
|
||||
private static final Wrapper[] FROM_PRIM = new Wrapper[16];
|
||||
private static final Wrapper[] FROM_WRAP = new Wrapper[16];
|
||||
private static final Wrapper[] FROM_CHAR = new Wrapper[16];
|
||||
private static int hashPrim(Class<?> x) {
|
||||
String xn = x.getName();
|
||||
if (xn.length() < 3) return 0;
|
||||
return (xn.charAt(0) + xn.charAt(2)) % 16;
|
||||
}
|
||||
private static int hashWrap(Class<?> x) {
|
||||
String xn = x.getName();
|
||||
final int offset = 10; assert(offset == "java.lang.".length());
|
||||
if (xn.length() < offset+3) return 0;
|
||||
return (3*xn.charAt(offset+1) + xn.charAt(offset+2)) % 16;
|
||||
}
|
||||
private static int hashChar(char x) {
|
||||
return (x + (x >> 1)) % 16;
|
||||
}
|
||||
static {
|
||||
for (Wrapper w : values()) {
|
||||
int pi = hashPrim(w.primitiveType);
|
||||
int wi = hashWrap(w.wrapperType);
|
||||
int ci = hashChar(w.basicTypeChar);
|
||||
assert(FROM_PRIM[pi] == null);
|
||||
assert(FROM_WRAP[wi] == null);
|
||||
assert(FROM_CHAR[ci] == null);
|
||||
FROM_PRIM[pi] = w;
|
||||
FROM_WRAP[wi] = w;
|
||||
FROM_CHAR[ci] = w;
|
||||
}
|
||||
//assert(jdk.sun.dyn.util.WrapperTest.test(false));
|
||||
}
|
||||
|
||||
/** What is the primitive type wrapped by this wrapper? */
|
||||
public Class<?> primitiveType() { return primitiveType; }
|
||||
|
||||
/** What is the wrapper type for this wrapper? */
|
||||
public Class<?> wrapperType() { return wrapperType; }
|
||||
|
||||
/** What is the wrapper type for this wrapper?
|
||||
* The example type must be the wrapper type,
|
||||
* or the corresponding primitive type.
|
||||
* The resulting class type has the same type parameter.
|
||||
*/
|
||||
public <T> Class<T> wrapperType(Class<T> exampleType) {
|
||||
if (exampleType == wrapperType) {
|
||||
return exampleType;
|
||||
} else if (exampleType == primitiveType ||
|
||||
wrapperType == Object.class ||
|
||||
exampleType.isInterface()) {
|
||||
return forceType(wrapperType, exampleType);
|
||||
}
|
||||
throw new ClassCastException(exampleType + " not <:" + wrapperType);
|
||||
}
|
||||
|
||||
/** If {@code type} is a primitive type, return the corresponding
|
||||
* wrapper type, else return {@code type} unchanged.
|
||||
*/
|
||||
public static <T> Class<T> asWrapperType(Class<T> type) {
|
||||
if (type.isPrimitive()) {
|
||||
return forPrimitiveType(type).wrapperType(type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/** If {@code type} is a wrapper type, return the corresponding
|
||||
* primitive type, else return {@code type} unchanged.
|
||||
*/
|
||||
public static <T> Class<T> asPrimitiveType(Class<T> type) {
|
||||
Wrapper w = findWrapperType(type);
|
||||
if (w != null) {
|
||||
return forceType(w.primitiveType(), type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/** Query: Is the given type a wrapper, such as {@code Integer} or {@code Void}? */
|
||||
public static boolean isWrapperType(Class<?> type) {
|
||||
return findWrapperType(type) != null;
|
||||
}
|
||||
|
||||
/** Query: Is the given type a primitive, such as {@code int} or {@code void}? */
|
||||
public static boolean isPrimitiveType(Class<?> type) {
|
||||
return type.isPrimitive();
|
||||
}
|
||||
|
||||
/** What is the bytecode signature character for this wrapper's
|
||||
* primitive type?
|
||||
*/
|
||||
public char basicTypeChar() { return basicTypeChar; }
|
||||
|
||||
/** What is the simple name of the wrapper type?
|
||||
*/
|
||||
public String simpleName() { return simpleName; }
|
||||
|
||||
// /** Wrap a value in the given type, which may be either a primitive or wrapper type.
|
||||
// * Performs standard primitive conversions, including truncation and float conversions.
|
||||
// */
|
||||
// public static <T> T wrap(Object x, Class<T> type) {
|
||||
// return Wrapper.valueOf(type).cast(x, type);
|
||||
// }
|
||||
|
||||
/** Cast a wrapped value to the given type, which may be either a primitive or wrapper type.
|
||||
* Performs standard primitive conversions, including truncation and float conversions.
|
||||
* The given type must be compatible with this wrapper. That is, it must either
|
||||
* be the wrapper type (or a subtype, in the case of {@code OBJECT} or else
|
||||
* it must be the wrapper's primitive type.
|
||||
* @throws ClassCastException if the given type is not compatible with this wrapper
|
||||
*/
|
||||
public <T> T cast(Object x, Class<T> type) {
|
||||
Class<T> wtype = wrapperType(type);
|
||||
if (wtype.isInstance(x))
|
||||
return wtype.cast(x);
|
||||
return wtype.cast(wrap(x));
|
||||
}
|
||||
|
||||
/** Cast a reference type to another reference type.
|
||||
* If the target type is an interface, perform no runtime check.
|
||||
* (This loophole is safe, and is allowed by the JVM verifier.)
|
||||
* If the target type is a primitive, change it to a wrapper.
|
||||
*/
|
||||
static <T> Class<T> forceType(Class<?> type, Class<T> exampleType) {
|
||||
assert(type == exampleType ||
|
||||
type == asWrapperType(exampleType) ||
|
||||
type == Object.class && exampleType.isInterface());
|
||||
Class<T> result = (Class<T>) type; // unchecked warning is expected here
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Wrap a value in this wrapper's type.
|
||||
* Performs standard primitive conversions, including truncation and float conversions.
|
||||
* Performs returns the unchanged reference for {@code OBJECT}.
|
||||
* Returns null for {@code VOID}.
|
||||
* Returns a zero value for a null input.
|
||||
* @throws ClassCastException if this wrapper is numeric and the operand
|
||||
* is not a number, character, boolean, or null
|
||||
*/
|
||||
public Object wrap(Object x) {
|
||||
// do non-numeric wrappers first
|
||||
switch (basicTypeChar) {
|
||||
case 'L': return x;
|
||||
case 'V': return null;
|
||||
}
|
||||
Number xn = numberValue(x);
|
||||
switch (basicTypeChar) {
|
||||
case 'I': return Integer.valueOf(xn.intValue());
|
||||
case 'J': return Long.valueOf(xn.longValue());
|
||||
case 'F': return Float.valueOf(xn.floatValue());
|
||||
case 'D': return Double.valueOf(xn.doubleValue());
|
||||
case 'S': return Short.valueOf((short) xn.intValue());
|
||||
case 'B': return Byte.valueOf((byte) xn.intValue());
|
||||
case 'C': return Character.valueOf((char) xn.intValue());
|
||||
case 'Z': return Boolean.valueOf(boolValue(xn.longValue()));
|
||||
}
|
||||
throw new InternalError("bad wrapper");
|
||||
}
|
||||
|
||||
/** Wrap a value (an int or smaller value) in this wrapper's type.
|
||||
* Performs standard primitive conversions, including truncation and float conversions.
|
||||
* Produces an {@code Integer} for {@code OBJECT}, although the exact type
|
||||
* of the operand is not known.
|
||||
* Returns null for {@code VOID}.
|
||||
*/
|
||||
public Object wrap(int x) {
|
||||
if (basicTypeChar == 'L') return (Integer)x;
|
||||
switch (basicTypeChar) {
|
||||
case 'L': throw newIllegalArgumentException("cannot wrap to object type");
|
||||
case 'V': return null;
|
||||
case 'I': return Integer.valueOf((int)x);
|
||||
case 'J': return Long.valueOf(x);
|
||||
case 'F': return Float.valueOf(x);
|
||||
case 'D': return Double.valueOf(x);
|
||||
case 'S': return Short.valueOf((short) x);
|
||||
case 'B': return Byte.valueOf((byte) x);
|
||||
case 'C': return Character.valueOf((char) x);
|
||||
case 'Z': return Boolean.valueOf(boolValue(x));
|
||||
}
|
||||
throw new InternalError("bad wrapper");
|
||||
}
|
||||
|
||||
/** Wrap a value (a long or smaller value) in this wrapper's type.
|
||||
* Does not perform floating point conversion.
|
||||
* Produces a {@code Long} for {@code OBJECT}, although the exact type
|
||||
* of the operand is not known.
|
||||
* Returns null for {@code VOID}.
|
||||
*/
|
||||
public Object wrapRaw(long x) {
|
||||
switch (basicTypeChar) {
|
||||
case 'F': return Float.valueOf(Float.intBitsToFloat((int)x));
|
||||
case 'D': return Double.valueOf(Double.longBitsToDouble(x));
|
||||
case 'L': // same as 'J':
|
||||
case 'J': return (Long) x;
|
||||
}
|
||||
// Other wrapping operations are just the same, given that the
|
||||
// operand is already promoted to an int.
|
||||
return wrap((int)x);
|
||||
}
|
||||
|
||||
/** Produce bitwise value which encodes the given wrapped value.
|
||||
* Does not perform floating point conversion.
|
||||
* Returns zero for {@code VOID}.
|
||||
*/
|
||||
public long unwrapRaw(Object x) {
|
||||
switch (basicTypeChar) {
|
||||
case 'F': return Float.floatToRawIntBits((Float) x);
|
||||
case 'D': return Double.doubleToRawLongBits((Double) x);
|
||||
|
||||
case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type");
|
||||
case 'V': return 0;
|
||||
case 'I': return (int)(Integer) x;
|
||||
case 'J': return (long)(Long) x;
|
||||
case 'S': return (short)(Short) x;
|
||||
case 'B': return (byte)(Byte) x;
|
||||
case 'C': return (char)(Character) x;
|
||||
case 'Z': return (boolean)(Boolean) x ? 1 : 0;
|
||||
}
|
||||
throw new InternalError("bad wrapper");
|
||||
}
|
||||
|
||||
/** Report what primitive type holds this guy's raw value. */
|
||||
public Class<?> rawPrimitiveType() {
|
||||
return rawPrimitive().primitiveType();
|
||||
}
|
||||
|
||||
/** Report, as a wrapper, what primitive type holds this guy's raw value.
|
||||
* Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE,
|
||||
* else returns INT.
|
||||
*/
|
||||
public Wrapper rawPrimitive() {
|
||||
switch (basicTypeChar) {
|
||||
case 'S': case 'B':
|
||||
case 'C': case 'Z':
|
||||
case 'V':
|
||||
case 'F':
|
||||
return INT;
|
||||
case 'D':
|
||||
return LONG;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static Number numberValue(Object x) {
|
||||
if (x instanceof Number) return (Number)x;
|
||||
if (x instanceof Character) return (int)(Character)x;
|
||||
if (x instanceof Boolean) return (Boolean)x ? 1 : 0;
|
||||
// Remaining allowed case of void: Must be a null reference.
|
||||
return (Number)x;
|
||||
}
|
||||
|
||||
private static boolean boolValue(long bits) {
|
||||
//bits &= 1; // simple 31-bit zero extension
|
||||
return (bits != 0);
|
||||
}
|
||||
|
||||
private static RuntimeException newIllegalArgumentException(String message, Object x) {
|
||||
return newIllegalArgumentException(message + x);
|
||||
}
|
||||
private static RuntimeException newIllegalArgumentException(String message) {
|
||||
return new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
31
jdk/src/share/classes/sun/dyn/util/package-info.java
Normal file
31
jdk/src/share/classes/sun/dyn/util/package-info.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extra support for using JSR 292 RI, package java.dyn.
|
||||
* @author jrose
|
||||
*/
|
||||
|
||||
package sun.dyn.util;
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 */ \
|
||||
|
@ -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 *context, unsigned int inumber, int offset)
|
||||
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 *context, unsigned int inumber, int offset)
|
||||
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 *context, unsigned int inumber, int offset)
|
||||
"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, unsigned int inumber, stack_info_type *new_stac
|
||||
|
||||
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, unsigned int inumber, stack_info_type *new_stac
|
||||
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, unsigned int inumber, stack_info_type *new_stac
|
||||
|
||||
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, unsigned int inumber, stack_info_type *new_stac
|
||||
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, unsigned int inumber, stack_info_type *new_sta
|
||||
|
||||
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;
|
||||
|
@ -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 */
|
||||
|
@ -475,49 +475,40 @@ class WindowsAsynchronousSocketChannelImpl
|
||||
// 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;
|
||||
}
|
||||
// read completed immediately:
|
||||
// 1. update buffer position
|
||||
// 2. reset read flag
|
||||
// 3. release waiters
|
||||
if (n == 0) {
|
||||
n = -1;
|
||||
} else {
|
||||
updateBuffers(n);
|
||||
}
|
||||
// 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(n));
|
||||
result.setResult((V)Long.valueOf(-1L));
|
||||
} else {
|
||||
result.setResult((V)Integer.valueOf(n));
|
||||
result.setResult((V)Integer.valueOf(-1));
|
||||
}
|
||||
} 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 WindowsAsynchronousSocketChannelImpl
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run() {
|
||||
int n = -1;
|
||||
long overlapped = 0L;
|
||||
boolean prepared = false;
|
||||
boolean pending = false;
|
||||
@ -736,56 +726,34 @@ class WindowsAsynchronousSocketChannelImpl
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
@ -157,14 +157,13 @@ Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_read0(JNIEnv* env, jclass t
|
||||
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_WindowsAsynchronousSocketChannelImpl_read0(JNIEnv* env, jclass t
|
||||
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_WindowsAsynchronousSocketChannelImpl_write0(JNIEnv* env, jclass
|
||||
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_WindowsAsynchronousSocketChannelImpl_write0(JNIEnv* env, jclass
|
||||
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
|
||||
return IOS_THROWN;
|
||||
}
|
||||
return (jint)nwritten;
|
||||
return IOS_UNAVAILABLE;
|
||||
}
|
||||
|
69
jdk/test/java/lang/Runtime/shutdown/ShutdownHooks.java
Normal file
69
jdk/test/java/lang/Runtime/shutdown/ShutdownHooks.java
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @bug 6829503
|
||||
* @summary 1) Test Console and DeleteOnExitHook can be initialized
|
||||
* while shutdown is in progress
|
||||
* 2) Test if files that are added by the application shutdown
|
||||
* hook are deleted on exit during shutdown
|
||||
*/
|
||||
import java.io.*;
|
||||
public class ShutdownHooks {
|
||||
private static File file;
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 2) {
|
||||
throw new IllegalArgumentException("Usage: ShutdownHooks <dir> <filename>");
|
||||
}
|
||||
|
||||
// Add a shutdown hook
|
||||
Runtime.getRuntime().addShutdownHook(new Cleaner());
|
||||
|
||||
File dir = new File(args[0]);
|
||||
file = new File(dir, args[1]);
|
||||
// write to file
|
||||
System.out.println("writing to "+ file);
|
||||
PrintWriter pw = new PrintWriter(file);
|
||||
pw.println("Shutdown begins");
|
||||
pw.close();
|
||||
}
|
||||
|
||||
public static class Cleaner extends Thread {
|
||||
public void run() {
|
||||
// register the Console's shutdown hook while the application
|
||||
// shutdown hook is running
|
||||
Console cons = System.console();
|
||||
// register the DeleteOnExitHook while the application
|
||||
// shutdown hook is running
|
||||
file.deleteOnExit();
|
||||
try {
|
||||
PrintWriter pw = new PrintWriter(file);
|
||||
pw.println("file is being deleted");
|
||||
pw.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
57
jdk/test/java/lang/Runtime/shutdown/ShutdownHooks.sh
Normal file
57
jdk/test/java/lang/Runtime/shutdown/ShutdownHooks.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
# @test
|
||||
# @bug 6829503
|
||||
# @summary 1) Test Console and DeleteOnExitHook can be initialized
|
||||
# while shutdown is in progress
|
||||
# 2) Test if files that are added by the application shutdown
|
||||
# hook are deleted on exit during shutdown
|
||||
#
|
||||
# @build ShutdownHooks
|
||||
# @run shell ShutdownHooks.sh
|
||||
|
||||
if [ "${TESTJAVA}" = "" ]
|
||||
then
|
||||
echo "TESTJAVA not set. Test cannot execute. Failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILENAME=fileToBeDeleted
|
||||
rm -f ${TESTCLASSES}/${FILENAME}
|
||||
|
||||
# create the file to be deleted on exit
|
||||
echo "testing shutdown" > ${TESTCLASSES}/${FILENAME}
|
||||
|
||||
${TESTJAVA}/bin/java ${TESTVMOPTS} -classpath ${TESTCLASSES} ShutdownHooks ${TESTCLASSES} $FILENAME
|
||||
if [ $? != 0 ] ; then
|
||||
echo "Test Failed"; exit 1
|
||||
fi
|
||||
|
||||
if [ -f ${TESTCLASSES}/${FILENAME} ]; then
|
||||
echo "Test Failed: ${TESTCLASSES}/${FILENAME} not deleted"; exit 2
|
||||
fi
|
||||
echo "ShutdownHooks test passed.";
|
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 6834246
|
||||
* @summary Stress test connections through the loopback interface
|
||||
*/
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.net.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Random;
|
||||
import java.io.IOException;
|
||||
|
||||
public class StressLoopback {
|
||||
static final Random rand = new Random();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// setup listener
|
||||
AsynchronousServerSocketChannel listener =
|
||||
AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0));
|
||||
int port =((InetSocketAddress)(listener.getLocalAddress())).getPort();
|
||||
InetAddress lh = InetAddress.getLocalHost();
|
||||
SocketAddress remote = new InetSocketAddress(lh, port);
|
||||
|
||||
// create sources and sinks
|
||||
int count = 2 + rand.nextInt(9);
|
||||
Source[] source = new Source[count];
|
||||
Sink[] sink = new Sink[count];
|
||||
for (int i=0; i<count; i++) {
|
||||
AsynchronousSocketChannel ch = AsynchronousSocketChannel.open();
|
||||
ch.connect(remote).get();
|
||||
source[i] = new Source(ch);
|
||||
sink[i] = new Sink(listener.accept().get());
|
||||
}
|
||||
|
||||
// start the sinks and sources
|
||||
for (int i=0; i<count; i++) {
|
||||
sink[i].start();
|
||||
source[i].start();
|
||||
}
|
||||
|
||||
// let the test run for a while
|
||||
Thread.sleep(20*1000);
|
||||
|
||||
// wait until everyone is done
|
||||
boolean failed = false;
|
||||
long total = 0L;
|
||||
for (int i=0; i<count; i++) {
|
||||
long nwrote = source[i].finish();
|
||||
long nread = sink[i].finish();
|
||||
if (nread != nwrote)
|
||||
failed = true;
|
||||
System.out.format("%d -> %d (%s)\n",
|
||||
nwrote, nread, (failed) ? "FAIL" : "PASS");
|
||||
total += nwrote;
|
||||
}
|
||||
if (failed)
|
||||
throw new RuntimeException("Test failed - see log for details");
|
||||
System.out.format("Total sent %d MB\n", total / (1024L * 1024L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes bytes to a channel until "done". When done the channel is closed.
|
||||
*/
|
||||
static class Source {
|
||||
private final AsynchronousByteChannel channel;
|
||||
private final ByteBuffer sentBuffer;
|
||||
private volatile long bytesSent;
|
||||
private volatile boolean finished;
|
||||
|
||||
Source(AsynchronousByteChannel channel) {
|
||||
this.channel = channel;
|
||||
int size = 1024 + rand.nextInt(10000);
|
||||
this.sentBuffer = (rand.nextBoolean()) ?
|
||||
ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size);
|
||||
}
|
||||
|
||||
void start() {
|
||||
sentBuffer.position(0);
|
||||
sentBuffer.limit(sentBuffer.capacity());
|
||||
channel.write(sentBuffer, null, new CompletionHandler<Integer,Void> () {
|
||||
public void completed(Integer nwrote, Void att) {
|
||||
bytesSent += nwrote;
|
||||
if (finished) {
|
||||
closeUnchecked(channel);
|
||||
} else {
|
||||
sentBuffer.position(0);
|
||||
sentBuffer.limit(sentBuffer.capacity());
|
||||
channel.write(sentBuffer, null, this);
|
||||
}
|
||||
}
|
||||
public void failed(Throwable exc, Void att) {
|
||||
exc.printStackTrace();
|
||||
closeUnchecked(channel);
|
||||
}
|
||||
public void cancelled(Void att) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
long finish() {
|
||||
finished = true;
|
||||
waitUntilClosed(channel);
|
||||
return bytesSent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes from a channel until EOF is received.
|
||||
*/
|
||||
static class Sink {
|
||||
private final AsynchronousByteChannel channel;
|
||||
private final ByteBuffer readBuffer;
|
||||
private volatile long bytesRead;
|
||||
|
||||
Sink(AsynchronousByteChannel channel) {
|
||||
this.channel = channel;
|
||||
int size = 1024 + rand.nextInt(10000);
|
||||
this.readBuffer = (rand.nextBoolean()) ?
|
||||
ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size);
|
||||
}
|
||||
|
||||
void start() {
|
||||
channel.read(readBuffer, null, new CompletionHandler<Integer,Void> () {
|
||||
public void completed(Integer nread, Void att) {
|
||||
if (nread < 0) {
|
||||
closeUnchecked(channel);
|
||||
} else {
|
||||
bytesRead += nread;
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
}
|
||||
public void failed(Throwable exc, Void att) {
|
||||
exc.printStackTrace();
|
||||
closeUnchecked(channel);
|
||||
}
|
||||
public void cancelled(Void att) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
long finish() {
|
||||
waitUntilClosed(channel);
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
static void waitUntilClosed(Channel c) {
|
||||
while (c.isOpen()) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
static void closeUnchecked(Channel c) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user