Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1 @@
bbc1d241f632078e8ecf146e9398406c5719dd2f

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.io.File;
class ClassLoaderHelper {
private ClassLoaderHelper() {}
/**
* Returns an alternate path name for the given file
* such that if the original pathname did not exist, then the
* file may be located at the alternate location.
* For mac, this replaces the final .dylib suffix with .jnilib
*/
static File mapAlternativeName(File lib) {
if (ikvm.internal.Util.MACOSX) {
String name = lib.toString();
int index = name.toLowerCase().lastIndexOf(".dylib");
if (index < 0) {
return null;
}
return new File(name.substring(0, index) + ".jnilib");
} else {
return null;
}
}
}

View File

@@ -0,0 +1,293 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import cli.System.Runtime.Serialization.IObjectReference;
import cli.System.Runtime.Serialization.SerializationException;
import cli.System.Runtime.Serialization.SerializationInfo;
import cli.System.Runtime.Serialization.StreamingContext;
/**
* This is the common base class of all Java language enumeration types.
*
* More information about enums, including descriptions of the
* implicitly declared methods synthesized by the compiler, can be
* found in section 8.9 of
* <cite>The Java&trade; Language Specification</cite>.
*
* <p> Note that when using an enumeration type as the type of a set
* or as the type of the keys in a map, specialized and efficient
* {@linkplain java.util.EnumSet set} and {@linkplain
* java.util.EnumMap map} implementations are available.
*
* @param <E> The enum type subclass
* @author Josh Bloch
* @author Neal Gafter
* @see Class#getEnumConstants()
* @see java.util.EnumSet
* @see java.util.EnumMap
* @since 1.5
*/
@cli.System.SerializableAttribute.Annotation
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
/**
* The name of this enum constant, as declared in the enum declaration.
* Most programmers should use the {@link #toString} method rather than
* accessing this field.
*/
private final String name;
/**
* Returns the name of this enum constant, exactly as declared in its
* enum declaration.
*
* <b>Most programmers should use the {@link #toString} method in
* preference to this one, as the toString method may return
* a more user-friendly name.</b> This method is designed primarily for
* use in specialized situations where correctness depends on getting the
* exact name, which will not vary from release to release.
*
* @return the name of this enum constant
*/
public final String name() {
return name;
}
/**
* The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*
* Most programmers will have no use for this field. It is designed
* for use by sophisticated enum-based data structures, such as
* {@link java.util.EnumSet} and {@link java.util.EnumMap}.
*/
private final int ordinal;
/**
* Returns the ordinal of this enumeration constant (its position
* in its enum declaration, where the initial constant is assigned
* an ordinal of zero).
*
* Most programmers will have no use for this method. It is
* designed for use by sophisticated enum-based data structures, such
* as {@link java.util.EnumSet} and {@link java.util.EnumMap}.
*
* @return the ordinal of this enumeration constant
*/
public final int ordinal() {
return ordinal;
}
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
/**
* Returns the name of this enum constant, as contained in the
* declaration. This method may be overridden, though it typically
* isn't necessary or desirable. An enum type should override this
* method when a more "programmer-friendly" string form exists.
*
* @return the name of this enum constant
*/
public String toString() {
return name;
}
/**
* Returns true if the specified object is equal to this
* enum constant.
*
* @param other the object to be compared for equality with this object.
* @return true if the specified object is equal to this
* enum constant.
*/
public final boolean equals(Object other) {
return this==other;
}
/**
* Returns a hash code for this enum constant.
*
* @return a hash code for this enum constant.
*/
public final int hashCode() {
return super.hashCode();
}
/**
* Throws CloneNotSupportedException. This guarantees that enums
* are never cloned, which is necessary to preserve their "singleton"
* status.
*
* @return (never returns)
*/
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* Compares this enum with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* Enum constants are only comparable to other enum constants of the
* same enum type. The natural order implemented by this
* method is the order in which the constants are declared.
*/
public final int compareTo(E o) {
Enum other = (Enum)o;
Enum self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
/**
* Returns the Class object corresponding to this enum constant's
* enum type. Two enum constants e1 and e2 are of the
* same enum type if and only if
* e1.getDeclaringClass() == e2.getDeclaringClass().
* (The value returned by this method may differ from the one returned
* by the {@link Object#getClass} method for enum constants with
* constant-specific class bodies.)
*
* @return the Class object corresponding to this enum constant's
* enum type
*/
public final Class<E> getDeclaringClass() {
Class clazz = getClass();
Class zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? clazz : zuper;
}
/**
* Returns the enum constant of the specified enum type with the
* specified name. The name must match exactly an identifier used
* to declare an enum constant in this type. (Extraneous whitespace
* characters are not permitted.)
*
* <p>Note that for a particular enum type {@code T}, the
* implicitly declared {@code public static T valueOf(String)}
* method on that enum may be used instead of this method to map
* from a name to the corresponding enum constant. All the
* constants of an enum type can be obtained by calling the
* implicit {@code public static T[] values()} method of that
* type.
*
* @param <T> The enum type whose constant is to be returned
* @param enumType the {@code Class} object of the enum type from which
* to return a constant
* @param name the name of the constant to return
* @return the enum constant of the specified enum type with the
* specified name
* @throws IllegalArgumentException if the specified enum type has
* no constant with the specified name, or the specified
* class object does not represent an enum type
* @throws NullPointerException if {@code enumType} or {@code name}
* is null
* @since 1.5
*/
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
/**
* enum classes cannot have finalize methods.
*/
protected final void finalize() { }
/**
* prevent default deserialization
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
// [IKVM] .NET serialization support starts here
// Note that we don't have a security demand, because the info is harmless.
@cli.IKVM.Attributes.HideFromJavaAttribute.Annotation
@cli.System.Security.SecurityCriticalAttribute.Annotation
public final void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("enumType", getDeclaringClass());
info.AddValue("name", name);
info.SetType(ikvm.runtime.Util.getInstanceTypeFromClass(EnumSerializationProxy.class));
}
}
@cli.System.SerializableAttribute.Annotation
final class EnumSerializationProxy implements IObjectReference
{
private Class enumType;
private String name;
@cli.System.Security.SecurityCriticalAttribute.Annotation
public Object GetRealObject(StreamingContext context)
{
try
{
return Enum.valueOf(enumType, name);
}
catch (IllegalArgumentException x)
{
ikvm.runtime.Util.throwException(new SerializationException("Enum value " + name + " not found in " + enumType, x));
return null;
}
}
}

View File

@@ -0,0 +1,122 @@
/*
Copyright (C) 2007-2011 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
package java.lang;
import ikvm.runtime.AssemblyClassLoader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.util.Enumeration;
import java.util.Map;
import sun.nio.ch.Interruptible;
import sun.reflect.annotation.AnnotationType;
import sun.security.action.GetPropertyAction;
@ikvm.lang.Internal
public class LangHelper
{
private static boolean addedSystemPackages;
private static void addSystemPackage(Map pkgMap)
{
// NOTE caller must have acquired lock on pkgMap
if (!addedSystemPackages)
{
addedSystemPackages = true;
String[] pkgs = getBootClassPackages();
for (int i = 0; i < pkgs.length; i++)
{
pkgMap.put(pkgs[i],
new Package(pkgs[i],
VMSystemProperties.SPEC_TITLE, // specTitle
VMSystemProperties.SPEC_VERSION, // specVersion
VMSystemProperties.SPEC_VENDOR, // specVendor
"IKVM.NET OpenJDK", // implTitle
PropertyConstants.openjdk_version, // implVersion
"Oracle Corporation & others", // implVendor
null, // sealBase
null)); // class loader
}
}
}
private static native String[] getBootClassPackages();
/* this method gets called by Package.getSystemPackage() via a redefined method in map.xml */
static Package getSystemPackage(Map pkgs, String name)
{
synchronized (pkgs)
{
addSystemPackage(pkgs);
return (Package)pkgs.get(name);
}
}
/* this method gets called by Package.getSystemPackages() via a redefined method in map.xml */
static Package[] getSystemPackages(Map pkgs)
{
synchronized (pkgs)
{
addSystemPackage(pkgs);
return (Package[])pkgs.values().toArray(new Package[pkgs.size()]);
}
}
public static sun.misc.JavaLangAccess getJavaLangAccess()
{
return new sun.misc.JavaLangAccess() {
public sun.reflect.ConstantPool getConstantPool(Class klass) {
return null;
}
public void setAnnotationType(Class klass, AnnotationType type) {
klass.setAnnotationType(type);
}
public AnnotationType getAnnotationType(Class klass) {
return klass.getAnnotationType();
}
public <E extends Enum<E>>
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();
}
public void blockedOn(Thread t, Interruptible b) {
t.blockedOn(b);
}
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
public int getStackTraceDepth(Throwable t) {
return t.getStackTraceDepth();
}
public StackTraceElement getStackTraceElement(Throwable t, int i) {
return t.getStackTraceElement(i);
}
public int getStringHash32(String string) {
return StringHelper.hash32(string);
}
};
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
/*
Copyright (C) 2007, 2009 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
package java.lang;
interface PropertyConstants
{
String awt_toolkit = "ikvm.awt.NetToolkit, @AWTASSEMBLY@";
String java_awt_graphicsenv = "ikvm.awt.NetGraphicsEnvironment, @AWTASSEMBLY@";
String java_vm_version = "@VERSION@";
String java_runtime_version = "@VERSION@";
String openjdk_version = "@OPENJDK_VERSION@";
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright (c) 1999, 2005, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import cli.System.AppDomain;
import cli.System.EventArgs;
import cli.System.EventHandler;
import cli.System.Threading.Monitor;
/**
* Package-private utility class containing data structures and logic
* governing the virtual-machine shutdown sequence.
*
* @author Mark Reinhold
* @since 1.3
*/
class Shutdown {
/* Shutdown state */
private static final int RUNNING = 0;
private static final int HOOKS = 1;
private static final int FINALIZERS = 2;
private static int state = RUNNING;
/* Should we run all finalizers upon exit? */
static volatile boolean runFinalizersOnExit = false;
// The system shutdown hooks are registered with a predefined slot.
// The list of shutdown hooks is as follows:
// (0) Console restore hook
// (1) Application hooks
// (2) DeleteOnExit hook
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;
// [IKVM] have we registered the AppDomain.ProcessExit event handler?
private static boolean registeredProcessExit;
/* The preceding static fields are protected by this lock */
private static class Lock { };
private static Object lock = new Lock();
/* Lock object for the native halt method */
private static Object haltLock = new Lock();
/* Invoked by Runtime.runFinalizersOnExit */
static void setRunFinalizersOnExit(boolean run) {
synchronized (lock) {
runFinalizersOnExit = run;
}
}
private static void registerProcessExit() {
try {
// MONOBUG Mono doesn't support starting a new thread during ProcessExit
// (and application shutdown hooks are based on threads)
// see https://bugzilla.xamarin.com/show_bug.cgi?id=5650
if (!ikvm.internal.Util.MONO) {
// AppDomain.ProcessExit has a LinkDemand, so we have to have a separate method
registerShutdownHook();
if (false) throw new cli.System.Security.SecurityException();
}
}
catch (cli.System.Security.SecurityException _) {
}
}
private static void registerShutdownHook()
{
AppDomain.get_CurrentDomain().add_ProcessExit(new EventHandler(new EventHandler.Method() {
public void Invoke(Object sender, EventArgs e) {
shutdown();
}
}));
}
/**
* 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, boolean registerShutdownInProgress, Runnable hook) {
synchronized (lock) {
if (hooks[slot] != null)
throw new InternalError("Shutdown hook at slot " + slot + " already registered");
if (!registerShutdownInProgress) {
if (state > RUNNING)
throw new IllegalStateException("Shutdown in progress");
} else {
if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
throw new IllegalStateException("Shutdown in progress");
}
if (!registeredProcessExit) {
registeredProcessExit = true;
registerProcessExit();
}
hooks[slot] = hook;
}
}
/* Run all registered shutdown hooks
*/
private static void runHooks() {
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) {
ThreadDeath td = (ThreadDeath)t;
throw td;
}
}
}
}
/* The halt method is synchronized on the halt lock
* to avoid corruption of the delete-on-shutdown file list.
* It invokes the true native halt method.
*/
static void halt(int status) {
synchronized (haltLock) {
halt0(status);
}
}
static void halt0(int status) {
cli.System.Environment.Exit(status);
}
/* Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers */
private static void runAllFinalizers() { /* [IKVM] Don't need to do anything here */ }
/* The actual shutdown sequence is defined here.
*
* If it weren't for runFinalizersOnExit, this would be simple -- we'd just
* run the hooks and then halt. Instead we need to keep track of whether
* we're running hooks or finalizers. In the latter case a finalizer could
* invoke exit(1) to cause immediate termination, while in the former case
* any further invocations of exit(n), for any n, simply stall. Note that
* if on-exit finalizers are enabled they're run iff the shutdown is
* initiated by an exit(0); they're never run on exit(n) for n != 0 or in
* response to SIGINT, SIGTERM, etc.
*/
private static void sequence() {
synchronized (lock) {
/* Guard against the possibility of a daemon thread invoking exit
* after DestroyJavaVM initiates the shutdown sequence
*/
if (state != HOOKS) return;
}
runHooks();
boolean rfoe;
synchronized (lock) {
state = FINALIZERS;
rfoe = runFinalizersOnExit;
}
if (rfoe) runAllFinalizers();
}
/* Invoked by Runtime.exit, which does all the security checks.
* Also invoked by handlers for system-provided termination events,
* which should pass a nonzero status code.
*/
static void exit(int status) {
boolean runMoreFinalizers = false;
synchronized (lock) {
if (status != 0) runFinalizersOnExit = false;
switch (state) {
case RUNNING: /* Initiate shutdown */
state = HOOKS;
break;
case HOOKS: /* Stall and halt */
break;
case FINALIZERS:
if (status != 0) {
/* Halt immediately on nonzero status */
halt(status);
} else {
/* Compatibility with old behavior:
* Run more finalizers and then halt
*/
runMoreFinalizers = runFinalizersOnExit;
}
break;
}
}
if (runMoreFinalizers) {
runAllFinalizers();
halt(status);
}
synchronized (Shutdown.class) {
/* Synchronize on the class object, causing any other thread
* that attempts to initiate shutdown to stall indefinitely
*/
sequence();
halt(status);
}
}
/* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon
* thread has finished. Unlike the exit method, this method does not
* actually halt the VM.
*/
static void shutdown() {
synchronized (lock) {
switch (state) {
case RUNNING: /* Initiate shutdown */
state = HOOKS;
break;
case HOOKS: /* Stall and then return */
case FINALIZERS:
break;
}
}
// [IKVM] We don't block here, because we're being called
// from the AppDomain.ProcessExit event and we don't want to
// deadlock with the thread that called exit.
// Note that our JNI DestroyJavaVM implementation doesn't
// call this method.
if (Monitor.TryEnter(Shutdown.class)) {
try {
sequence();
} finally {
Monitor.Exit(Shutdown.class);
}
}
}
}

