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("Operand's declaring block is built: " + operand);
 56             }
 57         }
 58         this.operands = List.copyOf(operands);
 59     }
 60 
 61     /**
 62      * Constructs an operation with operands mapped from, and location copied from, the given operation.
 63      * <p>
 64      * The constructed operation's operands are the values mapped, in order, from the given operation's operands using
 65      * the given code context. The new operation's location is the given operation's location, if any.
 66      *
 67      * @param that the operation
 68      * @param cc   the code context
 69      * @throws IllegalArgumentException if an operation's operand has no context mapping
 70      * @throws IllegalArgumentException if a mapped value's declaring block is built.
 71      */
 72     protected InternalAbstractOp(InternalAbstractOp that, CodeContext cc) {
 73         this(cc.getValues(that.operands));
 74         this.location = that.location;
 75     }
 76 
 77     @Override
 78     public final void setLocation(Location l) {
 79         // @@@ Fail if location != null?
 80         if (isRoot() || (result != null && result.block.isBuilt())) {
 81             throw new IllegalStateException("Built operation");
 82         }
 83 
 84         location = l;
 85     }
 86 
 87     @Override
 88     public final Location location() {
 89         return location;
 90     }
 91 
 92     @Override
 93     public final Block parent() {
 94         if (isRoot() || result == null) {
 95             return null;
 96         }
 97 
 98         if (!result.block.isBuilt()) {
 99             throw new IllegalStateException("Parent block is unobservable");
100         }
101 
102         return result.block;
103     }
104 
105     @Override
106     public final List<Body> children() {
107         return bodies();
108     }
109 
110     /**
111      * {@inheritDoc}
112      * @implSpec this implementation returns an unmodifiable empty list.
113      */
114     @Override
115     public List<Body> bodies() {
116         return List.of();
117     }
118 
119     @Override
120     public final Result result() {
121         return result == Result.ROOT_RESULT ? null : result;
122     }
123 
124     @Override
125     public final List<Value> operands() {
126         return operands;
127     }
128 
129     @Override
130     public final FunctionType opSignature() {
131         List<CodeType> operandTypes = operands.stream().map(Value::type).toList();
132         return CoreType.functionType(resultType(), operandTypes);
133     }
134 
135     @Override
136     public final List<Value> capturedValues() {
137         Set<Value> cvs = new LinkedHashSet<>();
138 
139         Deque<Body> bodyStack = new ArrayDeque<>();
140         for (Body childBody : bodies()) {
141             Body.capturedValues(cvs, bodyStack, childBody);
142         }
143         return new ArrayList<>(cvs);
144     }
145 
146     @Override
147     public final void buildAsRoot() {
148         if (result == Result.ROOT_RESULT) {
149             return;
150         }
151         if (!bodies().stream().allMatch(Body::isIsolated)) {
152             throw new IllegalStateException("One of the operation bodies is not isolated");
153         }
154         if (!operands().isEmpty()) {
155             throw new IllegalStateException("Operation has operands");
156         }
157         if (!successors().isEmpty()) {
158             throw new IllegalStateException("Operation has successors");
159         }
160         if (result != null) {
161             throw new IllegalStateException("Operation is placed in a block");
162         }
163         result = Result.ROOT_RESULT;
164     }
165 
166     @Override
167     public final boolean isRoot() {
168         return result == Result.ROOT_RESULT;
169     }
170 
171     @Override
172     public final boolean isPlacedInBlock() {
173         return !isRoot() && result != null;
174     }
175 
176     @Override
177     public final String toText() {
178         return OpWriter.toText(this);
179     }
180 }