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.internal;
27
28 import jdk.incubator.code.*;
29 import jdk.incubator.code.dialect.core.CoreType;
30 import jdk.incubator.code.dialect.core.FunctionType;
31 import jdk.incubator.code.dialect.java.*;
32 import jdk.incubator.code.extern.*;
33
34 import java.util.*;
35 import java.util.function.Function;
36
37 import static jdk.incubator.code.dialect.core.CoreOp.*;
38 import static jdk.incubator.code.dialect.core.CoreType.functionType;
39 import static jdk.incubator.code.dialect.java.JavaOp.*;
40 import static jdk.incubator.code.dialect.java.JavaType.*;
41
42 /**
43 * A transformer of code models to models that build them.
44 * <p>
45 * A building code model when executed will construct the same code model it was transformed from.
46 * Such a building code model could be transformed to bytecode and stored in class files.
47 */
48 public class OpBuilder {
49
50 static final JavaType J_C_E_EXTERNALIZED_OP = type(ExternalizedOp.class);
51
52 static final MethodRef DIALECT_FACTORY_OP_FACTORY = MethodRef.method(DialectFactory.class, "opFactory",
53 OpFactory.class);
54
55 static final MethodRef DIALECT_FACTORY_TYPE_ELEMENT_FACTORY = MethodRef.method(DialectFactory.class, "typeElementFactory",
56 TypeElementFactory.class);
57
58 static final MethodRef OP_FACTORY_CONSTRUCT = MethodRef.method(OpFactory.class, "constructOp",
59 Op.class, ExternalizedOp.class);
60
61 static final MethodRef TYPE_ELEMENT_FACTORY_CONSTRUCT = MethodRef.method(TypeElementFactory.class, "constructType",
62 TypeElement.class, ExternalizedTypeElement.class);
63
64 static final MethodRef BODY_BUILDER_OF = MethodRef.method(Body.Builder.class, "of",
65 Body.Builder.class, Body.Builder.class, FunctionType.class);
66
67 static final MethodRef BODY_BUILDER_ENTRY_BLOCK = MethodRef.method(Body.Builder.class, "entryBlock",
68 Block.Builder.class);
69
70 // instance varargs
71 static final MethodRef BLOCK_BUILDER_SUCCESSOR = MethodRef.method(Block.Builder.class, "successor",
72 Block.Reference.class, Value[].class);
73
74 static final MethodRef BLOCK_BUILDER_OP = MethodRef.method(Block.Builder.class, "op",
75 Op.Result.class, Op.class);
76
77 // instance varargs
78 static final MethodRef BLOCK_BUILDER_BLOCK = MethodRef.method(Block.Builder.class, "block",
79 Block.Builder.class, TypeElement[].class);
80
81 static final MethodRef BLOCK_BUILDER_PARAMETER = MethodRef.method(Block.Builder.class, "parameter",
82 Block.Parameter.class, TypeElement.class);
83
84 // static varargs
85 static final MethodRef FUNCTION_TYPE_FUNCTION_TYPE = MethodRef.method(CoreType.class, "functionType",
86 FunctionType.class, TypeElement.class, TypeElement[].class);
87
88
89 static final JavaType J_U_LIST = type(List.class);
90
91 static final MethodRef LIST_OF_ARRAY = MethodRef.method(J_U_LIST, "of",
92 J_U_LIST, array(J_L_OBJECT, 1));
93
94 static final JavaType J_U_MAP = type(Map.class);
95
96 static final JavaType J_U_MAP_ENTRY = type(Map.Entry.class);
97
98 static final MethodRef MAP_ENTRY = MethodRef.method(J_U_MAP, "entry",
99 J_U_MAP, J_L_OBJECT, J_L_OBJECT);
100
101 static final MethodRef MAP_OF_ARRAY = MethodRef.method(J_U_MAP, "of",
102 J_U_MAP, array(J_U_MAP_ENTRY, 1));
103
104
105 static final JavaType EX_TYPE_ELEM = type(ExternalizedTypeElement.class);
106
107 static final MethodRef EX_TYPE_ELEM_OF_LIST = MethodRef.method(EX_TYPE_ELEM, "of",
108 EX_TYPE_ELEM, J_L_STRING, J_U_LIST);
109
110
111 static final JavaType J_C_LOCATION = type(Location.class);
112
113 static final FunctionType EXTERNALIZED_OP_F_TYPE = functionType(
114 J_C_E_EXTERNALIZED_OP,
115 J_L_STRING,
116 J_C_LOCATION,
117 J_U_LIST,
118 J_U_LIST,
119 type(TypeElement.class),
120 J_U_MAP,
121 J_U_LIST);
122
123 static final FunctionType BUILDER_F_TYPE = functionType(type(Op.class));
124
125 static final MethodRef LIST_EMPTY = MethodRef.method(List.class, "of", List.class);
126 static final MethodRef LIST_OF_OBJECT = MethodRef.method(List.class, "of", List.class, Object.class);
127 static final MethodRef MAP_EMPTY = MethodRef.method(Map.class, "of", Map.class);
128 static final MethodRef MAP_OF_OBJECT_OBJECT = MethodRef.method(Map.class, "of", Map.class, Object.class, Object.class);
129
130 static final String LIST_BUILDER_F_NAME = "$list";
131 static final String MAP_BUILDER_F_NAME = "$map";
132 static final String OP_BUILDER_F_NAME_1 = "$op1";
133 static final String OP_BUILDER_F_NAME_2 = "$op2";
134 static final String OP_BUILDER_F_NAME_3 = "$op3";
135 static final String TYPE_BUILDER_F_NAME = "$type";
136 static final String EXTER_TYPE_BUILDER_F_NAME = "$exterType";
137
138 static final FunctionType LIST_BUILDER_F_TYPE = functionType(
139 J_U_LIST,
140 J_L_OBJECT);
141
142 static final FunctionType MAP_BUILDER_F_TYPE = functionType(
143 J_U_MAP,
144 J_L_OBJECT);
145
146 static final FunctionType OP_BUILDER_F_OVERRIDE_1 = functionType(
147 type(Op.class),
148 J_L_STRING, // op name
149 J_C_LOCATION, // location: Location or null
150 J_L_OBJECT, // operand(s): Value, List<Value> or null
151 J_L_OBJECT, // successor(s): Block.Reference, List<Block.Reference> or null
152 INT, // result type index
153 J_L_OBJECT, // attribute(s): Map<String, Object>, Object or null
154 J_L_OBJECT); // body definition(s): Body.Builder, List<Body.Builder> or null
155
156 static final FunctionType OP_BUILDER_F_OVERRIDE_2 = functionType(
157 type(Op.Result.class),
158 type(Block.Builder.class),
159 J_L_STRING, // op name
160 J_C_LOCATION, // location: Location or null
161 J_L_OBJECT, // operand(s): Value, List<Value> or null
162 J_L_OBJECT, // successor(s): Block.Reference, List<Block.Reference> or null
163 INT, // result type index
164 J_L_OBJECT, // attribute(s): Map<String, Object>, Object or null
165 J_L_OBJECT); // body definition(s): Body.Builder, List<Body.Builder> or null
166
167 static final FunctionType OP_BUILDER_F_OVERRIDE_3 = functionType(
168 type(Op.Result.class),
169 type(Block.Builder.class),
170 J_L_STRING, // op name
171 INT, // location.line
172 INT, // location.columnt
173 J_L_OBJECT, // operand(s): Value, List<Value> or null
174 J_L_OBJECT, // successor(s): Block.Reference, List<Block.Reference> or null
175 INT, // result type index
176 J_L_OBJECT, // attribute(s): Map<String, Object>, Object or null
177 J_L_OBJECT); // body definition(s): Body.Builder, List<Body.Builder> or null
178
179 static final FunctionType TYPE_BUILDER_F_TYPE = functionType(JavaType.type(TypeElement.class), INT);
180
181 final Map<Value, Value> valueMap;
182
183 final Map<Block, Value> blockMap;
184
185 final SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExternalizedTypes;
186
187 final Map<TypeElement, Value> typeElementMap;
188
189 final Block.Builder builder;
190
191 /**
192 * Transform the given code model to one that builds it.
193 * <p>
194 * This method initially applies the function {@code dialectFactoryF} to
195 * the block builder that is used to build resulting code model. The result
196 * is a dialect factory value which is subsequently used to build operations
197 * that construct type elements and operations present in the given code model.
198 *
199 * @param ops the named code models.
200 * @param dialectFactoryF a function that builds code items to produce a dialect factory value.
201 * @return the module with building code models and support functions.
202 */
203 public static ModuleOp createBuilderFunctions(SequencedMap<String, ? extends Op> ops, Function<Block.Builder, Value> dialectFactoryF) {
204 List<FuncOp> funcs = new ArrayList<>();
205 SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExternalizedTypes = new LinkedHashMap<>();
206 for (var e : ops.sequencedEntrySet()) {
207 OpBuilder opBuilder = new OpBuilder(registeredExternalizedTypes);
208 funcs.add(opBuilder.build(e.getKey(), e.getValue()));
209 registeredExternalizedTypes = opBuilder.registeredExternalizedTypes;
210 }
211 funcs.addAll(createSupportFunctions(dialectFactoryF));
212 funcs.add(createExternTypeHelperFunc(registeredExternalizedTypes));
213 ModuleOp module = module(funcs);
214 module.seal();
215 return module;
216 }
217
218 static List<FuncOp> createSupportFunctions(Function<Block.Builder, Value> dialectFactoryF) {
219 return List.of(
220 // static List $list(Object o) {
221 // if (o == null) return List.of();
222 // if (o instanceof List) return (List)o;
223 // return List.of(o);
224 // }
225 func(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE).body(bb -> {
226 Block.Builder b0 = bb.entryBlock(), b1 = b0.block(), b2 = b0.block(), b3 = b0.block(), b4 = b0.block();
227 Value arg = b0.parameters().get(0);
228 b0.op(conditionalBranch(b0.op(eq(arg, b0.op(constant(J_L_OBJECT, null)))), b1.successor(), b2.successor()));
229 b1.op(return_(b1.op(invoke(LIST_EMPTY))));
230 b2.op(conditionalBranch(b2.op(instanceOf(J_U_LIST, arg)), b3.successor(), b4.successor()));
231 b3.op(return_(b3.op(cast(J_U_LIST, arg))));
232 b4.op(return_(b4.op(invoke(LIST_OF_OBJECT, arg))));
233 }),
234 // static Map $map(Object o) {
235 // if (o == null) return Map.of();
236 // if (o instanceof Map) return (Map)o;
237 // return Map.of("", o);
238 // }
239 func(MAP_BUILDER_F_NAME, MAP_BUILDER_F_TYPE).body(bb -> {
240 Block.Builder b0 = bb.entryBlock(), b1 = b0.block(), b2 = b0.block(), b3 = b0.block(), b4 = b0.block();
241 Value arg = b0.parameters().get(0);
242 b0.op(conditionalBranch(b0.op(eq(arg, b0.op(constant(J_L_OBJECT, null)))), b1.successor(), b2.successor()));
243 b1.op(return_(b1.op(invoke(MAP_EMPTY))));
244 b2.op(conditionalBranch(b2.op(instanceOf(J_U_MAP, arg)), b3.successor(), b4.successor()));
245 b3.op(return_(b3.op(cast(J_U_MAP, arg))));
246 b4.op(return_(b4.op(invoke(MAP_OF_OBJECT_OBJECT, b4.op(constant(J_L_STRING, "")), arg))));
247 }),
248 // static Op $op1(String name,
249 // Location location,
250 // Object operands,
251 // Object successors,
252 // int resultTypeIndex,
253 // Object attributes,
254 // Object bodyDefinitions) {
255 // return <dialect factory>.opFactory().constructOp(
256 // new ExternalizedOp(name,
257 // location,
258 // $list(operands),
259 // $list(successors),
260 // $type(resultTypeIndex),
261 // $map(attributes),
262 // $list(bodyDefinitions)));
263 // }
264 func(OP_BUILDER_F_NAME_1, OP_BUILDER_F_OVERRIDE_1).body(bb -> {
265 Block.Builder b = bb.entryBlock();
266 List<Block.Parameter> args = b.parameters();
267 b.op(return_(b.op(invoke(OP_FACTORY_CONSTRUCT,
268 b.op(invoke(DIALECT_FACTORY_OP_FACTORY, dialectFactoryF.apply(b))),
269 b.op(new_(MethodRef.constructor(EXTERNALIZED_OP_F_TYPE),
270 args.get(0),
271 args.get(1),
272 b.op(funcCall(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE, args.get(2))),
273 b.op(funcCall(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE, args.get(3))),
274 b.op(funcCall(TYPE_BUILDER_F_NAME, TYPE_BUILDER_F_TYPE, args.get(4))),
275 b.op(funcCall(MAP_BUILDER_F_NAME, MAP_BUILDER_F_TYPE, args.get(5))),
276 b.op(funcCall(LIST_BUILDER_F_NAME, LIST_BUILDER_F_TYPE, args.get(6)))))))));
277 }),
278 // static Op.Result $op2(Block.Builder b,
279 // String name,
280 // Location location,
281 // Object operands,
282 // Object successors,
283 // int resultTypeIndex,
284 // Object attributes,
285 // Object bodyDefinitions) {
286 // return b.op($op1(name,
287 // location,
288 // operands,
289 // successors,
290 // resultType,
291 // attributes,
292 // bodyDefinitions));
293 // }
294 func(OP_BUILDER_F_NAME_2, OP_BUILDER_F_OVERRIDE_2).body(bb -> {
295 Block.Builder b = bb.entryBlock();
296 List<Block.Parameter> args = b.parameters();
297 b.op(return_(b.op(invoke(BLOCK_BUILDER_OP,
298 args.get(0),
299 b.op(funcCall(OP_BUILDER_F_NAME_1, OP_BUILDER_F_OVERRIDE_1,
300 args.get(1),
301 args.get(2),
302 args.get(3),
303 args.get(4),
304 args.get(5),
305 args.get(6),
306 args.get(7)))))));
307 }),
308 // static Op.Result $op3(Block.Builder b,
309 // String name,
310 // int line,
311 // int column,
312 // Object operands,
313 // Object successors,
314 // int resultTypeIndex,
315 // Object attributes,
316 // Object bodyDefinitions) {
317 // return $op2(b,
318 // name,
319 // new Location(line, column),
320 // operands,
321 // successors,
322 // resultType,
323 // attributes,
324 // bodyDefinitions);
325 // }
326 func(OP_BUILDER_F_NAME_3, OP_BUILDER_F_OVERRIDE_3).body(bb -> {
327 Block.Builder b = bb.entryBlock();
328 List<Block.Parameter> args = b.parameters();
329 b.op(return_(b.op(funcCall(OP_BUILDER_F_NAME_2, OP_BUILDER_F_OVERRIDE_2,
330 args.get(0),
331 args.get(1),
332 b.op(new_(MethodRef.constructor(Location.class, int.class, int.class), args.get(2), args.get(3))),
333 args.get(4),
334 args.get(5),
335 args.get(6),
336 args.get(7),
337 args.get(8)))));
338 }),
339 // static private TypeElement $type(int typeIndex) {
340 // return JavaOp.JAVA_DIALECT_FACTORY.typeElementFactory().constructType($exType(typeIndex));
341 // }
342 func(TYPE_BUILDER_F_NAME, CoreType.functionType(type(TypeElement.class))).body(b -> {
343 var i = b.parameter(INT);
344 var typeElementFactory = b.op(invoke(DIALECT_FACTORY_TYPE_ELEMENT_FACTORY, dialectFactoryF.apply(b)));
345 var exterType = b.op(funcCall(EXTER_TYPE_BUILDER_F_NAME, functionType(type(ExternalizedTypeElement.class)), i));
346 var typeElement = b.op(invoke(MethodRef.method(TypeElementFactory.class, "constructType", TypeElement.class, ExternalizedTypeElement.class), typeElementFactory, exterType));
347 b.op(return_(typeElement));
348 })
349 );
350 }
351
352 private static FuncOp createExternTypeHelperFunc(Map<ExternalizedTypeElement, List<Integer>> registeredExterTypes) {
353 /*
354 static private ExternalizedTypeElement $exType(int typeIndex) {
355 return switch(typeIndex) {
356 case 0 -> ExternalizedTypeElement.of("void");
357 case 1 -> ExternalizedTypeElement.of("java.type.primitive", exType(0));
358 default -> throw new IllegalStateException();
359 };
360 }
361 */
362 return func(EXTER_TYPE_BUILDER_F_NAME, functionType(type(ExternalizedTypeElement.class))).body(b -> {
363 Block.Parameter i = b.parameter(INT);
364 List<Body.Builder> swBodies = new ArrayList<>();
365 for (Map.Entry<ExternalizedTypeElement, List<Integer>> e : registeredExterTypes.entrySet()) {
366 Body.Builder l = Body.Builder.of(b.parentBody(), functionType(BOOLEAN));
367 Block.Parameter target = l.entryBlock().parameter(INT);
368 Integer typeIndex = e.getValue().getLast();
369 Result p = l.entryBlock().op(eq(target, l.entryBlock().op(constant(INT, typeIndex))));
370 l.entryBlock().op(core_yield(p));
371
372 Body.Builder expr = Body.Builder.of(b.parentBody(), functionType(type(ExternalizedTypeElement.class)));
373 List<Value> args = new ArrayList<>();
374 args.add(expr.entryBlock().op(constant(J_L_STRING, e.getKey().identifier())));
375 for (int j = 0; j < e.getValue().size() - 1; j++) {
376 Value index = expr.entryBlock().op(constant(INT, e.getValue().get(j)));
377 Result opr = expr.entryBlock().op(funcCall(EXTER_TYPE_BUILDER_F_NAME, functionType(type(ExternalizedTypeElement.class)), index));
378 args.add(opr);
379 }
380 MethodRef mr;
381 Result type;
382 if (e.getKey().arguments().size() < 5) {
383 List<Class<?>> params = new ArrayList<>();
384 params.add(String.class);
385 params.addAll(Collections.nCopies(e.getKey().arguments().size(), ExternalizedTypeElement.class));
386 mr = MethodRef.method(ExternalizedTypeElement.class, "of", ExternalizedTypeElement.class, params);
387 type = expr.entryBlock().op(invoke(mr, args));
388 } else {
389 mr = MethodRef.method(ExternalizedTypeElement.class, "of", ExternalizedTypeElement.class,
390 String.class, ExternalizedTypeElement[].class);
391 type = expr.entryBlock().op(invoke(InvokeOp.InvokeKind.STATIC, true, type(ExternalizedTypeElement.class), mr, args));
392 }
393 expr.entryBlock().op(core_yield(type));
394
395 swBodies.add(l);
396 swBodies.add(expr);
397 }
398
399 // default case
400 Body.Builder dl = Body.Builder.of(b.parentBody(), functionType(BOOLEAN));
401 Block.Parameter target = dl.entryBlock().parameter(INT);
402 dl.entryBlock().op(core_yield(dl.entryBlock().op(constant(BOOLEAN, true))));
403 Body.Builder de = Body.Builder.of(b.parentBody(), functionType(type(ExternalizedTypeElement.class)));
404 de.entryBlock().op(throw_(de.entryBlock().op(new_(MethodRef.constructor(IllegalStateException.class)))));
405 swBodies.add(dl);
406 swBodies.add(de);
407
408 var r = b.op(switchExpression(i, swBodies));
409 b.op(return_(r));
410 });
411 }
412
413 OpBuilder(SequencedMap<ExternalizedTypeElement, List<Integer>> registeredExternalizedTypes) {
414 this.valueMap = new HashMap<>();
415 this.blockMap = new HashMap<>();
416 this.typeElementMap = new HashMap<>();
417 this.registeredExternalizedTypes = registeredExternalizedTypes;
418
419 Body.Builder body = Body.Builder.of(null, BUILDER_F_TYPE);
420 this.builder = body.entryBlock();
421 }
422
423 FuncOp build(String name, Op op) {
424 Value ancestorBody = builder.op(constant(type(Body.Builder.class), null));
425 Value result = buildOp(null, ancestorBody, op);
426 // seal op
427 builder.op(invoke(MethodRef.method(Op.class, "seal", void.class), result));
428 builder.op(return_(result));
429
430 return func(name, builder.parentBody());
431 }
432
433
434 Value buildOp(Value blockBuilder, Value ancestorBody, Op inputOp) {
435 List<Value> bodies = new ArrayList<>();
436 for (Body inputBody : inputOp.bodies()) {
437 Value body = buildBody(ancestorBody, inputBody);
438 bodies.add(body);
439 }
440
441 List<Value> operands = new ArrayList<>();
442 for (Value inputOperand : inputOp.operands()) {
443 Value operand = valueMap.get(inputOperand);
444 operands.add(operand);
445 }
446
447 List<Value> successors = new ArrayList<>();
448 for (Block.Reference inputSuccessor : inputOp.successors()) {
449 List<Value> successorArgs = new ArrayList<>();
450 for (Value inputOperand : inputSuccessor.arguments()) {
451 Value operand = valueMap.get(inputOperand);
452 successorArgs.add(operand);
453 }
454 Value referencedBlock = blockMap.get(inputSuccessor.targetBlock());
455
456 List<Value> args = new ArrayList<>();
457 args.add(referencedBlock);
458 args.addAll(successorArgs);
459 Value successor = builder.op(invoke(
460 InvokeOp.InvokeKind.INSTANCE, true,
461 BLOCK_BUILDER_SUCCESSOR.type().returnType(),
462 BLOCK_BUILDER_SUCCESSOR, args));
463 successors.add(successor);
464 }
465
466 return buildOp(
467 blockBuilder,
468 inputOp,
469 inputOp.externalizeOpName(),
470 inputOp.location(),
471 operands,
472 successors,
473 inputOp.resultType(),
474 inputOp.externalize(),
475 bodies);
476 }
477
478 Value buildOp(Value blockBuilder,
479 Op inputOp,
480 String name,
481 Location location,
482 List<Value> operands,
483 List<Value> successors,
484 TypeElement resultType,
485 Map<String, Object> attributes,
486 List<Value> bodies) {
487
488 boolean bb = blockBuilder != null;
489 boolean simpleLoc = bb && location != null && location.sourceRef() == null;
490
491 List<Value> args = new ArrayList<>();
492 if (bb) {
493 args.add(blockBuilder);
494 }
495 args.add(builder.op(constant(J_L_STRING, name)));
496 if (simpleLoc) {
497 args.add(builder.op(constant(INT, location.line())));
498 args.add(builder.op(constant(INT, location.column())));
499 } else {
500 args.add(buildLocation(location));
501 }
502 args.add(buildFlexibleList(type(Value.class), operands));
503 args.add(buildFlexibleList(type(Block.Reference.class), successors));
504 args.add(builder.op(constant(INT, registerType(resultType.externalize()))));
505 args.add(buildAttributeMap(inputOp, attributes));
506 args.add(buildFlexibleList(type(Body.Builder.class), bodies));
507 return builder.op(bb ? simpleLoc ? funcCall(OP_BUILDER_F_NAME_3, OP_BUILDER_F_OVERRIDE_3, args)
508 : funcCall(OP_BUILDER_F_NAME_2, OP_BUILDER_F_OVERRIDE_2, args)
509 : funcCall(OP_BUILDER_F_NAME_1, OP_BUILDER_F_OVERRIDE_1, args));
510 }
511
512 Value buildFlexibleList(JavaType elementType, List<Value> elements) {
513 return switch (elements.size()) {
514 case 0 -> builder.op(constant(elementType, null));
515 case 1 -> elements.getFirst();
516 default -> buildList(elementType, elements);
517 };
518 }
519
520 Value buildLocation(Location l) {
521 if (l == null) {
522 return builder.op(constant(J_C_LOCATION, null));
523 } else {
524 return builder.op(new_(MethodRef.constructor(Location.class, String.class, int.class, int.class),
525 builder.op(constant(J_L_STRING, l.sourceRef())),
526 builder.op(constant(INT, l.line())),
527 builder.op(constant(INT, l.column()))));
528 }
529 }
530
531 Value buildBody(Value ancestorBodyValue, Body inputBody) {
532 Value yieldType = buildType(inputBody.yieldType());
533 Value bodyType = builder.op(invoke(
534 InvokeOp.InvokeKind.STATIC, true,
535 FUNCTION_TYPE_FUNCTION_TYPE.type().returnType(),
536 FUNCTION_TYPE_FUNCTION_TYPE, List.of(yieldType)));
537 Value body = builder.op(invoke(BODY_BUILDER_OF, ancestorBodyValue, bodyType));
538
539 Value entryBlock = null;
540 for (Block inputBlock : inputBody.blocks()) {
541 Value block;
542 if (inputBlock.isEntryBlock()) {
543 block = entryBlock = builder.op(invoke(BODY_BUILDER_ENTRY_BLOCK, body));
544 } else {
545 assert entryBlock != null;
546 block = builder.op(invoke(InvokeOp.InvokeKind.INSTANCE, true,
547 BLOCK_BUILDER_BLOCK.type().returnType(),
548 BLOCK_BUILDER_BLOCK, List.of(entryBlock)));
549 }
550 blockMap.put(inputBlock, block);
551
552 for (Block.Parameter inputP : inputBlock.parameters()) {
553 Value type = buildType(inputP.type());
554 Value blockParameter = builder.op(invoke(BLOCK_BUILDER_PARAMETER, block, type));
555 valueMap.put(inputP, blockParameter);
556 }
557 }
558
559 for (Block inputBlock : inputBody.blocks()) {
560 Value block = blockMap.get(inputBlock);
561 for (Op inputOp : inputBlock.ops()) {
562 valueMap.put(inputOp.result(), buildOp(block, body, inputOp));
563 }
564 }
565
566 return body;
567 }
568
569 private int registerType(ExternalizedTypeElement ete) {
570 if (!registeredExternalizedTypes.containsKey(ete)) {
571 List<Integer> values = new ArrayList<>();
572 for (ExternalizedTypeElement argument : ete.arguments()) {
573 values.add(registerType(argument));
574 }
575 values.add(registeredExternalizedTypes.size()); // insertion order of the new key
576 registeredExternalizedTypes.put(ete, values);
577 }
578 return registeredExternalizedTypes.get(ete).getLast(); // returns the insertion order of the key
579 }
580
581 Value buildType(TypeElement _t) {
582 return typeElementMap.computeIfAbsent(_t, t -> {
583 int typeIndex = registerType(_t.externalize());
584 Op.Result i = builder.op(constant(INT, typeIndex));
585 return builder.op(funcCall(TYPE_BUILDER_F_NAME, CoreType.functionType(type(TypeElement.class)), i));
586 });
587 }
588
589 Value buildAttributeMap(Op inputOp, Map<String, Object> attributes) {
590 if (attributes.isEmpty()) {
591 return builder.op(constant(type(Map.class), null));
592 }
593 if (attributes.size() == 1 && attributes.get("") instanceof Object o) {
594 return buildAttributeValue(o);
595 }
596 List<Value> keysAndValues = new ArrayList<>();
597 for (Map.Entry<String, Object> entry : attributes.entrySet()) {
598 Value key = builder.op(constant(J_L_STRING, entry.getKey()));
599 Value value = buildAttributeValue(entry.getValue());
600 keysAndValues.add(key);
601 keysAndValues.add(value);
602 }
603 return buildMap(J_L_STRING, J_L_OBJECT, keysAndValues);
604 }
605
606 private Value box(TypeElement to, Value v) {
607 return builder.op(invoke(MethodRef.method(to, "valueOf", to, v.type()), v));
608 }
609
610 Value buildAttributeValue(Object value) {
611 return switch (value) {
612 case Boolean _ ->
613 box(J_L_BOOLEAN, builder.op(constant(BOOLEAN, value)));
614 case Byte _ ->
615 box(J_L_BYTE, builder.op(constant(BYTE, value)));
616 case Short _ ->
617 box(J_L_SHORT, builder.op(constant(SHORT, value)));
618 case Character _ ->
619 box(J_L_CHARACTER, builder.op(constant(CHAR, value)));
620 case Integer _ ->
621 box(J_L_INTEGER, builder.op(constant(INT, value)));
622 case Long _ ->
623 box(J_L_LONG, builder.op(constant(LONG, value)));
624 case Float _ ->
625 box(J_L_FLOAT, builder.op(constant(FLOAT, value)));
626 case Double _ ->
627 box(J_L_DOUBLE, builder.op(constant(DOUBLE, value)));
628 case Class<?> v ->
629 buildType(JavaType.type(v));
630 case String s ->
631 builder.op(constant(J_L_STRING, value));
632 case TypeElement f ->
633 buildType(f);
634 case InvokeOp.InvokeKind ik -> {
635 FieldRef enumValueRef = FieldRef.field(InvokeOp.InvokeKind.class, ik.name(), InvokeOp.InvokeKind.class);
636 yield builder.op(fieldLoad(enumValueRef));
637 }
638 case Object o when value == ExternalizedOp.NULL_ATTRIBUTE_VALUE ->
639 builder.op(fieldLoad(FieldRef.field(ExternalizedOp.class,
640 "NULL_ATTRIBUTE_VALUE", Object.class)));
641 default -> {
642 // @@@ use the result of value.toString()?
643 throw new UnsupportedOperationException("Unsupported attribute value: " + value);
644 }
645 };
646 }
647
648
649 Value buildMap(JavaType keyType, JavaType valueType, List<Value> keysAndValues) {
650 JavaType mapType = parameterized(J_U_MAP, keyType, valueType);
651 if (keysAndValues.size() < 21) {
652 MethodRef mapOf = MethodRef.method(J_U_MAP, "of",
653 J_U_MAP, Collections.nCopies(keysAndValues.size(), J_L_OBJECT));
654 return builder.op(invoke(mapType, mapOf, keysAndValues));
655 } else {
656 JavaType mapEntryType = parameterized(J_U_MAP_ENTRY, keyType, valueType);
657 List<Value> elements = new ArrayList<>(keysAndValues.size() / 2);
658 for (int i = 0; i < keysAndValues.size(); i += 2) {
659 Value key = keysAndValues.get(i);
660 Value value = keysAndValues.get(i + 1);
661 Value entry = builder.op(invoke(mapEntryType, MAP_ENTRY, key, value));
662 elements.add(entry);
663 }
664 Value array = buildArray(mapEntryType, elements);
665 return builder.op(invoke(mapType, MAP_OF_ARRAY, array));
666 }
667 }
668
669
670 Value buildList(JavaType elementType, List<Value> elements) {
671 JavaType listType = parameterized(J_U_LIST, elementType);
672 if (elements.size() < 11) {
673 MethodRef listOf = MethodRef.method(J_U_LIST, "of",
674 J_U_LIST, Collections.nCopies(elements.size(), J_L_OBJECT));
675 return builder.op(invoke(listType, listOf, elements));
676 } else {
677 Value array = buildArray(elementType, elements);
678 return builder.op(invoke(listType, LIST_OF_ARRAY, array));
679 }
680 }
681
682
683 Value buildArray(JavaType elementType, List<Value> elements) {
684 Value array = builder.op(newArray(JavaType.array(elementType),
685 builder.op(constant(INT, elements.size()))));
686 for (int i = 0; i < elements.size(); i++) {
687 builder.op(arrayStoreOp(
688 array,
689 builder.op(constant(INT, i)),
690 elements.get(i)));
691 }
692 return array;
693 }
694 }