View File

@@ -0,0 +1 @@
98f7816c72a28907d154c1848447b1fda97ef6ca

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,345 @@
/*
* Copyright (c) 1994, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.io.*;
import java.util.*;
final class ThrowableHelper {
/**
* Holder class to defer initializing sentinel objects only used
* for serialization.
*/
private static class SentinelHolder {
/**
* {@linkplain #setStackTrace(StackTraceElement[]) Setting the
* stack trace} to a one-element array containing this sentinel
* value indicates future attempts to set the stack trace will be
* ignored. The sentinal is equal to the result of calling:<br>
* {@code new StackTraceElement("", "", null, Integer.MIN_VALUE)}
*/
public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL =
new StackTraceElement("", "", null, Integer.MIN_VALUE);
/**
* Sentinel value used in the serial form to indicate an immutable
* stack trace.
*/
public static final StackTraceElement[] STACK_TRACE_SENTINEL =
new StackTraceElement[] {STACK_TRACE_ELEMENT_SENTINEL};
}
/** Caption for labeling causative exception stack traces */
private static final String CAUSE_CAPTION = "Caused by: ";
/** Caption for labeling suppressed exception stack traces */
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
/**
* Prints this throwable and its backtrace to the
* standard error stream. This method prints a stack trace for this
* {@code Throwable} object on the error output stream that is
* the value of the field {@code System.err}. The first line of
* output contains the result of the {@link #toString()} method for
* this object. Remaining lines represent data previously recorded by
* the method {@link #fillInStackTrace()}. The format of this
* information depends on the implementation, but the following
* example may be regarded as typical:
* <blockquote><pre>
* java.lang.NullPointerException
* at MyClass.mash(MyClass.java:9)
* at MyClass.crunch(MyClass.java:6)
* at MyClass.main(MyClass.java:3)
* </pre></blockquote>
* This example was produced by running the program:
* <pre>
* class MyClass {
* public static void main(String[] args) {
* crunch(null);
* }
* static void crunch(int[] a) {
* mash(a);
* }
* static void mash(int[] b) {
* System.out.println(b[0]);
* }
* }
* </pre>
* The backtrace for a throwable with an initialized, non-null cause
* should generally include the backtrace for the cause. The format
* of this information depends on the implementation, but the following
* example may be regarded as typical:
* <pre>
* HighLevelException: MidLevelException: LowLevelException
* at Junk.a(Junk.java:13)
* at Junk.main(Junk.java:4)
* Caused by: MidLevelException: LowLevelException
* at Junk.c(Junk.java:23)
* at Junk.b(Junk.java:17)
* at Junk.a(Junk.java:11)
* ... 1 more
* Caused by: LowLevelException
* at Junk.e(Junk.java:30)
* at Junk.d(Junk.java:27)
* at Junk.c(Junk.java:21)
* ... 3 more
* </pre>
* Note the presence of lines containing the characters {@code "..."}.
* These lines indicate that the remainder of the stack trace for this
* exception matches the indicated number of frames from the bottom of the
* stack trace of the exception that was caused by this exception (the
* "enclosing" exception). This shorthand can greatly reduce the length
* of the output in the common case where a wrapped exception is thrown
* from same method as the "causative exception" is caught. The above
* example was produced by running the program:
* <pre>
* public class Junk {
* public static void main(String args[]) {
* try {
* a();
* } catch(HighLevelException e) {
* e.printStackTrace();
* }
* }
* static void a() throws HighLevelException {
* try {
* b();
* } catch(MidLevelException e) {
* throw new HighLevelException(e);
* }
* }
* static void b() throws MidLevelException {
* c();
* }
* static void c() throws MidLevelException {
* try {
* d();
* } catch(LowLevelException e) {
* throw new MidLevelException(e);
* }
* }
* static void d() throws LowLevelException {
* e();
* }
* static void e() throws LowLevelException {
* throw new LowLevelException();
* }
* }
*
* class HighLevelException extends Exception {
* HighLevelException(Throwable cause) { super(cause); }
* }
*
* class MidLevelException extends Exception {
* MidLevelException(Throwable cause) { super(cause); }
* }
*
* class LowLevelException extends Exception {
* }
* </pre>
* As of release 7, the platform supports the notion of
* <i>suppressed exceptions</i> (in conjunction with the {@code
* try}-with-resources statement). Any exceptions that were
* suppressed in order to deliver an exception are printed out
* beneath the stack trace. The format of this information
* depends on the implementation, but the following example may be
* regarded as typical:
*
* <pre>
* Exception in thread "main" java.lang.Exception: Something happened
* at Foo.bar(Foo.java:10)
* at Foo.main(Foo.java:5)
* Suppressed: Resource$CloseFailException: Resource ID = 0
* at Resource.close(Resource.java:26)
* at Foo.bar(Foo.java:9)
* ... 1 more
* </pre>
* Note that the "... n more" notation is used on suppressed exceptions
* just at it is used on causes. Unlike causes, suppressed exceptions are
* indented beyond their "containing exceptions."
*
* <p>An exception can have both a cause and one or more suppressed
* exceptions:
* <pre>
* Exception in thread "main" java.lang.Exception: Main block
* at Foo3.main(Foo3.java:7)
* Suppressed: Resource$CloseFailException: Resource ID = 2
* at Resource.close(Resource.java:26)
* at Foo3.main(Foo3.java:5)
* Suppressed: Resource$CloseFailException: Resource ID = 1
* at Resource.close(Resource.java:26)
* at Foo3.main(Foo3.java:5)
* Caused by: java.lang.Exception: I did it
* at Foo3.main(Foo3.java:8)
* </pre>
* Likewise, a suppressed exception can have a cause:
* <pre>
* Exception in thread "main" java.lang.Exception: Main block
* at Foo4.main(Foo4.java:6)
* Suppressed: Resource2$CloseFailException: Resource ID = 1
* at Resource2.close(Resource2.java:20)
* at Foo4.main(Foo4.java:5)
* Caused by: java.lang.Exception: Rats, you caught me
* at Resource2$CloseFailException.<init>(Resource2.java:45)
* ... 2 more
* </pre>
*/
public static void printStackTrace(Throwable _this) {
_this.printStackTrace(System.err);
}
/**
* Prints this throwable and its backtrace to the specified print stream.
*
* @param s {@code PrintStream} to use for output
*/
public static void printStackTrace(Throwable _this, PrintStream s) {
printStackTrace(_this, new WrappedPrintStream(s));
}
private static void printStackTrace(Throwable _this, PrintStreamOrWriter s) {
// Guard against malicious overrides of Throwable.equals by
// using a Set with identity equality semantics.
Set<Throwable> dejaVu =
Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
dejaVu.add(_this);
synchronized (s.lock()) {
// Print our stack trace
s.println(_this);
StackTraceElement[] trace = getOurStackTrace(_this);
for (StackTraceElement traceElement : trace)
s.println("\tat " + traceElement);
// Print suppressed exceptions, if any
for (Throwable se : _this.getSuppressed())
printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
// Print cause, if any
Throwable ourCause = _this.getCause();
if (ourCause != null)
printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, "", dejaVu);
}
}
/**
* Print our stack trace as an enclosed exception for the specified
* stack trace.
*/
private static void printEnclosedStackTrace(Throwable _this, PrintStreamOrWriter s,
StackTraceElement[] enclosingTrace,
String caption,
String prefix,
Set<Throwable> dejaVu) {
assert Thread.holdsLock(s.lock());
if (dejaVu.contains(_this)) {
s.println("\t[CIRCULAR REFERENCE:" + _this + "]");
} else {
dejaVu.add(_this);
// Compute number of frames in common between this and enclosing trace
StackTraceElement[] trace = getOurStackTrace(_this);
int m = trace.length - 1;
int n = enclosingTrace.length - 1;
while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
m--; n--;
}
int framesInCommon = trace.length - 1 - m;
// Print our stack trace
s.println(prefix + caption + _this);
for (int i = 0; i <= m; i++)
s.println(prefix + "\tat " + trace[i]);
if (framesInCommon != 0)
s.println(prefix + "\t... " + framesInCommon + " more");
// Print suppressed exceptions, if any
for (Throwable se : _this.getSuppressed())
printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION,
prefix +"\t", dejaVu);
// Print cause, if any
Throwable ourCause = _this.getCause();
if (ourCause != null)
printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, prefix, dejaVu);
}
}
private static native StackTraceElement[] getOurStackTrace(Throwable _this);
/**
* Prints this throwable and its backtrace to the specified
* print writer.
*
* @param s {@code PrintWriter} to use for output
* @since JDK1.1
*/
public static void printStackTrace(Throwable _this, PrintWriter s) {
printStackTrace(_this, new WrappedPrintWriter(s));
}
/**
* Wrapper class for PrintStream and PrintWriter to enable a single
* implementation of printStackTrace.
*/
private abstract static class PrintStreamOrWriter {
/** Returns the object to be locked when using this StreamOrWriter */
abstract Object lock();
/** Prints the specified string as a line on this StreamOrWriter */
abstract void println(Object o);
}
private static class WrappedPrintStream extends PrintStreamOrWriter {
private final PrintStream printStream;
WrappedPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
Object lock() {
return printStream;
}
void println(Object o) {
printStream.println(o);
}
}
private static class WrappedPrintWriter extends PrintStreamOrWriter {
private final PrintWriter printWriter;
WrappedPrintWriter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
Object lock() {
return printWriter;
}
void println(Object o) {
printWriter.println(o);
}
}
}

