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.List;
 29 import java.util.function.Function;
 30 
 31 import static java.util.stream.Collectors.toList;
 32 
 33 /**
 34  * A context utilized when transforming code models.
 35  * <p>
 36  * The context holds a mapping of input values to output values, input blocks to output block builders,
 37  * and input block references to output block references.
 38  * These mappings are built as an input model is transformed to produce an output model. Mappings are built implicitly
 39  * when an operation is transformed by copying, and can be explicitly added by the transformation when removing or
 40  * adding new operations.
 41  * <p>
 42  * Unless otherwise specified the passing of a {@code null} argument to the methods of this interface results in a
 43  * {@code NullPointerException}.
 44  */
 45 public sealed interface CopyContext permits CopyContextImpl {
 46 
 47     // @@@ ?
 48     // CopyContext parent();
 49 
 50 
 51     // Value mappings
 52 
 53     /**
 54      * {@return the output value mapped to the input value}
 55      * <p>
 56      * If this context is not isolated and there is no value mapping in this context then this method will return
 57      * the result of calling {@code getValue} on the parent context, if present. Otherwise if this context is isolated
 58      * or there is no parent context, then there is no mapping.
 59      *
 60      * @param input the input value
 61      * @throws IllegalArgumentException if there is no mapping
 62      */
 63     Value getValue(Value input);
 64 
 65     /**
 66      * {@return the output value mapped to the input value or a default value if no mapping}
 67      *
 68      * @param input the input value
 69      * @param defaultValue the default value to return if no mapping
 70      */
 71     Value getValueOrDefault(Value input, Value defaultValue);
 72 
 73     /**
 74      * Maps an input value to an output value.
 75      * <p>
 76      * Uses of the input value will be mapped to the output value when transforming.
 77      *
 78      * @param input the input value
 79      * @param output the output value
 80      * @throws IllegalArgumentException if the output value is already bound
 81      */
 82     void mapValue(Value input, Value output);
 83 
 84     /**
 85      * Maps an input value to an output value, if no such mapping exists.
 86      * <p>
 87      * Uses of the input value will be mapped to the output value when transforming.
 88      *
 89      * @param input the input value
 90      * @param output the output value
 91      * @return the previous mapped value, or null of there was no mapping.
 92      * @throws IllegalArgumentException if the output value is already bound
 93      */
 94     // @@@ Is this needed?
 95     Value mapValueIfAbsent(Value input, Value output);
 96 
 97     /**
 98      * Returns a list of mapped output values by obtaining, in order, the output value for each element in the list
 99      * of input values.
100      *
101      * @param inputs the list of input values
102      * @return a modifiable list of output values
103      * @throws IllegalArgumentException if an input value has no mapping
104      */
105     // @@@ If getValue is modified to return null then this method should fail on null
106     default List<Value> getValues(List<? extends Value> inputs) {
107         return inputs.stream().map(this::getValue).collect(toList());
108     }
109 
110     /**
111      * Maps the list of input values, in order, to the corresponding list of output values, up to the number of
112      * elements that is the minimum of the size of both lists.
113      * <p>
114      * Uses of an input value will be mapped to the corresponding output value when transforming.
115      *
116      * @param inputs the input values
117      * @param outputs the output values.
118      * @throws IllegalArgumentException if an output value is already bound
119      */
120     default void mapValues(List<? extends Value> inputs, List<? extends Value> outputs) {
121         // @@@ sizes should be the same?
122         for (int i = 0; i < Math.min(inputs.size(), outputs.size()); i++) {
123             mapValue(inputs.get(i), outputs.get(i));
124         }
125     }
126 
127 
128     // Block mappings
129 
130     /**
131      * {@return the output block builder mapped to the input block, otherwise null if no mapping}
132      *
133      * @param input the input block
134      */
135     Block.Builder getBlock(Block input);
136 
137     /**
138      * Maps an input block to an output block builder.
139      * <p>
140      * Uses of the input block will be mapped to the output block builder when transforming.
141      *
142      * @param input the input block
143      * @param output the output block builder
144      * @throws IllegalArgumentException if the output block is already bound
145      */
146     void mapBlock(Block input, Block.Builder output);
147 
148 
149     // Successor mappings
150 
151     /**
152      * {@return the output block reference mapped to the input block reference,
153      * otherwise null if no mapping}
154      *
155      * @param input the input reference
156      */
157     Block.Reference getSuccessor(Block.Reference input);
158 
159     /**
160      * Maps an input block reference to an output block reference.
161      * <p>
162      * Uses of the input block reference will be mapped to the output block reference when transforming.
163      *
164      * @param input the input block reference
165      * @param output the output block reference
166      * @throws IllegalArgumentException if the output block builder associated with the block reference or any of its
167      * argument values are already bound
168      */
169     void mapSuccessor(Block.Reference input, Block.Reference output);
170 
171     /**
172      * Returns a mapped output block reference, if present, otherwise creates a new, unmapped, reference from the input
173      * block reference.
174      * <p>
175      * A new, unmapped reference, is created by obtaining the mapped output block builder from the input reference's
176      * target block, and creating a successor from the output block builder with arguments that is the result of
177      * obtaining the mapped values from the input reference's arguments.
178      *
179      * @param input the input block reference
180      * @return the output block reference, if present, otherwise a created block reference
181      * @throws IllegalArgumentException if a new reference is to be created and there is no mapped output block builder
182      */
183     default Block.Reference getSuccessorOrCreate(Block.Reference input) {
184         Block.Reference successor = getSuccessor(input);
185         if (successor != null) {
186             return successor;
187         }
188 
189         // Create successor
190         Block.Builder outputBlock = getBlock(input.targetBlock());
191         if (outputBlock == null) {
192             throw new IllegalArgumentException("No mapping for input reference target block" + input.targetBlock());
193         }
194         return outputBlock.successor(getValues(input.arguments()));
195     }
196 
197 
198     // Properties mappings
199 
200     /**
201      * {@return an object associated with a property key}
202      *
203      * @param key the property key
204      */
205     Object getProperty(Object key);
206 
207     /**
208      * Associates an object with a property key.
209      *
210      * @param key the property key
211      * @param value the associated object
212      * @return the current associated object, or null if not associated
213      */
214     Object putProperty(Object key, Object value);
215 
216     /**
217      * If the property key is not already associated with an object, attempts to compute the object using the
218      * mapping function and associates it unless {@code null}.
219      *
220      * @param key the property key
221      * @param mappingFunction the mapping function
222      * @return the current (existing or computed) object associated with the property key,
223      * or null if the computed object is null
224      */
225     Object computePropertyIfAbsent(Object key, Function<Object, Object> mappingFunction);
226 
227 
228     // Factories
229 
230     /**
231      * {@return a new isolated context initialized with no mappings and no parent }
232      */
233     static CopyContext create() {
234         return new CopyContextImpl(null);
235     }
236 
237     /**
238      * {@return a new non-isolated context initialized with no mappings and a parent }
239      * The returned context will query value and property mappings in the parent context
240      * if a query of its value and property mappings yields no result.
241      */
242     static CopyContext create(CopyContext parent) {
243         return new CopyContextImpl((CopyContextImpl) parent);
244     }
245 }