Skip to content

Commit

Permalink
[GR-40485] Espresso Interop improvements.
Browse files Browse the repository at this point in the history
PullRequest: graal/12504
  • Loading branch information
javeleon committed Sep 10, 2022
2 parents 94f23cf + 14d223c commit ba30d78
Show file tree
Hide file tree
Showing 21 changed files with 666 additions and 259 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.espresso.polyglot;

public interface GuestTypeConversion<T> {
T toGuest(Object polyglotInstance);
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@ public List<String> apply(String strings) {
usageSyntax = "my.first.MyInterface;my.second.MySecondInterface;...") //
public static final OptionKey<List<String>> PolyglotInterfaceMappings = new OptionKey<>(Collections.emptyList(), STRINGS_OPTION_TYPE_SEPARATED_BY_SEMI_COLON);

@Option(help = "Option to enable target type conversion by specifying a conversion class.", //
category = OptionCategory.USER, //
stability = OptionStability.EXPERIMENTAL, //
usageSyntax = "java.PolyglotTypeConverters.java.lang.Optional=my.type.conversion.Implementation") //
public static final OptionKey<OptionMap<String>> PolyglotTypeConverters = OptionKey.mapOf(String.class);

@Option(help = "Enable assertions.", //
category = OptionCategory.USER, //
stability = OptionStability.STABLE, //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ public static void ensureInitialized() {
public static final Symbol<Name> RUNTIME_ERROR = StaticSymbols.putName("RUNTIME_ERROR");
public static final Symbol<Name> PARSE_ERROR = StaticSymbols.putName("PARSE_ERROR");
public static final Symbol<Name> create = StaticSymbols.putName("create");
public static final Symbol<Name> toGuest = StaticSymbols.putName("toGuest");

// Class redefinition plugin helpers
public static final Symbol<Name> flushFromCaches = StaticSymbols.putName("flushFromCaches");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ boolean isMetaObject() {
@ExportMessage
public Object getMetaQualifiedName() {
assert isMetaObject();
return getMeta().java_lang_Class_getTypeName.invokeDirect(mirror());
return getTypeName();
}

@ExportMessage
Expand Down Expand Up @@ -513,6 +513,9 @@ public int compare(ObjectKlass.KlassVersion k1, ObjectKlass.KlassVersion k2) {
@CompilationFinal //
private Class<?> dispatch;

@CompilationFinal //
private StaticObject typeName;

protected Object prepareThread;

// Raw modifiers provided by the VM.
Expand Down Expand Up @@ -693,6 +696,14 @@ public final StaticObject initializeEspressoClass() {
return result;
}

public final StaticObject getTypeName() {
if (typeName == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
typeName = (StaticObject) getMeta().java_lang_Class_getTypeName.invokeDirect(mirror());
}
return typeName;
}

/**
* Gets the array class type representing an array with elements of this type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,28 @@ public static String mangleMethod(Symbol<Type> declaringClass, String name, Symb
return result.toString();
}

/**
* Mangle a method name and signature to the symbols to be used for a Truffle jni-named method
* call. Signature must not be <code>null</code>. Truffle jni names are made up of the method
* name, the return type and the parameter types. Note that the declaring class and the 'Java_'
* marker is omitted from the result here.
*
* @param methodName a Java method name (not checked here for validity)
* @param signature if non-null, a method signature to include in the mangled name
* @return a mangled jni-style string as described above
*/
public static String truffleJniMethodName(String methodName, Symbol<Signature> signature) {
assert signature != null;
final StringBuilder result = new StringBuilder(100);
result.append(mangle(methodName)).append("__");
final String sig = signature.toString();
final String returnType = sig.substring(sig.lastIndexOf(')') + 1).replace('/', '.').replace('$', '.');
result.append(mangle(returnType));
final String parametersSignature = sig.substring(1, sig.lastIndexOf(')')).replace('/', '.').replace('$', '.');
result.append(mangle(parametersSignature));
return result.toString();
}

private static String mangleChar(char ch) {
final String s = Integer.toHexString(ch);
assert s.length() <= 4;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@
import com.oracle.truffle.api.impl.asm.ClassWriter;
import com.oracle.truffle.api.impl.asm.Label;
import com.oracle.truffle.api.impl.asm.MethodVisitor;
import com.oracle.truffle.espresso.descriptors.Symbol;
import com.oracle.truffle.espresso.impl.Klass;
import com.oracle.truffle.espresso.impl.Method;
import com.oracle.truffle.espresso.impl.ObjectKlass;
import com.oracle.truffle.espresso.jni.Mangle;
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.meta.JavaKind;
import com.oracle.truffle.espresso.meta.Meta;
Expand Down Expand Up @@ -294,6 +296,7 @@ private void addProxyMethod(Method m) {
Klass[] parameterTypes = m.resolveParameterKlasses();
Klass returnType = m.resolveReturnKlass();
ObjectKlass[] exceptionTypes = m.getCheckedExceptions();
Symbol<Symbol.Signature> signature = m.getRawSignature();

String sig = name + getParameterDescriptors(parameterTypes);
List<ProxyMethod> sigmethods = proxyMethods.get(sig);
Expand All @@ -319,7 +322,7 @@ private void addProxyMethod(Method m) {
proxyMethods.put(sig, sigmethods);
}
sigmethods.add(new ProxyMethod(name, parameterTypes, returnType,
exceptionTypes, isVarArgs(m.getModifiers())));
exceptionTypes, isVarArgs(m.getModifiers()), signature));
}

private static boolean isVarArgs(int modifiers) {
Expand Down Expand Up @@ -428,14 +431,16 @@ private final class ProxyMethod {
public Klass returnType;
public Klass[] exceptionTypes;
boolean isVarArgs;
Symbol<Symbol.Signature> signature;

private ProxyMethod(String methodName, Klass[] parameterTypes,
Klass returnType, Klass[] exceptionTypes, boolean isVarArgs) {
Klass returnType, Klass[] exceptionTypes, boolean isVarArgs, Symbol<Symbol.Signature> signature) {
this.methodName = methodName;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
this.exceptionTypes = exceptionTypes;
this.isVarArgs = isVarArgs;
this.signature = signature;
}

private void generateMethod(ClassWriter cw) {
Expand Down Expand Up @@ -476,7 +481,7 @@ private void generateMethod(ClassWriter cw) {
mv.visitLabel(startBlock);

mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(methodName);
mv.visitLdcInsn(Mangle.truffleJniMethodName(methodName, signature));

if (parameterTypes.length > 0) {
// Create an array and fill with the parameters converting primitives to wrappers
Expand Down Expand Up @@ -596,7 +601,11 @@ private void codeUnwrapReturnValue(MethodVisitor mv, Klass type) {
throw new AssertionError();
}
} else {
mv.visitTypeInsn(CHECKCAST, dotToSlash(type.getNameAsString()));
if (type.isArray()) {
mv.visitTypeInsn(CHECKCAST, type.getTypeAsString());
} else {
mv.visitTypeInsn(CHECKCAST, type.getNameAsString());
}
mv.visitInsn(ARETURN);
}
}
Expand Down Expand Up @@ -671,14 +680,7 @@ private static String getFieldType(Klass type) {
if (type.isPrimitive()) {
return String.valueOf(JavaKind.fromTypeString(type.getTypeAsString()).getTypeChar());
} else if (type.isArray()) {
/*
* According to JLS 20.3.2, the getName() method on Class does return the VM type
* descriptor format for array classes (only); using that should be quicker than the
* otherwise obvious code:
*
* return "[" + getTypeDescriptor(type.getComponentType());
*/
return type.getNameAsString().replace('.', '/');
return type.getTypeAsString();
} else {
return "L" + dotToSlash(type.getNameAsString()) + ";";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
*/
package com.oracle.truffle.espresso.nodes.interop;

import com.oracle.truffle.api.CompilerDirectives;
import java.util.HashSet;
import java.util.Set;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
Expand All @@ -39,10 +41,6 @@
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.nodes.EspressoNode;
import com.oracle.truffle.espresso.runtime.EspressoContext;
import com.oracle.truffle.espresso.runtime.PolyglotInterfaceMappings;

import java.util.HashSet;
import java.util.Set;

@GenerateUncached
public abstract class LookupProxyKlassNode extends EspressoNode {
Expand All @@ -51,46 +49,28 @@ public abstract class LookupProxyKlassNode extends EspressoNode {
LookupProxyKlassNode() {
}

public abstract ObjectKlass execute(Object metaObject, Klass targetType) throws ClassCastException;

static String getMetaName(Object metaObject, InteropLibrary interop) {
assert interop.isMetaObject(metaObject);
try {
return interop.asString(interop.getMetaQualifiedName(metaObject));
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw EspressoError.shouldNotReachHere();
}
}

static boolean isSame(Object metaObject, String metaQualifiedName, InteropLibrary interop) {
assert interop.isMetaObject(metaObject);
return getMetaName(metaObject, interop).equals(metaQualifiedName);
}
public abstract ObjectKlass execute(Object metaObject, String metaName, Klass targetType) throws ClassCastException;

@SuppressWarnings("unused")
@Specialization(guards = {"isSame(metaObject, metaName, interop)", "targetType == cachedTargetType"}, limit = "LIMIT")
ObjectKlass doCached(Object metaObject, Klass targetType,
@Specialization(guards = {"targetType == cachedTargetType", "metaName == cachedMetaName"}, limit = "LIMIT")
ObjectKlass doCached(Object metaObject, String metaName, Klass targetType,
@Cached("metaObject") Object cachedMetaObject,
@Cached("targetType") Klass cachedTargetType,
@CachedLibrary(limit = "LIMIT") InteropLibrary interop,
@Cached("getMetaName(metaObject, interop)") String metaName,
@Cached("doUncached(metaObject, targetType, interop)") ObjectKlass cachedProxyKlass) throws ClassCastException {
assert cachedProxyKlass == doUncached(metaObject, targetType, interop);
@Cached("metaName") String cachedMetaName,
@Cached("doUncached(metaObject, metaName, targetType, interop)") ObjectKlass cachedProxyKlass) throws ClassCastException {
assert cachedProxyKlass == doUncached(metaObject, metaName, targetType, interop);
return cachedProxyKlass;
}

@TruffleBoundary
@Specialization(replaces = "doCached")
ObjectKlass doUncached(Object metaObject, Klass targetType,
ObjectKlass doUncached(Object metaObject, String metaName, Klass targetType,
@CachedLibrary(limit = "LIMIT") InteropLibrary interop) throws ClassCastException {

assert interop.isMetaObject(metaObject);
String metaName;
try {
metaName = interop.asString(interop.getMetaQualifiedName(metaObject));
} catch (UnsupportedMessageException e) {
throw EspressoError.shouldNotReachHere();
if (!getContext().interfaceMappingsEnabled()) {
return null;
}
assert interop.isMetaObject(metaObject);
EspressoForeignProxyGenerator.GeneratedProxyBytes proxyBytes = getContext().getProxyBytesOrNull(metaName);
if (proxyBytes == null) {
// cache miss
Expand Down Expand Up @@ -126,15 +106,15 @@ private static Klass lookupOrDefineInBindingsLoader(EspressoForeignProxyGenerato
return proxyKlass;
}

private static void fillParentInterfaces(Object metaObject, InteropLibrary interop, PolyglotInterfaceMappings mappings, Set<ObjectKlass> parents) throws ClassCastException {
private static void fillParentInterfaces(Object metaObject, InteropLibrary interop, PolyglotTypeMappings mappings, Set<ObjectKlass> parents) throws ClassCastException {
try {
if (interop.hasMetaParents(metaObject)) {
Object metaParents = interop.getMetaParents(metaObject);

long arraySize = interop.getArraySize(metaParents);
for (long i = 0; i < arraySize; i++) {
Object parent = interop.readArrayElement(metaParents, i);
ObjectKlass mappedKlass = mappings.mapName(interop.asString(interop.getMetaQualifiedName(parent)));
ObjectKlass mappedKlass = mappings.mapInterfaceName(interop.asString(interop.getMetaQualifiedName(parent)));
if (mappedKlass != null) {
parents.add(mappedKlass);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2022, 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.
*
* 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.truffle.espresso.nodes.interop;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.espresso.nodes.EspressoNode;

@GenerateUncached
public abstract class LookupTypeConverterNode extends EspressoNode {
static final int LIMIT = 3;

LookupTypeConverterNode() {
}

public abstract PolyglotTypeMappings.TypeConverter execute(String metaName) throws ClassCastException;

@SuppressWarnings("unused")
@Specialization(guards = {"metaName == cachedMetaName"}, limit = "LIMIT")
PolyglotTypeMappings.TypeConverter doCached(String metaName,
@Cached("metaName") String cachedMetaName,
@Cached("doUncached(metaName)") PolyglotTypeMappings.TypeConverter converter) throws ClassCastException {
assert converter == doUncached(metaName);
return converter;
}

@TruffleBoundary
@Specialization(replaces = "doCached")
PolyglotTypeMappings.TypeConverter doUncached(String metaName) throws ClassCastException {
return getContext().getPolyglotInterfaceMappings().mapTypeConversion(metaName);
}
}
Loading

0 comments on commit ba30d78

Please sign in to comment.