package org.graalvm.compiler.replacements.verifier;
import java.io.PrintWriter;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import org.graalvm.compiler.replacements.verifier.InjectedDependencies.Dependency;
import org.graalvm.compiler.replacements.verifier.InjectedDependencies.WellKnownDependency;
public abstract class GeneratedPlugin {
protected final ExecutableElement intrinsicMethod;
private boolean needInjectionProvider;
private String pluginName;
public GeneratedPlugin(ExecutableElement intrinsicMethod) {
this.intrinsicMethod = intrinsicMethod;
this.needInjectionProvider = false;
this.pluginName = intrinsicMethod.getEnclosingElement().getSimpleName() + "_" + intrinsicMethod.getSimpleName();
}
public String getPluginName() {
return pluginName;
}
public void setPluginName(String pluginName) {
this.pluginName = pluginName;
}
public void generate(ProcessingEnvironment env, PrintWriter out) {
out.printf(" // class: %s\n", intrinsicMethod.getEnclosingElement());
out.printf(" // method: %s\n", intrinsicMethod);
out.printf(" // generated-by: %s\n", getClass().getName());
out.printf(" private static final class %s extends GeneratedInvocationPlugin {\n", pluginName);
out.printf("\n");
out.printf(" @Override\n");
out.printf(" public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] args) {\n");
InjectedDependencies deps = createExecute(env, out);
out.printf(" }\n");
createPrivateMembers(out, deps);
out.printf(" }\n");
}
public void register(PrintWriter out) {
out.printf(" plugins.register(new %s(", pluginName);
if (needInjectionProvider) {
out.printf("injection");
}
out.printf("), %s.class, \"%s\"", intrinsicMethod.getEnclosingElement(), intrinsicMethod.getSimpleName());
if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) {
out.printf(", InvocationPlugin.Receiver.class");
}
for (VariableElement arg : intrinsicMethod.getParameters()) {
out.printf(", %s.class", getErasedType(arg.asType()));
}
out.printf(");\n");
}
public abstract void (Set<String> imports);
protected abstract InjectedDependencies createExecute(ProcessingEnvironment env, PrintWriter out);
private static TypeMirror resolvedJavaTypeType(ProcessingEnvironment env) {
return env.getElementUtils().getTypeElement("jdk.vm.ci.meta.ResolvedJavaType").asType();
}
static String getErasedType(TypeMirror type) {
switch (type.getKind()) {
case DECLARED:
DeclaredType declared = (DeclaredType) type;
TypeElement element = (TypeElement) declared.asElement();
return element.getQualifiedName().toString();
case TYPEVAR:
return getErasedType(((TypeVariable) type).getUpperBound());
case WILDCARD:
return getErasedType(((WildcardType) type).getExtendsBound());
case ARRAY:
return getErasedType(((ArrayType) type).getComponentType()) + "[]";
default:
return type.toString();
}
}
static boolean hasRawtypeWarning(TypeMirror type) {
switch (type.getKind()) {
case DECLARED:
DeclaredType declared = (DeclaredType) type;
return declared.getTypeArguments().size() > 0;
case TYPEVAR:
return false;
case WILDCARD:
return false;
case ARRAY:
return hasRawtypeWarning(((ArrayType) type).getComponentType());
default:
return false;
}
}
static boolean hasUncheckedWarning(TypeMirror type) {
switch (type.getKind()) {
case DECLARED:
DeclaredType declared = (DeclaredType) type;
for (TypeMirror typeParam : declared.getTypeArguments()) {
if (hasUncheckedWarning(typeParam)) {
return true;
}
}
return false;
case TYPEVAR:
return true;
case WILDCARD:
return ((WildcardType) type).getExtendsBound() != null;
case ARRAY:
return hasUncheckedWarning(((ArrayType) type).getComponentType());
default:
return false;
}
}
private void createPrivateMembers(PrintWriter out, InjectedDependencies deps) {
if (!deps.isEmpty()) {
out.printf("\n");
for (Dependency dep : deps) {
out.printf(" private final %s %s;\n", dep.type, dep.name);
}
out.printf("\n");
out.printf(" private %s(InjectionProvider injection) {\n", pluginName);
for (Dependency dep : deps) {
out.printf(" this.%s = %s;\n", dep.name, dep.inject(intrinsicMethod));
}
out.printf(" }\n");
needInjectionProvider = true;
}
}
protected static String getReturnKind(ExecutableElement method) {
switch (method.getReturnType().getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case CHAR:
case INT:
return "Int";
case LONG:
return "Long";
case FLOAT:
return "Float";
case DOUBLE:
return "Double";
case VOID:
return "Void";
case ARRAY:
case TYPEVAR:
case DECLARED:
return "Object";
default:
throw new IllegalArgumentException(method.getReturnType().toString());
}
}
protected static void constantArgument(ProcessingEnvironment env, PrintWriter out, InjectedDependencies deps, int argIdx, TypeMirror type, int nodeIdx) {
if (hasRawtypeWarning(type)) {
out.printf(" @SuppressWarnings({\"rawtypes\"})\n");
}
out.printf(" %s arg%d;\n", getErasedType(type), argIdx);
out.printf(" if (args[%d].isConstant()) {\n", nodeIdx);
if (type.equals(resolvedJavaTypeType(env))) {
out.printf(" arg%d = %s.asJavaType(args[%d].asConstant());\n", argIdx, deps.use(WellKnownDependency.CONSTANT_REFLECTION), nodeIdx);
} else {
switch (type.getKind()) {
case BOOLEAN:
out.printf(" arg%d = args[%d].asJavaConstant().asInt() != 0;\n", argIdx, nodeIdx);
break;
case BYTE:
out.printf(" arg%d = (byte) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
break;
case CHAR:
out.printf(" arg%d = (char) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
break;
case SHORT:
out.printf(" arg%d = (short) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
break;
case INT:
out.printf(" arg%d = args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
break;
case LONG:
out.printf(" arg%d = args[%d].asJavaConstant().asLong();\n", argIdx, nodeIdx);
break;
case FLOAT:
out.printf(" arg%d = args[%d].asJavaConstant().asFloat();\n", argIdx, nodeIdx);
break;
case DOUBLE:
out.printf(" arg%d = args[%d].asJavaConstant().asDouble();\n", argIdx, nodeIdx);
break;
case ARRAY:
case DECLARED:
out.printf(" arg%d = %s.asObject(%s.class, args[%d].asJavaConstant());\n", argIdx, deps.use(WellKnownDependency.SNIPPET_REFLECTION), getErasedType(type), nodeIdx);
break;
default:
throw new IllegalArgumentException(type.toString());
}
}
out.printf(" } else {\n");
out.printf(" return false;\n");
out.printf(" }\n");
}
}