nashorn/samples/dynalink/MissingMethodLinkerExporter.java
changeset 34549 412a690d4414
parent 34457 81a65a2faef3
child 41422 97eda72f53b6
equal deleted inserted replaced
34548:44779bfb4c13 34549:412a690d4414
       
     1 /*
       
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  *   - Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer.
       
    10  *
       
    11  *   - Redistributions in binary form must reproduce the above copyright
       
    12  *     notice, this list of conditions and the following disclaimer in the
       
    13  *     documentation and/or other materials provided with the distribution.
       
    14  *
       
    15  *   - Neither the name of Oracle nor the names of its
       
    16  *     contributors may be used to endorse or promote products derived
       
    17  *     from this software without specific prior written permission.
       
    18  *
       
    19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       
    20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
       
    21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       
    23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
    27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
    28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
    29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    30  */
       
    31 
       
    32 import java.lang.invoke.MethodHandle;
       
    33 import java.lang.invoke.MethodHandles;
       
    34 import java.lang.invoke.MethodType;
       
    35 import java.util.ArrayList;
       
    36 import java.util.List;
       
    37 import jdk.dynalink.CallSiteDescriptor;
       
    38 import jdk.dynalink.CompositeOperation;
       
    39 import jdk.dynalink.NamedOperation;
       
    40 import jdk.dynalink.Operation;
       
    41 import jdk.dynalink.StandardOperation;
       
    42 import jdk.dynalink.beans.BeansLinker;
       
    43 import jdk.dynalink.linker.GuardingDynamicLinker;
       
    44 import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
       
    45 import jdk.dynalink.linker.GuardedInvocation;
       
    46 import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
       
    47 import jdk.dynalink.linker.LinkRequest;
       
    48 import jdk.dynalink.linker.LinkerServices;
       
    49 import jdk.dynalink.linker.support.Guards;
       
    50 import jdk.dynalink.linker.support.Lookup;
       
    51 
       
    52 /**
       
    53  * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276).
       
    54  * This linker routes missing methods to Smalltalk-style doesNotUnderstand method.
       
    55  * Object of any Java class that implements MissingMethodHandler is handled by this linker.
       
    56  * For any method call, if a matching Java method is found, it is called. If there is no
       
    57  * method by that name, then MissingMethodHandler.doesNotUnderstand is called.
       
    58  */
       
    59 public final class MissingMethodLinkerExporter extends GuardingDynamicLinkerExporter {
       
    60     static {
       
    61         System.out.println("pluggable dynalink missing method linker loaded");
       
    62     }
       
    63 
       
    64     // represents a MissingMethod - just stores as name and also serves a guard type
       
    65     public static class MissingMethod {
       
    66         private final String name;
       
    67 
       
    68         public MissingMethod(String name) {
       
    69             this.name = name;
       
    70         }
       
    71 
       
    72         public String getName() {
       
    73             return name;
       
    74         }
       
    75     }
       
    76 
       
    77     // MissingMethodHandler.doesNotUnderstand method
       
    78     private static final MethodHandle DOES_NOT_UNDERSTAND;
       
    79 
       
    80     // type of MissingMethodHandler - but "this" and String args are flipped
       
    81     private static final MethodType FLIPPED_DOES_NOT_UNDERSTAND_TYPE;
       
    82 
       
    83     // "is this a MissingMethod?" guard
       
    84     private static final MethodHandle IS_MISSING_METHOD;
       
    85 
       
    86     // MissingMethod object->it's name filter
       
    87     private static final MethodHandle MISSING_METHOD_TO_NAME;
       
    88 
       
    89     static {
       
    90         DOES_NOT_UNDERSTAND = Lookup.PUBLIC.findVirtual(
       
    91             MissingMethodHandler.class,
       
    92             "doesNotUnderstand",
       
    93             MethodType.methodType(Object.class, String.class, Object[].class));
       
    94         FLIPPED_DOES_NOT_UNDERSTAND_TYPE =
       
    95             MethodType.methodType(Object.class, String.class, MissingMethodHandler.class, Object[].class);
       
    96         IS_MISSING_METHOD = Guards.isOfClass(MissingMethod.class,
       
    97             MethodType.methodType(Boolean.TYPE, Object.class));
       
    98         MISSING_METHOD_TO_NAME = Lookup.PUBLIC.findVirtual(MissingMethod.class,
       
    99             "getName", MethodType.methodType(String.class));
       
   100     }
       
   101 
       
   102     // locate the first standard operation from the call descriptor
       
   103     private static StandardOperation getFirstStandardOperation(final CallSiteDescriptor desc) {
       
   104         final Operation base = NamedOperation.getBaseOperation(desc.getOperation());
       
   105         if (base instanceof StandardOperation) {
       
   106             return (StandardOperation)base;
       
   107         } else if (base instanceof CompositeOperation) {
       
   108             final CompositeOperation cop = (CompositeOperation)base;
       
   109             for(int i = 0; i < cop.getOperationCount(); ++i) {
       
   110                 final Operation op = cop.getOperation(i);
       
   111                 if (op instanceof StandardOperation) {
       
   112                     return (StandardOperation)op;
       
   113                 }
       
   114             }
       
   115         }
       
   116         return null;
       
   117     }
       
   118 
       
   119     @Override
       
   120     public List<GuardingDynamicLinker> get() {
       
   121         final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>();
       
   122         final BeansLinker beansLinker = new BeansLinker();
       
   123         linkers.add(new TypeBasedGuardingDynamicLinker() {
       
   124             // only handles MissingMethodHandler and MissingMethod objects
       
   125             @Override
       
   126             public boolean canLinkType(final Class<?> type) {
       
   127                 return
       
   128                     MissingMethodHandler.class.isAssignableFrom(type) ||
       
   129                     type == MissingMethod.class;
       
   130             }
       
   131 
       
   132             @Override
       
   133             public GuardedInvocation getGuardedInvocation(LinkRequest request,
       
   134                 LinkerServices linkerServices) throws Exception {
       
   135                 final Object self = request.getReceiver();
       
   136                 CallSiteDescriptor desc = request.getCallSiteDescriptor();
       
   137 
       
   138                 // any method call is done by two steps. Step (1) GET_METHOD and (2) is CALL
       
   139                 // For step (1), we check if GET_METHOD can succeed by Java linker, if so
       
   140                 // we return that method object. If not, we return a MissingMethod object.
       
   141                 if (self instanceof MissingMethodHandler) {
       
   142                     // Check if this is a named GET_METHOD first.
       
   143                     boolean isGetMethod = getFirstStandardOperation(desc) == StandardOperation.GET_METHOD;
       
   144                     Object name = NamedOperation.getName(desc.getOperation());
       
   145                     if (isGetMethod && name instanceof String) {
       
   146                         GuardingDynamicLinker javaLinker = beansLinker.getLinkerForClass(self.getClass());
       
   147                         GuardedInvocation inv;
       
   148                         try {
       
   149                             inv = javaLinker.getGuardedInvocation(request, linkerServices);
       
   150                         } catch (final Throwable th) {
       
   151                             inv = null;
       
   152                         }
       
   153 
       
   154                         String nameStr = name.toString();
       
   155                         if (inv == null) {
       
   156                             // use "this" for just guard and drop it -- return a constant Method handle
       
   157                             // that returns a newly created MissingMethod object
       
   158                             MethodHandle mh = MethodHandles.constant(Object.class, new MissingMethod(nameStr));
       
   159                             inv = new GuardedInvocation(
       
   160                                 MethodHandles.dropArguments(mh, 0, Object.class),
       
   161                                 Guards.isOfClass(self.getClass(), MethodType.methodType(Boolean.TYPE, Object.class)));
       
   162                         }
       
   163 
       
   164                         return inv;
       
   165                     }
       
   166                 } else if (self instanceof MissingMethod) {
       
   167                     // This is step (2). We call MissingMethodHandler.doesNotUnderstand here
       
   168                     // Check if this is this a CALL first.
       
   169                     boolean isCall = getFirstStandardOperation(desc) == StandardOperation.CALL;
       
   170                     if (isCall) {
       
   171                         MethodHandle mh = DOES_NOT_UNDERSTAND;
       
   172 
       
   173                         // flip "this" and method name (String)
       
   174                         mh = MethodHandles.permuteArguments(mh, FLIPPED_DOES_NOT_UNDERSTAND_TYPE, 1, 0, 2);
       
   175 
       
   176                         // collect rest of the arguments as vararg
       
   177                         mh = mh.asCollector(Object[].class, desc.getMethodType().parameterCount() - 2);
       
   178 
       
   179                         // convert MissingMethod object to it's name
       
   180                         mh = MethodHandles.filterArguments(mh, 0, MISSING_METHOD_TO_NAME);
       
   181                         return new GuardedInvocation(mh, IS_MISSING_METHOD);
       
   182                     }
       
   183                 }
       
   184 
       
   185                 return null;
       
   186             }
       
   187         });
       
   188         return linkers;
       
   189     }
       
   190 }