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