MissingMethodLinkerExporter.java revision 1805:7caf1f762f1d
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 32import java.lang.invoke.MethodHandle; 33import java.lang.invoke.MethodHandles; 34import java.lang.invoke.MethodType; 35import java.util.ArrayList; 36import java.util.List; 37import jdk.dynalink.CallSiteDescriptor; 38import jdk.dynalink.NamedOperation; 39import jdk.dynalink.NamespaceOperation; 40import jdk.dynalink.Operation; 41import jdk.dynalink.StandardNamespace; 42import jdk.dynalink.StandardOperation; 43import jdk.dynalink.beans.BeansLinker; 44import jdk.dynalink.linker.GuardedInvocation; 45import jdk.dynalink.linker.GuardingDynamicLinker; 46import jdk.dynalink.linker.GuardingDynamicLinkerExporter; 47import jdk.dynalink.linker.LinkRequest; 48import jdk.dynalink.linker.LinkerServices; 49import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker; 50import jdk.dynalink.linker.support.Guards; 51import jdk.dynalink.linker.support.Lookup; 52 53/** 54 * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276). 55 * This linker routes missing methods to Smalltalk-style doesNotUnderstand method. 56 * Object of any Java class that implements MissingMethodHandler is handled by this linker. 57 * For any method call, if a matching Java method is found, it is called. If there is no 58 * method by that name, then MissingMethodHandler.doesNotUnderstand is called. 59 */ 60public final class MissingMethodLinkerExporter extends GuardingDynamicLinkerExporter { 61 static { 62 System.out.println("pluggable dynalink missing method linker loaded"); 63 } 64 65 // represents a MissingMethod - just stores as name and also serves a guard type 66 public static class MissingMethod { 67 private final String name; 68 69 public MissingMethod(final String name) { 70 this.name = name; 71 } 72 73 public String getName() { 74 return name; 75 } 76 } 77 78 // MissingMethodHandler.doesNotUnderstand method 79 private static final MethodHandle DOES_NOT_UNDERSTAND; 80 81 // type of MissingMethodHandler - but "this" and String args are flipped 82 private static final MethodType FLIPPED_DOES_NOT_UNDERSTAND_TYPE; 83 84 // "is this a MissingMethod?" guard 85 private static final MethodHandle IS_MISSING_METHOD; 86 87 // MissingMethod object->it's name filter 88 private static final MethodHandle MISSING_METHOD_TO_NAME; 89 90 static { 91 DOES_NOT_UNDERSTAND = Lookup.PUBLIC.findVirtual( 92 MissingMethodHandler.class, 93 "doesNotUnderstand", 94 MethodType.methodType(Object.class, String.class, Object[].class)); 95 FLIPPED_DOES_NOT_UNDERSTAND_TYPE = 96 MethodType.methodType(Object.class, String.class, MissingMethodHandler.class, Object[].class); 97 IS_MISSING_METHOD = Guards.isOfClass(MissingMethod.class, 98 MethodType.methodType(Boolean.TYPE, Object.class)); 99 MISSING_METHOD_TO_NAME = Lookup.PUBLIC.findVirtual(MissingMethod.class, 100 "getName", MethodType.methodType(String.class)); 101 } 102 103 @Override 104 public List<GuardingDynamicLinker> get() { 105 final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>(); 106 final BeansLinker beansLinker = new BeansLinker(); 107 linkers.add(new TypeBasedGuardingDynamicLinker() { 108 // only handles MissingMethodHandler and MissingMethod objects 109 @Override 110 public boolean canLinkType(final Class<?> type) { 111 return 112 MissingMethodHandler.class.isAssignableFrom(type) || 113 type == MissingMethod.class; 114 } 115 116 @Override 117 public GuardedInvocation getGuardedInvocation(final LinkRequest request, 118 final LinkerServices linkerServices) throws Exception { 119 final Object self = request.getReceiver(); 120 final CallSiteDescriptor desc = request.getCallSiteDescriptor(); 121 122 // any method call is done by two steps. Step (1) GET_METHOD and (2) is CALL 123 // For step (1), we check if GET_METHOD can succeed by Java linker, if so 124 // we return that method object. If not, we return a MissingMethod object. 125 if (self instanceof MissingMethodHandler) { 126 // Check if this is a named GET_METHOD first. 127 final Operation namedOp = desc.getOperation(); 128 final Operation namespaceOp = NamedOperation.getBaseOperation(namedOp); 129 final Operation op = NamespaceOperation.getBaseOperation(namespaceOp); 130 131 final boolean isGetMethod = op == StandardOperation.GET && StandardNamespace.findFirst(namespaceOp) == StandardNamespace.METHOD; 132 final Object name = NamedOperation.getName(namedOp); 133 if (isGetMethod && name instanceof String) { 134 final GuardingDynamicLinker javaLinker = beansLinker.getLinkerForClass(self.getClass()); 135 GuardedInvocation inv; 136 try { 137 inv = javaLinker.getGuardedInvocation(request, linkerServices); 138 } catch (final Throwable th) { 139 inv = null; 140 } 141 142 final String nameStr = name.toString(); 143 if (inv == null) { 144 // use "this" for just guard and drop it -- return a constant Method handle 145 // that returns a newly created MissingMethod object 146 final MethodHandle mh = MethodHandles.constant(Object.class, new MissingMethod(nameStr)); 147 inv = new GuardedInvocation( 148 MethodHandles.dropArguments(mh, 0, Object.class), 149 Guards.isOfClass(self.getClass(), MethodType.methodType(Boolean.TYPE, Object.class))); 150 } 151 152 return inv; 153 } 154 } else if (self instanceof MissingMethod) { 155 // This is step (2). We call MissingMethodHandler.doesNotUnderstand here 156 // Check if this is this a CALL first. 157 final boolean isCall = NamedOperation.getBaseOperation(desc.getOperation()) == StandardOperation.CALL; 158 if (isCall) { 159 MethodHandle mh = DOES_NOT_UNDERSTAND; 160 161 // flip "this" and method name (String) 162 mh = MethodHandles.permuteArguments(mh, FLIPPED_DOES_NOT_UNDERSTAND_TYPE, 1, 0, 2); 163 164 // collect rest of the arguments as vararg 165 mh = mh.asCollector(Object[].class, desc.getMethodType().parameterCount() - 2); 166 167 // convert MissingMethod object to it's name 168 mh = MethodHandles.filterArguments(mh, 0, MISSING_METHOD_TO_NAME); 169 return new GuardedInvocation(mh, IS_MISSING_METHOD); 170 } 171 } 172 173 return null; 174 } 175 }); 176 return linkers; 177 } 178} 179