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