mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
48
src/java.base/share/classes/sun/invoke/WrapperInstance.java
Normal file
48
src/java.base/share/classes/sun/invoke/WrapperInstance.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2013, 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 sun.invoke;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
/**
|
||||
* Private API used inside of java.lang.invoke.MethodHandles.
|
||||
* Interface implemented by every object which is produced by
|
||||
* {@link java.lang.invoke.MethodHandleProxies#asInterfaceInstance MethodHandleProxies.asInterfaceInstance}.
|
||||
* The methods of this interface allow a caller to recover the parameters
|
||||
* to {@code asInstance}.
|
||||
* This allows applications to repeatedly convert between method handles
|
||||
* and SAM objects, without the risk of creating unbounded delegation chains.
|
||||
*/
|
||||
public interface WrapperInstance {
|
||||
/** Produce or recover a target method handle which is behaviorally
|
||||
* equivalent to the SAM method of this object.
|
||||
*/
|
||||
public MethodHandle getWrapperInstanceTarget();
|
||||
/** Recover the SAM type for which this object was created.
|
||||
*/
|
||||
public Class<?> getWrapperInstanceType();
|
||||
}
|
||||
|
40
src/java.base/share/classes/sun/invoke/empty/Empty.java
Normal file
40
src/java.base/share/classes/sun/invoke/empty/Empty.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 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 sun.invoke.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.
|
||||
* <p>
|
||||
* This class also stands (internally to sun.invoke) for the type of a
|
||||
* value that cannot be produced, because the expression of this type
|
||||
* always returns abnormally. (Cf. Nothing in the closures proposal.)
|
||||
* @author jrose
|
||||
*/
|
||||
public class Empty {
|
||||
private Empty() { throw new InternalError(); }
|
||||
}
|
31
src/java.base/share/classes/sun/invoke/package-info.java
Normal file
31
src/java.base/share/classes/sun/invoke/package-info.java
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation details for JSR 292 RI, package java.lang.invoke.
|
||||
* @author jrose
|
||||
*/
|
||||
|
||||
package sun.invoke;
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2015, 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 sun.invoke.util;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility routines for dealing with bytecode-level signatures.
|
||||
* @author jrose
|
||||
*/
|
||||
public class BytecodeDescriptor {
|
||||
|
||||
private BytecodeDescriptor() { } // cannot instantiate
|
||||
|
||||
/**
|
||||
* @param loader the class loader in which to look up the types (null means
|
||||
* bootstrap class loader)
|
||||
*/
|
||||
public static List<Class<?>> parseMethod(String bytecodeSignature, ClassLoader loader) {
|
||||
return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loader the class loader in which to look up the types (null means
|
||||
* bootstrap class loader)
|
||||
*/
|
||||
static List<Class<?>> parseMethod(String bytecodeSignature,
|
||||
int start, int end, ClassLoader loader) {
|
||||
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;
|
||||
}
|
||||
|
||||
private static void parseError(String str, String msg) {
|
||||
throw new IllegalArgumentException("bad signature: "+str+": "+msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loader the class loader in which to look up the types (null means
|
||||
* bootstrap class loader)
|
||||
*/
|
||||
private static 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 == null)
|
||||
? Class.forName(name, false, null)
|
||||
: 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.parameterArray());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static String unparseMethod(Class<?> rtype, Class<?>[] ptypes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
for (Class<?> pt : ptypes)
|
||||
unparseSig(pt, sb);
|
||||
sb.append(')');
|
||||
unparseSig(rtype, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static 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(';');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
627
src/java.base/share/classes/sun/invoke/util/BytecodeName.java
Normal file
627
src/java.base/share/classes/sun/invoke/util/BytecodeName.java
Normal file
|
@ -0,0 +1,627 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 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 sun.invoke.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 {@code {'<', "init", '>'}}.
|
||||
* The name {@code foo/bar$:baz} will be parsed into
|
||||
* {@code {"foo", '/', "bar", '$', ':', "baz"}}.
|
||||
* 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] = toSourceName(s.substring(lasti, i));
|
||||
fillp++;
|
||||
lasti = i+1;
|
||||
}
|
||||
if (whichDC >= DANGEROUS_CHAR_FIRST_INDEX) {
|
||||
if (pass != 0)
|
||||
res[fillp] = DANGEROUS_CHARS_CA[whichDC];
|
||||
fillp++;
|
||||
lasti = i+1;
|
||||
}
|
||||
}
|
||||
if (pass != 0) break;
|
||||
// between passes, build the result array
|
||||
res = new Object[fillp];
|
||||
if (fillp <= 1 && lasti == 0) {
|
||||
if (fillp != 0) res[0] = toSourceName(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) {
|
||||
Object[] components0 = components;
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
Object c = components[i];
|
||||
if (c instanceof String) {
|
||||
String mc = toBytecodeName((String) c);
|
||||
if (i == 0 && components.length == 1)
|
||||
return mc; // usual case
|
||||
if ((Object)mc != c) {
|
||||
if (components == components0)
|
||||
components = components.clone();
|
||||
components[i] = c = mc;
|
||||
}
|
||||
}
|
||||
}
|
||||
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.
|
||||
* Non-empty runs of non-dangerous characters are demangled
|
||||
* if necessary, and the resulting names are quoted if
|
||||
* they are not already valid Java identifiers, or if
|
||||
* they contain a dangerous character (i.e., dollar sign "$").
|
||||
* Single quotes are used when quoting.
|
||||
* Within quoted names, embedded single quotes and backslashes
|
||||
* are further escaped by prepended backslashes.
|
||||
*
|
||||
* @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 sn = (String) components[i];
|
||||
// note that the name is already demangled!
|
||||
//sn = toSourceName(sn);
|
||||
if (!isJavaIdent(sn) || sn.indexOf('$') >=0 ) {
|
||||
components[i] = quoteDisplay(sn);
|
||||
}
|
||||
}
|
||||
return appendAll(components);
|
||||
}
|
||||
private static boolean isJavaIdent(String s) {
|
||||
int slen = s.length();
|
||||
if (slen == 0) return false;
|
||||
if (!Character.isJavaIdentifierStart(s.charAt(0)))
|
||||
return false;
|
||||
for (int i = 1; i < slen; i++) {
|
||||
if (!Character.isJavaIdentifierPart(s.charAt(i)))
|
||||
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 c 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, 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, 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2013, 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 sun.invoke.util;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
public class ValueConversions {
|
||||
private static final Class<?> THIS_CLASS = ValueConversions.class;
|
||||
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
|
||||
|
||||
/**
|
||||
* Thread-safe canonicalized mapping from Wrapper to MethodHandle
|
||||
* with unsynchronized reads and synchronized writes.
|
||||
* It's safe to publish MethodHandles by data race because they are immutable.
|
||||
*/
|
||||
private static class WrapperCache {
|
||||
@Stable
|
||||
private final MethodHandle[] map = new MethodHandle[Wrapper.COUNT];
|
||||
|
||||
public MethodHandle get(Wrapper w) {
|
||||
return map[w.ordinal()];
|
||||
}
|
||||
public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
|
||||
MethodHandle prev = map[w.ordinal()];
|
||||
if (prev != null) {
|
||||
return prev;
|
||||
} else {
|
||||
map[w.ordinal()] = mh;
|
||||
return mh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static WrapperCache[] newWrapperCaches(int n) {
|
||||
WrapperCache[] caches = new WrapperCache[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
caches[i] = new WrapperCache();
|
||||
return caches;
|
||||
}
|
||||
|
||||
/// Converting references to values.
|
||||
|
||||
// There are several levels of this unboxing conversions:
|
||||
// no conversions: exactly Integer.valueOf, etc.
|
||||
// implicit conversions sanctioned by JLS 5.1.2, etc.
|
||||
// explicit conversions as allowed by explicitCastArguments
|
||||
|
||||
static int unboxInteger(Integer x) {
|
||||
return x;
|
||||
}
|
||||
static int unboxInteger(Object x, boolean cast) {
|
||||
if (x instanceof Integer)
|
||||
return (Integer) x;
|
||||
return primitiveConversion(Wrapper.INT, x, cast).intValue();
|
||||
}
|
||||
|
||||
static byte unboxByte(Byte x) {
|
||||
return x;
|
||||
}
|
||||
static byte unboxByte(Object x, boolean cast) {
|
||||
if (x instanceof Byte)
|
||||
return (Byte) x;
|
||||
return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
|
||||
}
|
||||
|
||||
static short unboxShort(Short x) {
|
||||
return x;
|
||||
}
|
||||
static short unboxShort(Object x, boolean cast) {
|
||||
if (x instanceof Short)
|
||||
return (Short) x;
|
||||
return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
|
||||
}
|
||||
|
||||
static boolean unboxBoolean(Boolean x) {
|
||||
return x;
|
||||
}
|
||||
static boolean unboxBoolean(Object x, boolean cast) {
|
||||
if (x instanceof Boolean)
|
||||
return (Boolean) x;
|
||||
return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
|
||||
}
|
||||
|
||||
static char unboxCharacter(Character x) {
|
||||
return x;
|
||||
}
|
||||
static char unboxCharacter(Object x, boolean cast) {
|
||||
if (x instanceof Character)
|
||||
return (Character) x;
|
||||
return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
|
||||
}
|
||||
|
||||
static long unboxLong(Long x) {
|
||||
return x;
|
||||
}
|
||||
static long unboxLong(Object x, boolean cast) {
|
||||
if (x instanceof Long)
|
||||
return (Long) x;
|
||||
return primitiveConversion(Wrapper.LONG, x, cast).longValue();
|
||||
}
|
||||
|
||||
static float unboxFloat(Float x) {
|
||||
return x;
|
||||
}
|
||||
static float unboxFloat(Object x, boolean cast) {
|
||||
if (x instanceof Float)
|
||||
return (Float) x;
|
||||
return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
|
||||
}
|
||||
|
||||
static double unboxDouble(Double x) {
|
||||
return x;
|
||||
}
|
||||
static double unboxDouble(Object x, boolean cast) {
|
||||
if (x instanceof Double)
|
||||
return (Double) x;
|
||||
return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
|
||||
}
|
||||
|
||||
private static MethodType unboxType(Wrapper wrap, int kind) {
|
||||
if (kind == 0)
|
||||
return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());
|
||||
return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
|
||||
}
|
||||
|
||||
private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);
|
||||
|
||||
private static MethodHandle unbox(Wrapper wrap, int kind) {
|
||||
// kind 0 -> strongly typed with NPE
|
||||
// kind 1 -> strongly typed but zero for null,
|
||||
// kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE
|
||||
// kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null
|
||||
WrapperCache cache = UNBOX_CONVERSIONS[kind];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
switch (wrap) {
|
||||
case OBJECT:
|
||||
case VOID:
|
||||
throw new IllegalArgumentException("unbox "+wrap);
|
||||
}
|
||||
// look up the method
|
||||
String name = "unbox" + wrap.wrapperSimpleName();
|
||||
MethodType type = unboxType(wrap, kind);
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
if (mh != null) {
|
||||
if (kind > 0) {
|
||||
boolean cast = (kind != 2);
|
||||
mh = MethodHandles.insertArguments(mh, 1, cast);
|
||||
}
|
||||
if (kind == 1) { // casting but exact (null -> zero)
|
||||
mh = mh.asType(unboxType(wrap, 0));
|
||||
}
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
|
||||
+ (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));
|
||||
}
|
||||
|
||||
/** Return an exact unboxer for the given primitive type. */
|
||||
public static MethodHandle unboxExact(Wrapper type) {
|
||||
return unbox(type, 0);
|
||||
}
|
||||
|
||||
/** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
|
||||
* The boolean says whether to throw an NPE on a null value (false means unbox a zero).
|
||||
* The type of the unboxer is of a form like (Integer)int.
|
||||
*/
|
||||
public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {
|
||||
return unbox(type, throwNPE ? 0 : 1);
|
||||
}
|
||||
|
||||
/** Return a widening unboxer for the given primitive type.
|
||||
* Widen narrower primitive boxes to the given type.
|
||||
* Do not narrow any primitive values or convert null to zero.
|
||||
* The type of the unboxer is of a form like (Object)int.
|
||||
*/
|
||||
public static MethodHandle unboxWiden(Wrapper type) {
|
||||
return unbox(type, 2);
|
||||
}
|
||||
|
||||
/** Return a casting unboxer for the given primitive type.
|
||||
* Widen or narrow primitive values to the given type, or convert null to zero, as needed.
|
||||
* The type of the unboxer is of a form like (Object)int.
|
||||
*/
|
||||
public static MethodHandle unboxCast(Wrapper type) {
|
||||
return unbox(type, 3);
|
||||
}
|
||||
|
||||
private static final Integer ZERO_INT = 0, ONE_INT = 1;
|
||||
|
||||
/// Primitive conversions
|
||||
/**
|
||||
* Produce a Number which represents the given value {@code x}
|
||||
* according to the primitive type of the given wrapper {@code wrap}.
|
||||
* Caller must invoke intValue, byteValue, longValue (etc.) on the result
|
||||
* to retrieve the desired primitive value.
|
||||
*/
|
||||
public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
|
||||
// Maybe merge this code with Wrapper.convert/cast.
|
||||
Number res;
|
||||
if (x == null) {
|
||||
if (!cast) return null;
|
||||
return ZERO_INT;
|
||||
}
|
||||
if (x instanceof Number) {
|
||||
res = (Number) x;
|
||||
} else if (x instanceof Boolean) {
|
||||
res = ((boolean)x ? ONE_INT : ZERO_INT);
|
||||
} else if (x instanceof Character) {
|
||||
res = (int)(char)x;
|
||||
} else {
|
||||
// this will fail with the required ClassCastException:
|
||||
res = (Number) x;
|
||||
}
|
||||
Wrapper xwrap = Wrapper.findWrapperType(x.getClass());
|
||||
if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap))
|
||||
// this will fail with the required ClassCastException:
|
||||
return (Number) wrap.wrapperType().cast(x);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM verifier allows boolean, byte, short, or char to widen to int.
|
||||
* Support exactly this conversion, from a boxed value type Boolean,
|
||||
* Byte, Short, Character, or Integer.
|
||||
*/
|
||||
public static int widenSubword(Object x) {
|
||||
if (x instanceof Integer)
|
||||
return (int) x;
|
||||
else if (x instanceof Boolean)
|
||||
return fromBoolean((boolean) x);
|
||||
else if (x instanceof Character)
|
||||
return (char) x;
|
||||
else if (x instanceof Short)
|
||||
return (short) x;
|
||||
else if (x instanceof Byte)
|
||||
return (byte) x;
|
||||
else
|
||||
// Fail with a ClassCastException.
|
||||
return (int) x;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
private static MethodType boxType(Wrapper wrap) {
|
||||
// be exact, since return casts are hard to compose
|
||||
Class<?> boxType = wrap.wrapperType();
|
||||
return MethodType.methodType(boxType, wrap.primitiveType());
|
||||
}
|
||||
|
||||
private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);
|
||||
|
||||
public static MethodHandle boxExact(Wrapper wrap) {
|
||||
WrapperCache cache = BOX_CONVERSIONS[0];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// look up the method
|
||||
String name = "box" + wrap.wrapperSimpleName();
|
||||
MethodType type = boxType(wrap);
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
if (mh != null) {
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find box adapter for " + wrap);
|
||||
}
|
||||
|
||||
/// Constant functions
|
||||
|
||||
static void ignore(Object x) {
|
||||
// no value to return; this is an unbox of null
|
||||
}
|
||||
|
||||
static void empty() {
|
||||
}
|
||||
|
||||
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 WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);
|
||||
|
||||
public static MethodHandle zeroConstantFunction(Wrapper wrap) {
|
||||
WrapperCache cache = CONSTANT_FUNCTIONS[0];
|
||||
MethodHandle mh = cache.get(wrap);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
MethodType type = MethodType.methodType(wrap.primitiveType());
|
||||
switch (wrap) {
|
||||
case VOID:
|
||||
mh = EMPTY;
|
||||
break;
|
||||
case OBJECT:
|
||||
case INT: case LONG: case FLOAT: case DOUBLE:
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (mh != null) {
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
|
||||
// use zeroInt and cast the result
|
||||
if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
|
||||
mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
|
||||
return cache.put(wrap, mh);
|
||||
}
|
||||
throw new IllegalArgumentException("cannot find zero constant for " + wrap);
|
||||
}
|
||||
|
||||
private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY;
|
||||
static {
|
||||
try {
|
||||
MethodType idType = MethodType.genericMethodType(1);
|
||||
MethodType ignoreType = idType.changeReturnType(void.class);
|
||||
CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
|
||||
IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
|
||||
EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
|
||||
} catch (NoSuchMethodException | IllegalAccessException ex) {
|
||||
throw newInternalError("uncaught exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodHandle ignore() {
|
||||
return IGNORE;
|
||||
}
|
||||
|
||||
/** Return a method that casts its second argument (an Object) to the given type (a Class). */
|
||||
public static MethodHandle cast() {
|
||||
return CAST_REFERENCE;
|
||||
}
|
||||
|
||||
/// Primitive conversions.
|
||||
// These are supported directly by the JVM, usually by a single instruction.
|
||||
// In the case of narrowing to a subword, there may be a pair of instructions.
|
||||
// In the case of booleans, there may be a helper routine to manage a 1-bit value.
|
||||
// This is the full 8x8 matrix (minus the diagonal).
|
||||
|
||||
// narrow double to all other types:
|
||||
static float doubleToFloat(double x) { // bytecode: d2f
|
||||
return (float) x;
|
||||
}
|
||||
static long doubleToLong(double x) { // bytecode: d2l
|
||||
return (long) x;
|
||||
}
|
||||
static int doubleToInt(double x) { // bytecode: d2i
|
||||
return (int) x;
|
||||
}
|
||||
static short doubleToShort(double x) { // bytecodes: d2i, i2s
|
||||
return (short) x;
|
||||
}
|
||||
static char doubleToChar(double x) { // bytecodes: d2i, i2c
|
||||
return (char) x;
|
||||
}
|
||||
static byte doubleToByte(double x) { // bytecodes: d2i, i2b
|
||||
return (byte) x;
|
||||
}
|
||||
static boolean doubleToBoolean(double x) {
|
||||
return toBoolean((byte) x);
|
||||
}
|
||||
|
||||
// widen float:
|
||||
static double floatToDouble(float x) { // bytecode: f2d
|
||||
return x;
|
||||
}
|
||||
// narrow float:
|
||||
static long floatToLong(float x) { // bytecode: f2l
|
||||
return (long) x;
|
||||
}
|
||||
static int floatToInt(float x) { // bytecode: f2i
|
||||
return (int) x;
|
||||
}
|
||||
static short floatToShort(float x) { // bytecodes: f2i, i2s
|
||||
return (short) x;
|
||||
}
|
||||
static char floatToChar(float x) { // bytecodes: f2i, i2c
|
||||
return (char) x;
|
||||
}
|
||||
static byte floatToByte(float x) { // bytecodes: f2i, i2b
|
||||
return (byte) x;
|
||||
}
|
||||
static boolean floatToBoolean(float x) {
|
||||
return toBoolean((byte) x);
|
||||
}
|
||||
|
||||
// widen long:
|
||||
static double longToDouble(long x) { // bytecode: l2d
|
||||
return x;
|
||||
}
|
||||
static float longToFloat(long x) { // bytecode: l2f
|
||||
return x;
|
||||
}
|
||||
// narrow long:
|
||||
static int longToInt(long x) { // bytecode: l2i
|
||||
return (int) x;
|
||||
}
|
||||
static short longToShort(long x) { // bytecodes: f2i, i2s
|
||||
return (short) x;
|
||||
}
|
||||
static char longToChar(long x) { // bytecodes: f2i, i2c
|
||||
return (char) x;
|
||||
}
|
||||
static byte longToByte(long x) { // bytecodes: f2i, i2b
|
||||
return (byte) x;
|
||||
}
|
||||
static boolean longToBoolean(long x) {
|
||||
return toBoolean((byte) x);
|
||||
}
|
||||
|
||||
// widen int:
|
||||
static double intToDouble(int x) { // bytecode: i2d
|
||||
return x;
|
||||
}
|
||||
static float intToFloat(int x) { // bytecode: i2f
|
||||
return x;
|
||||
}
|
||||
static long intToLong(int x) { // bytecode: i2l
|
||||
return x;
|
||||
}
|
||||
// narrow int:
|
||||
static short intToShort(int x) { // bytecode: i2s
|
||||
return (short) x;
|
||||
}
|
||||
static char intToChar(int x) { // bytecode: i2c
|
||||
return (char) x;
|
||||
}
|
||||
static byte intToByte(int x) { // bytecode: i2b
|
||||
return (byte) x;
|
||||
}
|
||||
static boolean intToBoolean(int x) {
|
||||
return toBoolean((byte) x);
|
||||
}
|
||||
|
||||
// widen short:
|
||||
static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i')
|
||||
return x;
|
||||
}
|
||||
static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i')
|
||||
return x;
|
||||
}
|
||||
static long shortToLong(short x) { // bytecode: i2l (implicit 's2i')
|
||||
return x;
|
||||
}
|
||||
static int shortToInt(short x) { // (implicit 's2i')
|
||||
return x;
|
||||
}
|
||||
// narrow short:
|
||||
static char shortToChar(short x) { // bytecode: i2c (implicit 's2i')
|
||||
return (char)x;
|
||||
}
|
||||
static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i')
|
||||
return (byte)x;
|
||||
}
|
||||
static boolean shortToBoolean(short x) {
|
||||
return toBoolean((byte) x);
|
||||
}
|
||||
|
||||
// widen char:
|
||||
static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i')
|
||||
return x;
|
||||
}
|
||||
static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i')
|
||||
return x;
|
||||
}
|
||||
static long charToLong(char x) { // bytecode: i2l (implicit 'c2i')
|
||||
return x;
|
||||
}
|
||||
static int charToInt(char x) { // (implicit 'c2i')
|
||||
return x;
|
||||
}
|
||||
// narrow char:
|
||||
static short charToShort(char x) { // bytecode: i2s (implicit 'c2i')
|
||||
return (short)x;
|
||||
}
|
||||
static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i')
|
||||
return (byte)x;
|
||||
}
|
||||
static boolean charToBoolean(char x) {
|
||||
return toBoolean((byte) x);
|
||||
}
|
||||
|
||||
// widen byte:
|
||||
static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i')
|
||||
return x;
|
||||
}
|
||||
static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i')
|
||||
return x;
|
||||
}
|
||||
static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i')
|
||||
return x;
|
||||
}
|
||||
static int byteToInt(byte x) { // (implicit 'b2i')
|
||||
return x;
|
||||
}
|
||||
static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i')
|
||||
return (short)x;
|
||||
}
|
||||
static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i')
|
||||
return (char)x;
|
||||
}
|
||||
// narrow byte to boolean:
|
||||
static boolean byteToBoolean(byte x) {
|
||||
return toBoolean(x);
|
||||
}
|
||||
|
||||
// widen boolean to all types:
|
||||
static double booleanToDouble(boolean x) {
|
||||
return fromBoolean(x);
|
||||
}
|
||||
static float booleanToFloat(boolean x) {
|
||||
return fromBoolean(x);
|
||||
}
|
||||
static long booleanToLong(boolean x) {
|
||||
return fromBoolean(x);
|
||||
}
|
||||
static int booleanToInt(boolean x) {
|
||||
return fromBoolean(x);
|
||||
}
|
||||
static short booleanToShort(boolean x) {
|
||||
return fromBoolean(x);
|
||||
}
|
||||
static char booleanToChar(boolean x) {
|
||||
return (char)fromBoolean(x);
|
||||
}
|
||||
static byte booleanToByte(boolean x) {
|
||||
return fromBoolean(x);
|
||||
}
|
||||
|
||||
// helpers to force boolean into the conversion scheme:
|
||||
static boolean toBoolean(byte x) {
|
||||
// see javadoc for MethodHandles.explicitCastArguments
|
||||
return ((x & 1) != 0);
|
||||
}
|
||||
static byte fromBoolean(boolean x) {
|
||||
// see javadoc for MethodHandles.explicitCastArguments
|
||||
return (x ? (byte)1 : (byte)0);
|
||||
}
|
||||
|
||||
private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.COUNT);
|
||||
|
||||
public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
|
||||
WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
|
||||
MethodHandle mh = cache.get(wdst);
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
// slow path
|
||||
Class<?> src = wsrc.primitiveType();
|
||||
Class<?> dst = wdst.primitiveType();
|
||||
MethodType type = MethodType.methodType(dst, src);
|
||||
if (wsrc == wdst) {
|
||||
mh = MethodHandles.identity(src);
|
||||
} else {
|
||||
assert(src.isPrimitive() && dst.isPrimitive());
|
||||
try {
|
||||
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
mh = null;
|
||||
}
|
||||
}
|
||||
if (mh != null) {
|
||||
assert(mh.type() == type) : mh;
|
||||
return cache.put(wdst, mh);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("cannot find primitive conversion function for " +
|
||||
src.getSimpleName()+" -> "+dst.getSimpleName());
|
||||
}
|
||||
|
||||
public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {
|
||||
return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));
|
||||
}
|
||||
|
||||
private static String capitalize(String x) {
|
||||
return Character.toUpperCase(x.charAt(0))+x.substring(1);
|
||||
}
|
||||
|
||||
// handy shared exception makers (they simplify the common case code)
|
||||
private static InternalError newInternalError(String message, Throwable cause) {
|
||||
return new InternalError(message, cause);
|
||||
}
|
||||
private static InternalError newInternalError(Throwable cause) {
|
||||
return new InternalError(cause);
|
||||
}
|
||||
}
|
398
src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
Normal file
398
src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
Normal file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2017, 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 sun.invoke.util;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import static java.lang.reflect.Modifier.*;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
/**
|
||||
* This class centralizes information about the JVM's linkage access control.
|
||||
* @author jrose
|
||||
*/
|
||||
public class VerifyAccess {
|
||||
|
||||
private VerifyAccess() { } // cannot instantiate
|
||||
|
||||
private static final int UNCONDITIONAL_ALLOWED = java.lang.invoke.MethodHandles.Lookup.UNCONDITIONAL;
|
||||
private static final int MODULE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.MODULE;
|
||||
private static final int PACKAGE_ONLY = 0;
|
||||
private static final int PACKAGE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.PACKAGE;
|
||||
private static final int PROTECTED_OR_PACKAGE_ALLOWED = (PACKAGE_ALLOWED|PROTECTED);
|
||||
private static final int ALL_ACCESS_MODES = (PUBLIC|PRIVATE|PROTECTED|PACKAGE_ONLY);
|
||||
private static final boolean ALLOW_NESTMATE_ACCESS = false;
|
||||
|
||||
/**
|
||||
* Evaluate the JVM linkage rules for access to the given method
|
||||
* on behalf of a caller class which proposes to perform the access.
|
||||
* Return true if the caller class has privileges to invoke a method
|
||||
* or access a field with the given properties.
|
||||
* This requires an accessibility check of the referencing class,
|
||||
* plus an accessibility check of the member within the class,
|
||||
* which depends on the member's modifier flags.
|
||||
* <p>
|
||||
* The relevant properties include the defining class ({@code defc})
|
||||
* of the member, and its modifier flags ({@code mods}).
|
||||
* Also relevant is the class used to make the initial symbolic reference
|
||||
* to the member ({@code refc}). If this latter class is not distinguished,
|
||||
* the defining class should be passed for both arguments ({@code defc == refc}).
|
||||
* <h3>JVM Specification, 5.4.4 "Access Control"</h3>
|
||||
* A field or method R is accessible to a class or interface D if
|
||||
* and only if any of the following conditions is true:<ul>
|
||||
* <li>R is public.
|
||||
* <li>R is protected and is declared in a class C, and D is either
|
||||
* a subclass of C or C itself. Furthermore, if R is not
|
||||
* static, then the symbolic reference to R must contain a
|
||||
* symbolic reference to a class T, such that T is either a
|
||||
* subclass of D, a superclass of D or D itself.
|
||||
* <li>R is either protected or has default access (that is,
|
||||
* neither public nor protected nor private), and is declared
|
||||
* by a class in the same runtime package as D.
|
||||
* <li>R is private and is declared in D.
|
||||
* </ul>
|
||||
* This discussion of access control omits a related restriction
|
||||
* on the target of a protected field access or method invocation
|
||||
* (the target must be of class D or a subtype of D). That
|
||||
* requirement is checked as part of the verification process
|
||||
* (5.4.1); it is not part of link-time access control.
|
||||
* @param refc the class used in the symbolic reference to the proposed member
|
||||
* @param defc the class in which the proposed member is actually defined
|
||||
* @param mods modifier flags for the proposed member
|
||||
* @param lookupClass the class for which the access check is being made
|
||||
* @return true iff the accessing class can access such a member
|
||||
*/
|
||||
public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class
|
||||
Class<?> defc, // actual def class
|
||||
int mods, // actual member mods
|
||||
Class<?> lookupClass,
|
||||
int allowedModes) {
|
||||
if (allowedModes == 0) return false;
|
||||
assert((allowedModes & PUBLIC) != 0 &&
|
||||
(allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
|
||||
// The symbolic reference class (refc) must always be fully verified.
|
||||
if (!isClassAccessible(refc, lookupClass, allowedModes)) {
|
||||
return false;
|
||||
}
|
||||
// Usually refc and defc are the same, but verify defc also in case they differ.
|
||||
if (defc == lookupClass &&
|
||||
(allowedModes & PRIVATE) != 0)
|
||||
return true; // easy check; all self-access is OK
|
||||
switch (mods & ALL_ACCESS_MODES) {
|
||||
case PUBLIC:
|
||||
return true; // already checked above
|
||||
case PROTECTED:
|
||||
assert !defc.isInterface(); // protected members aren't allowed in interfaces
|
||||
if ((allowedModes & PROTECTED_OR_PACKAGE_ALLOWED) != 0 &&
|
||||
isSamePackage(defc, lookupClass))
|
||||
return true;
|
||||
if ((allowedModes & PROTECTED) == 0)
|
||||
return false;
|
||||
// Protected members are accessible by subclasses, which does not include interfaces.
|
||||
// Interfaces are types, not classes. They should not have access to
|
||||
// protected members in j.l.Object, even though it is their superclass.
|
||||
if ((mods & STATIC) != 0 &&
|
||||
!isRelatedClass(refc, lookupClass))
|
||||
return false;
|
||||
if ((allowedModes & PROTECTED) != 0 &&
|
||||
isSubClass(lookupClass, defc))
|
||||
return true;
|
||||
return false;
|
||||
case PACKAGE_ONLY: // That is, zero. Unmarked member is package-only access.
|
||||
assert !defc.isInterface(); // package-private members aren't allowed in interfaces
|
||||
return ((allowedModes & PACKAGE_ALLOWED) != 0 &&
|
||||
isSamePackage(defc, lookupClass));
|
||||
case PRIVATE:
|
||||
// Loosened rules for privates follows access rules for inner classes.
|
||||
return (ALLOW_NESTMATE_ACCESS &&
|
||||
(allowedModes & PRIVATE) != 0 &&
|
||||
isSamePackageMember(defc, lookupClass));
|
||||
default:
|
||||
throw new IllegalArgumentException("bad modifiers: "+Modifier.toString(mods));
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isRelatedClass(Class<?> refc, Class<?> lookupClass) {
|
||||
return (refc == lookupClass ||
|
||||
isSubClass(refc, lookupClass) ||
|
||||
isSubClass(lookupClass, refc));
|
||||
}
|
||||
|
||||
static boolean isSubClass(Class<?> lookupClass, Class<?> defc) {
|
||||
return defc.isAssignableFrom(lookupClass) &&
|
||||
!lookupClass.isInterface(); // interfaces are types, not classes.
|
||||
}
|
||||
|
||||
static int getClassModifiers(Class<?> c) {
|
||||
// This would return the mask stored by javac for the source-level modifiers.
|
||||
// return c.getModifiers();
|
||||
// But what we need for JVM access checks are the actual bits from the class header.
|
||||
// ...But arrays and primitives are synthesized with their own odd flags:
|
||||
if (c.isArray() || c.isPrimitive())
|
||||
return c.getModifiers();
|
||||
return Reflection.getClassAccessFlags(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the JVM linkage rules for access to the given class on behalf of caller.
|
||||
* <h3>JVM Specification, 5.4.4 "Access Control"</h3>
|
||||
* A class or interface C is accessible to a class or interface D
|
||||
* if and only if any of the following conditions are true:<ul>
|
||||
* <li>C is public and in the same module as D.
|
||||
* <li>D is in a module that reads the module containing C, C is public and in a
|
||||
* package that is exported to the module that contains D.
|
||||
* <li>C and D are members of the same runtime package.
|
||||
* </ul>
|
||||
* @param refc the symbolic reference class to which access is being checked (C)
|
||||
* @param lookupClass the class performing the lookup (D)
|
||||
*/
|
||||
public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,
|
||||
int allowedModes) {
|
||||
if (allowedModes == 0) return false;
|
||||
assert((allowedModes & PUBLIC) != 0 &&
|
||||
(allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
|
||||
int mods = getClassModifiers(refc);
|
||||
if (isPublic(mods)) {
|
||||
|
||||
Module lookupModule = lookupClass.getModule();
|
||||
Module refModule = refc.getModule();
|
||||
|
||||
// early VM startup case, java.base not defined
|
||||
if (lookupModule == null) {
|
||||
assert refModule == null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// trivially allow
|
||||
if ((allowedModes & MODULE_ALLOWED) != 0 &&
|
||||
(lookupModule == refModule))
|
||||
return true;
|
||||
|
||||
// check readability when UNCONDITIONAL not allowed
|
||||
if (((allowedModes & UNCONDITIONAL_ALLOWED) != 0)
|
||||
|| lookupModule.canRead(refModule)) {
|
||||
|
||||
// check that refc is in an exported package
|
||||
if ((allowedModes & MODULE_ALLOWED) != 0) {
|
||||
if (refModule.isExported(refc.getPackageName(), lookupModule))
|
||||
return true;
|
||||
} else {
|
||||
// exported unconditionally
|
||||
if (refModule.isExported(refc.getPackageName()))
|
||||
return true;
|
||||
}
|
||||
|
||||
// not exported but allow access during VM initialization
|
||||
// because java.base does not have its exports setup
|
||||
if (!jdk.internal.misc.VM.isModuleSystemInited())
|
||||
return true;
|
||||
}
|
||||
|
||||
// public class not accessible to lookupClass
|
||||
return false;
|
||||
}
|
||||
if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
|
||||
isSamePackage(lookupClass, refc))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if the given method type, attributed to a member or symbolic
|
||||
* reference of a given reference class, is really visible to that class.
|
||||
* @param type the supposed type of a member or symbolic reference of refc
|
||||
* @param refc the class attempting to make the reference
|
||||
*/
|
||||
public static boolean isTypeVisible(Class<?> type, Class<?> refc) {
|
||||
if (type == refc) {
|
||||
return true; // easy check
|
||||
}
|
||||
while (type.isArray()) type = type.getComponentType();
|
||||
if (type.isPrimitive() || type == Object.class) {
|
||||
return true;
|
||||
}
|
||||
ClassLoader typeLoader = type.getClassLoader();
|
||||
ClassLoader refcLoader = refc.getClassLoader();
|
||||
if (typeLoader == refcLoader) {
|
||||
return true;
|
||||
}
|
||||
if (refcLoader == null && typeLoader != null) {
|
||||
return false;
|
||||
}
|
||||
if (typeLoader == null && type.getName().startsWith("java.")) {
|
||||
// Note: The API for actually loading classes, ClassLoader.defineClass,
|
||||
// guarantees that classes with names beginning "java." cannot be aliased,
|
||||
// because class loaders cannot load them directly.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do it the hard way: Look up the type name from the refc loader.
|
||||
//
|
||||
// Force the refc loader to report and commit to a particular binding for this type name (type.getName()).
|
||||
//
|
||||
// In principle, this query might force the loader to load some unrelated class,
|
||||
// which would cause this query to fail (and the original caller to give up).
|
||||
// This would be wasted effort, but it is expected to be very rare, occurring
|
||||
// only when an attacker is attempting to create a type alias.
|
||||
// In the normal case, one class loader will simply delegate to the other,
|
||||
// and the same type will be visible through both, with no extra loading.
|
||||
//
|
||||
// It is important to go through Class.forName instead of ClassLoader.loadClass
|
||||
// because Class.forName goes through the JVM system dictionary, which records
|
||||
// the class lookup once for all. This means that even if a not-well-behaved class loader
|
||||
// would "change its mind" about the meaning of the name, the Class.forName request
|
||||
// will use the result cached in the JVM system dictionary. Note that the JVM system dictionary
|
||||
// will record the first successful result. Unsuccessful results are not stored.
|
||||
//
|
||||
// We use doPrivileged in order to allow an unprivileged caller to ask an arbitrary
|
||||
// class loader about the binding of the proposed name (type.getName()).
|
||||
// The looked up type ("res") is compared for equality against the proposed
|
||||
// type ("type") and then is discarded. Thus, the worst that can happen to
|
||||
// the "child" class loader is that it is bothered to load and report a class
|
||||
// that differs from "type"; this happens once due to JVM system dictionary
|
||||
// memoization. And the caller never gets to look at the alternate type binding
|
||||
// ("res"), whether it exists or not.
|
||||
final String name = type.getName();
|
||||
Class<?> res = java.security.AccessController.doPrivileged(
|
||||
new java.security.PrivilegedAction<>() {
|
||||
public Class<?> run() {
|
||||
try {
|
||||
return Class.forName(name, false, refcLoader);
|
||||
} catch (ClassNotFoundException | LinkageError e) {
|
||||
return null; // Assume the class is not found
|
||||
}
|
||||
}
|
||||
});
|
||||
return (type == res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide if the given method type, attributed to a member or symbolic
|
||||
* reference of a given reference class, is really visible to that class.
|
||||
* @param type the supposed type of a member or symbolic reference of refc
|
||||
* @param refc the class attempting to make the reference
|
||||
*/
|
||||
public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {
|
||||
if (!isTypeVisible(type.returnType(), refc)) {
|
||||
return false;
|
||||
}
|
||||
for (int n = 0, max = type.parameterCount(); n < max; n++) {
|
||||
if (!isTypeVisible(type.parameterType(n), refc)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if two classes are in the same module.
|
||||
* @param class1 a class
|
||||
* @param class2 another class
|
||||
* @return whether they are in the same module
|
||||
*/
|
||||
public static boolean isSameModule(Class<?> class1, Class<?> class2) {
|
||||
return class1.getModule() == class2.getModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if two classes have the same class loader and package qualifier.
|
||||
* @param class1 a class
|
||||
* @param class2 another class
|
||||
* @return whether they are in the same package
|
||||
*/
|
||||
public static boolean isSamePackage(Class<?> class1, Class<?> class2) {
|
||||
assert(!class1.isArray() && !class2.isArray());
|
||||
if (class1 == class2)
|
||||
return true;
|
||||
if (class1.getClassLoader() != class2.getClassLoader())
|
||||
return false;
|
||||
return Objects.equals(class1.getPackageName(), class2.getPackageName());
|
||||
}
|
||||
|
||||
/** Return the package name for this class.
|
||||
*/
|
||||
public static String getPackageName(Class<?> cls) {
|
||||
assert (!cls.isArray());
|
||||
String name = cls.getName();
|
||||
int dot = name.lastIndexOf('.');
|
||||
if (dot < 0) return "";
|
||||
return name.substring(0, dot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 a class
|
||||
* @param class2 another class
|
||||
* @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,
|
||||
boolean loader1MustBeParent) {
|
||||
if (loader1 == loader2 || loader1 == null
|
||||
|| (loader2 == null && !loader1MustBeParent)) {
|
||||
return true;
|
||||
}
|
||||
for (ClassLoader scan2 = loader2;
|
||||
scan2 != null; scan2 = scan2.getParent()) {
|
||||
if (scan2 == loader1) return true;
|
||||
}
|
||||
if (loader1MustBeParent) return false;
|
||||
// see if loader2 is a parent of loader1:
|
||||
for (ClassLoader scan1 = loader1;
|
||||
scan1 != null; scan1 = scan1.getParent()) {
|
||||
if (scan1 == loader2) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the class loader of parentClass identical to, or an ancestor of,
|
||||
* the class loader of childClass?
|
||||
* @param parentClass a class
|
||||
* @param childClass another class, which may be a descendent of the first class
|
||||
* @return whether parentClass precedes or equals childClass in class loader order
|
||||
*/
|
||||
public static boolean classLoaderIsAncestor(Class<?> parentClass, Class<?> childClass) {
|
||||
return loadersAreRelated(parentClass.getClassLoader(), childClass.getClassLoader(), true);
|
||||
}
|
||||
}
|
202
src/java.base/share/classes/sun/invoke/util/VerifyType.java
Normal file
202
src/java.base/share/classes/sun/invoke/util/VerifyType.java
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2013, 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 sun.invoke.util;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import sun.invoke.empty.Empty;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* If both types are references, we apply the verifier's subclass check
|
||||
* (or subtyping, if keepInterfaces).
|
||||
* If the src type is a type guaranteed to be null (Void) it can be converted
|
||||
* to any other reference type.
|
||||
* <p>
|
||||
* If both types are primitives, we apply the verifier's primitive conversions.
|
||||
* These do not include Java conversions such as long to double, since those
|
||||
* require computation and (in general) stack depth changes.
|
||||
* But very simple 32-bit viewing changes, such as byte to int,
|
||||
* are null conversions, because they do not require any computation.
|
||||
* These conversions are from any type to a wider type up to 32 bits,
|
||||
* as long as the conversion is not signed to unsigned (byte to char).
|
||||
* <p>
|
||||
* The primitive type 'void' does not interconvert with any other type,
|
||||
* even though it is legal to drop any type from the stack and "return void".
|
||||
* The stack effects, though are different between void and any other type,
|
||||
* so it is safer to report a non-trivial conversion.
|
||||
*
|
||||
* @param src the type of a stacked value
|
||||
* @param dst the type by which we'd like to treat it
|
||||
* @param keepInterfaces if false, we treat any interface as if it were Object
|
||||
* @return whether the retyping can be done without motion or reformatting
|
||||
*/
|
||||
public static boolean isNullConversion(Class<?> src, Class<?> dst, boolean keepInterfaces) {
|
||||
if (src == dst) return true;
|
||||
// Verifier allows any interface to be treated as Object:
|
||||
if (!keepInterfaces) {
|
||||
if (dst.isInterface()) dst = Object.class;
|
||||
if (src.isInterface()) src = Object.class;
|
||||
if (src == dst) return true; // check again
|
||||
}
|
||||
if (isNullType(src)) return !dst.isPrimitive();
|
||||
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
|
||||
if (!dst.isPrimitive()) return false;
|
||||
// Verifier allows an int to carry byte, short, char, or even boolean:
|
||||
Wrapper sw = Wrapper.forPrimitiveType(src);
|
||||
if (dst == int.class) return sw.isSubwordOrInt();
|
||||
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||
if (!sw.isSubwordOrInt()) return false;
|
||||
if (!dw.isSubwordOrInt()) return false;
|
||||
if (!dw.isSigned() && sw.isSigned()) return false;
|
||||
return dw.bitWidth() > sw.bitWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of isNullConversion to reference types.
|
||||
* @param src the type of a stacked value
|
||||
* @param dst 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 java.lang.Null or an equivalent null-only type?
|
||||
*/
|
||||
public static boolean isNullType(Class<?> type) {
|
||||
// Any reference statically typed as Void is guaranteed to be null.
|
||||
// Therefore, it can be safely treated as a value of any
|
||||
// other type that admits null, i.e., a reference type.
|
||||
if (type == Void.class) return true;
|
||||
// Locally known null-only class:
|
||||
if (type == Empty.class) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, boolean keepInterfaces) {
|
||||
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), keepInterfaces))
|
||||
return false;
|
||||
return isNullConversion(recv.returnType(), call.returnType(), keepInterfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
return 1;
|
||||
}
|
||||
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 boolean isSpreadArgType(Class<?> spreadArg) {
|
||||
return spreadArg.isArray();
|
||||
}
|
||||
public static Class<?> spreadArgElementType(Class<?> spreadArg, int i) {
|
||||
return spreadArg.getComponentType();
|
||||
}
|
||||
}
|
630
src/java.base/share/classes/sun/invoke/util/Wrapper.java
Normal file
630
src/java.base/share/classes/sun/invoke/util/Wrapper.java
Normal file
|
@ -0,0 +1,630 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 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 sun.invoke.util;
|
||||
|
||||
public enum Wrapper {
|
||||
// wrapperType simple primitiveType simple char emptyArray format
|
||||
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1)),
|
||||
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
|
||||
// Avoid boxing integral types here to defer initialization of internal caches
|
||||
BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8)),
|
||||
SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16)),
|
||||
CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16)),
|
||||
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32)),
|
||||
LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64)),
|
||||
FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32)),
|
||||
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64)),
|
||||
OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1)),
|
||||
// VOID must be the last type, since it is "assignable" from any other type:
|
||||
VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0)),
|
||||
;
|
||||
|
||||
public static final int COUNT = 10;
|
||||
|
||||
private final Class<?> wrapperType;
|
||||
private final Class<?> primitiveType;
|
||||
private final char basicTypeChar;
|
||||
private final Object emptyArray;
|
||||
private final int format;
|
||||
private final String wrapperSimpleName;
|
||||
private final String primitiveSimpleName;
|
||||
|
||||
private Wrapper(Class<?> wtype, String wtypeName, Class<?> ptype, String ptypeName, char tchar, Object emptyArray, int format) {
|
||||
this.wrapperType = wtype;
|
||||
this.primitiveType = ptype;
|
||||
this.basicTypeChar = tchar;
|
||||
this.emptyArray = emptyArray;
|
||||
this.format = format;
|
||||
this.wrapperSimpleName = wtypeName;
|
||||
this.primitiveSimpleName = ptypeName;
|
||||
}
|
||||
|
||||
/** For debugging, give the details of this wrapper. */
|
||||
public String detailString() {
|
||||
return wrapperSimpleName+
|
||||
java.util.Arrays.asList(wrapperType, primitiveType,
|
||||
basicTypeChar, zero(),
|
||||
"0x"+Integer.toHexString(format));
|
||||
}
|
||||
|
||||
private abstract static 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 final int
|
||||
INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
|
||||
SHORT = SIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
|
||||
BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT),
|
||||
CHAR = UNSIGNED | (16 << 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; }
|
||||
/** Is the wrapped type either void or a reference? */
|
||||
public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; }
|
||||
|
||||
/** Does the JLS 5.1.2 allow a variable of this wrapper's
|
||||
* primitive type to be assigned from a value of the given wrapper's primitive type?
|
||||
* Cases:
|
||||
* <ul>
|
||||
* <li>unboxing followed by widening primitive conversion
|
||||
* <li>any type converted to {@code void} (i.e., dropping a method call's value)
|
||||
* <li>boxing conversion followed by widening reference conversion to {@code Object}
|
||||
* </ul>
|
||||
* These are the cases allowed by MethodHandle.asType.
|
||||
*/
|
||||
public boolean isConvertibleFrom(Wrapper source) {
|
||||
if (this == source) return true;
|
||||
if (this.compareTo(source) < 0) {
|
||||
// At best, this is a narrowing conversion.
|
||||
return false;
|
||||
}
|
||||
// All conversions are allowed in the enum order between floats and signed ints.
|
||||
// First detect non-signed non-float types (boolean, char, Object, void).
|
||||
boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0);
|
||||
if (!floatOrSigned) {
|
||||
if (this.isOther()) return true;
|
||||
// can convert char to int or wider, but nothing else
|
||||
if (source.format == Format.CHAR) return true;
|
||||
// no other conversions are classified as widening
|
||||
return false;
|
||||
}
|
||||
// All signed and float conversions in the enum order are widening.
|
||||
assert(this.isFloating() || this.isSigned());
|
||||
assert(source.isFloating() || source.isSigned());
|
||||
return true;
|
||||
}
|
||||
|
||||
static {
|
||||
assert(checkConvertibleFrom());
|
||||
assert(COUNT == Wrapper.values().length);
|
||||
}
|
||||
private static boolean checkConvertibleFrom() {
|
||||
// Check the matrix for correct classification of widening conversions.
|
||||
for (Wrapper w : values()) {
|
||||
assert(w.isConvertibleFrom(w));
|
||||
assert(VOID.isConvertibleFrom(w));
|
||||
if (w != VOID) {
|
||||
assert(OBJECT.isConvertibleFrom(w));
|
||||
assert(!w.isConvertibleFrom(VOID));
|
||||
}
|
||||
// check relations with unsigned integral types:
|
||||
if (w != CHAR) {
|
||||
assert(!CHAR.isConvertibleFrom(w));
|
||||
if (!w.isConvertibleFrom(INT))
|
||||
assert(!w.isConvertibleFrom(CHAR));
|
||||
}
|
||||
if (w != BOOLEAN) {
|
||||
assert(!BOOLEAN.isConvertibleFrom(w));
|
||||
if (w != VOID && w != OBJECT)
|
||||
assert(!w.isConvertibleFrom(BOOLEAN));
|
||||
}
|
||||
// check relations with signed integral types:
|
||||
if (w.isSigned()) {
|
||||
for (Wrapper x : values()) {
|
||||
if (w == x) continue;
|
||||
if (x.isFloating())
|
||||
assert(!w.isConvertibleFrom(x));
|
||||
else if (x.isSigned()) {
|
||||
if (w.compareTo(x) < 0)
|
||||
assert(!w.isConvertibleFrom(x));
|
||||
else
|
||||
assert(w.isConvertibleFrom(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
// check relations with floating types:
|
||||
if (w.isFloating()) {
|
||||
for (Wrapper x : values()) {
|
||||
if (w == x) continue;
|
||||
if (x.isSigned())
|
||||
assert(w.isConvertibleFrom(x));
|
||||
else if (x.isFloating()) {
|
||||
if (w.compareTo(x) < 0)
|
||||
assert(!w.isConvertibleFrom(x));
|
||||
else
|
||||
assert(w.isConvertibleFrom(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true; // i.e., assert(true)
|
||||
}
|
||||
|
||||
/** 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() {
|
||||
switch (this) {
|
||||
case BOOLEAN:
|
||||
return Boolean.FALSE;
|
||||
case INT:
|
||||
return (Integer)0;
|
||||
case BYTE:
|
||||
return (Byte)(byte)0;
|
||||
case CHAR:
|
||||
return (Character)(char)0;
|
||||
case SHORT:
|
||||
return (Short)(short)0;
|
||||
case LONG:
|
||||
return (Long)(long)0;
|
||||
case FLOAT:
|
||||
return FLOAT_ZERO;
|
||||
case DOUBLE:
|
||||
return DOUBLE_ZERO;
|
||||
case VOID:
|
||||
case OBJECT:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object DOUBLE_ZERO = (Double)(double)0;
|
||||
private static final Object FLOAT_ZERO = (Float)(float)0;
|
||||
|
||||
/** Produce a zero value for the given wrapper type T.
|
||||
* The optional argument must a type compatible with this wrapper.
|
||||
* Equivalent to {@code this.cast(this.zero(), type)}.
|
||||
*/
|
||||
public <T> T zero(Class<T> type) { return convert(zero(), 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 = findPrimitiveType(type);
|
||||
if (w != null) return w;
|
||||
if (type.isPrimitive())
|
||||
throw new InternalError(); // redo hash function
|
||||
throw newIllegalArgumentException("not primitive: "+type);
|
||||
}
|
||||
|
||||
static Wrapper findPrimitiveType(Class<?> type) {
|
||||
Wrapper w = FROM_PRIM[hashPrim(type)];
|
||||
if (w != null && w.primitiveType == type) {
|
||||
return w;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Return the wrapper that wraps values into the given wrapper type.
|
||||
* If it is {@code Object}, 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 (x.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;
|
||||
}
|
||||
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.invoke.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?
|
||||
* Otherwise, the example type must be the wrapper type,
|
||||
* or the corresponding primitive type.
|
||||
* (For {@code OBJECT}, the example type can be any non-primitive,
|
||||
* and is normalized to {@code Object.class}.)
|
||||
* 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 newClassCastException(exampleType, primitiveType);
|
||||
}
|
||||
|
||||
private static ClassCastException newClassCastException(Class<?> actual, Class<?> expected) {
|
||||
return new ClassCastException(actual + " is not compatible with " + expected);
|
||||
}
|
||||
|
||||
/** 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 type?
|
||||
* All non-primitives, including array types, report as 'L', the signature character for references.
|
||||
*/
|
||||
public static char basicTypeChar(Class<?> type) {
|
||||
if (!type.isPrimitive())
|
||||
return 'L';
|
||||
else
|
||||
return forPrimitiveType(type).basicTypeChar();
|
||||
}
|
||||
|
||||
/** 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 wrapperSimpleName() { return wrapperSimpleName; }
|
||||
|
||||
/** What is the simple name of the primitive type?
|
||||
*/
|
||||
public String primitiveSimpleName() { return primitiveSimpleName; }
|
||||
|
||||
// /** 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.
|
||||
* The given target type must be this wrapper's primitive or wrapper type.
|
||||
* If this wrapper is OBJECT, the target type may also be an interface, perform no runtime check.
|
||||
* 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.
|
||||
* Primitive conversions are only performed if the given type is itself a primitive.
|
||||
* @throws ClassCastException if the given type is not compatible with this wrapper
|
||||
*/
|
||||
public <T> T cast(Object x, Class<T> type) {
|
||||
return convert(x, type, true);
|
||||
}
|
||||
|
||||
/** Convert a wrapped value to the given type.
|
||||
* The given target type must be this wrapper's primitive or wrapper type.
|
||||
* This is equivalent to {@link #cast}, except that it refuses to perform
|
||||
* narrowing primitive conversions.
|
||||
*/
|
||||
public <T> T convert(Object x, Class<T> type) {
|
||||
return convert(x, type, false);
|
||||
}
|
||||
|
||||
private <T> T convert(Object x, Class<T> type, boolean isCast) {
|
||||
if (this == OBJECT) {
|
||||
// If the target wrapper is OBJECT, just do a reference cast.
|
||||
// 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.
|
||||
assert(!type.isPrimitive());
|
||||
if (!type.isInterface())
|
||||
type.cast(x);
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) x; // unchecked warning is expected here
|
||||
return result;
|
||||
}
|
||||
Class<T> wtype = wrapperType(type);
|
||||
if (wtype.isInstance(x)) {
|
||||
return wtype.cast(x);
|
||||
}
|
||||
if (!isCast) {
|
||||
Class<?> sourceType = x.getClass(); // throw NPE if x is null
|
||||
Wrapper source = findWrapperType(sourceType);
|
||||
if (source == null || !this.isConvertibleFrom(source)) {
|
||||
throw newClassCastException(wtype, sourceType);
|
||||
}
|
||||
} else if (x == null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T z = (T) zero();
|
||||
return z;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) wrap(x); // unchecked warning is expected here
|
||||
assert (result == null ? Void.class : result.getClass()) == wtype;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 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.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) ||
|
||||
exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) ||
|
||||
type == Object.class && !exampleType.isPrimitive());
|
||||
@SuppressWarnings("unchecked")
|
||||
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.byteValue()));
|
||||
}
|
||||
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(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((byte) x));
|
||||
}
|
||||
throw new InternalError("bad wrapper");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Parameter type of boolValue must be byte, because
|
||||
// MethodHandles.explicitCastArguments defines boolean
|
||||
// conversion as first converting to byte.
|
||||
private static boolean boolValue(byte 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);
|
||||
}
|
||||
|
||||
// primitive array support
|
||||
public Object makeArray(int len) {
|
||||
return java.lang.reflect.Array.newInstance(primitiveType, len);
|
||||
}
|
||||
public Class<?> arrayType() {
|
||||
return emptyArray.getClass();
|
||||
}
|
||||
public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) {
|
||||
if (a.getClass() != arrayType())
|
||||
arrayType().cast(a); // throw NPE or CCE if bad type
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object value = values[i+vpos];
|
||||
value = convert(value, primitiveType);
|
||||
java.lang.reflect.Array.set(a, i+apos, value);
|
||||
}
|
||||
}
|
||||
public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) {
|
||||
if (a.getClass() != arrayType())
|
||||
arrayType().cast(a); // throw NPE or CCE if bad type
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object value = java.lang.reflect.Array.get(a, i+apos);
|
||||
//Already done: value = convert(value, primitiveType);
|
||||
assert(value.getClass() == wrapperType);
|
||||
values[i+vpos] = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extra support for using JSR 292 RI, package java.lang.invoke.
|
||||
* @author jrose
|
||||
*/
|
||||
|
||||
package sun.invoke.util;
|
Loading…
Add table
Add a link
Reference in a new issue