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 jdk.incubator.code.op; 27 28 import jdk.incubator.code.*; 29 import java.util.HashMap; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.function.Function; 33 34 /** 35 * An operation that supports externalization of its content and reconstruction 36 * via an instance of {@link ExternalizedOp}. 37 * <p> 38 * The specific content of an externalizable operation can be externalized to a 39 * map of {@link #attributes attributes}, and is reconstructed from the 40 * attributes component of an instance of {@link ExternalizedOp}. 41 * <p> 42 * An externalizable operation could be externalized via serialization to 43 * a textual representation. That textual representation could then be deserialized, 44 * via parsing, into an instance of {@link ExternalizedOp} from which a new 45 * externalizable operation can be reconstructed that is identical to one that 46 * was serialized. 47 */ 48 public abstract class ExternalizableOp extends Op { 49 50 /** 51 * An operation's externalized content (a record) that can be utilized to construct an instance 52 * of an {@link ExternalizableOp} associated with the operation's name. 53 * 54 * @param name the operation name 55 * @param operands the list of operands 56 * @param successors the list of successors 57 * @param resultType the operation result type 58 * @param attributes the operation's specific content as an attributes map, modifiable 59 * @param bodyDefinitions the list of body builders for building the operation's bodies 60 * @apiNote Deserializers of operations may utilize this record to construct operations, 61 * thereby separating the specifics of deserializing from construction. 62 */ 63 public record ExternalizedOp(String name, 64 List<Value> operands, 65 List<Block.Reference> successors, 66 TypeElement resultType, 67 Map<String, Object> attributes, 68 List<Body.Builder> bodyDefinitions) { 69 70 /** 71 * Removes an attribute value from the attributes map, converts the value by applying it 72 * to mapping function, and returns the result. 73 * 74 * <p>If the attribute is a default attribute then this method first attempts to 75 * remove the attribute whose name is the empty string, otherwise if there is no such 76 * attribute present or the attribute is not a default attribute then this method 77 * attempts to remove the attribute with the given name. 78 * 79 * <p>On successful removal of the attribute its value is converted by applying the value 80 * to the mapping function. A {@code null} value is represented by the value 81 * {@link #NULL_ATTRIBUTE_VALUE}. 82 * 83 * <p>If no attribute is present the {@code null} value is applied to the mapping function. 84 * 85 * @param name the attribute name. 86 * @param isDefault true if the attribute is a default attribute 87 * @param <T> the converted attribute value type 88 * @return the converted attribute value 89 */ 90 public <T> T extractAttributeValue(String name, boolean isDefault, Function<Object, T> mapper) { 91 Object value = null; 92 if (isDefault && attributes.containsKey("")) { 93 value = attributes.remove(""); 94 assert value != null; 95 } 96 97 if (value == null && attributes.containsKey(name)) { 98 value = attributes.remove(name); 99 assert value != null; 100 } 101 102 return mapper.apply(value); 103 } 104 105 /** 106 * Externalizes an operation's content. 107 * <p> 108 * If the operation is an instanceof {@code ExternalizableOp} then the operation's 109 * specific content is externalized to an attribute map, otherwise the attribute map 110 * is empty. 111 * 112 * @param cc the copy context 113 * @param op the operation 114 * @return the operation's content. 115 */ 116 public static ExternalizedOp externalizeOp(CopyContext cc, Op op) { 117 return new ExternalizedOp( 118 op.opName(), 119 cc.getValues(op.operands()), 120 op.successors().stream().map(cc::getSuccessorOrCreate).toList(), 121 op.resultType(), 122 op instanceof ExternalizableOp exop ? new HashMap<>(exop.attributes()) : new HashMap<>(), 123 op.bodies().stream().map(b -> b.copy(cc)).toList() 124 ); 125 } 126 } 127 128 /** 129 * The attribute name associated with the location attribute. 130 */ 131 public static final String ATTRIBUTE_LOCATION = "loc"; 132 133 /** 134 * The attribute value that represents the external null value. 135 */ 136 public static final Object NULL_ATTRIBUTE_VALUE = new Object(); 137 138 /** 139 * Constructs an operation by copying given operation. 140 * 141 * @param that the operation to copy. 142 * @param cc the copy context. 143 * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list 144 * values computed, in order, by mapping the operation's operands using the copy context. 145 */ 146 protected ExternalizableOp(Op that, CopyContext cc) { 147 super(that, cc); 148 } 149 150 /** 151 * Constructs an operation with a name, operation result type, and list of operands. 152 * 153 * @param name the operation name. 154 * @param operands the list of operands, a copy of the list is performed if required. 155 */ 156 protected ExternalizableOp(String name, List<? extends Value> operands) { 157 super(name, operands); 158 } 159 160 /** 161 * Constructs an operation from its external content. 162 * 163 * @param def the operation's external content. 164 * @implSpec This implementation invokes the {@link Op#Op(String, List) constructor} 165 * accepting the non-optional components of the operation's content, {@code name}, 166 * and {@code operands}: 167 * <pre> {@code 168 * this(def.name(), def.operands()); 169 * }</pre> 170 */ 171 @SuppressWarnings("this-escape") 172 protected ExternalizableOp(ExternalizedOp def) { 173 super(def.name(), def.operands()); 174 setLocation(extractLocation(def)); 175 } 176 177 static Location extractLocation(ExternalizedOp def) { 178 Object v = def.attributes().get(ATTRIBUTE_LOCATION); 179 return switch (v) { 180 case String s -> Location.fromString(s); 181 case Location loc -> loc; 182 case null -> null; 183 default -> throw new UnsupportedOperationException("Unsupported location value:" + v); 184 }; 185 } 186 187 /** 188 * Externalizes the operation's specific content as a map of attributes. 189 * 190 * <p>A null attribute value is represented by the constant 191 * value {@link #NULL_ATTRIBUTE_VALUE}. 192 * 193 * @return the operation's attributes, as an unmodifiable map 194 */ 195 public Map<String, Object> attributes() { 196 Location l = location(); 197 return l == null ? Map.of() : Map.of(ATTRIBUTE_LOCATION, l); 198 } 199 }