/*
* Copyright (c) 2016, 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.dsl.processor.interop;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.MessageResolution;
import com.oracle.truffle.api.interop.Resolve;
import com.oracle.truffle.dsl.processor.java.ElementUtils;
/**
* THIS IS NOT PUBLIC API.
*/
public abstract class MessageGenerator {
protected static final String ACCESS_METHOD_NAME = "access";
protected final TypeElement element;
protected final String packageName;
protected final String clazzName;
protected final String messageName;
protected final String userClassName;
protected final String receiverClassName;
protected final ProcessingEnvironment processingEnv;
protected final ForeignAccessFactoryGenerator containingForeignAccessFactory;
MessageGenerator(ProcessingEnvironment processingEnv, Resolve resolveAnnotation, MessageResolution messageResolutionAnnotation, TypeElement element,
ForeignAccessFactoryGenerator containingForeignAccessFactory) {
this.processingEnv = processingEnv;
this.element = element;
this.receiverClassName = Utils.getReceiverTypeFullClassName(messageResolutionAnnotation);
this.packageName = ElementUtils.getPackageName(element);
this.messageName = resolveAnnotation.message();
this.userClassName = ElementUtils.getQualifiedName(element);
this.clazzName = Utils.getSimpleResolveClassName(element);
this.containingForeignAccessFactory = containingForeignAccessFactory;
}
public final void generate() throws IOException {
JavaFileObject file = processingEnv.getFiler().createSourceFile(Utils.getFullResolveClassName(element), element);
Writer w = file.openWriter();
w.append("package ").append(packageName).append(";\n");
appendImports(w);
Utils.appendMessagesGeneratedByInformation(w, "", containingForeignAccessFactory.getFullClassName(), ElementUtils.getQualifiedName(element));
Utils.appendVisibilityModifier(w, element);
w.append("abstract class ").append(clazzName).append(" extends ").append(userClassName).append(" {\n");
appendExecuteWithTarget(w);
appendSpecializations(w);
appendRootNode(w);
appendRootNodeFactory(w);
w.append("}\n");
w.close();
}
public final List<ExecutableElement> getAccessMethods() {
List<ExecutableElement> methods = new ArrayList<>();
for (Element m : element.getEnclosedElements()) {
if (m.getKind() != ElementKind.METHOD) {
continue;
}
if (!m.getSimpleName().contentEquals(ACCESS_METHOD_NAME)) {
continue;
}
ExecutableElement method = (ExecutableElement) m;
methods.add(method);
}
return methods;
}
void appendImports(Writer w) throws IOException {
w.append("import com.oracle.truffle.api.frame.VirtualFrame;").append("\n");
w.append("import com.oracle.truffle.api.dsl.Specialization;").append("\n");
w.append("import com.oracle.truffle.api.nodes.RootNode;").append("\n");
w.append("import com.oracle.truffle.api.TruffleLanguage;").append("\n");
w.append("import com.oracle.truffle.api.interop.ForeignAccess;").append("\n");
w.append("import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;").append("\n");
w.append("import com.oracle.truffle.api.interop.UnsupportedTypeException;").append("\n");
}
abstract int getParameterCount();
public String checkSignature(ExecutableElement method) {
if (method.getThrownTypes().size() > 0) {
return "Method access must not throw a checked exception. Use an InteropException (e.g. UnknownIdentifierException.raise() ) to report an error to the host language.";
}
return null;
}
abstract String getTargetableNodeName();
void appendExecuteWithTarget(Writer w) throws IOException {
w.append(" public abstract Object executeWithTarget(VirtualFrame frame");
for (int i = 0; i < Math.max(1, getParameterCount()); i++) {
w.append(", ").append("Object ").append("o").append(String.valueOf(i));
}
w.append(");\n");
}
void appendSpecializations(Writer w) throws IOException {
String sep = "";
for (ExecutableElement method : getAccessMethods()) {
final List<? extends VariableElement> params = method.getParameters();
w.append(" @Specialization\n");
w.append(" protected Object ").append(ACCESS_METHOD_NAME).append("WithTarget");
w.append("(");
sep = "";
for (VariableElement p : params) {
w.append(sep).append(ElementUtils.getUniqueIdentifier(p.asType())).append(" ").append(p.getSimpleName());
sep = ", ";
}
w.append(") {\n");
w.append(" return ").append(ACCESS_METHOD_NAME).append("(");
sep = "";
for (VariableElement p : params) {
w.append(sep).append(p.getSimpleName());
sep = ", ";
}
w.append(");\n");
w.append(" }\n");
}
}
abstract void appendRootNode(Writer w) throws IOException;
abstract String getRootNodeName();
void appendRootNodeFactory(Writer w) throws IOException {
w.append(" @Deprecated\n");
w.append(" @SuppressWarnings(\"unused\")\n");
w.append(" public static RootNode createRoot(Class<? extends TruffleLanguage<?>> language) {\n");
w.append(" return createRoot();\n");
w.append(" }\n");
w.append(" public static RootNode createRoot() {\n");
w.append(" return new ").append(getRootNodeName()).append("();\n");
w.append(" }\n");
}
public String getRootNodeFactoryInvokation() {
return packageName + "." + clazzName + ".createRoot()";
}
@Override
public String toString() {
return clazzName;
}
public static MessageGenerator getGenerator(ProcessingEnvironment processingEnv, Resolve resolveAnnotation, MessageResolution messageResolutionAnnotation, TypeElement element,
ForeignAccessFactoryGenerator containingForeignAccessFactory) {
String messageName = resolveAnnotation.message();
Object currentMessage = Utils.getMessage(processingEnv, messageName);
if (currentMessage != null) {
if (Message.READ.toString().equalsIgnoreCase(messageName) || Message.KEY_INFO.toString().equalsIgnoreCase(messageName)) {
return new ReadGenerator(processingEnv, resolveAnnotation, messageResolutionAnnotation, element, containingForeignAccessFactory);
} else if (Message.WRITE.toString().equalsIgnoreCase(messageName)) {
return new WriteGenerator(processingEnv, resolveAnnotation, messageResolutionAnnotation, element, containingForeignAccessFactory);
} else if (Message.IS_NULL.toString().equalsIgnoreCase(messageName) || Message.IS_EXECUTABLE.toString().equalsIgnoreCase(messageName) ||
Message.IS_BOXED.toString().equalsIgnoreCase(messageName) || Message.HAS_SIZE.toString().equalsIgnoreCase(messageName) ||
Message.GET_SIZE.toString().equalsIgnoreCase(messageName) || Message.UNBOX.toString().equalsIgnoreCase(messageName) ||
Message.IS_POINTER.toString().equalsIgnoreCase(messageName) ||
Message.AS_POINTER.toString().equalsIgnoreCase(messageName) || Message.TO_NATIVE.toString().equalsIgnoreCase(messageName)) {
return new UnaryGenerator(processingEnv, resolveAnnotation, messageResolutionAnnotation, element, containingForeignAccessFactory);
} else if (Message.KEYS.toString().equalsIgnoreCase(messageName)) {
return new KeysGenerator(processingEnv, resolveAnnotation, messageResolutionAnnotation, element, containingForeignAccessFactory);
} else if (Message.createExecute(0).toString().equalsIgnoreCase(messageName) || Message.createInvoke(0).toString().equalsIgnoreCase(messageName) ||
Message.createNew(0).toString().equalsIgnoreCase(messageName)) {
return new ExecuteGenerator(processingEnv, resolveAnnotation, messageResolutionAnnotation, element, containingForeignAccessFactory);
} else {
assert !InteropDSLProcessor.KNOWN_MESSAGES.contains(currentMessage);
return new GenericGenerator(processingEnv, resolveAnnotation, messageResolutionAnnotation, element, containingForeignAccessFactory);
}
}
return null;
}
protected void appendHandleUnsupportedTypeException(Writer w) throws IOException {
w.append(" if (e.getNode() instanceof ").append(clazzName).append(") {\n");
w.append(" throw UnsupportedTypeException.raise(e, e.getSuppliedValues());\n");
w.append(" } else {\n");
w.append(" throw e;\n");
w.append(" }\n");
}
}