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 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 public ExternalizedOp { 71 attributes = Map.copyOf(attributes); 72 } 73 74 /** 75 * Gets an attribute value from the attributes map, converts the value by applying it 76 * to mapping function, and returns the result. 77 * 78 * <p>If the attribute is a default attribute then this method first attempts to 79 * get the attribute whose name is the empty string, otherwise if there is no such 80 * attribute present or the attribute is not a default attribute then this method 81 * attempts to get the attribute with the given name. 82 * 83 * <p>On successfully obtaining the attribute its value is converted by applying the value 84 * to the mapping function. A {@code null} value is represented by the value 85 * {@link #NULL_ATTRIBUTE_VALUE}. 86 * 87 * <p>If no attribute is present the {@code null} value is applied to the mapping function. 88 * 89 * @param name the attribute name. 90 * @param isDefault true if the attribute is a default attribute 91 * @param <T> the converted attribute value type 92 * @return the converted attribute value 93 */ 94 public <T> T extractAttributeValue(String name, boolean isDefault, Function<Object, T> mapper) { 95 Object value = null; 96 if (isDefault && attributes.containsKey("")) { 97 value = attributes.get(""); 98 assert value != null; 99 } 100 101 if (value == null && attributes.containsKey(name)) { 102 value = attributes.get(name); 103 assert value != null; 104 } 105 106 return mapper.apply(value); 107 } 108 109 /** 110 * Externalizes an operation's content. 111 * <p> 112 * If the operation is an instanceof {@code ExternalizableOp} then the operation's 113 * specific content is externalized to an attribute map, otherwise the attribute map 114 * is empty. 115 * 116 * @param cc the copy context 117 * @param op the operation 118 * @return the operation's content. 119 */ 120 public static ExternalizedOp externalizeOp(CopyContext cc, Op op) { 121 return new ExternalizedOp( 122 op.opName(), 123 cc.getValues(op.operands()), 124 op.successors().stream().map(cc::getSuccessorOrCreate).toList(), 125 op.resultType(), 126 op instanceof ExternalizableOp exop ? exop.attributes() : Map.of(), 127 op.bodies().stream().map(b -> b.copy(cc)).toList() 128 ); 129 } 130 } 131 132 /** 133 * The attribute name associated with the location attribute. 134 */ 135 public static final String ATTRIBUTE_LOCATION = "loc"; 136 137 /** 138 * The attribute value that represents the external null value. 139 */ 140 public static final Object NULL_ATTRIBUTE_VALUE = new Object(); 141 142 /** 143 * Constructs an operation by copying given operation. 144 * 145 * @param that the operation to copy. 146 * @param cc the copy context. 147 * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list 148 * values computed, in order, by mapping the operation's operands using the copy context. 149 */ 150 protected ExternalizableOp(Op that, CopyContext cc) { 151 super(that, cc); 152 } 153 154 /** 155 * Constructs an operation with a name, operation result type, and list of operands. 156 * 157 * @param name the operation name. 158 * @param operands the list of operands, a copy of the list is performed if required. 159 */ 160 protected ExternalizableOp(String name, List<? extends Value> operands) { 161 super(name, operands); 162 } 163 164 /** 165 * Constructs an operation from its external content. 166 * 167 * @param def the operation's external content. 168 * @implSpec This implementation invokes the {@link Op#Op(String, List) constructor} 169 * accepting the non-optional components of the operation's content, {@code name}, 170 * and {@code operands}: 171 * <pre> {@code 172 * this(def.name(), def.operands()); 173 * }</pre> 174 */ 175 @SuppressWarnings("this-escape") 176 protected ExternalizableOp(ExternalizedOp def) { 177 super(def.name(), def.operands()); 178 setLocation(extractLocation(def)); 179 } 180 181 static Location extractLocation(ExternalizedOp def) { 182 Object v = def.attributes().get(ATTRIBUTE_LOCATION); 183 return switch (v) { 184 case String s -> Location.fromString(s); 185 case Location loc -> loc; 186 case null -> null; 187 default -> throw new UnsupportedOperationException("Unsupported location value:" + v); 188 }; 189 } 190 191 /** 192 * Externalizes the operation's specific content as a map of attributes. 193 * 194 * <p>A null attribute value is represented by the constant 195 * value {@link #NULL_ATTRIBUTE_VALUE}. 196 * 197 * @return the operation's attributes, as an unmodifiable map 198 */ 199 public Map<String, Object> attributes() { 200 Location l = location(); 201 return l == null ? Map.of() : Map.of(ATTRIBUTE_LOCATION, l); 202 } 203 }