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.MemoryLayouts;
32 import jdk.incubator.foreign.ResourceScope;
33 import jdk.incubator.foreign.SegmentAllocator;
34 import jdk.incubator.foreign.SymbolLookup;
35 
36 import java.lang.invoke.MethodHandle;
37 import java.lang.invoke.MethodHandles;
38 import java.lang.invoke.MethodType;
39 import java.nio.charset.Charset;
40 import java.nio.file.Path;
41 import java.util.HashMap;
42 import java.util.Map;
43 
44 public class MethodHandleInvoker {
45     public void call(MethodHandle methodHandle) throws Throwable {
46         try {
47             Object[] args = makeArgs(methodHandle.type());
48             methodHandle.invokeWithArguments(args);
49             throw new AssertionError("Call to restricted method did not fail as expected!");
50         } catch (IllegalCallerException ex) {
51             if (!ex.getMessage().contains("lookup_module")) {
52                 throw new AssertionError("Caller module is not lookup_module!");
53             }
54         } catch (Throwable ex) {
55             throw new AssertionError("Call to restricted method did not fail as expected!");
56         }
57     }
58 
59     static final Map<Class<?>, Object> DEFAULT_VALUES = new HashMap<>();
60 
61     static <Z> void addDefaultMapping(Class<Z> carrier, Z value) {
62         DEFAULT_VALUES.put(carrier, value);
63     }
64 
65     static {
66         addDefaultMapping(CLinker.class, CLinker.getInstance());
67         addDefaultMapping(long.class, 0L);
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, MemoryLayouts.JAVA_INT);
77         addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid());
78         addDefaultMapping(SymbolLookup.class, SymbolLookup.loaderLookup());
79         addDefaultMapping(ResourceScope.class, ResourceScope.newImplicitScope());
80         addDefaultMapping(SegmentAllocator.class, (size, align) -> null);
81     }
82 
83     static Object[] makeArgs(MethodType type) {
84         return type.parameterList().stream()
85                 .map(MethodHandleInvoker::makeArg)
86                 .toArray();
87     }
88 
89     static Object makeArg(Class<?> clazz) {
90         Object value = DEFAULT_VALUES.get(clazz);
91         if (value == null) {
92             throw new UnsupportedOperationException(clazz.getName());
93         }
94         return value;
95     }
96 }