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 java.lang.reflect.code;
 27 
 28 import java.io.OutputStream;
 29 import java.io.OutputStreamWriter;
 30 import java.io.StringWriter;
 31 import java.io.Writer;
 32 import java.lang.reflect.code.type.FunctionType;
 33 import java.lang.reflect.code.writer.OpWriter;
 34 import java.nio.charset.StandardCharsets;
 35 import java.util.*;
 36 import java.util.function.BiFunction;
 37 
 38 /**
 39  * An operation modelling a unit of functionality.
 40  * <p>
 41  * An operation might model the addition of two 32-bit integers, or a Java method call.
 42  * Alternatively an operation may model something more complex like method bodies, lambda bodies, or
 43  * try/catch/finally statements. In this case such an operation will contain one or more bodies modelling
 44  * the nested structure.
 45  */
 46 public non-sealed abstract class Op implements CodeElement<Op, Body> {
 47 
 48     /**
 49      * An operation characteristic indicating the operation is pure and has no side effects.
 50      */
 51     public interface Pure {
 52     }
 53 
 54     /**
 55      * An operation characteristic indicating the operation has one or more bodies.
 56      */
 57     public interface Nested {
 58         List<Body> bodies();
 59     }
 60 
 61     /**
 62      * An operation characteristic indicating the operation represents a loop
 63      */
 64     public interface Loop extends Nested {
 65         Body loopBody();
 66     }
 67 
 68     /**
 69      * An operation characteristic indicating the operation has one or more bodies,
 70      * all of which are isolated.
 71      */
 72     public interface Isolated extends Nested {
 73     }
 74 
 75     /**
 76      * An operation characteristic indicating the operation is invokable, so the operation may be interpreted
 77      * or compiled.
 78      */
 79     public interface Invokable extends Nested {
 80         /**
 81          * {@return the body of the invokable operation.}
 82          */
 83         Body body();
 84 
 85         /**
 86          * {@return the function type describing the invokable operation's parameter types and return type.}
 87          */
 88         FunctionType invokableType();
 89 
 90         /**
 91          * {@return the entry block parameters of this operation's body}
 92          */
 93         default List<Block.Parameter> parameters() {
 94             return body().entryBlock().parameters();
 95         }
 96 
 97         /**
 98          * Computes values captured by this invokable operation's body.
 99          *
100          * @return the captured values.
101          * @see Body#capturedValues()
102          */
103         default List<Value> capturedValues() {
104             return List.of();
105         }
106     }
107 
108     /**
109      * An operation characteristic indicating the operation can replace itself with a lowered form,
110      * consisting only of operations in the core dialect.
111      */
112     public interface Lowerable {
113         default Block.Builder lower(Block.Builder b) {
114             return lower(b, OpTransformer.NOOP_TRANSFORMER);
115         }
116 
117         Block.Builder lower(Block.Builder b, OpTransformer opT);
118     }
119 
120     /**
121      * An operation characteristic indicating the operation is a terminating operation
122      * occurring as the last operation in a block.
123      * <p>
124      * A terminating operation passes control to either another block within the same parent body
125      * or to that parent body.
126      */
127     public interface Terminating {
128     }
129 
130     /**
131      * An operation characteristic indicating the operation is a body terminating operation
132      * occurring as the last operation in a block.
133      * <p>
134      * A body terminating operation passes control back to its nearest ancestor body.
135      */
136     public interface BodyTerminating extends Terminating {
137     }
138 
139     /**
140      * An operation characteristic indicating the operation is a block terminating operation
141      * occurring as the last operation in a block.
142      * <p>
143      * The operation has one or more successors to other blocks within the same parent body, and passes
144      * control to one of those blocks.
145      */
146     public interface BlockTerminating extends Terminating {
147         List<Block.Reference> successors();
148     }
149 
150     /**
151      * A value that is the result of an operation.
152      */
153     public static final class Result extends Value {
154         final Op op;
155 
156         Result(Block block, Op op) {
157             super(block, op.resultType());
158 
159             this.op = op;
160         }
161 
162         @Override
163         public Set<Value> dependsOn() {
164             Set<Value> depends = new LinkedHashSet<>(op.operands());
165             if (op instanceof Terminating) {
166                 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
167             }
168 
169             return Collections.unmodifiableSet(depends);
170         }
171 
172         /**
173          * Returns the result's operation.
174          *
175          * @return the result's operation.
176          */
177         public Op op() {
178             return op;
179         }
180     }
181 
182     // Set when op is bound to block, otherwise null when unbound
183     Result result;
184 
185     // null if not specified
186     Location location;
187 
188     final String name;
189 
190     final List<Value> operands;
191 
192     /**
193      * Constructs an operation by copying given operation.
194      *
195      * @param that the operation to copy.
196      * @param cc   the copy context.
197      * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list
198      * values computed, in order, by mapping the operation's operands using the copy context.
199      */
200     protected Op(Op that, CopyContext cc) {
201         this(that.name, cc.getValues(that.operands));
202         this.location = that.location;
203     }
204 
205     /**
206      * Copies this operation and its bodies, if any.
207      * <p>
208      * The returned operation is structurally identical to this operation and is otherwise independent
209      * of the values declared and used.
210      *
211      * @return the copied operation.
212      */
213     public Op copy() {
214         return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER);
215     }
216 
217     /**
218      * Copies this operation and its bodies, if any.
219      * <p>
220      * The returned operation is structurally identical to this operation and is otherwise independent
221      * of the values declared and used.
222      *
223      * @param cc the copy context.
224      * @return the copied operation.
225      */
226     public Op copy(CopyContext cc) {
227         return transform(cc, OpTransformer.COPYING_TRANSFORMER);
228     }
229 
230     /**
231      * Copies this operation and transforms its bodies, if any.
232      * <p>
233      * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and
234      * operation transformer.
235      *
236      * @param cc the copy context.
237      * @param ot the operation transformer.
238      * @return the transformed operation.
239      */
240     public abstract Op transform(CopyContext cc, OpTransformer ot);
241 
242     /**
243      * Constructs an operation with a name and list of operands.
244      *
245      * @param name       the operation name.
246      * @param operands   the list of operands, a copy of the list is performed if required.
247      */
248     protected Op(String name, List<? extends Value> operands) {
249         this.name = name;
250         this.operands = List.copyOf(operands);
251     }
252 
253     /**
254      * Sets the originating source location of this operation, if unbound.
255      *
256      * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
257      * @throws IllegalStateException if this operation is bound
258      */
259     public final void setLocation(Location l) {
260         // @@@ Fail if location != null?
261         if (result != null && result.block.isBound()) {
262             throw new IllegalStateException();
263         }
264 
265         location = l;
266     }
267 
268     /**
269      * {@return the originating source location of this operation, otherwise {@code null} if not specified}
270      */
271     public final Location location() {
272         return location;
273     }
274 
275     /**
276      * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
277      *
278      * @return operation's parent block, or {@code null} if the operation is not assigned to a block.
279      */
280     @Override
281     public final Block parent() {
282         return parentBlock();
283     }
284 
285     /**
286      * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
287      *
288      * @return operation's parent block, or {@code null} if the operation is not assigned to a block.
289      */
290     public final Block parentBlock() {
291         if (result == null) {
292             return null;
293         }
294 
295         if (!result.block.isBound()) {
296             throw new IllegalStateException("Parent block is partially constructed");
297         }
298 
299         return result.block;
300     }
301 
302     @Override
303     public final List<Body> children() {
304         return bodies();
305     }
306 
307     /**
308      * {@return the operation's bodies, as an unmodifiable list}
309      * @implSpec this implementation returns an unmodifiable empty list.
310      */
311     public List<Body> bodies() {
312         return List.of();
313     }
314 
315     /**
316      * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block.
317      *
318      * @return the operation's result, or {@code null} if not assigned to a block.
319      */
320     public final Result result() {
321         return result;
322     }
323 
324 
325     /**
326      * Returns this operation's nearest ancestor body (the parent body of this operation's parent block),
327      * otherwise {@code null} if the operation is not assigned to a block.
328      *
329      * @return operation's nearest ancestor body, or {@code null} if the operation is not assigned to a block.
330      */
331     public final Body ancestorBody() {
332         if (result == null) {
333             return null;
334         }
335 
336         if (!result.block.isBound()) {
337             throw new IllegalStateException("Parent body is partially constructed");
338         }
339 
340         return result.block.parentBody;
341     }
342 
343     /**
344      * {@return the operation name}
345      */
346     public String opName() {
347         return name;
348     }
349 
350     /**
351      * {@return the operation's operands, as an unmodifiable list}
352      */
353     public List<Value> operands() {
354         return operands;
355     }
356 
357     /**
358      * {@return the operation's successors, as an unmodifiable list}
359      */
360     public List<Block.Reference> successors() {
361         return List.of();
362     }
363 
364     /**
365      * {@return the operation's result type}
366      */
367     public abstract TypeElement resultType();
368 
369     /**
370      * Returns the operation's function type.
371      * <p>
372      * The function type's result type is the operation's result type and the function type's parameter types are the
373      * operation's operand types, in order.
374      *
375      * @return the function type
376      */
377     public FunctionType opType() {
378         List<TypeElement> operandTypes = operands.stream().map(Value::type).toList();
379         return FunctionType.functionType(resultType(), operandTypes);
380     }
381 
382     /**
383      * Traverse the operands of this operation that are the results of prior operations, recursively.
384      * <p>
385      * Traversal is performed in pre-order, reporting the operation of each operand to the visitor.
386      *
387      * @param t   the traversing accumulator
388      * @param v   the visitor
389      * @param <T> accumulator type
390      * @return the traversing accumulator
391      * @apiNote A visitor that implements the abstract method of {@code OpVisitor} and does not override any
392      * other default method will only visit operations. As such a lambda expression or method reference
393      * may be used to visit operations.
394      */
395     public final <T> T traverseOperands(T t, BiFunction<T, Op, T> v) {
396         for (Value arg : operands()) {
397             if (arg instanceof Result or) {
398                 t = v.apply(t, or.op);
399                 t = or.op.traverseOperands(t, v);
400             }
401         }
402 
403         return t;
404     }
405 
406     /**
407      * Computes values captured by this operation. A captured value is a value that dominates
408      * this operation and is used by a descendant operation.
409      * <p>
410      * The order of the captured values is first use encountered in depth
411      * first search of this operation's descendant operations.
412      *
413      * @return the list of captured values, modifiable
414      * @see Body#capturedValues()
415      */
416     public List<Value> capturedValues() {
417         Set<Value> cvs = new LinkedHashSet<>();
418 
419         capturedValues(cvs, new ArrayDeque<>(), this);
420         return new ArrayList<>(cvs);
421     }
422 
423     static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) {
424         for (Body childBody : op.bodies()) {
425             Body.capturedValues(capturedValues, bodyStack, childBody);
426         }
427     }
428 
429     /**
430      * Writes the textual form of this operation to the given output stream, using the UTF-8 character set.
431      *
432      * @param out the stream to write to.
433      */
434     public void writeTo(OutputStream out) {
435         writeTo(new OutputStreamWriter(out, StandardCharsets.UTF_8));
436     }
437 
438     /**
439      * Writes the textual form of this operation to the given writer.
440      *
441      * @param w the writer to write to.
442      */
443     public void writeTo(Writer w) {
444         OpWriter.writeTo(w, this);
445     }
446 
447     /**
448      * Returns the textual form of this operation.
449      *
450      * @return the textual form of this operation.
451      */
452     public String toText() {
453         return OpWriter.toText(this);
454     }
455 }