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; 27 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.Objects; 31 import java.util.function.Function; 32 33 final class CopyContextImpl implements CopyContext { 34 35 private static final Map<?, ?> EMPTY_MAP = Map.of(); 36 37 final CopyContextImpl parent; 38 39 Map<Value, Value> valueMap; 40 Map<Block, Block.Builder> blockMap; 41 Map<Block.Reference, Block.Reference> successorMap; 42 Map<Object, Object> propertiesMap; 43 44 CopyContextImpl(CopyContextImpl that) { 45 this.parent = that; 46 this.blockMap = emptyMap(); 47 this.valueMap = emptyMap(); 48 this.successorMap = emptyMap(); 49 this.propertiesMap = emptyMap(); 50 } 51 52 @SuppressWarnings("unchecked") 53 private static <K, V> Map<K, V> emptyMap() { 54 return (Map<K, V>) EMPTY_MAP; 55 } 56 57 58 // Values 59 60 @Override 61 public Value getValue(Value input) { 62 Value output = getValueOrNull(input); 63 if (output != null) { 64 return output; 65 } 66 throw new IllegalArgumentException("No mapping for input value: " + input); 67 } 68 69 @Override 70 public Value getValueOrDefault(Value input, Value defaultValue) { 71 Value output = getValueOrNull(input); 72 if (output != null) { 73 return output; 74 } 75 return defaultValue; 76 } 77 78 private Value getValueOrNull(Value input) { 79 Objects.requireNonNull(input); 80 81 CopyContextImpl p = this; 82 do { 83 Value output = p.valueMap.get(input); 84 if (output != null) { 85 return output; 86 } 87 p = p.parent; 88 } while (p != null); 89 90 return null; 91 } 92 93 @Override 94 public void mapValue(Value input, Value output) { 95 Objects.requireNonNull(input); 96 Objects.requireNonNull(output); 97 98 if (output.isBound()) { 99 throw new IllegalArgumentException("Output value bound: " + output); 100 } 101 102 if (valueMap == EMPTY_MAP) { 103 valueMap = new HashMap<>(); 104 } 105 valueMap.put(input, output); 106 } 107 108 @Override 109 public Value mapValueIfAbsent(Value input, Value output) { 110 Objects.requireNonNull(input); 111 Objects.requireNonNull(output); 112 113 if (output.isBound()) { 114 throw new IllegalArgumentException("Output value is bound: " + output); 115 } 116 117 if (valueMap == EMPTY_MAP) { 118 valueMap = new HashMap<>(); 119 } 120 return valueMap.putIfAbsent(input, output); 121 } 122 123 124 // Blocks 125 126 @Override 127 public Block.Builder getBlock(Block input) { 128 Objects.requireNonNull(input); 129 130 return blockMap.get(input); 131 } 132 133 @Override 134 public void mapBlock(Block input, Block.Builder output) { 135 Objects.requireNonNull(input); 136 Objects.requireNonNull(output); 137 138 if (output.target().isBound()) { 139 throw new IllegalArgumentException("Output block builder is built: " + output); 140 } 141 142 if (blockMap == EMPTY_MAP) { 143 blockMap = new HashMap<>(); 144 } 145 blockMap.put(input, output); 146 } 147 148 149 // Successors 150 151 @Override 152 public Block.Reference getSuccessor(Block.Reference input) { 153 Objects.requireNonNull(input); 154 155 return successorMap.get(input); 156 } 157 158 @Override 159 public void mapSuccessor(Block.Reference input, Block.Reference output) { 160 Objects.requireNonNull(input); 161 Objects.requireNonNull(output); 162 163 if (output.target.isBound()) { 164 throw new IllegalArgumentException("Output block reference target is built: " + output); 165 } 166 167 for (Value outputArgument : output.arguments()) { 168 if (outputArgument.isBound()) { 169 throw new IllegalArgumentException("Output block reference argument is bound: " + outputArgument); 170 } 171 } 172 173 if (successorMap == EMPTY_MAP) { 174 successorMap = new HashMap<>(); 175 } 176 successorMap.put(input, output); 177 } 178 179 180 // Properties 181 182 @Override 183 public Object getProperty(Object key) { 184 CopyContextImpl p = this; 185 do { 186 Object value = p.propertiesMap.get(key); 187 if (value != null) { 188 return value; 189 } 190 p = p.parent; 191 } while (p != null); 192 193 return null; 194 } 195 196 @Override 197 public Object putProperty(Object key, Object value) { 198 if (propertiesMap == EMPTY_MAP) { 199 propertiesMap = new HashMap<>(); 200 } 201 return propertiesMap.put(key, value); 202 } 203 204 @Override 205 public Object computePropertyIfAbsent(Object key, Function<Object, Object> mappingFunction) { 206 if (propertiesMap == EMPTY_MAP) { 207 propertiesMap = new HashMap<>(); 208 } 209 Object value = getProperty(key); 210 if (value != null) { 211 return value; 212 } 213 propertiesMap.put(key, value = mappingFunction.apply(key)); 214 return value; 215 } 216 }