/*
 * Copyright (c) 2017, 2017, 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 com.oracle.svm.jni.access;

import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.word.PointerBase;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.c.CGlobalData;
import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
import com.oracle.svm.core.jdk.NativeLibrarySupport;
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport;
import com.oracle.svm.hosted.c.CGlobalDataFeature;

import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaUtil;
import jdk.vm.ci.meta.Signature;

Encapsulates the code address of a native method's implementation at runtime. This object is accessed in the method's compiled JNINativeCallWrapperMethod.
/** * Encapsulates the code address of a {@code native} method's implementation at runtime. This object * is accessed in the method's compiled {@code JNINativeCallWrapperMethod}. */
public final class JNINativeLinkage { private PointerBase entryPoint = WordFactory.nullPointer(); private final String declaringClass; private final String name; private final String descriptor; private CGlobalDataInfo builtInAddress = null;
Creates an object for linking the address of a native method.
Params:
  • declaringClass – the name of the class declaring the native method
  • name – the name of the native method
  • descriptor – the descriptor of the native method
/** * Creates an object for linking the address of a native method. * * @param declaringClass the {@linkplain JavaType#getName() name} of the class declaring the * native method * @param name the name of the native method * @param descriptor the {@linkplain Signature#toMethodDescriptor() descriptor} of the native * method */
public JNINativeLinkage(String declaringClass, String name, String descriptor) { assert declaringClass.startsWith("L") && declaringClass.endsWith(";") : declaringClass; this.declaringClass = declaringClass; this.name = name; this.descriptor = descriptor; } public String getDeclaringClassName() { return declaringClass; } public String getName() { return name; } public String getDescriptor() { return descriptor; } public boolean isBuiltInFunction() { return (PlatformNativeLibrarySupport.singleton().isBuiltinPkgNative(this.getShortName())); } public CGlobalDataInfo getBuiltInAddress() { assert this.isBuiltInFunction(); if (builtInAddress == null) { CGlobalData<CFunctionPointer> linkage = CGlobalDataFactory.forSymbol(this.getShortName()); builtInAddress = CGlobalDataFeature.singleton().registerAsAccessedOrGet(linkage); } return builtInAddress; }
Sets the native address for the native method represented by this object.
/** * Sets the native address for the {@code native} method represented by this object. */
public void setEntryPoint(CFunctionPointer fnptr) { entryPoint = fnptr; }
Resets the entry point stored for the native method represented by this object, triggering a symbol lookup when the method is called the next time.
/** * Resets the entry point stored for the native method represented by this object, triggering a * symbol lookup when the method is called the next time. */
public void unsetEntryPoint() { entryPoint = WordFactory.nullPointer(); } @Override public int hashCode() { return (((name.hashCode() * 31) + descriptor.hashCode()) * 31) + declaringClass.hashCode(); }
Returns true iff obj is a JNINativeLinkage and has the same declaring class, name and descriptor as this object.
/** * Returns {@code true} iff {@code obj} is a {@link JNINativeLinkage} and has the same declaring * class, name and descriptor as this object. */
@Override public boolean equals(Object obj) { if (obj instanceof JNINativeLinkage) { JNINativeLinkage that = (JNINativeLinkage) obj; return (that == this) || (this.declaringClass.equals(that.declaringClass) && this.name.equals(that.name) && this.descriptor.equals(that.descriptor)); } return false; } @Override public String toString() { String shortName = getShortName(); return MetaUtil.internalNameToJava(declaringClass, true, false) + "." + name + descriptor + " [symbol: " + shortName + " or " + shortName + "__" + getSignature() + "]"; }
Gets the native address for the native method represented by this object, attempting to resolve it if it is currently 0.
/** * Gets the native address for the {@code native} method represented by this object, attempting * to resolve it if it is currently 0. */
public PointerBase getOrFindEntryPoint() { if (entryPoint.isNull()) { String shortName = getShortName(); entryPoint = NativeLibrarySupport.singleton().findSymbol(shortName); if (entryPoint.isNull()) { String longName = shortName + "__" + getSignature(); entryPoint = NativeLibrarySupport.singleton().findSymbol(longName); if (entryPoint.isNull()) { throw new UnsatisfiedLinkError(toString()); } } } return entryPoint; } private String getShortName() { StringBuilder sb = new StringBuilder("Java_"); mangleName(declaringClass, 1, declaringClass.length() - 1, sb); sb.append('_'); mangleName(name, 0, name.length(), sb); return sb.toString(); } private String getSignature() { int closing = descriptor.indexOf(')'); assert descriptor.startsWith("(") && descriptor.indexOf(')') == closing && closing != -1; return mangleName(descriptor, 1, closing, new StringBuilder()).toString(); } private static StringBuilder mangleName(String name, int beginIndex, int endIndex, StringBuilder sb) { // from OpenJDK: nativeLookup.cpp, mangle_name_on() for (int i = beginIndex; i < endIndex; i++) { char c = name.charAt(i); if (c <= 0x7f && Character.isLetterOrDigit(c)) { sb.append(c); } else { switch (c) { case '/': sb.append("_"); break; case '_': sb.append("_1"); break; case ';': sb.append("_2"); break; case '[': sb.append("_3"); break; default: // _0xxxx, where xxxx is lower-case hexadecimal Unicode value sb.append('_'); String hex = Integer.toHexString(c); for (int j = hex.length(); j < 5; j++) { sb.append('0'); // padding } sb.append(hex); break; } } } return sb; } public static String mangle(String s) { return mangleName(s, 0, s.length(), new StringBuilder()).toString(); } }