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 CodeContextImpl implements CodeContext {
34
35 private static final Map<?, ?> EMPTY_MAP = Map.of();
36
37 final CodeContextImpl 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 CodeContextImpl(CodeContextImpl 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 CodeContextImpl 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 CodeContextImpl 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 }