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;
 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 }