View File

@@ -0,0 +1,421 @@
/*
Copyright (C) 2004-2011 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
package java.lang;
import java.util.Properties;
import static ikvm.internal.Util.SafeGetEnvironmentVariable;
final class VMSystemProperties
{
private VMSystemProperties() { }
public static final String SPEC_TITLE = "Java Platform API Specification";
public static final String SPEC_VERSION = "1.7";
public static final String SPEC_VENDOR = "Oracle Corporation";
private static String getLibraryPath()
{
String libraryPath;
if(ikvm.internal.Util.WINDOWS)
{
// see /hotspot/src/os/windows/vm/os_windows.cpp for the comment that describes how we build the path
String windir = SafeGetEnvironmentVariable("SystemRoot");
if(windir != null)
{
libraryPath = cli.System.IO.Path.PathSeparator + windir + "\\Sun\\Java\\bin";
}
else
{
libraryPath = null;
}
try
{
if(false) throw new cli.System.Security.SecurityException();
if (libraryPath == null)
{
libraryPath = cli.System.Environment.get_SystemDirectory();
}
else
{
libraryPath += cli.System.IO.Path.PathSeparator + cli.System.Environment.get_SystemDirectory();
}
}
catch(cli.System.Security.SecurityException _)
{
}
if(windir != null)
{
libraryPath += cli.System.IO.Path.PathSeparator + windir;
}
String path = SafeGetEnvironmentVariable("PATH");
if(path != null)
{
libraryPath += cli.System.IO.Path.PathSeparator + path;
}
}
else if(ikvm.internal.Util.MACOSX)
{
libraryPath = ".";
}
else /* assume Linux, since that's the only other platform we support */
{
// on Linux we have some hardcoded paths (from /hotspot/src/os/linux/vm/os_linux.cpp)
// and we can only guess the cpu arch based on bitness (that means only x86 and x64)
String cpu_arch = cli.System.IntPtr.get_Size() == 4 ? "i386" : "amd64";
libraryPath = "/usr/java/packages/lib/" + cpu_arch + ":/lib:/usr/lib";
String ld_library_path = SafeGetEnvironmentVariable("LD_LIBRARY_PATH");
if(ld_library_path != null)
{
libraryPath = ld_library_path + ":" + libraryPath;
}
}
try
{
cli.System.Reflection.Assembly entryAsm = cli.System.Reflection.Assembly.GetEntryAssembly();
// If the CLR was started by a native app (e.g. via COM interop) there is no entry assembly
if (entryAsm != null)
{
// the application (or launcher) directory is prepended to the library path
// (similar to how the JDK prepends its directory to the path)
libraryPath = new cli.System.IO.FileInfo(entryAsm.get_Location()).get_DirectoryName() + cli.System.IO.Path.PathSeparator + libraryPath;
}
}
catch(Throwable _)
{
// ignore
}
if(ikvm.internal.Util.WINDOWS)
{
libraryPath += cli.System.IO.Path.PathSeparator + ".";
}
return libraryPath;
}
private static void initCommonProperties(Properties p)
{
p.setProperty("java.version", "1.7.0");
p.setProperty("java.vendor", "Jeroen Frijters");
p.setProperty("java.vendor.url", "http://ikvm.net/");
p.setProperty("java.vendor.url.bug", "http://www.ikvm.net/bugs");
p.setProperty("java.vm.specification.version", "1.7");
p.setProperty("java.vm.specification.vendor", "Oracle Corporation");
p.setProperty("java.vm.specification.name", "Java Virtual Machine Specification");
p.setProperty("java.vm.version", PropertyConstants.java_vm_version);
p.setProperty("java.vm.vendor", "Jeroen Frijters");
p.setProperty("java.vm.name", "IKVM.NET");
p.setProperty("java.runtime.name", "IKVM.NET");
p.setProperty("java.runtime.version", PropertyConstants.java_runtime_version);
p.setProperty("java.specification.version", SPEC_VERSION);
p.setProperty("java.specification.vendor", SPEC_VENDOR);
p.setProperty("java.specification.name", SPEC_TITLE);
p.setProperty("java.class.version", "51.0");
p.setProperty("java.class.path", "");
p.setProperty("java.library.path", getLibraryPath());
try
{
if(false) throw new cli.System.Security.SecurityException();
p.setProperty("java.io.tmpdir", cli.System.IO.Path.GetTempPath());
}
catch(cli.System.Security.SecurityException _)
{
// TODO should we set another value?
p.setProperty("java.io.tmpdir", ".");
}
p.setProperty("java.ext.dirs", "");
// NOTE os.name *must* contain "Windows" when running on Windows, because Classpath tests on that
String osname = null;
String osver = null;
cli.System.OperatingSystem os = cli.System.Environment.get_OSVersion();
int major = os.get_Version().get_Major();
int minor = os.get_Version().get_Minor();
switch(os.get_Platform().Value)
{
case cli.System.PlatformID.Win32NT:
osname = "Windows NT (unknown)";
switch(major)
{
case 3:
case 4:
osver = major + "." + minor;
osname = "Windows NT";
break;
case 5:
switch(minor)
{
case 0:
osver = "5.0";
osname = "Windows 2000";
break;
case 1:
osver = "5.1";
osname = "Windows XP";
break;
case 2:
osver = "5.2";
osname = "Windows 2003";
break;
}
break;
case 6:
// since there appears to be no managed way to differentiate between Client/Server, we report client names
switch(minor)
{
case 0:
osver = "6.0";
osname = "Windows Vista";
break;
case 1:
osver = "6.1";
osname = "Windows 7";
break;
}
break;
}
break;
case cli.System.PlatformID.Win32Windows:
if(major == 4)
{
switch(minor)
{
case 0:
osver = "4.0";
osname = "Windows 95";
break;
case 10:
osver = "4.10";
osname = "Windows 98";
break;
case 90:
osver = "4.90";
osname = "Windows Me";
break;
}
}
break;
case cli.System.PlatformID.Unix:
if(ikvm.internal.Util.MACOSX)
{
// for back compat Mono will return PlatformID.Unix when running on the Mac,
// so we handle that explicitly here
osname = "Mac OS X";
// HACK this tries to map the Darwin version to the OS X version
// (based on http://en.wikipedia.org/wiki/Darwin_(operating_system)#Releases)
cli.System.Version ver = cli.System.Environment.get_OSVersion().get_Version();
osver = "10." + (ver.get_Major() - 4) + "." + ver.get_Minor();
}
break;
}
if(osname == null)
{
osname = cli.System.Environment.get_OSVersion().ToString();
}
if(osver == null)
{
osver = major + "." + minor;
}
p.setProperty("os.name", osname);
p.setProperty("os.version", osver);
String arch = SafeGetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
if(arch == null)
{
// we don't know, so we make a guess
if(cli.System.IntPtr.get_Size() == 4)
{
arch = ikvm.internal.Util.WINDOWS ? "x86" : "i386";
}
else
{
arch = "amd64";
}
}
if(arch.equals("AMD64"))
{
arch = "amd64";
}
p.setProperty("os.arch", arch);
p.setProperty("sun.arch.data.model", "" + (cli.System.IntPtr.get_Size() * 8));
p.setProperty("file.separator", "" + cli.System.IO.Path.DirectorySeparatorChar);
p.setProperty("file.encoding", cli.System.Text.Encoding.get_Default().get_WebName());
p.setProperty("path.separator", "" + cli.System.IO.Path.PathSeparator);
p.setProperty("line.separator", cli.System.Environment.get_NewLine());
try
{
if(false) throw new cli.System.Security.SecurityException();
p.setProperty("user.name", cli.System.Environment.get_UserName());
}
catch(cli.System.Security.SecurityException _)
{
p.setProperty("user.name", "(unknown)");
}
String home = SafeGetEnvironmentVariable("USERPROFILE");
if(home == null)
{
// maybe we're on *nix
home = SafeGetEnvironmentVariable("HOME");
if(home == null)
{
// TODO maybe there is a better way
// NOTE on MS .NET this doesn't return the correct path
// (it returns "C:\\Documents and Settings\\username\\My Documents", but we really need
// "C:\\Documents and Settings\\username" to be compatible with Sun, that's why we use %USERPROFILE% if it exists)
try
{
if(false) throw new cli.System.Security.SecurityException();
home = cli.System.Environment.GetFolderPath(cli.System.Environment.SpecialFolder.wrap(cli.System.Environment.SpecialFolder.Personal));
}
catch(cli.System.Security.SecurityException _)
{
home = ".";
}
}
}
p.setProperty("user.home", home);
try
{
if(false) throw new cli.System.Security.SecurityException();
p.setProperty("user.dir", cli.System.Environment.get_CurrentDirectory());
}
catch(cli.System.Security.SecurityException _)
{
p.setProperty("user.dir", ".");
}
p.setProperty("awt.toolkit", PropertyConstants.awt_toolkit);
}
public static void initProperties(Properties p)
{
p.setProperty("openjdk.version", PropertyConstants.openjdk_version);
String vfsroot = getVirtualFileSystemRoot();
p.setProperty("java.home", vfsroot.substring(0, vfsroot.length() - 1));
// the %home%\lib\endorsed directory does not exist, but neither does it on JDK 1.7
p.setProperty("java.endorsed.dirs", vfsroot + "lib" + cli.System.IO.Path.DirectorySeparatorChar + "endorsed");
p.setProperty("sun.boot.library.path", vfsroot + "bin");
p.setProperty("sun.boot.class.path", getBootClassPath());
initCommonProperties(p);
setupI18N(p);
p.setProperty("sun.cpu.endian", cli.System.BitConverter.IsLittleEndian ? "little" : "big");
p.setProperty("file.encoding.pkg", "sun.io");
p.setProperty("user.timezone", "");
p.setProperty("sun.os.patch.level", cli.System.Environment.get_OSVersion().get_ServicePack());
p.setProperty("java.vm.info", "compiled mode");
p.setProperty("sun.nio.MaxDirectMemorySize", "-1");
p.setProperty("java.awt.graphicsenv", PropertyConstants.java_awt_graphicsenv);
p.setProperty("java.awt.printerjob", "sun.awt.windows.WPrinterJob");
// TODO
// sun.cpu.isalist:=pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86
// sun.desktop:=windows
// sun.io.unicode.encoding:=UnicodeLittle
// sun.jnu.encoding:=Cp1252
// sun.management.compiler:=HotSpot Client Compiler
try
{
// read properties from app.config
if(false) throw new cli.System.Configuration.ConfigurationException();
cli.System.Collections.Specialized.NameValueCollection appSettings = cli.System.Configuration.ConfigurationSettings.get_AppSettings();
cli.System.Collections.IEnumerator keys = appSettings.GetEnumerator();
while(keys.MoveNext())
{
String key = (String)keys.get_Current();
if(key.startsWith("ikvm:"))
{
p.setProperty(key.substring(5), appSettings.get_Item(key));
}
}
}
catch(cli.System.Configuration.ConfigurationException _)
{
// app.config is invalid, ignore
}
// set the properties that were specified with ikvm.runtime.Startup.setProperties()
cli.System.Collections.IDictionary props = ikvm.runtime.Startup.props;
if(props != null)
{
cli.System.Collections.IDictionaryEnumerator entries = props.GetEnumerator();
while(entries.MoveNext())
{
p.setProperty((String)entries.get_Key(), (String)entries.get_Value());
}
}
}
private static void setupI18N(Properties p)
{
String[] culture = ((cli.System.String)(Object)cli.System.Globalization.CultureInfo.get_CurrentCulture().get_Name()).Split(new char[] { '-' });
String language;
String script;
String region;
String variant;
if (culture.length == 2)
{
language = culture[0];
if (culture[1].length() == 4)
{
script = culture[1];
region = "";
}
else
{
script = "";
region = culture[1];
}
}
else if (culture.length == 3)
{
language = culture[0];
script = culture[1];
region = culture[2];
}
else
{
language = "en";
script = "";
region = "US";
}
// Norwegian
if (language.equals("nb"))
{
language = "no";
region = "NO";
variant = "";
}
else if (language.equals("nn"))
{
language = "no";
region = "NO";
variant = "NY";
}
else
{
variant = "";
}
p.setProperty("user.language", language);
p.setProperty("user.country", region);
p.setProperty("user.variant", variant);
p.setProperty("user.script", script);
}
private static native String getVirtualFileSystemRoot();
private static native String getBootClassPath();
}

