1 /*
  2  *  Copyright (c) 2019, 2020, 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 package jdk.internal.foreign;
 28 
 29 import jdk.incubator.foreign.CSupport;
 30 import jdk.incubator.foreign.MemoryAddress;
 31 import jdk.incubator.foreign.MemoryHandles;
 32 import jdk.incubator.foreign.MemoryLayout;
 33 import jdk.incubator.foreign.ForeignLinker;
 34 import jdk.incubator.foreign.ValueLayout;
 35 import jdk.internal.access.foreign.MemoryAddressProxy;
 36 import jdk.internal.foreign.abi.SharedUtils;
 37 import jdk.internal.misc.VM;
 38 import sun.invoke.util.Wrapper;
 39 
 40 import java.lang.invoke.MethodHandle;
 41 import java.lang.invoke.MethodHandles;
 42 import java.lang.invoke.MethodType;
 43 import java.lang.invoke.VarHandle;
 44 import java.util.Optional;
 45 import java.util.function.Supplier;
 46 
 47 /**
 48  * This class contains misc helper functions to support creation of memory segments.
 49  */
 50 public final class Utils {
 51 
 52     private static final String foreignRestrictedAccess = Optional.ofNullable(VM.getSavedProperty("foreign.restricted"))
 53             .orElse("deny");
 54 
 55     private static final MethodHandle ADDRESS_FILTER;
 56 
 57     static {
 58         try {
 59             ADDRESS_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterAddress",
 60                     MethodType.methodType(MemoryAddressProxy.class, MemoryAddress.class));
 61         } catch (Throwable ex) {
 62             throw new ExceptionInInitializerError(ex);
 63         }
 64     }
 65 
 66     public static long alignUp(long n, long alignment) {
 67         return (n + alignment - 1) & -alignment;
 68     }
 69 
 70     public static MemoryAddress alignUp(MemoryAddress ma, long alignment) {
 71         long offset = ma.toRawLongValue();
 72         return ma.addOffset(alignUp(offset, alignment) - offset);
 73     }
 74 
 75     public static long bitsToBytesOrThrow(long bits, Supplier<RuntimeException> exFactory) {
 76         if (bits % 8 == 0) {
 77             return bits / 8;
 78         } else {
 79             throw exFactory.get();
 80         }
 81     }
 82 
 83     public static VarHandle fixUpVarHandle(VarHandle handle) {
 84         // This adaptation is required, otherwise the memory access var handle will have type MemoryAddressProxy,
 85         // and not MemoryAddress (which the user expects), which causes performance issues with asType() adaptations.
 86         return MemoryHandles.filterCoordinates(handle, 0, ADDRESS_FILTER);
 87     }
 88 
 89     private static MemoryAddressProxy filterAddress(MemoryAddress addr) {
 90         return (MemoryAddressImpl)addr;
 91     }
 92 
 93     public static void checkRestrictedAccess(String method) {
 94         switch (foreignRestrictedAccess) {
 95             case "deny" -> throwIllegalAccessError(foreignRestrictedAccess, method);
 96             case "warn" -> System.err.println("WARNING: Accessing restricted foreign method: " + method);
 97             case "debug" -> {
 98                 StringBuilder sb = new StringBuilder("DEBUG: restricted foreign method: \" + method");
 99                 StackWalker.getInstance().forEach(f -> sb.append(System.lineSeparator())
100                         .append("\tat ")
101                         .append(f));
102                 System.err.println(sb.toString());
103             }
104             case "permit" -> {}
105             default -> throwIllegalAccessError(foreignRestrictedAccess, method);
106         }
107     }
108 
109     private static void throwIllegalAccessError(String value, String method) {
110         throw new IllegalAccessError("Illegal access to restricted foreign method: " + method +
111                 " ; system property 'foreign.restricted' is set to '" + value + "'");
112     }
113 
114     public static <Z extends MemoryLayout> Z pick(Z sysv, Z win64, Z aarch64) {
115         ForeignLinker abi = SharedUtils.getSystemLinker();
116         return switch (abi.name()) {
117             case CSupport.SysV.NAME -> sysv;
118             case CSupport.Win64.NAME -> win64;
119             case CSupport.AArch64.NAME -> aarch64;
120             default -> throw new ExceptionInInitializerError("Unexpected ABI: " + abi.name());
121         };
122     }
123 
124     public static void checkPrimitiveCarrierCompat(Class<?> carrier, MemoryLayout layout) {
125         checkLayoutType(layout, ValueLayout.class);
126         if (!isValidPrimitiveCarrier(carrier))
127             throw new IllegalArgumentException("Unsupported carrier: " + carrier);
128         if (Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize())
129             throw new IllegalArgumentException("Carrier size mismatch: " + carrier + " != " + layout);
130     }
131 
132     public static boolean isValidPrimitiveCarrier(Class<?> carrier) {
133         return carrier == byte.class
134             || carrier == short.class
135             || carrier == char.class
136             || carrier == int.class
137             || carrier == long.class
138             || carrier == float.class
139             || carrier == double.class;
140     }
141 
142     public static void checkLayoutType(MemoryLayout layout, Class<? extends MemoryLayout> layoutType) {
143         if (!layoutType.isInstance(layout))
144             throw new IllegalArgumentException("Expected a " + layoutType.getSimpleName() + ": " + layout);
145     }
146 }