nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/support/Guards.java
changeset 34456 84eaea8d0574
parent 34096 5ac6287ec71a
parent 34455 cc9f05d3caf0
child 34457 81a65a2faef3
equal deleted inserted replaced
34096:5ac6287ec71a 34456:84eaea8d0574
     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 
       
    84 package jdk.internal.dynalink.linker.support;
       
    85 
       
    86 import java.lang.invoke.MethodHandle;
       
    87 import java.lang.invoke.MethodHandles;
       
    88 import java.lang.invoke.MethodType;
       
    89 import java.util.logging.Level;
       
    90 import java.util.logging.Logger;
       
    91 import jdk.internal.dynalink.DynamicLinker;
       
    92 import jdk.internal.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  */
       
    99 public final class Guards {
       
   100     private static final Logger LOG = Logger
       
   101             .getLogger(Guards.class.getName(), "jdk.internal.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 }