1 /*
  2  * Copyright (c) 2026, 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.internal;
 27 
 28 import java.util.List;
 29 import java.util.Optional;
 30 import jdk.incubator.code.Block;
 31 import jdk.incubator.code.Body;
 32 import jdk.incubator.code.CodeType;
 33 import jdk.incubator.code.Value;
 34 import jdk.incubator.code.dialect.core.CoreType;
 35 import jdk.incubator.code.dialect.core.FunctionType;
 36 import jdk.incubator.code.dialect.java.JavaType;
 37 import jdk.incubator.code.extern.ExternalizedOp;
 38 
 39 public final class StructuralPreconditions {
 40 
 41     private StructuralPreconditions() {
 42     }
 43 
 44     public static void requireNoOperands(ExternalizedOp def) {
 45         requireOperands(def, 0);
 46     }
 47 
 48     public static Value requireSingleOperand(ExternalizedOp def) {
 49         return requireOperands(def, 1).getFirst();
 50     }
 51 
 52     public static List<Value> requireOperands(ExternalizedOp def, int expCount) {
 53         List<Value> operands = def.operands();
 54         if (operands.size() != expCount) {
 55             throw structuralException(def.name(), "requires %d operand%s, found %d".formatted(expCount, expCount == 1 ? "" : "s", operands.size()));
 56         }
 57         return operands;
 58     }
 59 
 60     public static List<Value> requireOperands(ExternalizedOp def, int expCount1, int expCount2) {
 61         int count = def.operands().size();
 62         if (count != expCount1 && count != expCount2) {
 63             throw structuralException(def.name(), "requires %d or %d operands, found %d".formatted(expCount1, expCount2, count));
 64         }
 65         return def.operands();
 66     }
 67 
 68     public static Body.Builder requireSingleBody(ExternalizedOp def) {
 69         return requireBodies(def, 1).getFirst();
 70     }
 71 
 72     public static List<Body.Builder> requireBodies(ExternalizedOp def, int expCount) {
 73         List<Body.Builder> bodies = def.bodyDefinitions();
 74         if (bodies.size() != expCount) {
 75             throw structuralException(def.name(), "requires %d bod%s, found %d".formatted(expCount, expCount == 1 ? "y" : "ies", bodies.size()));
 76         }
 77         return bodies;
 78     }
 79 
 80     public static List<Body.Builder> requireMinBodies(String opName, List<Body.Builder> bodies, int expCount) {
 81         if (bodies.size() < expCount) {
 82             throw structuralException(opName, "requires at least %d bod%s, found %d".formatted(expCount, expCount == 1 ? "y" : "ies", bodies.size()));
 83         }
 84         return bodies;
 85     }
 86 
 87     public static List<Body.Builder> requireBodyPairs(String opName, List<Body.Builder> bodies) {
 88         int count = bodies.size();
 89         if (count < 2 || count % 2 == 1) {
 90             throw structuralException(opName, "requires one or more body pairs, found %d".formatted(count));
 91         }
 92         return bodies;
 93     }
 94 
 95     public static Block.Reference requireSingleSuccessor(ExternalizedOp def) {
 96         return requireSuccessors(def, 1).getFirst();
 97     }
 98 
 99     public static List<Block.Reference> requireSuccessors(ExternalizedOp def, int expCount) {
100         List<Block.Reference> succ = def.successors();
101         if (succ.size() != expCount) {
102             throw structuralException(def.name(), "requires %d successor%s, found %d".formatted(expCount, expCount == 1 ? "" : "s", succ.size()));
103         }
104         return succ;
105     }
106 
107     @SuppressWarnings("unchecked")
108     public static <T> T requireAttribute(ExternalizedOp def, String attributeName, boolean isDefaultAttribute, Class<T> attributeType) {
109         Object attr = requireAttribute(def, attributeName, isDefaultAttribute);
110         if (attributeType.isInstance(attr)) {
111             return (T)attr;
112         }
113         throw unsupportedAttributeValueException(def, attributeName, attr);
114     }
115 
116     public static Object requireAttribute(ExternalizedOp def, String attributeName, boolean isDefaultAttribute) {
117         if (isDefaultAttribute && def.attributes().containsKey("")) {
118             return def.attributes().get("");
119         }
120         if (def.attributes().containsKey(attributeName)) {
121             return def.attributes().get(attributeName);
122         }
123         throw structuralException(def.name(), "requires attribute %s".formatted(attributeName));
124     }
125 
126     public static boolean optionalBooleanAttribute(ExternalizedOp def, String attributeName) {
127         return optionalAttribute(def, attributeName, false, Boolean.class).orElse(false);
128     }
129 
130     @SuppressWarnings("unchecked")
131     public static <T> Optional<T> optionalAttribute(ExternalizedOp def, String attributeName, boolean isDefaultAttribute, Class<T> attributeType) {
132         Object attr = def.attributes().get(isDefaultAttribute && def.attributes().containsKey("") ? "" : attributeName);
133         if (attr == null || attributeType.isInstance(attr)) {
134             return Optional.ofNullable((T)attr);
135         }
136         throw unsupportedAttributeValueException(def, attributeName, attr);
137     }
138 
139     public static Body.Builder requireVoidBodySignature(String opName, Body.Builder bodyC) {
140         return requireBodySignature(opName, bodyC, CoreType.FUNCTION_TYPE_VOID);
141     }
142 
143     public static Body.Builder requireBodySignature(String opName, Body.Builder bodyC, FunctionType signature) {
144         if (!bodyC.bodySignature().equals(signature)) {
145             throw structuralException(opName, "requires body signature %s, found %s".formatted(signature, bodyC.bodySignature()));
146         }
147         return bodyC;
148     }
149 
150     public static Body.Builder requireNonVoidReturnType(String opName, Body.Builder bodyC, int parameters) {
151         if (bodyC.bodySignature().returnType().equals(JavaType.VOID)) {
152             throw structuralException(opName, "requires non-void return type");
153         }
154         if (bodyC.bodySignature().parameterTypes().size() != parameters) {
155             throw structuralException(opName, "requires %d parameters, found %d".formatted(parameters, bodyC.bodySignature().parameterTypes().size()));
156         }
157         return bodyC;
158     }
159 
160     public static Body.Builder requireVoidReturnType(String opName, Body.Builder bodyC, int parameters) {
161         if (!bodyC.bodySignature().returnType().equals(JavaType.VOID)) {
162             throw structuralException(opName, "requires void return type, found: %s".formatted(bodyC.bodySignature().returnType()));
163         }
164         if (bodyC.bodySignature().parameterTypes().size() != parameters) {
165             throw structuralException(opName, "requires %d parameters, found %d".formatted(parameters, bodyC.bodySignature().parameterTypes().size()));
166         }
167         return bodyC;
168     }
169 
170     public static Body.Builder requireNoParameters(String opName, Body.Builder bodyC) {
171         if (!bodyC.bodySignature().parameterTypes().isEmpty()) {
172             throw structuralException(opName, "requires no parameters, found %s".formatted(bodyC.bodySignature()));
173         }
174         return bodyC;
175     }
176 
177     public static IllegalStateException structuralException(String opName, String msg) {
178         return new IllegalStateException("Operation " + opName + " " + msg);
179     }
180 
181     public static UnsupportedOperationException unsupportedAttributeValueException(ExternalizedOp def, String attributeName, Object value) {
182         return new UnsupportedOperationException(
183                 "Operation %s attribute %s has unsupported value: %s".formatted(def.name(), attributeName, value));
184     }
185 }