/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2015-2016, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.kotlin;
import co.paralleluniverse.fibers.instrument.LogLevel;
import co.paralleluniverse.fibers.instrument.MethodDatabase;
import co.paralleluniverse.fibers.instrument.SimpleSuspendableClassifier;
import co.paralleluniverse.fibers.instrument.SuspendableClassifier;
import java.util.ArrayList;
/**
* Quasar-Kotlin M14 integration.
*
* @author circlespainter
*/
public class KotlinClassifier implements SuspendableClassifier {
private static final String PKG_PREFIX = "kotlin";
private static final String[][] supers;
private static final String[] excludePrefixes;
static {
final ArrayList<String[]> res = new ArrayList<>();
// Kotlin reflection support
res.add(sa("kotlin/reflect/KCallable", "call", "callBy"));
res.add(sa("kotlin/reflect/KProperty0", "get"));
res.add(sa("kotlin/reflect/KMutableProperty0", "set"));
res.add(sa("kotlin/reflect/KProperty1", "get"));
res.add(sa("kotlin/reflect/KMutableProperty1", "set"));
res.add(sa("kotlin/reflect/KProperty2", "get"));
res.add(sa("kotlin/reflect/KMutableProperty2", "set"));
// Kotlin functions support
for (int i = 0; i <= 22; i++)
res.add(sa("kotlin/jvm/functions/Function" + i, "invoke"));
// Kotlin M14 doesn't seem to add `@Suspendable` to the generated `run` when passing a `@Suspendable` lambda
res.add(sa("co/paralleluniverse/strands/SuspendableCallable", "run"));
res.add(sa("co/paralleluniverse/strands/SuspendableRunnable", "run"));
supers = res.toArray(new String[0][0]);
// Class prefixes that are known not to suspend
excludePrefixes = new String[] {
// TODO: this specifically is also known to cause a `VerifyError` when instrumented, see #146
"kotlin/reflect/jvm/internal/impl/descriptors/impl/ModuleDescriptorImpl",
// Handle the same class, when shaded within kotlin-compiler[-embeddable]
"org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl"
};
}
@Override
public MethodDatabase.SuspendableType isSuspendable (
MethodDatabase db,
String sourceName, String sourceDebugInfo,
boolean isInterface, String className, String superClassName, String[] interfaces,
String methodName, String methodDesc, String methodSignature, String[] methodExceptions
) {
for (final String[] s : supers) {
if (className.equals(s[0]))
for (int i = 1; i < s.length; i++) {
if (methodName.matches(s[i])) {
if (db.isVerbose())
db.getLog().log(LogLevel.INFO, KotlinClassifier.class.getName() + ": " + className + "." + methodName + " supersOrEqual " + s[0] + "." + s[i]);
return MethodDatabase.SuspendableType.SUSPENDABLE_SUPER;
}
}
}
// Don't consider Kotlin user files without inner classes
if (className != null && !className.startsWith(PKG_PREFIX)
&& !(className.contains("$") && sourceName != null && sourceName.toLowerCase().endsWith(".kt")))
return null;
// Exclude packages known not to suspend
for (final String s : excludePrefixes) {
if (className != null && className.startsWith(s))
return null;
}
for (final String[] s : supers) {
if (SimpleSuspendableClassifier.extendsOrImplements(s[0], db, className, superClassName, interfaces))
for (int i = 1; i < s.length; i++) {
if (methodName.matches(s[i])) {
if (db.isVerbose())
db.getLog().log(LogLevel.INFO, KotlinClassifier.class.getName() + ": " + className + "." + methodName + " extends " + s[0] + "." + s[i]);
return MethodDatabase.SuspendableType.SUSPENDABLE;
}
}
}
return null;
}
private static String[] sa(String... elems) {
return elems;
}
}