1 package jdk.incubator.code.extern;
 2 
 3 import jdk.incubator.code.*;
 4 
 5 import java.util.List;
 6 import java.util.Map;
 7 import java.util.function.Function;
 8 
 9 /**
10  * An operation's externalized state (a record) that can be utilized to construct an instance
11  * of an {@link Op} associated with that state, such as the operation's name.
12  *
13  * @param name            the operation name
14  * @param location        the source location associated with the operation, may be null
15  * @param operands        the list of operands
16  * @param successors      the list of successors
17  * @param resultType      the operation result type
18  * @param attributes      the operation's specific state as a map of attributes
19  * @param bodyDefinitions the list of body builders for building the operation's bodies
20  * @apiNote Deserializers of operations may utilize this record to construct operations,
21  * thereby separating the specifics of deserializing from construction.
22  */
23 public record ExternalizedOp(String name,
24                              Location location,
25                              List<Value> operands,
26                              List<Block.Reference> successors,
27                              TypeElement resultType,
28                              Map<String, Object> attributes,
29                              List<Body.Builder> bodyDefinitions) {
30 
31     /**
32      * The attribute value that represents the external null value.
33      */
34     public static final Object NULL_ATTRIBUTE_VALUE = new Object();
35 
36     public ExternalizedOp {
37         attributes = Map.copyOf(attributes);
38     }
39 
40     /**
41      * Gets an attribute value from the attributes map, converts the value by applying it
42      * to mapping function, and returns the result.
43      *
44      * <p>If the attribute is a default attribute then this method first attempts to
45      * get the attribute whose name is the empty string, otherwise if there is no such
46      * attribute present or the attribute is not a default attribute then this method
47      * attempts to get the attribute with the given name.
48      *
49      * <p>On successfully obtaining the attribute its value is converted by applying the value
50      * to the mapping function. A {@code null} value is represented by the value
51      * {@link ExternalizedOp#NULL_ATTRIBUTE_VALUE}.
52      *
53      * <p>If no attribute is present the {@code null} value is applied to the mapping function.
54      *
55      * @param name      the attribute name.
56      * @param isDefault true if the attribute is a default attribute
57      * @param <T>       the converted attribute value type
58      * @return the converted attribute value
59      */
60     public <T> T extractAttributeValue(String name, boolean isDefault, Function<Object, T> mapper) {
61         Object value = null;
62         if (isDefault && attributes.containsKey("")) {
63             value = attributes.get("");
64             assert value != null;
65         }
66 
67         if (value == null && attributes.containsKey(name)) {
68             value = attributes.get(name);
69             assert value != null;
70         }
71 
72         return mapper.apply(value);
73     }
74 
75     /**
76      * Externalizes an operation's content.
77      * <p>
78      * If the operation is an instanceof {@code ExternalizableOp} then the operation's
79      * specific content is externalized to an attribute map, otherwise the attribute map
80      * is empty.
81      *
82      * @param cc the copy context
83      * @param op the operation
84      * @return the operation's content.
85      */
86     public static ExternalizedOp externalizeOp(CopyContext cc, Op op) {
87         return new ExternalizedOp(
88                 op.opName(),
89                 op.location(),
90                 cc.getValues(op.operands()),
91                 op.successors().stream().map(cc::getSuccessorOrCreate).toList(),
92                 op.resultType(),
93                 op.externalize(),
94                 op.bodies().stream().map(b -> b.copy(cc)).toList()
95         );
96     }
97 }