/* * 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.lang.model.type.TypeKind; import javax.tools.JavaFileObject; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.MessageResolution; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.dsl.processor.java.ElementUtils; /** * THIS IS NOT PUBLIC API. */ public final class LanguageCheckGenerator { protected static final String TEST_METHOD_NAME = "test"; protected final TypeElement element; protected final String packageName; protected final String clazzName; protected final String userClassName; protected final ProcessingEnvironment processingEnv; protected String receiverClassName; protected ForeignAccessFactoryGenerator containingForeignAccessFactory; LanguageCheckGenerator(ProcessingEnvironment processingEnv, MessageResolution messageResolutionAnnotation, TypeElement element, ForeignAccessFactoryGenerator containingForeignAccessFactory) { this.processingEnv = processingEnv; this.element = element; this.packageName = ElementUtils.getPackageName(element); this.userClassName = ElementUtils.getQualifiedName(element); this.clazzName = ElementUtils.getSimpleName(element) + "Sub"; this.receiverClassName = Utils.getReceiverTypeFullClassName(messageResolutionAnnotation); this.containingForeignAccessFactory = containingForeignAccessFactory; } public void generate() throws IOException { JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + clazzName, element); Writer w = file.openWriter(); w.append("package ").append(packageName).append(";\n"); appendImports(w); appendGeneratedFor(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(); } void appendGeneratedFor(Writer w, String ident) throws IOException { w.append(ident).append("/**\n"); w.append(ident).append(" * Generated for {@link ").append(receiverClassName).append("}\n"); w.append(ident).append(" */\n"); } public List<ExecutableElement> getTestMethods() { List<ExecutableElement> methods = new ArrayList<>(); for (Element m : element.getEnclosedElements()) { if (m.getKind() != ElementKind.METHOD) { continue; } if (!m.getSimpleName().contentEquals(TEST_METHOD_NAME)) { continue; } ExecutableElement method = (ExecutableElement) m; methods.add(method); } return methods; } static 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"); } public String checkSignature(ExecutableElement method) { final List<? extends VariableElement> params = method.getParameters(); boolean hasFrameArgument = false; if (params.size() >= 1) { hasFrameArgument = ElementUtils.typeEquals(params.get(0).asType(), Utils.getTypeMirror(processingEnv, VirtualFrame.class)); } int expectedNumberOfArguments = hasFrameArgument ? 2 : 1; if (!ElementUtils.typeEquals(params.get(hasFrameArgument ? 1 : 0).asType(), Utils.getTypeMirror(processingEnv, TruffleObject.class))) { return "The receiver type must be TruffleObject"; } if (!ElementUtils.isPrimitive(method.getReturnType()) || method.getReturnType().getKind() != TypeKind.BOOLEAN) { return "Method must return a boolean value"; } if (params.size() != expectedNumberOfArguments) { return "Wrong number of arguments."; } if (method.getThrownTypes().size() > 0) { return "Method test must not throw a checked exception."; } return null; } static void appendExecuteWithTarget(Writer w) throws IOException { w.append(" public abstract Object executeWithTarget(VirtualFrame frame, ").append("Object ").append("o").append(");\n"); } void appendSpecializations(Writer w) throws IOException { String sep = ""; List<ExecutableElement> testMethods = getTestMethods(); assert testMethods.size() == 1; final List<? extends VariableElement> params = testMethods.get(0).getParameters(); w.append(" @Specialization\n"); w.append(" protected Object ").append(TEST_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(TEST_METHOD_NAME).append("("); sep = ""; for (VariableElement p : params) { w.append(sep).append(p.getSimpleName()); sep = ", "; } w.append(");\n"); w.append(" }\n"); } void appendRootNode(Writer w) throws IOException { w.append(" private static final class LanguageCheckRootNode extends RootNode {\n"); w.append(" protected LanguageCheckRootNode() {\n"); w.append(" super(null);\n"); w.append(" }\n"); w.append("\n"); w.append(" @Child private ").append(clazzName).append(" node = ").append(packageName).append(".").append(clazzName).append("NodeGen.create();"); w.append("\n"); w.append(" @Override\n"); w.append(" public Object execute(VirtualFrame frame) {\n"); w.append(" try {\n"); w.append(" Object receiver = ForeignAccess.getReceiver(frame);\n"); w.append(" return node.executeWithTarget(frame, receiver);\n"); w.append(" } catch (UnsupportedSpecializationException e) {\n"); appendHandleUnsupportedTypeException(w); w.append(" }\n"); w.append(" }\n"); w.append("\n"); w.append(" }\n"); } static 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 LanguageCheckRootNode();\n"); w.append(" }\n"); } 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"); } public String getRootNodeFactoryInvokation() { return packageName + "." + clazzName + ".createRoot()"; } @Override public String toString() { return clazzName; } }