1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.code.extern;
 27 
 28 import jdk.incubator.code.Op;
 29 
 30 /**
 31  * An operation factory for constructing an {@link Op operation} from its
 32  * {@link ExternalizedOp external content}.
 33  */
 34 @FunctionalInterface
 35 public interface OpFactory {
 36 
 37     /**
 38      * Constructs an {@link Op operation} from its external content.
 39      * <p>
 40      * If there is no mapping from the operation's name to a concrete
 41      * class of an {@code Op} then this method returns null.
 42      *
 43      * @param def the operation's external content
 44      * @return the operation, otherwise null
 45      */
 46     Op constructOp(ExternalizedOp def);
 47 
 48     /**
 49      * Constructs an {@link Op operation} from its external content.
 50      * <p>
 51      * If there is no mapping from the operation's name to a concrete
 52      * class of an {@code Op} then this method throws UnsupportedOperationException.
 53      *
 54      * @param def the operation's external content
 55      * @return the operation
 56      * @throws UnsupportedOperationException if there is no mapping from the operation's
 57      *                                       name to a concrete class of an {@code Op}
 58      */
 59     default Op constructOpOrFail(ExternalizedOp def) {
 60         Op op = constructOp(def);
 61         if (op == null) {
 62             throw new UnsupportedOperationException("Unsupported operation: " + def.name());
 63         }
 64 
 65         return op;
 66     }
 67 
 68     /**
 69      * Composes this operation factory with another operation factory.
 70      * <p>
 71      * If there is no mapping in this operation factory then the result
 72      * of the other operation factory is returned.
 73      *
 74      * @param after the other operation factory.
 75      * @return the composed operation factory.
 76      */
 77     default OpFactory andThen(OpFactory after) {
 78         return def -> {
 79             Op op = constructOp(def);
 80             return op != null ? op : after.constructOp(def);
 81         };
 82     }
 83 
 84 //    // Uncomment the following and execute using an exploded build like as follows to generate a factory method
 85 //    // for enclosed concrete operations
 86 //    // java --add-modules jdk.incubator.code jdk.incubator.code.extern.OpFactory jdk.incubator.code.dialect.core.CoreOp
 87 //    static void main(String[] args) {
 88 //        Class<?> enclosingOpClass = null;
 89 //        try {
 90 //            enclosingOpClass = Class.forName(args[0]);
 91 //        } catch (ClassNotFoundException e) {
 92 //            throw new RuntimeException(e);
 93 //        }
 94 //        generateSwitchExpression(enclosingOpClass, System.out);
 95 //    }
 96 //
 97 //    static void generateSwitchExpression(Class<?> enclosingOpClass, java.io.PrintStream out) {
 98 //        java.util.Map<String, java.lang.reflect.Executable> opNameMap = new java.util.TreeMap<>();
 99 //        for (Class<?> opClass : enclosingOpClass.getNestMembers()) {
100 //            if (!Op.class.isAssignableFrom(opClass)) {
101 //                continue;
102 //            }
103 //            if (!java.lang.reflect.Modifier.isFinal(opClass.getModifiers())) {
104 //                continue;
105 //            }
106 //
107 //            var opDecl = opClass.getAnnotation(jdk.incubator.code.internal.OpDeclaration.class);
108 //            String name = opDecl.value();
109 //
110 //            var e = getOpConstructorExecutable(opClass);
111 //            opNameMap.put(name, e);
112 //        }
113 //
114 //        out.println("static Op createOp(ExternalizedOp def) {");
115 //        out.println("    Op op = switch (def.name()) {");
116 //        opNameMap.forEach((name, e) -> {
117 //            out.print("        case \"" + name + "\" -> ");
118 //            switch (e) {
119 //                case java.lang.reflect.Constructor<?> constructor -> {
120 //                    out.println("new " + name(enclosingOpClass, constructor.getDeclaringClass()) + "(def);");
121 //                }
122 //                case java.lang.reflect.Method method -> {
123 //                    out.println(name(enclosingOpClass, method.getDeclaringClass()) + "." + method.getName() + "(def);");
124 //                }
125 //            }
126 //        });
127 //        out.println("        default -> null;");
128 //        out.println("    };");
129 //        out.print(
130 //                """
131 //                    if (op != null) {
132 //                        op.setLocation(def.location());
133 //                    }
134 //                    return op;
135 //                """);
136 //        out.println("}");
137 //    }
138 //
139 //    private static java.lang.reflect.Executable getOpConstructorExecutable(Class<?> opClass) {
140 //        java.lang.reflect.Executable e = null;
141 //        try {
142 //            e = opClass.getDeclaredMethod("create", ExternalizedOp.class);
143 //        } catch (NoSuchMethodException _) {
144 //        }
145 //
146 //        if (e != null) {
147 //            if (!java.lang.reflect.Modifier.isStatic(e.getModifiers())) {
148 //                throw new InternalError("Operation constructor is not a static method: " + e);
149 //            }
150 //            return e;
151 //        }
152 //
153 //        try {
154 //            e = opClass.getDeclaredConstructor(ExternalizedOp.class);
155 //        } catch (NoSuchMethodException _) {
156 //            return null;
157 //        }
158 //
159 //        return e;
160 //    }
161 //
162 //    static String name(Class<?> enclosingOpClass, Class<?> declaringClass) {
163 //        return declaringClass.getCanonicalName().substring(enclosingOpClass.getCanonicalName().length() + 1);
164 //    }
165 }