/*
 * Copyright (c) 2020, 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.invoke.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 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.
/** * 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 <em>fallback</em> method handle, which can be used * when intrinsification of this method handle is not possible. */
/*non-public*/ class NativeMethodHandle extends MethodHandle { final NativeEntryPoint nep; final MethodHandle fallback; private NativeMethodHandle(MethodType type, LambdaForm form, MethodHandle fallback, NativeEntryPoint nep) { super(type, form); this.fallback = fallback; this.nep = nep; }
Creates a new native method handle with given NativeEntryPoint and fallback method handle.
/** * Creates a new native method handle with given {@link NativeEntryPoint} and <em>fallback</em> method handle. */
public static MethodHandle make(NativeEntryPoint nep, MethodHandle fallback) { MethodType type = nep.type(); if (!allTypesPrimitive(type)) throw new IllegalArgumentException("Type must only contain primitives: " + type); if (type != fallback.type()) throw new IllegalArgumentException("Type of fallback must match"); LambdaForm lform = preparedLambdaForm(type); return new NativeMethodHandle(type, lform, fallback, 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.insertParameterTypes(0, MethodHandle.class) .appendParameterTypes(Object.class); 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_FALLBACK = nameCursor++; 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_FALLBACK] = new LambdaForm.Name(Lazy.NF_internalFallback, names[NMH_THIS]); names[GET_NEP] = new LambdaForm.Name(Lazy.NF_internalNativeEntryPoint, names[NMH_THIS]); Object[] outArgs = new Object[linkerType.parameterCount()]; // Need to pass fallback here so we can call it without destroying the receiver register!! outArgs[0] = names[GET_FALLBACK]; System.arraycopy(names, ARG_BASE, outArgs, 1, 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, fallback, nep); } @Override BoundMethodHandle rebind() { return BoundMethodHandle.makeReinvoker(this); } @ForceInline static Object internalNativeEntryPoint(Object mh) { return ((NativeMethodHandle)mh).nep; } @ForceInline static MethodHandle internalFallback(Object mh) { return ((NativeMethodHandle)mh).fallback; }
Pre-initialized NamedFunctions for bootstrapping purposes. Factored in an inner class to delay initialization until first usage.
/** * 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 final NamedFunction NF_internalFallback; static { try { Class<NativeMethodHandle> THIS_CLASS = NativeMethodHandle.class; NamedFunction[] nfs = new NamedFunction[]{ NF_internalNativeEntryPoint = new NamedFunction( THIS_CLASS.getDeclaredMethod("internalNativeEntryPoint", Object.class)), NF_internalFallback = new NamedFunction( THIS_CLASS.getDeclaredMethod("internalFallback", 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); } } } }