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