/* * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang.invoke; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.foreign.abi.NativeEntryPoint; import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.MethodHandleNatives.Constants.LM_TRUSTED; import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic; import static java.lang.invoke.MethodHandleStatics.newInternalError; /** * This class models a method handle to a native function. A native method handle is made up of a {@link NativeEntryPoint}, * which is used to capture the characteristics of the native call (such as calling convention to be used, * or whether a native transition is required) and a fallback method handle, which can be used * when intrinsification of this method handle is not possible. */ /*non-public*/ final class NativeMethodHandle extends MethodHandle { final NativeEntryPoint nep; private NativeMethodHandle(MethodType type, LambdaForm form, NativeEntryPoint nep) { super(type, form); this.nep = nep; } /** * Creates a new native method handle with given {@link NativeEntryPoint} and fallback method handle. */ public static MethodHandle make(NativeEntryPoint nep) { MethodType type = nep.type(); if (!allTypesPrimitive(type)) throw new IllegalArgumentException("Type must only contain primitives: " + type); LambdaForm lform = preparedLambdaForm(type); return new NativeMethodHandle(type, lform, nep); } private static boolean allTypesPrimitive(MethodType type) { if (!type.returnType().isPrimitive()) return false; for (Class pType : type.parameterArray()) { if (!pType.isPrimitive()) return false; } return true; } private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); private static LambdaForm preparedLambdaForm(MethodType mtype) { int id = MethodTypeForm.LF_INVNATIVE; mtype = mtype.basicType(); LambdaForm lform = mtype.form().cachedLambdaForm(id); if (lform != null) return lform; lform = makePreparedLambdaForm(mtype); return mtype.form().setCachedLambdaForm(id, lform); } private static LambdaForm makePreparedLambdaForm(MethodType mtype) { MethodType linkerType = mtype .appendParameterTypes(Object.class); // NEP MemberName linker = new MemberName(MethodHandle.class, "linkToNative", linkerType, REF_invokeStatic); try { linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, LM_TRUSTED, NoSuchMethodException.class); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } final int NMH_THIS = 0; final int ARG_BASE = 1; final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); int nameCursor = ARG_LIMIT; final int GET_NEP = nameCursor++; final int LINKER_CALL = nameCursor++; LambdaForm.Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); assert (names.length == nameCursor); names[GET_NEP] = new LambdaForm.Name(Lazy.NF_internalNativeEntryPoint, names[NMH_THIS]); Object[] outArgs = new Object[linkerType.parameterCount()]; System.arraycopy(names, ARG_BASE, outArgs, 0, mtype.parameterCount()); outArgs[outArgs.length - 1] = names[GET_NEP]; names[LINKER_CALL] = new LambdaForm.Name(linker, outArgs); LambdaForm lform = new LambdaForm(ARG_LIMIT, names, LAST_RESULT); // This is a tricky bit of code. Don't send it through the LF interpreter. lform.compileToBytecode(); return lform; } final @Override MethodHandle copyWith(MethodType mt, LambdaForm lf) { assert (this.getClass() == NativeMethodHandle.class); // must override in subclasses return new NativeMethodHandle(mt, lf, nep); } @Override BoundMethodHandle rebind() { return BoundMethodHandle.makeReinvoker(this); } @ForceInline static Object internalNativeEntryPoint(Object mh) { return ((NativeMethodHandle)mh).nep; } /** * Pre-initialized NamedFunctions for bootstrapping purposes. * Factored in an inner class to delay initialization until first usage. */ private static class Lazy { static final NamedFunction NF_internalNativeEntryPoint; static { try { Class THIS_CLASS = NativeMethodHandle.class; NamedFunction[] nfs = new NamedFunction[]{ NF_internalNativeEntryPoint = new NamedFunction( THIS_CLASS.getDeclaredMethod("internalNativeEntryPoint", Object.class)), }; for (NamedFunction nf : nfs) { // Each nf must be statically invocable or we get tied up in our bootstraps. assert (InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; nf.resolve(); } } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } } } }