1 /*
  2  * Copyright (c) 2024, 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;
 27 
 28 import jdk.incubator.code.dialect.core.CoreType;
 29 import jdk.incubator.code.dialect.core.FunctionType;
 30 import jdk.incubator.code.extern.OpWriter;
 31 
 32 import java.util.*;
 33 
 34 sealed abstract class InternalAbstractOp implements Op permits AbstractOp, AbstractOp.Terminating {
 35 
 36     // Set when op is placed in a block or as a root operation, otherwise null when unplaced
 37     // @@@ stable value?
 38     Result result;
 39 
 40     // null if not specified
 41     // @@@ stable value?
 42     Location location;
 43 
 44     final List<Value> operands;
 45 
 46     /**
 47      * Constructs an operation with a list of operands.
 48      *
 49      * @param operands the list of operands, a copy of the list is performed if required.
 50      * @throws IllegalArgumentException if an operand's declaring block is built.
 51      */
 52     protected InternalAbstractOp(List<? extends Value> operands) {
 53         for (Value operand : operands) {
 54             if (operand.isBuilt()) {
 55                 throw new IllegalArgumentException(
 56                         "Operand's declaring block is built\n"
 57                         + operand.block.diagnosticText(operand, "operand"));
 58             }
 59         }
 60         this.operands = List.copyOf(operands);
 61     }
 62 
 63     /**
 64      * Constructs an operation with operands mapped from, and location copied from, the given operation.
 65      * <p>
 66      * The constructed operation's operands are the values mapped, in order, from the given operation's operands using
 67      * the given code context. The new operation's location is the given operation's location, if any.
 68      *
 69      * @param that the operation
 70      * @param cc   the code context
 71      * @throws IllegalArgumentException if an operation's operand has no context mapping
 72      * @throws IllegalArgumentException if a mapped value's declaring block is built.
 73      */
 74     protected InternalAbstractOp(InternalAbstractOp that, CodeContext cc) {
 75         this(cc.getValues(that.operands));
 76         this.location = that.location;
 77     }
 78 
 79     @Override
 80     public final void setLocation(Location l) {
 81         // @@@ Fail if location != null?
 82         if (isRoot() || (result != null && result.block.isBuilt())) {
 83             throw new IllegalStateException("Built operation");
 84         }
 85 
 86         location = l;
 87     }
 88 
 89     @Override
 90     public final Location location() {
 91         return location;
 92     }
 93 
 94     @Override
 95     public final Block parent() {
 96         if (isRoot() || result == null) {
 97             return null;
 98         }
 99 
100         if (!result.block.isBuilt()) {
101             throw new IllegalStateException(
102                     "Operation's parent block is unobservable\n"
103                     + result.block.diagnosticText(this, "operation with unobservable parent block"));
104         }
105 
106         return result.block;
107     }
108 
109     @Override
110     public final List<Body> children() {
111         return bodies();
112     }
113 
114     /**
115      * {@inheritDoc}
116      * @implSpec this implementation returns an unmodifiable empty list.
117      */
118     @Override
119     public List<Body> bodies() {
120         return List.of();
121     }
122 
123     @Override
124     public final Result result() {
125         return result == Result.ROOT_RESULT ? null : result;
126     }
127 
128     @Override
129     public final List<Value> operands() {
130         return operands;
131     }
132 
133     @Override
134     public final FunctionType opSignature() {
135         List<CodeType> operandTypes = operands.stream().map(Value::type).toList();
136         return CoreType.functionType(resultType(), operandTypes);
137     }
138 
139     @Override
140     public final List<Value> capturedValues() {
141         Set<Value> cvs = new LinkedHashSet<>();
142 
143         Deque<Body> bodyStack = new ArrayDeque<>();
144         for (Body childBody : bodies()) {
145             Body.capturedValues(cvs, bodyStack, childBody);
146         }
147         return new ArrayList<>(cvs);
148     }
149 
150     @Override
151     public final void buildAsRoot() {
152         if (result == Result.ROOT_RESULT) {
153             return;
154         }
155         if (!bodies().stream().allMatch(Body::isIsolated)) {
156             throw new IllegalStateException("One of the operation bodies is not isolated");
157         }
158         if (!operands().isEmpty()) {
159             throw new IllegalStateException("Operation has operands");
160         }
161         if (!successors().isEmpty()) {
162             throw new IllegalStateException("Operation has successors");
163         }
164         if (result != null) {
165             throw new IllegalStateException("Operation is placed in a block");
166         }
167         result = Result.ROOT_RESULT;
168     }
169 
170     @Override
171     public final boolean isRoot() {
172         return result == Result.ROOT_RESULT;
173     }
174 
175     @Override
176     public final boolean isPlacedInBlock() {
177         return !isRoot() && result != null;
178     }
179 
180     @Override
181     public final String toText() {
182         return OpWriter.toText(this);
183     }
184 }