1 /*
  2  * Copyright (c) 2021, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 package handle.invoker;
 25 
 26 import jdk.incubator.foreign.Addressable;
 27 import jdk.incubator.foreign.CLinker;
 28 import jdk.incubator.foreign.FunctionDescriptor;
 29 import jdk.incubator.foreign.MemoryAddress;
 30 import jdk.incubator.foreign.MemoryLayout;
 31 import jdk.incubator.foreign.MemorySegment;
 32 import jdk.incubator.foreign.ResourceScope;
 33 import jdk.incubator.foreign.SegmentAllocator;
 34 import jdk.incubator.foreign.SymbolLookup;
 35 import jdk.incubator.foreign.ValueLayout;
 36 
 37 import java.lang.invoke.MethodHandle;
 38 import java.lang.invoke.MethodHandles;
 39 import java.lang.invoke.MethodType;
 40 import java.nio.charset.Charset;
 41 import java.nio.file.Path;
 42 import java.util.HashMap;
 43 import java.util.Map;
 44 
 45 public class MethodHandleInvoker {
 46     public void call(MethodHandle methodHandle) throws Throwable {
 47         try {
 48             Object[] args = makeArgs(methodHandle.type());
 49             methodHandle.invokeWithArguments(args);
 50             throw new AssertionError("Call to restricted method did not fail as expected!");
 51         } catch (IllegalCallerException ex) {
 52             if (!ex.getMessage().contains("lookup_module")) {
 53                 throw new AssertionError("Caller module is not lookup_module!");
 54             }
 55         } catch (Throwable ex) {
 56             throw new AssertionError("Call to restricted method did not fail as expected!", ex);
 57         }
 58     }
 59 
 60     static final Map<Class<?>, Object> DEFAULT_VALUES = new HashMap<>();
 61 
 62     static <Z> void addDefaultMapping(Class<Z> carrier, Z value) {
 63         DEFAULT_VALUES.put(carrier, value);
 64     }
 65 
 66     static {
 67         addDefaultMapping(CLinker.class, CLinker.systemCLinker());
 68         addDefaultMapping(Path.class, Path.of("nonExistent"));
 69         addDefaultMapping(String.class, "Hello!");
 70         addDefaultMapping(Runnable.class, () -> {});
 71         addDefaultMapping(MethodHandle.class, MethodHandles.identity(int.class));
 72         addDefaultMapping(Charset.class, Charset.defaultCharset());
 73         addDefaultMapping(MethodType.class, MethodType.methodType(void.class));
 74         addDefaultMapping(MemoryAddress.class, MemoryAddress.NULL);
 75         addDefaultMapping(Addressable.class, MemoryAddress.NULL);
 76         addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT);
 77         addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid());
 78         addDefaultMapping(SymbolLookup.class, SymbolLookup.loaderLookup());
 79         addDefaultMapping(ResourceScope.class, ResourceScope.newImplicitScope());
 80         addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10])));
 81         addDefaultMapping(ValueLayout.OfByte.class, ValueLayout.JAVA_BYTE);
 82         addDefaultMapping(ValueLayout.OfBoolean.class, ValueLayout.JAVA_BOOLEAN);
 83         addDefaultMapping(ValueLayout.OfChar.class, ValueLayout.JAVA_CHAR);
 84         addDefaultMapping(ValueLayout.OfShort.class, ValueLayout.JAVA_SHORT);
 85         addDefaultMapping(ValueLayout.OfInt.class, ValueLayout.JAVA_INT);
 86         addDefaultMapping(ValueLayout.OfFloat.class, ValueLayout.JAVA_FLOAT);
 87         addDefaultMapping(ValueLayout.OfLong.class, ValueLayout.JAVA_LONG);
 88         addDefaultMapping(ValueLayout.OfDouble.class, ValueLayout.JAVA_DOUBLE);
 89         addDefaultMapping(ValueLayout.OfAddress.class, ValueLayout.ADDRESS);
 90         addDefaultMapping(byte.class, (byte)0);
 91         addDefaultMapping(boolean.class, true);
 92         addDefaultMapping(char.class, (char)0);
 93         addDefaultMapping(short.class, (short)0);
 94         addDefaultMapping(int.class, 0);
 95         addDefaultMapping(float.class, 0f);
 96         addDefaultMapping(long.class, 0L);
 97         addDefaultMapping(double.class, 0d);
 98     }
 99 
100     static Object[] makeArgs(MethodType type) {
101         return type.parameterList().stream()
102                 .map(MethodHandleInvoker::makeArg)
103                 .toArray();
104     }
105 
106     static Object makeArg(Class<?> clazz) {
107         Object value = DEFAULT_VALUES.get(clazz);
108         if (value == null) {
109             throw new UnsupportedOperationException(clazz.getName());
110         }
111         return value;
112     }
113 }