View File

@@ -0,0 +1,477 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Extensively modified for IKVM.NET by Jeroen Frijters
* Copyright (C) 2011 Jeroen Frijters
*/
package java.lang.invoke;
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import sun.invoke.util.ValueConversions;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import static java.lang.invoke.MethodHandleStatics.*;
import ikvm.internal.NotYetImplementedError;
/**
* This method handle performs simple conversion or checking of a single argument.
* @author jrose
*/
class AdapterMethodHandle extends BoundMethodHandle {
AdapterMethodHandle(MethodType type) {
super(type, null, -1);
}
AdapterMethodHandle(MethodType type, Object vmtarget) {
super(type, null, -1);
this.vmtarget = vmtarget;
}
/** Can a JVM-level adapter directly implement the proposed
* argument conversion, as if by fixed-arity MethodHandle.asType?
*/
static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
return true;
}
/**
* 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.
* The argument conversions allowed are casting, boxing and unboxing,
* integral widening or narrowing, and floating point widening or narrowing.
* @param newType required call type
* @param target original method handle
* @param level which strength of conversion is allowed
* @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
*/
static native MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level);
/* 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 astart,
MethodType targetType, int tstart,
int nargs, boolean raw) {
assert(nargs >= 0);
int res = 0;
for (int i = 0; i < nargs; i++) {
Class<?> src = adapterType.parameterType(astart+i);
Class<?> dest = targetType.parameterType(tstart+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? */
static boolean canRetypeOnly(MethodType newType, MethodType targetType) {
return canRetype(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 canRetypeRaw(MethodType newType, MethodType targetType) {
return canRetype(newType, targetType, true);
}
static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) {
int diff = diffTypes(newType, targetType, raw);
// %%% This assert is too strong. Factor diff into VerifyType and reconcile.
assert(raw || (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.
*/
static MethodHandle makeRetypeOnly(MethodType newType, MethodHandle target) {
return makeRetype(newType, target, false);
}
static MethodHandle makeRetypeRaw(MethodType newType, MethodHandle target) {
return makeRetype(newType, target, true);
}
static native MethodHandle makeRetype(MethodType newType, MethodHandle target, boolean raw);
static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
MethodType type = target.type();
int last = type.parameterCount() - 1;
if (type.parameterType(last) != arrayType)
target = target.asType(type.changeParameterType(last, arrayType));
target = target.asFixedArity(); // make sure this attribute is turned off
return new AsVarargsCollector(target, arrayType);
}
static class AsVarargsCollector extends AdapterMethodHandle {
final MethodHandle target;
final Class<?> arrayType;
MethodHandle cache;
AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
super(target.type());
this.vmtarget = target.vmtarget;
this.target = target;
this.arrayType = arrayType;
this.cache = target.asCollector(arrayType, 0);
}
@Override
public boolean isVarargsCollector() {
return true;
}
@Override
public MethodHandle asFixedArity() {
return target;
}
@Override
public MethodHandle asType(MethodType newType) {
MethodType type = this.type();
int collectArg = type.parameterCount() - 1;
int newArity = newType.parameterCount();
if (newArity == collectArg+1 &&
type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
// if arity and trailing parameter are compatible, do normal thing
return super.asType(newType);
}
// check cache
if (cache.type().parameterCount() == newArity)
return cache.asType(newType);
// build and cache a collector
int arrayLength = newArity - collectArg;
MethodHandle collector;
try {
collector = target.asCollector(arrayType, arrayLength);
} catch (IllegalArgumentException ex) {
throw new WrongMethodTypeException("cannot build collector");
}
cache = collector;
return collector.asType(newType);
}
}
/** Can a checkcast adapter validly convert the target to newType?
* The JVM supports all kind of reference casts, even silly ones.
*/
static boolean canCheckCast(MethodType newType, MethodType targetType,
int arg, Class<?> castType) {
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) || (diff == 0); // arg is sole non-trivial diff
}
/** Can an primitive conversion adapter validly convert src to dst? */
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.
*/
static MethodHandle makeCheckCast(MethodType newType, MethodHandle target,
int arg, Class<?> castType) {
if (!canCheckCast(newType, target.type(), arg, castType))
return null;
throw new NotYetImplementedError();
}
/** Can an adapter simply drop arguments to convert the target to newType? */
static boolean canDropArguments(MethodType newType, MethodType targetType,
int dropArgPos, int dropArgCount) {
if (dropArgCount == 0)
return canRetypeOnly(newType, targetType);
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.
*/
static MethodHandle makeDropArguments(MethodType newType, MethodHandle target,
int dropArgPos, int dropArgCount) {
if (dropArgCount == 0)
return makeRetypeOnly(newType, target);
if (!canDropArguments(newType, target.type(), dropArgPos, dropArgCount))
return null;
int[] permute = new int[target.type().parameterCount()];
for (int i = 0, arg = 0; i < permute.length; i++) {
if (arg == dropArgPos)
arg += dropArgCount;
permute[i] = arg++;
}
return MethodHandleImpl.permuteArguments(target, newType, target.type(), permute);
}
/** Can an adapter duplicate an argument to convert the target to newType? */
static boolean canDupArguments(MethodType newType, MethodType targetType,
int dupArgPos, int dupArgCount) {
if (diffReturnTypes(newType, targetType, false) != 0)
return false;
int nptypes = newType.parameterCount();
if (dupArgCount < 0 || dupArgPos + dupArgCount > nptypes)
return false;
if (targetType.parameterCount() != nptypes + dupArgCount)
return false;
// parameter types must be the same up to the duplicated arguments
if (diffParamTypes(newType, 0, targetType, 0, nptypes, false) != 0)
return false;
// duplicated types must be, well, duplicates
if (diffParamTypes(newType, dupArgPos, targetType, nptypes, dupArgCount, false) != 0)
return false;
return true;
}
/** Factory method: Duplicate the selected argument.
* Return null if this is not possible.
*/
static MethodHandle makeDupArguments(MethodType newType, MethodHandle target,
int dupArgPos, int dupArgCount) {
if (!canDupArguments(newType, target.type(), dupArgPos, dupArgCount))
return null;
if (dupArgCount == 0)
return target;
// in arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... ]
// out arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... | dup... ]
throw new NotYetImplementedError();
}
/** Can an adapter swap two arguments to convert the target to newType? */
static boolean canSwapArguments(MethodType newType, MethodType targetType,
int swapArg1, int swapArg2) {
if (diffReturnTypes(newType, targetType, false) != 0)
return false;
if (swapArg1 >= swapArg2) return false; // caller resp
int nptypes = newType.parameterCount();
if (targetType.parameterCount() != nptypes)
return false;
if (swapArg1 < 0 || swapArg2 >= nptypes)
return false;
if (diffParamTypes(newType, 0, targetType, 0, swapArg1, false) != 0)
return false;
if (diffParamTypes(newType, swapArg1, targetType, swapArg2, 1, false) != 0)
return false;
if (diffParamTypes(newType, swapArg1+1, targetType, swapArg1+1, swapArg2-swapArg1-1, false) != 0)
return false;
if (diffParamTypes(newType, swapArg2, targetType, swapArg1, 1, false) != 0)
return false;
if (diffParamTypes(newType, swapArg2+1, targetType, swapArg2+1, nptypes-swapArg2-1, false) != 0)
return false;
return true;
}
/** Factory method: Swap the selected arguments.
* Return null if this is not possible.
*/
static MethodHandle makeSwapArguments(MethodType newType, MethodHandle target,
int swapArg1, int swapArg2) {
if (swapArg1 == swapArg2)
return target;
if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; }
if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2))
return null;
// in arglist: [0: ...keep1 | pos1: a1 | pos1+1: keep2... | pos2: a2 | pos2+1: keep3... ]
// out arglist: [0: ...keep1 | pos1: a2 | pos1+1: keep2... | pos2: a1 | pos2+1: keep3... ]
throw new NotYetImplementedError();
}
static int positiveRotation(int argCount, int rotateBy) {
assert(argCount > 0);
if (rotateBy >= 0) {
if (rotateBy < argCount)
return rotateBy;
return rotateBy % argCount;
} else if (rotateBy >= -argCount) {
return rotateBy + argCount;
} else {
return (-1-((-1-rotateBy) % argCount)) + argCount;
}
}
final static int MAX_ARG_ROTATION = 1;
/** Can an adapter rotate arguments to convert the target to newType? */
static boolean canRotateArguments(MethodType newType, MethodType targetType,
int firstArg, int argCount, int rotateBy) {
rotateBy = positiveRotation(argCount, rotateBy);
if (rotateBy == 0) return false; // no rotation
if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION)
return false; // too many argument positions
// Rotate incoming args right N to the out args, N in 1..(argCouunt-1).
if (diffReturnTypes(newType, targetType, false) != 0)
return false;
int nptypes = newType.parameterCount();
if (targetType.parameterCount() != nptypes)
return false;
if (firstArg < 0 || firstArg >= nptypes) return false;
int argLimit = firstArg + argCount;
if (argLimit > nptypes) return false;
if (diffParamTypes(newType, 0, targetType, 0, firstArg, false) != 0)
return false;
int newChunk1 = argCount - rotateBy, newChunk2 = rotateBy;
// swap new chunk1 with target chunk2
if (diffParamTypes(newType, firstArg, targetType, argLimit-newChunk1, newChunk1, false) != 0)
return false;
// swap new chunk2 with target chunk1
if (diffParamTypes(newType, firstArg+newChunk1, targetType, firstArg, newChunk2, false) != 0)
return false;
return true;
}
/** Factory method: Rotate the selected argument range.
* Return null if this is not possible.
*/
static MethodHandle makeRotateArguments(MethodType newType, MethodHandle target,
int firstArg, int argCount, int rotateBy) {
rotateBy = positiveRotation(argCount, rotateBy);
if (!canRotateArguments(newType, target.type(), firstArg, argCount, rotateBy))
return null;
throw new NotYetImplementedError();
}
/** Can an adapter spread an argument to convert the target to newType? */
static boolean canSpreadArguments(MethodType newType, MethodType targetType,
Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
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 - (spreadArgPos + 1);
if (spreadArgPos < 0 || spreadArgPos >= nptypes ||
spreadArgCount < 0 ||
targetType.parameterCount() != afterPos + afterCount)
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 || !canConvertArgument(src, dst, 1))
return false;
}
return true;
}
/** Factory method: Spread selected argument. */
static native MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target, Class<?> spreadArgType, int spreadArgPos, int spreadArgCount);
/** Can an adapter collect a series of arguments, replacing them by zero or one results? */
static boolean canCollectArguments(MethodType targetType,
MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
int collectArgCount = collectorType.parameterCount();
Class<?> rtype = collectorType.returnType();
assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
// [(Object)Object[], (Object[])Object[], 0, 1]
: Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
;
return true;
}
/** Factory method: Collect or filter selected argument(s). */
static native MethodHandle makeCollectArguments(MethodHandle target, MethodHandle collector, int collectArgPos, boolean retainOriginalArgs);
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Extensively modified for IKVM.NET by Jeroen Frijters
* Copyright (C) 2011 Jeroen Frijters
*/
package java.lang.invoke;
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import static java.lang.invoke.MethodHandleStatics.*;
/**
* 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
*/
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.
*/
/*non-public*/ BoundMethodHandle(DirectMethodHandle mh, Object argument) {
super(mh.type().dropParameterTypes(0, 1));
// check the type now, once for all:
this.argument = checkReferenceArgument(argument, mh, 0);
this.vmargslot = this.type().parameterSlotCount();
initTarget(mh, 0);
}
/** Insert an argument into an arbitrary method handle.
* If argnum is zero, inserts the first argument, etc.
* The argument type must be a reference.
*/
/*non-public*/ BoundMethodHandle(MethodHandle mh, Object argument, int argnum) {
this(mh.type().dropParameterTypes(argnum, argnum+1),
mh, argument, argnum);
}
/** Insert an argument into an arbitrary method handle.
* If argnum is zero, inserts the first argument, etc.
*/
/*non-public*/ BoundMethodHandle(MethodType type, MethodHandle mh, Object argument, int argnum) {
super(type);
if (mh.type().parameterType(argnum).isPrimitive())
this.argument = bindPrimitiveArgument(argument, mh, argnum);
else {
this.argument = checkReferenceArgument(argument, mh, argnum);
}
this.vmargslot = type.parameterSlotDepth(argnum);
initTarget(mh, argnum);
}
private void initTarget(MethodHandle mh, int argnum) {
vmtarget = createDelegate(type(), mh, argnum, argument);
}
private static native Object createDelegate(MethodType newType, MethodHandle mh, int argnum, Object argument);
/** For the AdapterMethodHandle subclass.
*/
/*non-public*/ BoundMethodHandle(MethodType type, Object argument, int vmargslot) {
super(type);
this.argument = argument;
this.vmargslot = vmargslot;
assert(this instanceof AdapterMethodHandle);
}
/** Initialize the current object as a self-bound 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.
*/
/*non-public*/ BoundMethodHandle(MethodHandle entryPoint) {
super(entryPoint.type().dropParameterTypes(0, 1));
this.argument = this; // kludge; get rid of
this.vmargslot = this.type().parameterSlotDepth(0);
initTarget(entryPoint, 0);
}
/** 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 ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Extensively modified for IKVM.NET by Jeroen Frijters
* Copyright (C) 2011 Jeroen Frijters
*/
package java.lang.invoke;
import sun.invoke.empty.Empty;
import sun.misc.Unsafe;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
* A {@code CallSite} is a holder for a variable {@link MethodHandle},
* which is called its {@code target}.
* An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
* all calls to the site's current target.
* A {@code CallSite} may be associated with several {@code invokedynamic}
* instructions, or it may be "free floating", associated with none.
* In any case, it may be invoked through an associated method handle
* called its {@linkplain #dynamicInvoker dynamic invoker}.
* <p>
* {@code CallSite} is an abstract class which does not allow
* direct subclassing by users. It has three immediate,
* concrete subclasses that may be either instantiated or subclassed.
* <ul>
* <li>If a mutable target is not required, an {@code invokedynamic} instruction
* may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
* <li>If a mutable target is required which has volatile variable semantics,
* because updates to the target must be immediately and reliably witnessed by other threads,
* a {@linkplain VolatileCallSite volatile call site} may be used.
* <li>Otherwise, if a mutable target is required,
* a {@linkplain MutableCallSite mutable call site} may be used.
* </ul>
* <p>
* A non-constant call site may be <em>relinked</em> by changing its target.
* The new target must have the same {@linkplain MethodHandle#type() type}
* as the previous target.
* Thus, though a call site can be relinked to a series of
* successive targets, it cannot change its type.
* <p>
* Here is a sample use of call sites and bootstrap methods which links every
* dynamic call site to print its arguments:
<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
static void test() throws Throwable {
// THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
}
private static void printArgs(Object... args) {
System.out.println(java.util.Arrays.deepToString(args));
}
private static final MethodHandle printArgs;
static {
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class thisClass = lookup.lookupClass(); // (who am I?)
printArgs = lookup.findStatic(thisClass,
"printArgs", MethodType.methodType(void.class, Object[].class));
}
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
// ignore caller and name, but match the type:
return new ConstantCallSite(printArgs.asType(type));
}
</pre></blockquote>
* @author John Rose, JSR 292 EG
*/
abstract
public class CallSite {
interface IndyCallSite {
void setTarget(Object target);
}
// The actual payload of this call site:
/*package-private*/
volatile MethodHandle target;
final IndyCallSite ics;
/**
* Make a blank call site object with the given method type.
* An initial target method is supplied which will throw
* an {@link IllegalStateException} if called.
* <p>
* Before this {@code CallSite} object is returned from a bootstrap method,
* it is usually provided with a more useful target method,
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
* @throws NullPointerException if the proposed type is null
*/
/*package-private*/
CallSite(MethodType type) {
this(type.invokers().uninitializedCallSite());
}
/**
* Make a call site object equipped with an initial target method handle.
* @param target the method handle which will be the initial target of the call site
* @throws NullPointerException if the proposed target is null
*/
/*package-private*/
CallSite(MethodHandle target) {
target.getClass(); // null check
ics = createIndyCallSite(target.vmtarget);
setTargetNormal(target);
}
private static native IndyCallSite createIndyCallSite(Object target);
final void setTargetNormal(MethodHandle target) {
this.target = target;
ics.setTarget(target.vmtarget);
}
final void setTargetVolatile(MethodHandle target) {
synchronized(ics) {
setTargetNormal(target);
}
}
final MethodHandle getTargetVolatile() {
synchronized(ics) {
return target;
}
}
/**
* Make a call site object equipped with an initial target method handle.
* @param targetType the desired type of the call site
* @param createTargetHook a hook which will bind the call site to the target method handle
* @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
* or if the target returned by the hook is not of the given {@code targetType}
* @throws NullPointerException if the hook returns a null value
* @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
* @throws Throwable anything else thrown by the the hook function
*/
/*package-private*/
CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
this(targetType);
ConstantCallSite selfCCS = (ConstantCallSite) this;
MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
checkTargetChange(this.target, boundTarget);
setTargetNormal(boundTarget);
}
/**
* Returns the type of this call site's target.
* Although targets may change, any call site's type is permanent, and can never change to an unequal type.
* The {@code setTarget} method enforces this invariant by refusing any new target that does
* not have the previous target's type.
* @return the type of the current target, which is also the type of any future target
*/
public MethodType type() {
// warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException
return target.type();
}
/**
* Returns the target method of the call site, according to the
* behavior defined by this call site's specific class.
* The immediate subclasses of {@code CallSite} document the
* class-specific behaviors of this method.
*
* @return the current linkage state of the call site, its target method handle
* @see ConstantCallSite
* @see VolatileCallSite
* @see #setTarget
* @see ConstantCallSite#getTarget
* @see MutableCallSite#getTarget
* @see VolatileCallSite#getTarget
*/
public abstract MethodHandle getTarget();
/**
* Updates the target method of this call site, according to the
* behavior defined by this call site's specific class.
* The immediate subclasses of {@code CallSite} document the
* class-specific behaviors of this method.
* <p>
* The type of the new target must be {@linkplain MethodType#equals equal to}
* the type of the old target.
*
* @param newTarget the new target
* @throws NullPointerException if the proposed new target is null
* @throws WrongMethodTypeException if the proposed new target
* has a method type that differs from the previous target
* @see CallSite#getTarget
* @see ConstantCallSite#setTarget
* @see MutableCallSite#setTarget
* @see VolatileCallSite#setTarget
*/
public abstract void setTarget(MethodHandle newTarget);
void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
MethodType oldType = oldTarget.type();
MethodType newType = newTarget.type(); // null check!
if (!newType.equals(oldType))
throw wrongTargetType(newTarget, oldType);
}
private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
}
/**
* Produces a method handle equivalent to an invokedynamic instruction
* which has been linked to this call site.
* <p>
* This method is equivalent to the following code:
* <blockquote><pre>
* MethodHandle getTarget, invoker, result;
* getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
* invoker = MethodHandles.exactInvoker(this.type());
* result = MethodHandles.foldArguments(invoker, getTarget)
* </pre></blockquote>
*
* @return a method handle which always invokes this call site's current target
*/
public abstract MethodHandle dynamicInvoker();
/*non-public*/ MethodHandle makeDynamicInvoker() {
MethodHandle getTarget = MethodHandleImpl.bindReceiver(GET_TARGET, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
}
private static final MethodHandle GET_TARGET;
static {
try {
GET_TARGET = IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
} catch (ReflectiveOperationException ignore) {
throw new InternalError();
}
}
/** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
/*package-private*/
static Empty uninitializedCallSite() {
throw new IllegalStateException("uninitialized call site");
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
/**
* A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
* An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
* bound to the call site's target.
* @author John Rose, JSR 292 EG
*/
public class ConstantCallSite extends CallSite {
private final boolean isFrozen;
/**
* Creates a call site with a permanent target.
* @param target the target to be permanently associated with this call site
* @throws NullPointerException if the proposed target is null
*/
public ConstantCallSite(MethodHandle target) {
super(target);
isFrozen = true;
}
/**
* Creates a call site with a permanent target, possibly bound to the call site itself.
* <p>
* During construction of the call site, the {@code createTargetHook} is invoked to
* produce the actual target, as if by a call of the form
* {@code (MethodHandle) createTargetHook.invoke(this)}.
* <p>
* Note that user code cannot perform such an action directly in a subclass constructor,
* since the target must be fixed before the {@code ConstantCallSite} constructor returns.
* <p>
* The hook is said to bind the call site to a target method handle,
* and a typical action would be {@code someTarget.bindTo(this)}.
* However, the hook is free to take any action whatever,
* including ignoring the call site and returning a constant target.
* <p>
* The result returned by the hook must be a method handle of exactly
* the same type as the call site.
* <p>
* While the hook is being called, the new {@code ConstantCallSite}
* object is in a partially constructed state.
* In this state,
* a call to {@code getTarget}, or any other attempt to use the target,
* will result in an {@code IllegalStateException}.
* It is legal at all times to obtain the call site's type using the {@code type} method.
*
* @param targetType the type of the method handle to be permanently associated with this call site
* @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target
* @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
* or if the target returned by the hook is not of the given {@code targetType}
* @throws NullPointerException if the hook returns a null value
* @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
* @throws Throwable anything else thrown by the the hook function
*/
protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
super(targetType, createTargetHook);
isFrozen = true;
}
/**
* Returns the target method of the call site, which behaves
* like a {@code final} field of the {@code ConstantCallSite}.
* That is, the the target is always the original value passed
* to the constructor call which created this instance.
*
* @return the immutable linkage state of this call site, a constant method handle
* @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
*/
@Override public final MethodHandle getTarget() {
if (!isFrozen) throw new IllegalStateException();
return target;
}
/**
* Always throws an {@link UnsupportedOperationException}.
* This kind of call site cannot change its target.
* @param ignore a new target proposed for the call site, which is ignored
* @throws UnsupportedOperationException because this kind of call site cannot change its target
*/
@Override public final void setTarget(MethodHandle ignore) {
throw new UnsupportedOperationException();
}
/**
* Returns this call site's permanent target.
* Since that target will never change, this is a correct implementation
* of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
* @return the immutable linkage state of this call site, a constant method handle
* @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
*/
@Override
public final MethodHandle dynamicInvoker() {
return getTarget();
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
/**
* 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
// Constructors in this class *must* be package scoped or private.
DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class<?> lookupClass) {
super(mtype);
assert(m.isMethod() || !doDispatch && m.isConstructor());
if (!m.isResolved())
throw new InternalError();
vmtarget = createDelegate(mtype, m, doDispatch, lookupClass);
}
private static native Object createDelegate(MethodType type, MemberName m, boolean doDispatch, Class<?> lookupClass);
boolean isValid() {
return vmtarget != null;
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More