src/jdk/internal/dynalink/support/Guards.java

Thu, 14 Feb 2013 13:22:26 +0100

author
attila
date
Thu, 14 Feb 2013 13:22:26 +0100
changeset 90
5a820fb11814
child 101
f8221ce53c2e
permissions
-rw-r--r--

8008085: Integrate Dynalink source code into Nashorn codebase
Reviewed-by: jlaskey, lagergren, sundar

attila@90 1 /*
attila@90 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
attila@90 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
attila@90 4 *
attila@90 5 * This code is free software; you can redistribute it and/or modify it
attila@90 6 * under the terms of the GNU General Public License version 2 only, as
attila@90 7 * published by the Free Software Foundation. Oracle designates this
attila@90 8 * particular file as subject to the "Classpath" exception as provided
attila@90 9 * by Oracle in the LICENSE file that accompanied this code.
attila@90 10 *
attila@90 11 * This code is distributed in the hope that it will be useful, but WITHOUT
attila@90 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
attila@90 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
attila@90 14 * version 2 for more details (a copy is included in the LICENSE file that
attila@90 15 * accompanied this code).
attila@90 16 *
attila@90 17 * You should have received a copy of the GNU General Public License version
attila@90 18 * 2 along with this work; if not, write to the Free Software Foundation,
attila@90 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
attila@90 20 *
attila@90 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
attila@90 22 * or visit www.oracle.com if you need additional information or have any
attila@90 23 * questions.
attila@90 24 */
attila@90 25
attila@90 26 /*
attila@90 27 * This file is available under and governed by the GNU General Public
attila@90 28 * License version 2 only, as published by the Free Software Foundation.
attila@90 29 * However, the following notice accompanied the original version of this
attila@90 30 * file, and Oracle licenses the original version of this file under the BSD
attila@90 31 * license:
attila@90 32 */
attila@90 33 /*
attila@90 34 Copyright 2009-2013 Attila Szegedi
attila@90 35
attila@90 36 Licensed under both the Apache License, Version 2.0 (the "Apache License")
attila@90 37 and the BSD License (the "BSD License"), with licensee being free to
attila@90 38 choose either of the two at their discretion.
attila@90 39
attila@90 40 You may not use this file except in compliance with either the Apache
attila@90 41 License or the BSD License.
attila@90 42
attila@90 43 If you choose to use this file in compliance with the Apache License, the
attila@90 44 following notice applies to you:
attila@90 45
attila@90 46 You may obtain a copy of the Apache License at
attila@90 47
attila@90 48 http://www.apache.org/licenses/LICENSE-2.0
attila@90 49
attila@90 50 Unless required by applicable law or agreed to in writing, software
attila@90 51 distributed under the License is distributed on an "AS IS" BASIS,
attila@90 52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
attila@90 53 implied. See the License for the specific language governing
attila@90 54 permissions and limitations under the License.
attila@90 55
attila@90 56 If you choose to use this file in compliance with the BSD License, the
attila@90 57 following notice applies to you:
attila@90 58
attila@90 59 Redistribution and use in source and binary forms, with or without
attila@90 60 modification, are permitted provided that the following conditions are
attila@90 61 met:
attila@90 62 * Redistributions of source code must retain the above copyright
attila@90 63 notice, this list of conditions and the following disclaimer.
attila@90 64 * Redistributions in binary form must reproduce the above copyright
attila@90 65 notice, this list of conditions and the following disclaimer in the
attila@90 66 documentation and/or other materials provided with the distribution.
attila@90 67 * Neither the name of the copyright holder nor the names of
attila@90 68 contributors may be used to endorse or promote products derived from
attila@90 69 this software without specific prior written permission.
attila@90 70
attila@90 71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
attila@90 72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
attila@90 73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
attila@90 74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
attila@90 75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
attila@90 76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
attila@90 77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
attila@90 78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
attila@90 79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
attila@90 80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
attila@90 81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
attila@90 82 */
attila@90 83
attila@90 84 package jdk.internal.dynalink.support;
attila@90 85
attila@90 86 import java.lang.invoke.MethodHandle;
attila@90 87 import java.lang.invoke.MethodHandles;
attila@90 88 import java.lang.invoke.MethodType;
attila@90 89 import java.util.logging.Level;
attila@90 90 import java.util.logging.Logger;
attila@90 91 import jdk.internal.dynalink.linker.LinkerServices;
attila@90 92
attila@90 93
attila@90 94 /**
attila@90 95 * Utility methods for creating typical guards. TODO: introduce reasonable caching of created guards.
attila@90 96 *
attila@90 97 * @author Attila Szegedi
attila@90 98 */
attila@90 99 public class Guards {
attila@90 100 private static final Logger LOG = Logger
attila@90 101 .getLogger(Guards.class.getName(), "jdk.internal.dynalink.support.messages");
attila@90 102
attila@90 103 private Guards() {
attila@90 104 }
attila@90 105
attila@90 106 /**
attila@90 107 * Creates a guard method handle with arguments of a specified type, but with boolean return value. When invoked, it
attila@90 108 * returns true if the first argument is of the specified class (exactly of it, not a subclass). The rest of the
attila@90 109 * arguments will be ignored.
attila@90 110 *
attila@90 111 * @param clazz the class of the first argument to test for
attila@90 112 * @param type the method type
attila@90 113 * @return a method handle testing whether its first argument is of the specified class.
attila@90 114 */
attila@90 115 @SuppressWarnings("boxing")
attila@90 116 public static MethodHandle isOfClass(Class<?> clazz, MethodType type) {
attila@90 117 final Class<?> declaredType = type.parameterType(0);
attila@90 118 if(clazz == declaredType) {
attila@90 119 LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type });
attila@90 120 return constantTrue(type);
attila@90 121 }
attila@90 122 if(!declaredType.isAssignableFrom(clazz)) {
attila@90 123 LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type });
attila@90 124 return constantFalse(type);
attila@90 125 }
attila@90 126 return getClassBoundArgumentTest(IS_OF_CLASS, clazz, 0, type);
attila@90 127 }
attila@90 128
attila@90 129 /**
attila@90 130 * Creates a method handle with arguments of a specified type, but with boolean return value. When invoked, it
attila@90 131 * returns true if the first argument is instance of the specified class or its subclass). The rest of the arguments
attila@90 132 * will be ignored.
attila@90 133 *
attila@90 134 * @param clazz the class of the first argument to test for
attila@90 135 * @param type the method type
attila@90 136 * @return a method handle testing whether its first argument is of the specified class or subclass.
attila@90 137 */
attila@90 138 public static MethodHandle isInstance(Class<?> clazz, MethodType type) {
attila@90 139 return isInstance(clazz, 0, type);
attila@90 140 }
attila@90 141
attila@90 142 /**
attila@90 143 * Creates a method handle with arguments of a specified type, but with boolean return value. When invoked, it
attila@90 144 * returns true if the n'th argument is instance of the specified class or its subclass). The rest of the arguments
attila@90 145 * will be ignored.
attila@90 146 *
attila@90 147 * @param clazz the class of the first argument to test for
attila@90 148 * @param pos the position on the argument list to test
attila@90 149 * @param type the method type
attila@90 150 * @return a method handle testing whether its first argument is of the specified class or subclass.
attila@90 151 */
attila@90 152 @SuppressWarnings("boxing")
attila@90 153 public static MethodHandle isInstance(Class<?> clazz, int pos, MethodType type) {
attila@90 154 final Class<?> declaredType = type.parameterType(pos);
attila@90 155 if(clazz.isAssignableFrom(declaredType)) {
attila@90 156 LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type });
attila@90 157 return constantTrue(type);
attila@90 158 }
attila@90 159 if(!declaredType.isAssignableFrom(clazz)) {
attila@90 160 LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type });
attila@90 161 return constantFalse(type);
attila@90 162 }
attila@90 163 return getClassBoundArgumentTest(IS_INSTANCE, clazz, pos, type);
attila@90 164 }
attila@90 165
attila@90 166 /**
attila@90 167 * Creates a method handle that returns true if the argument in the specified position is a Java array.
attila@90 168 *
attila@90 169 * @param pos the position in the argument lit
attila@90 170 * @param type the method type of the handle
attila@90 171 * @return a method handle that returns true if the argument in the specified position is a Java array; the rest of
attila@90 172 * the arguments are ignored.
attila@90 173 */
attila@90 174 @SuppressWarnings("boxing")
attila@90 175 public static MethodHandle isArray(int pos, MethodType type) {
attila@90 176 final Class<?> declaredType = type.parameterType(pos);
attila@90 177 if(declaredType.isArray()) {
attila@90 178 LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type });
attila@90 179 return constantTrue(type);
attila@90 180 }
attila@90 181 if(!declaredType.isAssignableFrom(Object[].class)) {
attila@90 182 LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type });
attila@90 183 return constantFalse(type);
attila@90 184 }
attila@90 185 return asType(IS_ARRAY, pos, type);
attila@90 186 }
attila@90 187
attila@90 188 /**
attila@90 189 * Return true if it is safe to strongly reference a class from the referred class loader from a class associated
attila@90 190 * with the referring class loader without risking a class loader memory leak.
attila@90 191 *
attila@90 192 * @param referrerLoader the referrer class loader
attila@90 193 * @param referredLoader the referred class loader
attila@90 194 * @return true if it is safe to strongly reference the class
attila@90 195 */
attila@90 196 public static boolean canReferenceDirectly(ClassLoader referrerLoader, final ClassLoader referredLoader) {
attila@90 197 if(referredLoader == null) {
attila@90 198 // Can always refer directly to a system class
attila@90 199 return true;
attila@90 200 }
attila@90 201 if(referrerLoader == null) {
attila@90 202 // System classes can't refer directly to any non-system class
attila@90 203 return false;
attila@90 204 }
attila@90 205 // Otherwise, can only refer directly to classes residing in same or
attila@90 206 // parent class loader.
attila@90 207
attila@90 208 ClassLoader referrer = referrerLoader;
attila@90 209 do {
attila@90 210 if(referrer == referredLoader) {
attila@90 211 return true;
attila@90 212 }
attila@90 213 referrer = referrer.getParent();
attila@90 214 } while(referrer != null);
attila@90 215 return false;
attila@90 216 }
attila@90 217
attila@90 218 private static MethodHandle getClassBoundArgumentTest(MethodHandle test, Class<?> clazz, int pos, MethodType type) {
attila@90 219 // Bind the class to the first argument of the test
attila@90 220 return asType(test.bindTo(clazz), pos, type);
attila@90 221 }
attila@90 222
attila@90 223 /**
attila@90 224 * Takes a guard-test method handle, and adapts it to the requested type, returning a boolean. Only applies
attila@90 225 * conversions as per {@link MethodHandle#asType(MethodType)}.
attila@90 226 * @param test the test method handle
attila@90 227 * @param type the type to adapt the method handle to
attila@90 228 * @return the adapted method handle
attila@90 229 */
attila@90 230 public static MethodHandle asType(MethodHandle test, MethodType type) {
attila@90 231 return test.asType(getTestType(test, type));
attila@90 232 }
attila@90 233
attila@90 234 /**
attila@90 235 * Takes a guard-test method handle, and adapts it to the requested type, returning a boolean. Applies the passed
attila@90 236 * {@link LinkerServices} object's {@link LinkerServices#asType(MethodHandle, MethodType)}.
attila@90 237 * @param linkerServices the linker services to use for type conversions
attila@90 238 * @param test the test method handle
attila@90 239 * @param type the type to adapt the method handle to
attila@90 240 * @return the adapted method handle
attila@90 241 */
attila@90 242 public static MethodHandle asType(LinkerServices linkerServices, MethodHandle test, MethodType type) {
attila@90 243 return linkerServices.asType(test, getTestType(test, type));
attila@90 244 }
attila@90 245
attila@90 246 private static MethodType getTestType(MethodHandle test, MethodType type) {
attila@90 247 return type.dropParameterTypes(test.type().parameterCount(),
attila@90 248 type.parameterCount()).changeReturnType(boolean.class);
attila@90 249 }
attila@90 250
attila@90 251 private static MethodHandle asType(MethodHandle test, int pos, MethodType type) {
attila@90 252 assert test != null;
attila@90 253 assert type != null;
attila@90 254 assert type.parameterCount() > 0;
attila@90 255 assert pos >= 0 && pos < type.parameterCount();
attila@90 256 assert test.type().parameterCount() == 1;
attila@90 257 assert test.type().returnType() == Boolean.TYPE;
attila@90 258 return MethodHandles.permuteArguments(test.asType(test.type().changeParameterType(0, type.parameterType(pos))),
attila@90 259 type.changeReturnType(Boolean.TYPE), new int[] { pos });
attila@90 260 }
attila@90 261
attila@90 262 private static final MethodHandle IS_OF_CLASS = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
attila@90 263 "isOfClass", MethodType.methodType(Boolean.TYPE, Class.class, Object.class));
attila@90 264
attila@90 265 private static final MethodHandle IS_INSTANCE = Lookup.PUBLIC.findVirtual(Class.class, "isInstance",
attila@90 266 MethodType.methodType(Boolean.TYPE, Object.class));
attila@90 267
attila@90 268 private static final MethodHandle IS_ARRAY = new Lookup(MethodHandles.lookup()).findStatic(Guards.class, "isArray",
attila@90 269 MethodType.methodType(Boolean.TYPE, Object.class));
attila@90 270
attila@90 271 private static final MethodHandle IS_IDENTICAL = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
attila@90 272 "isIdentical", MethodType.methodType(Boolean.TYPE, Object.class, Object.class));
attila@90 273
attila@90 274 private static final MethodHandle IS_NULL = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
attila@90 275 "isNull", MethodType.methodType(Boolean.TYPE, Object.class));
attila@90 276
attila@90 277 private static final MethodHandle IS_NOT_NULL = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
attila@90 278 "isNotNull", MethodType.methodType(Boolean.TYPE, Object.class));
attila@90 279
attila@90 280 /**
attila@90 281 * Creates a guard method that tests its only argument for being of an exact particular class.
attila@90 282 * @param clazz the class to test for.
attila@90 283 * @return the desired guard method.
attila@90 284 */
attila@90 285 public static MethodHandle getClassGuard(Class<?> clazz) {
attila@90 286 return IS_OF_CLASS.bindTo(clazz);
attila@90 287 }
attila@90 288
attila@90 289 /**
attila@90 290 * Creates a guard method that tests its only argument for being an instance of a particular class.
attila@90 291 * @param clazz the class to test for.
attila@90 292 * @return the desired guard method.
attila@90 293 */
attila@90 294 public static MethodHandle getInstanceOfGuard(Class<?> clazz) {
attila@90 295 return IS_INSTANCE.bindTo(clazz);
attila@90 296 }
attila@90 297
attila@90 298 /**
attila@90 299 * Creates a guard method that tests its only argument for being referentially identical to another object
attila@90 300 * @param obj the object used as referential identity test
attila@90 301 * @return the desired guard method.
attila@90 302 */
attila@90 303 public static MethodHandle getIdentityGuard(Object obj) {
attila@90 304 return IS_IDENTICAL.bindTo(obj);
attila@90 305 }
attila@90 306
attila@90 307 /**
attila@90 308 * Returns a guard that tests whether the first argument is null.
attila@90 309 * @return a guard that tests whether the first argument is null.
attila@90 310 */
attila@90 311 public static MethodHandle isNull() {
attila@90 312 return IS_NULL;
attila@90 313 }
attila@90 314
attila@90 315 /**
attila@90 316 * Returns a guard that tests whether the first argument is not null.
attila@90 317 * @return a guard that tests whether the first argument is not null.
attila@90 318 */
attila@90 319 public static MethodHandle isNotNull() {
attila@90 320 return IS_NOT_NULL;
attila@90 321 }
attila@90 322
attila@90 323 @SuppressWarnings("unused")
attila@90 324 private static boolean isNull(Object obj) {
attila@90 325 return obj == null;
attila@90 326 }
attila@90 327
attila@90 328 @SuppressWarnings("unused")
attila@90 329 private static boolean isNotNull(Object obj) {
attila@90 330 return obj != null;
attila@90 331 }
attila@90 332
attila@90 333 @SuppressWarnings("unused")
attila@90 334 private static boolean isArray(Object o) {
attila@90 335 return o != null && o.getClass().isArray();
attila@90 336 }
attila@90 337
attila@90 338 @SuppressWarnings("unused")
attila@90 339 private static boolean isOfClass(Class<?> c, Object o) {
attila@90 340 return o != null && o.getClass() == c;
attila@90 341 }
attila@90 342
attila@90 343 @SuppressWarnings("unused")
attila@90 344 private static boolean isIdentical(Object o1, Object o2) {
attila@90 345 return o1 == o2;
attila@90 346 }
attila@90 347
attila@90 348 private static MethodHandle constantTrue(MethodType type) {
attila@90 349 return constantBoolean(Boolean.TRUE, type);
attila@90 350 }
attila@90 351
attila@90 352 private static MethodHandle constantFalse(MethodType type) {
attila@90 353 return constantBoolean(Boolean.FALSE, type);
attila@90 354 }
attila@90 355
attila@90 356 private static MethodHandle constantBoolean(Boolean value, MethodType type) {
attila@90 357 return MethodHandles.permuteArguments(MethodHandles.constant(Boolean.TYPE, value),
attila@90 358 type.changeReturnType(Boolean.TYPE));
attila@90 359 }
attila@90 360 }

mercurial