From 3cd8a018602ce3bbb46dc9c007f8da75d37f8a17 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Mon, 25 Nov 2024 17:33:53 +0100 Subject: [PATCH 01/18] Initial draft of CodeModelAttribute and CodeModelAttributeMapper --- .../code/internal/CodeModelAttribute.java | 39 +++ .../internal/CodeModelAttributeMapper.java | 282 ++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java create mode 100644 src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java new file mode 100644 index 00000000000..274d4288e1e --- /dev/null +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.code.internal; + +import java.lang.classfile.CustomAttribute; +import jdk.incubator.code.Op; + +public class CodeModelAttribute extends CustomAttribute{ + + final Op op; + + CodeModelAttribute(Op op) { + super(CodeModelAttributeMapper.INSTANCE); + this.op = op; + } +} diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java new file mode 100644 index 00000000000..ef1b55ec811 --- /dev/null +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.code.internal; + +import java.lang.classfile.AttributeMapper; +import java.lang.classfile.AttributedElement; +import java.lang.classfile.BufWriter; +import java.lang.classfile.ClassReader; +import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.incubator.code.Block; +import jdk.incubator.code.Body; +import jdk.incubator.code.Op; +import jdk.incubator.code.TypeElement; +import jdk.incubator.code.Value; +import jdk.incubator.code.op.ExtendedOp; +import jdk.incubator.code.op.ExternalizableOp; +import jdk.incubator.code.type.FunctionType; +import jdk.incubator.code.type.JavaType; +import jdk.incubator.code.type.VarType; + +import static java.lang.constant.ConstantDescs.CD_void; + + +public class CodeModelAttributeMapper implements AttributeMapper { + + public static final CodeModelAttributeMapper INSTANCE = new CodeModelAttributeMapper(); + public static final String NAME = "CodeModel"; + + @Override + public String name() { + return NAME; + } + + @Override + public CodeModelAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) { + return new CodeModelAttribute(readOp(new BufReader(cf, pos), false, null, null, new ArrayList<>())); + } + + @Override + public void writeAttribute(BufWriter buf, CodeModelAttribute attr) { + writeOp(buf, attr.op, new HashMap<>()); + } + + @Override + public AttributeStability stability() { + return AttributeStability.CP_REFS; + } + + static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { + return ExtendedOp.FACTORY.constructOpOrFail(readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues)); + } + + static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { + String name = buf.readUtf8(); + List operands = readValues(buf, allValues); + String rType = buf.readUtf8OrNull(); + Map attributes = Map.of(); // @@@ attributes + List bodies = readNestedBodies(buf, ancestorBody, allValues); + return new ExternalizableOp.ExternalizedOp( + name, + operands, + terminal ? readSuccessors(buf, ancestorBodyBlocks, allValues) : List.of(), // successors follow terminal ops + rType == null ? JavaType.VOID : JavaType.type(ClassDesc.ofDescriptor(rType)), + attributes, + bodies); + } + + static void writeOp(BufWriter buf, Op op, Map valueMap) { + // name + buf.writeIndex(buf.constantPool().utf8Entry(op.opName())); + // operands + writeValues(buf, op.operands(), valueMap); + // result type + ClassDesc rt = toCD(op.resultType()); + buf.writeIndexOrZero(rt == CD_void ? null : buf.constantPool().utf8Entry(rt)); + // @@@ attributes + + // nested bodies + writeNestedBodies(buf, op.bodies(), valueMap); + + valueMap.put(op.result(), valueMap.size()); + } + + static List readValues(BufReader buf, List allValues) { + // number of values + var values = new Value[buf.readU2()]; + for (int i = 0; i < values.length; i++) { + // value by index + values[i] = allValues.get(buf.readU2()); + } + return List.of(values); + } + + static void writeValues(BufWriter buf, List values, Map valueMap) { + // number of values + buf.writeU2(values.size()); + for (Value v : values) { + // value index + buf.writeU2(valueMap.get(v)); + } + } + + static List readNestedBodies(BufReader buf, Body.Builder ancestorBody, List allValues) { + // number of bodies + var bodies = new Body.Builder[buf.readU2()]; + for (int i = 0; i < bodies.length; i++) { + // body type + bodies[i] = Body.Builder.of(ancestorBody, toFuncType(MethodTypeDesc.ofDescriptor(buf.readUtf8()))); + // blocks + readBlocks(buf, bodies[i], allValues); + } + return List.of(bodies); + } + + static void writeNestedBodies(BufWriter buf, List bodies, Map valueMap) { + // number of bodies + buf.writeU2(bodies.size()); + for (Body body : bodies) { + // body type + buf.writeIndex(buf.constantPool().utf8Entry(toMTD(body.bodyType()))); + // blocks + writeBlocks(buf, body.blocks(), valueMap); + } + } + + static void readBlocks(BufReader buf, Body.Builder bob, List allValues) { + // number of blocks + var blocks = new Block.Builder[buf.readU2()]; + blocks[0] = bob.entryBlock(); + for (int bi = 1; bi < blocks.length; bi++) { + blocks[bi] = bob.entryBlock().block(); + readBlockParameters(buf, blocks[bi], allValues); + } + for (Block.Builder bb : blocks) { + readOps(buf, bb, blocks, allValues); + } + } + + static void writeBlocks(BufWriter buf, List blocks, Map valueMap) { + // number of blocks + buf.writeU2(blocks.size()); + for (Block block : blocks) { + // parameters + if (!block.isEntryBlock()) writeBlockParameters(buf, block.parameters(), valueMap); + // ops + writeOps(buf, block.ops(), valueMap); + // successors + writeSuccessors(buf, block.successors(), valueMap); + } + } + + static void readBlockParameters(BufReader buf, Block.Builder bb, List allValues) { + // number of block parameters + int bpnum = buf.readU2(); + for (int i = 0; i < bpnum; i++) { + // block parameter type + allValues.add(bb.parameter(JavaType.type(ClassDesc.ofDescriptor(buf.readUtf8())))); + } + } + + static void writeBlockParameters(BufWriter buf, List parameters, Map valueMap) { + // number of block parameters + buf.writeU2(parameters.size()); + for (Block.Parameter bp : parameters) { + // block parameter type + buf.writeIndex(buf.constantPool().utf8Entry(toCD(bp.type()))); + valueMap.put(bp, valueMap.size()); + } + } + + static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] allBlocks, List allValues) { + // number of ops + int opnum = buf.readU2(); + for (int i = 0; i < opnum; i++) { + // op + bb.op(readOp(buf, i == opnum - 1, bb.parentBody(), allBlocks, allValues)); + } + } + + static void writeOps(BufWriter buf, List ops, Map valueMap) { + // number of ops + buf.writeU2(ops.size()); + for (Op op : ops) { + // op + writeOp(buf, op, valueMap); + } + } + + static List readSuccessors(BufReader buf, Block.Builder[] ancestorBodyBlocks, List allValues) { + // number of successors + var refs = new Block.Reference[buf.readU2()]; + for (int i = 0; i < refs.length; i++) { + // block from index + arguments + refs[i] = ancestorBodyBlocks[buf.readU2()].successor(readValues(buf, allValues)); + } + return List.of(refs); + } + + static void writeSuccessors(BufWriter buf, List successors, Map valueMap) { + // number of successors + buf.writeU2(successors.size()); + for (Block.Reference succ : successors) { + // block index + buf.writeU2(succ.targetBlock().index()); + // arguments + writeValues(buf, succ.arguments(), valueMap); + } + } + + static FunctionType toFuncType(MethodTypeDesc mtd) { + return FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList()); + } + + static MethodTypeDesc toMTD(FunctionType ftype) { + return MethodTypeDesc.of(toCD(ftype.returnType()), ftype.parameterTypes().stream().map(CodeModelAttributeMapper::toCD).toList()); + } + + static ClassDesc toCD(TypeElement type) { + return switch (type) { + case JavaType jt -> jt.toNominalDescriptor(); + case VarType vt -> toCD(vt.valueType()); + default -> throw new IllegalArgumentException(type.toString()); + }; + } + + static class BufReader { + private final ClassReader cr; + private int offset; + BufReader(ClassReader cr, int offset) { + this.cr = cr; + this.offset = offset; + } + + int readU2() { + int i = cr.readInt(offset); + offset += 2; + return i; + } + + String readUtf8() { + String s = cr.readEntry(offset, Utf8Entry.class).stringValue(); + offset += 2; + return s; + } + + String readUtf8OrNull() { + Utf8Entry u = cr.readEntry(offset, Utf8Entry.class); + offset += 2; + return u == null ? null : u.stringValue(); + } + } +} From 91a70833dfca3891982f22abb9f8eb7b5b2f4a09 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Mon, 25 Nov 2024 17:50:25 +0100 Subject: [PATCH 02/18] merge into CodeModelAttribute --- .../code/internal/CodeModelAttribute.java | 255 +++++++++++++++- .../internal/CodeModelAttributeMapper.java | 282 ------------------ 2 files changed, 254 insertions(+), 283 deletions(-) delete mode 100644 src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index 274d4288e1e..a01334ce57b 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -25,15 +25,268 @@ package jdk.incubator.code.internal; +import java.lang.classfile.AttributeMapper; +import java.lang.classfile.AttributedElement; +import java.lang.classfile.BufWriter; +import java.lang.classfile.ClassReader; import java.lang.classfile.CustomAttribute; +import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import jdk.incubator.code.Block; +import jdk.incubator.code.Body; import jdk.incubator.code.Op; +import jdk.incubator.code.TypeElement; +import jdk.incubator.code.Value; +import jdk.incubator.code.op.ExtendedOp; +import jdk.incubator.code.op.ExternalizableOp; +import jdk.incubator.code.type.FunctionType; +import jdk.incubator.code.type.JavaType; +import jdk.incubator.code.type.VarType; + +import static java.lang.constant.ConstantDescs.CD_void; public class CodeModelAttribute extends CustomAttribute{ + public static final String NAME = "CodeModel"; + + public static final AttributeMapper MAPPER = new AttributeMapper<>() { + + @Override + public String name() { + return NAME; + } + + @Override + public CodeModelAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) { + return new CodeModelAttribute(readOp(new BufReader(cf, pos), false, null, null, new ArrayList<>())); + } + + @Override + public void writeAttribute(BufWriter buf, CodeModelAttribute attr) { + writeOp(buf, attr.op, new HashMap<>()); + } + + @Override + public AttributeMapper.AttributeStability stability() { + return AttributeMapper.AttributeStability.CP_REFS; + } + }; + final Op op; CodeModelAttribute(Op op) { - super(CodeModelAttributeMapper.INSTANCE); + super(MAPPER); this.op = op; } + + + + private static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { + return ExtendedOp.FACTORY.constructOpOrFail(readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues)); + } + + private static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { + String name = buf.readUtf8(); + List operands = readValues(buf, allValues); + String rType = buf.readUtf8OrNull(); + Map attributes = Map.of(); // @@@ attributes + List bodies = readNestedBodies(buf, ancestorBody, allValues); + return new ExternalizableOp.ExternalizedOp( + name, + operands, + terminal ? readSuccessors(buf, ancestorBodyBlocks, allValues) : List.of(), // successors follow terminal ops + rType == null ? JavaType.VOID : JavaType.type(ClassDesc.ofDescriptor(rType)), + attributes, + bodies); + } + + private static void writeOp(BufWriter buf, Op op, Map valueMap) { + // name + buf.writeIndex(buf.constantPool().utf8Entry(op.opName())); + // operands + writeValues(buf, op.operands(), valueMap); + // result type + ClassDesc rt = toCD(op.resultType()); + buf.writeIndexOrZero(rt == CD_void ? null : buf.constantPool().utf8Entry(rt)); + // @@@ attributes + + // nested bodies + writeNestedBodies(buf, op.bodies(), valueMap); + + valueMap.put(op.result(), valueMap.size()); + } + + private static List readValues(BufReader buf, List allValues) { + // number of values + var values = new Value[buf.readU2()]; + for (int i = 0; i < values.length; i++) { + // value by index + values[i] = allValues.get(buf.readU2()); + } + return List.of(values); + } + + private static void writeValues(BufWriter buf, List values, Map valueMap) { + // number of values + buf.writeU2(values.size()); + for (Value v : values) { + // value index + buf.writeU2(valueMap.get(v)); + } + } + + private static List readNestedBodies(BufReader buf, Body.Builder ancestorBody, List allValues) { + // number of bodies + var bodies = new Body.Builder[buf.readU2()]; + for (int i = 0; i < bodies.length; i++) { + // body type + bodies[i] = Body.Builder.of(ancestorBody, toFuncType(MethodTypeDesc.ofDescriptor(buf.readUtf8()))); + // blocks + readBlocks(buf, bodies[i], allValues); + } + return List.of(bodies); + } + + private static void writeNestedBodies(BufWriter buf, List bodies, Map valueMap) { + // number of bodies + buf.writeU2(bodies.size()); + for (Body body : bodies) { + // body type + buf.writeIndex(buf.constantPool().utf8Entry(toMTD(body.bodyType()))); + // blocks + writeBlocks(buf, body.blocks(), valueMap); + } + } + + private static void readBlocks(BufReader buf, Body.Builder bob, List allValues) { + // number of blocks + var blocks = new Block.Builder[buf.readU2()]; + blocks[0] = bob.entryBlock(); + for (int bi = 1; bi < blocks.length; bi++) { + blocks[bi] = bob.entryBlock().block(); + readBlockParameters(buf, blocks[bi], allValues); + } + for (Block.Builder bb : blocks) { + readOps(buf, bb, blocks, allValues); + } + } + + private static void writeBlocks(BufWriter buf, List blocks, Map valueMap) { + // number of blocks + buf.writeU2(blocks.size()); + for (Block block : blocks) { + // parameters + if (!block.isEntryBlock()) writeBlockParameters(buf, block.parameters(), valueMap); + // ops + writeOps(buf, block.ops(), valueMap); + // successors + writeSuccessors(buf, block.successors(), valueMap); + } + } + + private static void readBlockParameters(BufReader buf, Block.Builder bb, List allValues) { + // number of block parameters + int bpnum = buf.readU2(); + for (int i = 0; i < bpnum; i++) { + // block parameter type + allValues.add(bb.parameter(JavaType.type(ClassDesc.ofDescriptor(buf.readUtf8())))); + } + } + + private static void writeBlockParameters(BufWriter buf, List parameters, Map valueMap) { + // number of block parameters + buf.writeU2(parameters.size()); + for (Block.Parameter bp : parameters) { + // block parameter type + buf.writeIndex(buf.constantPool().utf8Entry(toCD(bp.type()))); + valueMap.put(bp, valueMap.size()); + } + } + + private static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] allBlocks, List allValues) { + // number of ops + int opnum = buf.readU2(); + for (int i = 0; i < opnum; i++) { + // op + bb.op(readOp(buf, i == opnum - 1, bb.parentBody(), allBlocks, allValues)); + } + } + + private static void writeOps(BufWriter buf, List ops, Map valueMap) { + // number of ops + buf.writeU2(ops.size()); + for (Op op : ops) { + // op + writeOp(buf, op, valueMap); + } + } + + private static List readSuccessors(BufReader buf, Block.Builder[] ancestorBodyBlocks, List allValues) { + // number of successors + var refs = new Block.Reference[buf.readU2()]; + for (int i = 0; i < refs.length; i++) { + // block from index + arguments + refs[i] = ancestorBodyBlocks[buf.readU2()].successor(readValues(buf, allValues)); + } + return List.of(refs); + } + + private static void writeSuccessors(BufWriter buf, List successors, Map valueMap) { + // number of successors + buf.writeU2(successors.size()); + for (Block.Reference succ : successors) { + // block index + buf.writeU2(succ.targetBlock().index()); + // arguments + writeValues(buf, succ.arguments(), valueMap); + } + } + + private static FunctionType toFuncType(MethodTypeDesc mtd) { + return FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList()); + } + + private static MethodTypeDesc toMTD(FunctionType ftype) { + return MethodTypeDesc.of(toCD(ftype.returnType()), ftype.parameterTypes().stream().map(CodeModelAttribute::toCD).toList()); + } + + private static ClassDesc toCD(TypeElement type) { + return switch (type) { + case JavaType jt -> jt.toNominalDescriptor(); + case VarType vt -> toCD(vt.valueType()); + default -> throw new IllegalArgumentException(type.toString()); + }; + } + + private static final class BufReader { + private final ClassReader cr; + private int offset; + BufReader(ClassReader cr, int offset) { + this.cr = cr; + this.offset = offset; + } + + int readU2() { + int i = cr.readInt(offset); + offset += 2; + return i; + } + + String readUtf8() { + String s = cr.readEntry(offset, Utf8Entry.class).stringValue(); + offset += 2; + return s; + } + + String readUtf8OrNull() { + Utf8Entry u = cr.readEntry(offset, Utf8Entry.class); + offset += 2; + return u == null ? null : u.stringValue(); + } + } } diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java deleted file mode 100644 index ef1b55ec811..00000000000 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttributeMapper.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.incubator.code.internal; - -import java.lang.classfile.AttributeMapper; -import java.lang.classfile.AttributedElement; -import java.lang.classfile.BufWriter; -import java.lang.classfile.ClassReader; -import java.lang.classfile.constantpool.Utf8Entry; -import java.lang.constant.ClassDesc; -import java.lang.constant.MethodTypeDesc; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import jdk.incubator.code.Block; -import jdk.incubator.code.Body; -import jdk.incubator.code.Op; -import jdk.incubator.code.TypeElement; -import jdk.incubator.code.Value; -import jdk.incubator.code.op.ExtendedOp; -import jdk.incubator.code.op.ExternalizableOp; -import jdk.incubator.code.type.FunctionType; -import jdk.incubator.code.type.JavaType; -import jdk.incubator.code.type.VarType; - -import static java.lang.constant.ConstantDescs.CD_void; - - -public class CodeModelAttributeMapper implements AttributeMapper { - - public static final CodeModelAttributeMapper INSTANCE = new CodeModelAttributeMapper(); - public static final String NAME = "CodeModel"; - - @Override - public String name() { - return NAME; - } - - @Override - public CodeModelAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) { - return new CodeModelAttribute(readOp(new BufReader(cf, pos), false, null, null, new ArrayList<>())); - } - - @Override - public void writeAttribute(BufWriter buf, CodeModelAttribute attr) { - writeOp(buf, attr.op, new HashMap<>()); - } - - @Override - public AttributeStability stability() { - return AttributeStability.CP_REFS; - } - - static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { - return ExtendedOp.FACTORY.constructOpOrFail(readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues)); - } - - static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { - String name = buf.readUtf8(); - List operands = readValues(buf, allValues); - String rType = buf.readUtf8OrNull(); - Map attributes = Map.of(); // @@@ attributes - List bodies = readNestedBodies(buf, ancestorBody, allValues); - return new ExternalizableOp.ExternalizedOp( - name, - operands, - terminal ? readSuccessors(buf, ancestorBodyBlocks, allValues) : List.of(), // successors follow terminal ops - rType == null ? JavaType.VOID : JavaType.type(ClassDesc.ofDescriptor(rType)), - attributes, - bodies); - } - - static void writeOp(BufWriter buf, Op op, Map valueMap) { - // name - buf.writeIndex(buf.constantPool().utf8Entry(op.opName())); - // operands - writeValues(buf, op.operands(), valueMap); - // result type - ClassDesc rt = toCD(op.resultType()); - buf.writeIndexOrZero(rt == CD_void ? null : buf.constantPool().utf8Entry(rt)); - // @@@ attributes - - // nested bodies - writeNestedBodies(buf, op.bodies(), valueMap); - - valueMap.put(op.result(), valueMap.size()); - } - - static List readValues(BufReader buf, List allValues) { - // number of values - var values = new Value[buf.readU2()]; - for (int i = 0; i < values.length; i++) { - // value by index - values[i] = allValues.get(buf.readU2()); - } - return List.of(values); - } - - static void writeValues(BufWriter buf, List values, Map valueMap) { - // number of values - buf.writeU2(values.size()); - for (Value v : values) { - // value index - buf.writeU2(valueMap.get(v)); - } - } - - static List readNestedBodies(BufReader buf, Body.Builder ancestorBody, List allValues) { - // number of bodies - var bodies = new Body.Builder[buf.readU2()]; - for (int i = 0; i < bodies.length; i++) { - // body type - bodies[i] = Body.Builder.of(ancestorBody, toFuncType(MethodTypeDesc.ofDescriptor(buf.readUtf8()))); - // blocks - readBlocks(buf, bodies[i], allValues); - } - return List.of(bodies); - } - - static void writeNestedBodies(BufWriter buf, List bodies, Map valueMap) { - // number of bodies - buf.writeU2(bodies.size()); - for (Body body : bodies) { - // body type - buf.writeIndex(buf.constantPool().utf8Entry(toMTD(body.bodyType()))); - // blocks - writeBlocks(buf, body.blocks(), valueMap); - } - } - - static void readBlocks(BufReader buf, Body.Builder bob, List allValues) { - // number of blocks - var blocks = new Block.Builder[buf.readU2()]; - blocks[0] = bob.entryBlock(); - for (int bi = 1; bi < blocks.length; bi++) { - blocks[bi] = bob.entryBlock().block(); - readBlockParameters(buf, blocks[bi], allValues); - } - for (Block.Builder bb : blocks) { - readOps(buf, bb, blocks, allValues); - } - } - - static void writeBlocks(BufWriter buf, List blocks, Map valueMap) { - // number of blocks - buf.writeU2(blocks.size()); - for (Block block : blocks) { - // parameters - if (!block.isEntryBlock()) writeBlockParameters(buf, block.parameters(), valueMap); - // ops - writeOps(buf, block.ops(), valueMap); - // successors - writeSuccessors(buf, block.successors(), valueMap); - } - } - - static void readBlockParameters(BufReader buf, Block.Builder bb, List allValues) { - // number of block parameters - int bpnum = buf.readU2(); - for (int i = 0; i < bpnum; i++) { - // block parameter type - allValues.add(bb.parameter(JavaType.type(ClassDesc.ofDescriptor(buf.readUtf8())))); - } - } - - static void writeBlockParameters(BufWriter buf, List parameters, Map valueMap) { - // number of block parameters - buf.writeU2(parameters.size()); - for (Block.Parameter bp : parameters) { - // block parameter type - buf.writeIndex(buf.constantPool().utf8Entry(toCD(bp.type()))); - valueMap.put(bp, valueMap.size()); - } - } - - static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] allBlocks, List allValues) { - // number of ops - int opnum = buf.readU2(); - for (int i = 0; i < opnum; i++) { - // op - bb.op(readOp(buf, i == opnum - 1, bb.parentBody(), allBlocks, allValues)); - } - } - - static void writeOps(BufWriter buf, List ops, Map valueMap) { - // number of ops - buf.writeU2(ops.size()); - for (Op op : ops) { - // op - writeOp(buf, op, valueMap); - } - } - - static List readSuccessors(BufReader buf, Block.Builder[] ancestorBodyBlocks, List allValues) { - // number of successors - var refs = new Block.Reference[buf.readU2()]; - for (int i = 0; i < refs.length; i++) { - // block from index + arguments - refs[i] = ancestorBodyBlocks[buf.readU2()].successor(readValues(buf, allValues)); - } - return List.of(refs); - } - - static void writeSuccessors(BufWriter buf, List successors, Map valueMap) { - // number of successors - buf.writeU2(successors.size()); - for (Block.Reference succ : successors) { - // block index - buf.writeU2(succ.targetBlock().index()); - // arguments - writeValues(buf, succ.arguments(), valueMap); - } - } - - static FunctionType toFuncType(MethodTypeDesc mtd) { - return FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList()); - } - - static MethodTypeDesc toMTD(FunctionType ftype) { - return MethodTypeDesc.of(toCD(ftype.returnType()), ftype.parameterTypes().stream().map(CodeModelAttributeMapper::toCD).toList()); - } - - static ClassDesc toCD(TypeElement type) { - return switch (type) { - case JavaType jt -> jt.toNominalDescriptor(); - case VarType vt -> toCD(vt.valueType()); - default -> throw new IllegalArgumentException(type.toString()); - }; - } - - static class BufReader { - private final ClassReader cr; - private int offset; - BufReader(ClassReader cr, int offset) { - this.cr = cr; - this.offset = offset; - } - - int readU2() { - int i = cr.readInt(offset); - offset += 2; - return i; - } - - String readUtf8() { - String s = cr.readEntry(offset, Utf8Entry.class).stringValue(); - offset += 2; - return s; - } - - String readUtf8OrNull() { - Utf8Entry u = cr.readEntry(offset, Utf8Entry.class); - offset += 2; - return u == null ? null : u.stringValue(); - } - } -} From 911c6c24201eeda9b90b8471f1d0cd6641f8ed80 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Mon, 25 Nov 2024 18:17:18 +0100 Subject: [PATCH 03/18] CodeModelAttribute work in progress --- .../code/internal/CodeModelAttribute.java | 21 ++++++++++++---- .../reflect/code/bytecode/TestBytecode.java | 24 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index a01334ce57b..aa621527220 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -77,14 +77,20 @@ public AttributeMapper.AttributeStability stability() { } }; - final Op op; + public static CodeModelAttribute of(Op op) { + return new CodeModelAttribute(op); + } + + private final Op op; - CodeModelAttribute(Op op) { + private CodeModelAttribute(Op op) { super(MAPPER); this.op = op; } - + public Op op() { + return op; + } private static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { return ExtendedOp.FACTORY.constructOpOrFail(readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues)); @@ -167,6 +173,7 @@ private static void readBlocks(BufReader buf, Body.Builder bob, List allV // number of blocks var blocks = new Block.Builder[buf.readU2()]; blocks[0] = bob.entryBlock(); + allValues.addAll(bob.entryBlock().parameters()); for (int bi = 1; bi < blocks.length; bi++) { blocks[bi] = bob.entryBlock().block(); readBlockParameters(buf, blocks[bi], allValues); @@ -181,7 +188,13 @@ private static void writeBlocks(BufWriter buf, List blocks, Map e.equalsString(CodeModelAttribute.NAME) ? CodeModelAttribute.MAPPER : null)); + var newbytes = cf.transformClass(CLASS_MODEL, ClassTransform.transformingMethods( + mm -> mm.methodName().equalsString(d.testMethod.getName()), + MethodTransform.endHandler(mb -> mb.with(CodeModelAttribute.of(func))))); + System.out.println(func.toText()); + for (var mm : cf.parse(newbytes).methods()) { + mm.findAttribute(CodeModelAttribute.MAPPER).ifPresent(cma -> System.out.println(cma.op().toText())); + } + } } From f7577ec04de2b26b28e6c61a33e7022ddbfa5ca6 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Tue, 26 Nov 2024 13:14:41 +0100 Subject: [PATCH 04/18] CodeModelAttribute work in progress --- .../code/internal/CodeModelAttribute.java | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index aa621527220..c371ae77107 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -37,8 +37,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import jdk.incubator.code.Block; import jdk.incubator.code.Body; +import jdk.incubator.code.CopyContext; import jdk.incubator.code.Op; import jdk.incubator.code.TypeElement; import jdk.incubator.code.Value; @@ -68,7 +70,12 @@ public CodeModelAttribute readAttribute(AttributedElement enclosing, ClassReader @Override public void writeAttribute(BufWriter buf, CodeModelAttribute attr) { + buf.writeIndex(buf.constantPool().utf8Entry(NAME)); + int lengthIndex = buf.size(); + buf.writeInt(0); writeOp(buf, attr.op, new HashMap<>()); + int written = buf.size() - lengthIndex - 4; + buf.patchInt(lengthIndex, 4, written); } @Override @@ -93,25 +100,33 @@ public Op op() { } private static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { - return ExtendedOp.FACTORY.constructOpOrFail(readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues)); + var extOp = readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues); + System.out.println(extOp); + return ExtendedOp.FACTORY.constructOpOrFail(extOp); } private static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { String name = buf.readUtf8(); List operands = readValues(buf, allValues); - String rType = buf.readUtf8OrNull(); + String rTypeStr = buf.readUtf8OrNull(); + TypeElement rType = rTypeStr == null ? JavaType.VOID : JavaType.type(ClassDesc.ofDescriptor(rTypeStr)); + if (name.equals("var")) { + // wrap in var type + rType = VarType.varType(rType); + } Map attributes = Map.of(); // @@@ attributes List bodies = readNestedBodies(buf, ancestorBody, allValues); return new ExternalizableOp.ExternalizedOp( name, operands, terminal ? readSuccessors(buf, ancestorBodyBlocks, allValues) : List.of(), // successors follow terminal ops - rType == null ? JavaType.VOID : JavaType.type(ClassDesc.ofDescriptor(rType)), + rType, attributes, bodies); } private static void writeOp(BufWriter buf, Op op, Map valueMap) { + System.out.println(new ExternalizableOp.ExternalizedOp(op.opName(), op.operands(), op.successors(), op.resultType(), op instanceof ExternalizableOp exop ? new HashMap<>(exop.attributes()) : new HashMap<>(), null)); // name buf.writeIndex(buf.constantPool().utf8Entry(op.opName())); // operands @@ -124,7 +139,9 @@ private static void writeOp(BufWriter buf, Op op, Map valueMap) // nested bodies writeNestedBodies(buf, op.bodies(), valueMap); - valueMap.put(op.result(), valueMap.size()); + if (op.result() != null) { + valueMap.put(op.result(), valueMap.size()); + } } private static List readValues(BufReader buf, List allValues) { @@ -173,12 +190,15 @@ private static void readBlocks(BufReader buf, Body.Builder bob, List allV // number of blocks var blocks = new Block.Builder[buf.readU2()]; blocks[0] = bob.entryBlock(); - allValues.addAll(bob.entryBlock().parameters()); for (int bi = 1; bi < blocks.length; bi++) { blocks[bi] = bob.entryBlock().block(); - readBlockParameters(buf, blocks[bi], allValues); } for (Block.Builder bb : blocks) { + if (bb.isEntryBlock()) { + allValues.addAll(bob.entryBlock().parameters()); + } else { + readBlockParameters(buf, bb, allValues); + } readOps(buf, bb, blocks, allValues); } } @@ -226,7 +246,11 @@ private static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] all int opnum = buf.readU2(); for (int i = 0; i < opnum; i++) { // op - bb.op(readOp(buf, i == opnum - 1, bb.parentBody(), allBlocks, allValues)); + Op op = readOp(buf, i == opnum - 1, bb.parentBody(), allBlocks, allValues); + bb.op(op); + if (op.result() != null) { + allValues.add(op.result()); + } } } @@ -285,7 +309,7 @@ private static final class BufReader { } int readU2() { - int i = cr.readInt(offset); + int i = cr.readU2(offset); offset += 2; return i; } @@ -297,7 +321,7 @@ String readUtf8() { } String readUtf8OrNull() { - Utf8Entry u = cr.readEntry(offset, Utf8Entry.class); + Utf8Entry u = cr.readEntryOrNull(offset, Utf8Entry.class); offset += 2; return u == null ? null : u.stringValue(); } From 88d41631f477b158ebfb6c2b7574ad4f7bbeff3e Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Tue, 26 Nov 2024 14:38:33 +0100 Subject: [PATCH 05/18] CodeModelAttribute work in progress --- .../code/internal/CodeModelAttribute.java | 111 +++++++++++++----- .../reflect/code/bytecode/TestBytecode.java | 34 ++++-- 2 files changed, 106 insertions(+), 39 deletions(-) diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index c371ae77107..3bd7d190df0 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -30,28 +30,29 @@ import java.lang.classfile.BufWriter; import java.lang.classfile.ClassReader; import java.lang.classfile.CustomAttribute; +import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.lang.classfile.constantpool.PoolEntry; +import java.lang.classfile.constantpool.StringEntry; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; import jdk.incubator.code.Block; import jdk.incubator.code.Body; -import jdk.incubator.code.CopyContext; import jdk.incubator.code.Op; import jdk.incubator.code.TypeElement; import jdk.incubator.code.Value; import jdk.incubator.code.op.ExtendedOp; import jdk.incubator.code.op.ExternalizableOp; +import jdk.incubator.code.type.CoreTypeFactory; import jdk.incubator.code.type.FunctionType; import jdk.incubator.code.type.JavaType; import jdk.incubator.code.type.VarType; -import static java.lang.constant.ConstantDescs.CD_void; - public class CodeModelAttribute extends CustomAttribute{ public static final String NAME = "CodeModel"; @@ -101,20 +102,14 @@ public Op op() { private static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { var extOp = readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues); - System.out.println(extOp); return ExtendedOp.FACTORY.constructOpOrFail(extOp); } private static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) { String name = buf.readUtf8(); List operands = readValues(buf, allValues); - String rTypeStr = buf.readUtf8OrNull(); - TypeElement rType = rTypeStr == null ? JavaType.VOID : JavaType.type(ClassDesc.ofDescriptor(rTypeStr)); - if (name.equals("var")) { - // wrap in var type - rType = VarType.varType(rType); - } - Map attributes = Map.of(); // @@@ attributes + TypeElement rType = toType(buf.readEntryOrNull()); + Map attributes = readAttributes(buf); List bodies = readNestedBodies(buf, ancestorBody, allValues); return new ExternalizableOp.ExternalizedOp( name, @@ -126,16 +121,14 @@ private static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean t } private static void writeOp(BufWriter buf, Op op, Map valueMap) { - System.out.println(new ExternalizableOp.ExternalizedOp(op.opName(), op.operands(), op.successors(), op.resultType(), op instanceof ExternalizableOp exop ? new HashMap<>(exop.attributes()) : new HashMap<>(), null)); // name buf.writeIndex(buf.constantPool().utf8Entry(op.opName())); // operands writeValues(buf, op.operands(), valueMap); // result type - ClassDesc rt = toCD(op.resultType()); - buf.writeIndexOrZero(rt == CD_void ? null : buf.constantPool().utf8Entry(rt)); - // @@@ attributes - + buf.writeIndexOrZero(toEntry(buf.constantPool(), op.resultType())); + // attributes + writeAttributes(buf, op instanceof ExternalizableOp extOp ? extOp.attributes() : Map.of()); // nested bodies writeNestedBodies(buf, op.bodies(), valueMap); @@ -144,6 +137,28 @@ private static void writeOp(BufWriter buf, Op op, Map valueMap) } } + private static Map readAttributes(BufReader buf) { + // number of attributes + int size = buf.readU2(); + var attrs = new LinkedHashMap(size); + for (int i = 0; i < size; i++) { + // attribute name + value + attrs.put(buf.readUtf8OrNull(), buf.readUtf8OrNull()); + } + return attrs; + } + + private static void writeAttributes(BufWriter buf, Map attributes) { + // number of attributes + buf.writeU2(attributes.size()); + for (var attre : attributes.entrySet()) { + // attribute name + buf.writeIndexOrZero(attre.getKey() == null ? null : buf.constantPool().utf8Entry(attre.getKey())); + // attribute value + buf.writeIndexOrZero(attre.getValue() == null ? null : buf.constantPool().utf8Entry(attre.getValue().toString())); + } + } + private static List readValues(BufReader buf, List allValues) { // number of values var values = new Value[buf.readU2()]; @@ -168,7 +183,7 @@ private static List readNestedBodies(BufReader buf, Body.Builder a var bodies = new Body.Builder[buf.readU2()]; for (int i = 0; i < bodies.length; i++) { // body type - bodies[i] = Body.Builder.of(ancestorBody, toFuncType(MethodTypeDesc.ofDescriptor(buf.readUtf8()))); + bodies[i] = Body.Builder.of(ancestorBody, toFuncType(buf.readEntryOrNull())); // blocks readBlocks(buf, bodies[i], allValues); } @@ -180,7 +195,7 @@ private static void writeNestedBodies(BufWriter buf, List bodies, Map pa buf.writeU2(parameters.size()); for (Block.Parameter bp : parameters) { // block parameter type - buf.writeIndex(buf.constantPool().utf8Entry(toCD(bp.type()))); + buf.writeIndexOrZero(toEntry(buf.constantPool(), bp.type())); valueMap.put(bp, valueMap.size()); } } @@ -284,19 +299,51 @@ private static void writeSuccessors(BufWriter buf, List success } } - private static FunctionType toFuncType(MethodTypeDesc mtd) { - return FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList()); + private static FunctionType toFuncType(PoolEntry entry) { + return switch (entry) { + case Utf8Entry ue -> { + var mtd = MethodTypeDesc.ofDescriptor(ue.stringValue()); + yield FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList()); + } + case StringEntry se -> + (FunctionType)CoreTypeFactory.CORE_TYPE_FACTORY.constructType(TypeElement.ExternalizedTypeElement.ofString(se.stringValue())); + default -> + throw new IllegalArgumentException(entry.toString()); + }; } - private static MethodTypeDesc toMTD(FunctionType ftype) { - return MethodTypeDesc.of(toCD(ftype.returnType()), ftype.parameterTypes().stream().map(CodeModelAttribute::toCD).toList()); + private static PoolEntry toEntry(ConstantPoolBuilder cp, FunctionType ftype) { + if (ftype.returnType() instanceof JavaType jret && ftype.parameterTypes().stream().allMatch(JavaType.class::isInstance)) { + // prefer to store as method type descriptor + return cp.utf8Entry(MethodTypeDesc.of(jret.toNominalDescriptor(), ftype.parameterTypes().stream().map(te -> ((JavaType)te).toNominalDescriptor()).toList())); + } else { + // fallback + return cp.stringEntry(ftype.externalize().toString()); + } } - private static ClassDesc toCD(TypeElement type) { + private static TypeElement toType(PoolEntry entry) { + return switch (entry) { + case Utf8Entry ue -> + JavaType.type(ClassDesc.ofDescriptor(ue.stringValue())); + case StringEntry se -> + VarType.varType(JavaType.type(ClassDesc.ofDescriptor(se.stringValue()))); + case null -> + JavaType.VOID; + default -> + throw new IllegalArgumentException(entry.toString()); + }; + } + + private static PoolEntry toEntry(ConstantPoolBuilder cp, TypeElement type) { + if (type.equals(JavaType.VOID)) return null; return switch (type) { - case JavaType jt -> jt.toNominalDescriptor(); - case VarType vt -> toCD(vt.valueType()); - default -> throw new IllegalArgumentException(type.toString()); + case JavaType jt -> + cp.utf8Entry(jt.toNominalDescriptor()); + case VarType vt when vt.valueType() instanceof JavaType jt -> + cp.stringEntry(cp.utf8Entry(jt.toNominalDescriptor())); + default -> + throw new IllegalArgumentException(type.toString()); }; } @@ -325,5 +372,11 @@ String readUtf8OrNull() { offset += 2; return u == null ? null : u.stringValue(); } + + PoolEntry readEntryOrNull() { + PoolEntry e = cr.readEntryOrNull(offset); + offset += 2; + return e; + } } } diff --git a/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java b/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java index 58e48fcc70d..afa80acbdf6 100644 --- a/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java +++ b/test/jdk/java/lang/reflect/code/bytecode/TestBytecode.java @@ -740,21 +740,35 @@ public void testGenerate(TestData d) throws Throwable { } @Test(dataProvider = "testMethods") - public void testAttribute(TestData d) throws Throwable { + public void testModelAttribute(TestData d) throws Throwable { + testModelAttribute(Op.ofMethod(d.testMethod).get()); + } + + @Test(dataProvider = "testMethods") + public void testLowModelAttribute(TestData d) throws Throwable { CoreOp.FuncOp func = Op.ofMethod(d.testMethod).get(); -// CoreOp.FuncOp lfunc; -// try { -// lfunc = func.transform(CopyContext.create(), OpTransformer.LOWERING_TRANSFORMER); -// } catch (UnsupportedOperationException uoe) { -// throw new SkipException("lowering caused:", uoe); -// } + try { + testModelAttribute(func.transform(CopyContext.create(), OpTransformer.LOWERING_TRANSFORMER)); + } catch (UnsupportedOperationException uoe) { + throw new SkipException("lowering caused:", uoe); + } + } + + private void testModelAttribute(CoreOp.FuncOp func) { var cf = ClassFile.of(ClassFile.AttributeMapperOption.of(e -> e.equalsString(CodeModelAttribute.NAME) ? CodeModelAttribute.MAPPER : null)); var newbytes = cf.transformClass(CLASS_MODEL, ClassTransform.transformingMethods( - mm -> mm.methodName().equalsString(d.testMethod.getName()), + mm -> mm.methodName().equalsString(func.funcName()), MethodTransform.endHandler(mb -> mb.with(CodeModelAttribute.of(func))))); - System.out.println(func.toText()); + String oldModel = func.toText(); for (var mm : cf.parse(newbytes).methods()) { - mm.findAttribute(CodeModelAttribute.MAPPER).ifPresent(cma -> System.out.println(cma.op().toText())); + mm.findAttribute(CodeModelAttribute.MAPPER).ifPresent(cma -> { + String newModel = cma.op().toText(); + if (!oldModel.equals(newModel)) { + System.out.println(oldModel); + System.out.println(newModel); + throw new AssertionError("Models mismatch"); + } + }); } } } From 981ae7c815906aa23cd885c2cc09bfd45694fa9e Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Tue, 26 Nov 2024 14:52:28 +0100 Subject: [PATCH 06/18] fixed types serialization --- .../code/internal/CodeModelAttribute.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index 3bd7d190df0..427d5f582b9 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -51,7 +51,6 @@ import jdk.incubator.code.type.CoreTypeFactory; import jdk.incubator.code.type.FunctionType; import jdk.incubator.code.type.JavaType; -import jdk.incubator.code.type.VarType; public class CodeModelAttribute extends CustomAttribute{ @@ -313,7 +312,10 @@ private static FunctionType toFuncType(PoolEntry entry) { } private static PoolEntry toEntry(ConstantPoolBuilder cp, FunctionType ftype) { - if (ftype.returnType() instanceof JavaType jret && ftype.parameterTypes().stream().allMatch(JavaType.class::isInstance)) { + if (ftype.returnType() instanceof JavaType jret + && jret.erasure().equals(jret) + && ftype.parameterTypes().stream().allMatch(te -> + te instanceof JavaType jt && jt.erasure().equals(jt))) { // prefer to store as method type descriptor return cp.utf8Entry(MethodTypeDesc.of(jret.toNominalDescriptor(), ftype.parameterTypes().stream().map(te -> ((JavaType)te).toNominalDescriptor()).toList())); } else { @@ -327,7 +329,7 @@ private static TypeElement toType(PoolEntry entry) { case Utf8Entry ue -> JavaType.type(ClassDesc.ofDescriptor(ue.stringValue())); case StringEntry se -> - VarType.varType(JavaType.type(ClassDesc.ofDescriptor(se.stringValue()))); + CoreTypeFactory.CORE_TYPE_FACTORY.constructType(TypeElement.ExternalizedTypeElement.ofString(se.stringValue())); case null -> JavaType.VOID; default -> @@ -337,14 +339,9 @@ private static TypeElement toType(PoolEntry entry) { private static PoolEntry toEntry(ConstantPoolBuilder cp, TypeElement type) { if (type.equals(JavaType.VOID)) return null; - return switch (type) { - case JavaType jt -> - cp.utf8Entry(jt.toNominalDescriptor()); - case VarType vt when vt.valueType() instanceof JavaType jt -> - cp.stringEntry(cp.utf8Entry(jt.toNominalDescriptor())); - default -> - throw new IllegalArgumentException(type.toString()); - }; + return type instanceof JavaType jt && jt.erasure().equals(jt) + ? cp.utf8Entry(jt.toNominalDescriptor()) + : cp.stringEntry(type.externalize().toString()); } private static final class BufReader { From e8a63d843c0691ce0cd70b2cb17a3cbb09af80d9 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Tue, 26 Nov 2024 18:47:49 +0100 Subject: [PATCH 07/18] custom serialization of location attribute --- .../code/internal/CodeModelAttribute.java | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index 427d5f582b9..a5416bc2502 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -43,14 +43,17 @@ import java.util.Map; import jdk.incubator.code.Block; import jdk.incubator.code.Body; +import jdk.incubator.code.Location; import jdk.incubator.code.Op; import jdk.incubator.code.TypeElement; import jdk.incubator.code.Value; +import jdk.incubator.code.op.CoreOp; import jdk.incubator.code.op.ExtendedOp; import jdk.incubator.code.op.ExternalizableOp; import jdk.incubator.code.type.CoreTypeFactory; import jdk.incubator.code.type.FunctionType; import jdk.incubator.code.type.JavaType; +import jdk.incubator.code.type.VarType; public class CodeModelAttribute extends CustomAttribute{ @@ -108,6 +111,7 @@ private static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean t String name = buf.readUtf8(); List operands = readValues(buf, allValues); TypeElement rType = toType(buf.readEntryOrNull()); + if (name.equals(CoreOp.VarOp.NAME)) rType = VarType.varType(rType); Map attributes = readAttributes(buf); List bodies = readNestedBodies(buf, ancestorBody, allValues); return new ExternalizableOp.ExternalizedOp( @@ -124,8 +128,8 @@ private static void writeOp(BufWriter buf, Op op, Map valueMap) buf.writeIndex(buf.constantPool().utf8Entry(op.opName())); // operands writeValues(buf, op.operands(), valueMap); - // result type - buf.writeIndexOrZero(toEntry(buf.constantPool(), op.resultType())); + // result type, saving CP space by unwrapping VarType + buf.writeIndexOrZero(toEntry(buf.constantPool(), op.resultType() instanceof VarType vt ? vt.valueType() : op.resultType())); // attributes writeAttributes(buf, op instanceof ExternalizableOp extOp ? extOp.attributes() : Map.of()); // nested bodies @@ -141,8 +145,14 @@ private static Map readAttributes(BufReader buf) { int size = buf.readU2(); var attrs = new LinkedHashMap(size); for (int i = 0; i < size; i++) { - // attribute name + value - attrs.put(buf.readUtf8OrNull(), buf.readUtf8OrNull()); + // attribute name + String name = buf.readUtf8OrNull(); + // attribute value + if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(name)) { + attrs.put(name, new Location(buf.readUtf8OrNull(), buf.readU2(), buf.readU2())); + } else { + attrs.put(name, buf.readUtf8OrNull()); + } } return attrs; } @@ -154,7 +164,18 @@ private static void writeAttributes(BufWriter buf, Map attribute // attribute name buf.writeIndexOrZero(attre.getKey() == null ? null : buf.constantPool().utf8Entry(attre.getKey())); // attribute value - buf.writeIndexOrZero(attre.getValue() == null ? null : buf.constantPool().utf8Entry(attre.getValue().toString())); + if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(attre.getKey())) { + Location loc = switch (attre.getValue()) { + case Location l -> l; + case String s -> Location.fromString(s); + default -> throw new IllegalArgumentException(attre.toString()); + }; + buf.writeIndexOrZero(loc.sourceRef() == null ? null : buf.constantPool().utf8Entry(loc.sourceRef())); + buf.writeU2(loc.line()); + buf.writeU2(loc.column()); + } else { + buf.writeIndexOrZero(attre.getValue() == null ? null : buf.constantPool().utf8Entry(attre.getValue().toString())); + } } } From ea9ebccbe3d15287826e81510c2fd07db7c96183 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Tue, 26 Nov 2024 19:43:23 +0100 Subject: [PATCH 08/18] CodeModeAtrtribute documentation work in progress --- .../code/internal/CodeModelAttribute.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java index a5416bc2502..a1514ca089e 100644 --- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java @@ -55,6 +55,39 @@ import jdk.incubator.code.type.JavaType; import jdk.incubator.code.type.VarType; +/** + *
+ * CodeModel_attribute {
+ *    u2 attribute_name_index;
+ *    u4 attribute_length;
+ *    u2 op_name_index;
+ *    u2 op_operands_length;
+ *    u2 op_operands[op_operands_length];
+ *    u2 op_result_type_index;
+ *    u2 op_attributes_length;
+ *    {   u2 attribute_name_index;
+ *        u2 attribute_value_index;
+ *        u2 line number; // only for location attribute
+ *        u2 column number; // only for location attribute
+ *    } op_attributes_table[op_attributes_length];
+ *    u2 nested_bodies_length;
+ *    {   u2 body_func_type_index;
+ *        u2 blocks_length;
+ *        {   u2 block_parameters_length; // except for entry block
+ *            u2 block_parameter_type_index[block_parameters_length]; // except for entry block
+ *            u2 ops_length;
+ *            {   u2 op_name_index;
+ *                //  recurent declaration of op / nested bodies / blocks / ops
+ *            } ops_table[ops_length];
+ *            u2 successors_length; // declared at block level however applied to the block terminal op
+ *            {   u2 successor_block_index;
+ *                u2 block_arguments_length;
+ *                u2 block_arguments[block_arguments_length];
+ *            } successor_table[successors_length]
+ *        } blocks_table[blocks_length];
+ *    } nested_bodies_table[nested_bodies_length];
+ *}
+ */
 public class CodeModelAttribute extends CustomAttribute{
 
     public static final String NAME = "CodeModel";

From 72721a9b65a5dc60822821bc7d966e502864161a Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Wed, 27 Nov 2024 09:47:21 +0100
Subject: [PATCH 09/18] CodeModelAttribute structure cleanup + javadoc work in
 progress

---
 .../code/internal/CodeModelAttribute.java     | 107 +++++++++++-------
 1 file changed, 68 insertions(+), 39 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index a1514ca089e..e8a2d1b3393 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -58,35 +58,61 @@
 /**
  * 
  * CodeModel_attribute {
- *    u2 attribute_name_index;
- *    u4 attribute_length;
- *    u2 op_name_index;
- *    u2 op_operands_length;
- *    u2 op_operands[op_operands_length];
- *    u2 op_result_type_index;
- *    u2 op_attributes_length;
- *    {   u2 attribute_name_index;
- *        u2 attribute_value_index;
- *        u2 line number; // only for location attribute
- *        u2 column number; // only for location attribute
- *    } op_attributes_table[op_attributes_length];
- *    u2 nested_bodies_length;
- *    {   u2 body_func_type_index;
- *        u2 blocks_length;
- *        {   u2 block_parameters_length; // except for entry block
- *            u2 block_parameter_type_index[block_parameters_length]; // except for entry block
- *            u2 ops_length;
- *            {   u2 op_name_index;
- *                //  recurent declaration of op / nested bodies / blocks / ops
- *            } ops_table[ops_length];
- *            u2 successors_length; // declared at block level however applied to the block terminal op
- *            {   u2 successor_block_index;
- *                u2 block_arguments_length;
- *                u2 block_arguments[block_arguments_length];
- *            } successor_table[successors_length]
- *        } blocks_table[blocks_length];
- *    } nested_bodies_table[nested_bodies_length];
- *}
+ *     u2 attribute_name_index;
+ *     u4 attribute_length;
+ *     op_info;
+ * }
+ *
+ * op_info {
+ *     u2 op_name_index;
+ *     u2 op_operands_length;
+ *     u2 op_operands[op_operands_length];
+ *     u2 op_result_type_index;
+ *     u2 op_attributes_length;
+ *     op_attribute_info op_attributes_table[op_attributes_length];
+ *     u2 nested_bodies_length;
+ *     {   u2 body_func_type_index;
+ *         block_content_info; // entry block
+ *         u2 blocks_length;
+ *         {   u2 block_parameters_length;
+ *             u2 block_parameter_type_index[block_parameters_length];
+ *             block_content_info;
+ *         } blocks_table[blocks_length];
+ *     } nested_bodies_table[nested_bodies_length];
+ * }
+ *
+ * union op_attribute_info {
+ *     value_attribute_info;
+ *     location_attribute_info;
+ * }
+ *
+ * value_attribute_info {
+ *     u2 attribute_name_index;
+ *     u2 attribute_value_index;
+ * }
+ *
+ * location_attribute_info {
+ *     u2 location_attribute_name_index;
+ *     u2 source_index;
+ *     u2 line_number;
+ *     u2 column_number;
+ * }
+ *
+ * block_content_info {
+ *     u2 ops_length;
+ *     op_info ops_table[ops_length];
+ *     terminal_op_info;
+ * } blocks_table[blocks_length];
+ *
+ *
+ * terminal_op_info {
+ *     op_info;
+ *     u2 successors_length;
+ *     {   u2 successor_block_index;
+ *         u2 block_arguments_length;
+ *         u2 block_arguments[block_arguments_length];
+ *     } successor_table[successors_length]
+ * }
  */
 public class CodeModelAttribute extends CustomAttribute{
 
@@ -171,6 +197,11 @@ private static void writeOp(BufWriter buf, Op op, Map valueMap)
         if (op.result() != null) {
             valueMap.put(op.result(), valueMap.size());
         }
+
+        // @@@ assumption terminating op is only the last one in each block
+        if (op instanceof Op.Terminating) {
+            writeSuccessors(buf, op.successors(), valueMap);
+        }
     }
 
     private static Map readAttributes(BufReader buf) {
@@ -256,7 +287,7 @@ private static void writeNestedBodies(BufWriter buf, List bodies, Map allValues) {
         // number of blocks
-        var blocks = new Block.Builder[buf.readU2()];
+        var blocks = new Block.Builder[buf.readU2() + 1]; // entry block is mandatory
         blocks[0] = bob.entryBlock();
         for (int bi = 1; bi < blocks.length; bi++) {
             blocks[bi] = bob.entryBlock().block();
@@ -272,11 +303,11 @@ private static void readBlocks(BufReader buf, Body.Builder bob, List allV
     }
 
     private static void writeBlocks(BufWriter buf, List blocks, Map valueMap) {
-        // number of blocks
-        buf.writeU2(blocks.size());
+        // number of blocks - entry block
+        buf.writeU2(blocks.size() - 1);
         for (Block block : blocks) {
             // parameters
-            if (block.isEntryBlock()) {
+            if (block.isEntryBlock()) { // @@@ assumption entry block is the first one
                 for (var bp : block.parameters()) {
                     valueMap.put(bp, valueMap.size());
                 }
@@ -285,8 +316,6 @@ private static void writeBlocks(BufWriter buf, List blocks, Map pa
     private static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] allBlocks, List allValues) {
         // number of ops
         int opnum = buf.readU2();
-        for (int i = 0; i < opnum; i++) {
+        for (int i = 0; i <= opnum; i++) { // +1 terminal op
             // op
-            Op op = readOp(buf, i == opnum - 1, bb.parentBody(), allBlocks, allValues);
+            Op op = readOp(buf, i == opnum, bb.parentBody(), allBlocks, allValues);
             bb.op(op);
             if (op.result() != null) {
                 allValues.add(op.result());
@@ -323,8 +352,8 @@ private static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] all
     }
 
     private static void writeOps(BufWriter buf, List ops, Map valueMap) {
-        // number of ops
-        buf.writeU2(ops.size());
+        // number of ops - mandatory terminal op
+        buf.writeU2(ops.size() - 1);
         for (Op op : ops) {
             // op
             writeOp(buf, op, valueMap);

From e8dcde541703509e54413350bf8db68926d7349f Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Wed, 27 Nov 2024 09:50:05 +0100
Subject: [PATCH 10/18] typo

---
 .../jdk/incubator/code/internal/CodeModelAttribute.java        | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index e8a2d1b3393..0a3ce235a75 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -104,14 +104,13 @@
  *     terminal_op_info;
  * } blocks_table[blocks_length];
  *
- *
  * terminal_op_info {
  *     op_info;
  *     u2 successors_length;
  *     {   u2 successor_block_index;
  *         u2 block_arguments_length;
  *         u2 block_arguments[block_arguments_length];
- *     } successor_table[successors_length]
+ *     } successors_table[successors_length]
  * }
  */
 public class CodeModelAttribute extends CustomAttribute{

From ce1ec7d550c8e59be8ab21d70e8c6b777b08e35b Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Thu, 28 Nov 2024 17:08:30 +0100
Subject: [PATCH 11/18] Hard-coded CodeModelAttribute work in progress

---
 .../code/internal/CodeModelAttribute.java     | 408 ++++------------
 .../jdk/incubator/code/internal/OpReader.java | 380 +++++++++++++++
 .../jdk/incubator/code/internal/OpWriter.java | 442 ++++++++++++++++++
 .../jdk/incubator/code/op/ExtendedOp.java     |   6 +-
 4 files changed, 910 insertions(+), 326 deletions(-)
 create mode 100644 src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
 create mode 100644 src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index 0a3ce235a75..6a4fb422ea8 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -30,30 +30,9 @@
 import java.lang.classfile.BufWriter;
 import java.lang.classfile.ClassReader;
 import java.lang.classfile.CustomAttribute;
-import java.lang.classfile.constantpool.ConstantPoolBuilder;
-import java.lang.classfile.constantpool.PoolEntry;
-import java.lang.classfile.constantpool.StringEntry;
-import java.lang.classfile.constantpool.Utf8Entry;
-import java.lang.constant.ClassDesc;
-import java.lang.constant.MethodTypeDesc;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import jdk.incubator.code.Block;
-import jdk.incubator.code.Body;
-import jdk.incubator.code.Location;
 import jdk.incubator.code.Op;
-import jdk.incubator.code.TypeElement;
-import jdk.incubator.code.Value;
 import jdk.incubator.code.op.CoreOp;
 import jdk.incubator.code.op.ExtendedOp;
-import jdk.incubator.code.op.ExternalizableOp;
-import jdk.incubator.code.type.CoreTypeFactory;
-import jdk.incubator.code.type.FunctionType;
-import jdk.incubator.code.type.JavaType;
-import jdk.incubator.code.type.VarType;
 
 /**
  * 
@@ -115,6 +94,88 @@
  */
 public class CodeModelAttribute extends CustomAttribute{
 
+    public enum OpTag {
+        AddOp(CoreOp.AddOp.NAME),
+        AndOp(CoreOp.AndOp.NAME),
+        ArrayLoadOp(CoreOp.ArrayAccessOp.ArrayLoadOp.NAME),
+        ArrayStoreOp(CoreOp.ArrayAccessOp.ArrayStoreOp.NAME),
+        ArrayLengthOp(CoreOp.ArrayLengthOp.NAME),
+        AshrOp(CoreOp.AshrOp.NAME),
+        AssertOp(CoreOp.AssertOp.NAME),
+        BranchOp(CoreOp.BranchOp.NAME),
+        CastOp(CoreOp.CastOp.NAME),
+        ClosureCallOp(CoreOp.ClosureCallOp.NAME),
+        ClosureOp(CoreOp.ClosureOp.NAME),
+        ComplOp(CoreOp.ComplOp.NAME),
+        ConcatOp(CoreOp.ConcatOp.NAME),
+        ConditionalBranchOp(CoreOp.ConditionalBranchOp.NAME),
+        ConstantOp(CoreOp.ConstantOp.NAME),
+        ConvOp(CoreOp.ConvOp.NAME),
+        DivOp(CoreOp.DivOp.NAME),
+        EqOp(CoreOp.EqOp.NAME),
+        ExceptionRegionEnter(CoreOp.ExceptionRegionEnter.NAME),
+        ExceptionRegionExit(CoreOp.ExceptionRegionExit.NAME),
+        FieldLoadOp(CoreOp.FieldAccessOp.FieldLoadOp.NAME),
+        FieldStoreOp(CoreOp.FieldAccessOp.FieldStoreOp.NAME),
+        FuncCallOp(CoreOp.FuncCallOp.NAME),
+        FuncOp(CoreOp.FuncOp.NAME),
+        GeOp(CoreOp.GeOp.NAME),
+        GtOp(CoreOp.GtOp.NAME),
+        InstanceOfOp(CoreOp.InstanceOfOp.NAME),
+        InvokeOp(CoreOp.InvokeOp.NAME),
+        LambdaOp(CoreOp.LambdaOp.NAME),
+        LeOp(CoreOp.LeOp.NAME),
+        LshlOp(CoreOp.LshlOp.NAME),
+        LshrOp(CoreOp.LshrOp.NAME),
+        LtOp(CoreOp.LtOp.NAME),
+        ModOp(CoreOp.ModOp.NAME),
+        ModuleOp(CoreOp.ModuleOp.NAME),
+        MonitorEnterOp(CoreOp.MonitorOp.MonitorEnterOp.NAME),
+        MonitorExitOp(CoreOp.MonitorOp.MonitorExitOp.NAME),
+        MulOp(CoreOp.MulOp.NAME),
+        NegOp(CoreOp.NegOp.NAME),
+        NeqOp(CoreOp.NeqOp.NAME),
+        NewOp(CoreOp.NewOp.NAME),
+        NotOp(CoreOp.NotOp.NAME),
+        OrOp(CoreOp.OrOp.NAME),
+        QuotedOp(CoreOp.QuotedOp.NAME),
+        ReturnOp(CoreOp.ReturnOp.NAME),
+        SubOp(CoreOp.SubOp.NAME),
+        ThrowOp(CoreOp.ThrowOp.NAME),
+        TupleLoadOp(CoreOp.TupleLoadOp.NAME),
+        TupleOp(CoreOp.TupleOp.NAME),
+        TupleWithOp(CoreOp.TupleWithOp.NAME),
+        UnreachableOp(CoreOp.UnreachableOp.NAME),
+        VarLoadOp(CoreOp.VarAccessOp.VarLoadOp.NAME),
+        VarStoreOp(CoreOp.VarAccessOp.VarStoreOp.NAME),
+        VarOp(CoreOp.VarOp.NAME),
+        XorOp(CoreOp.XorOp.NAME),
+        YieldOp(CoreOp.YieldOp.NAME),
+        JavaBlockOp(ExtendedOp.JavaBlockOp.NAME),
+        JavaBreakOp(ExtendedOp.JavaBreakOp.NAME),
+        JavaConditionalAndOp(ExtendedOp.JavaConditionalAndOp.NAME),
+        JavaConditionalExpressionOp(ExtendedOp.JavaConditionalExpressionOp.NAME),
+        JavaConditionalOrOp(ExtendedOp.JavaConditionalOrOp.NAME),
+        JavaContinueOp(ExtendedOp.JavaContinueOp.NAME),
+        JavaDoWhileOp(ExtendedOp.JavaDoWhileOp.NAME),
+        JavaEnhancedForOp(ExtendedOp.JavaEnhancedForOp.NAME),
+        JavaForOp(ExtendedOp.JavaForOp.NAME),
+        JavaIfOp(ExtendedOp.JavaIfOp.NAME),
+        JavaLabeledOp(ExtendedOp.JavaLabeledOp.NAME),
+        JavaSwitchExpressionOp(ExtendedOp.JavaSwitchExpressionOp.NAME),
+        JavaSwitchFallthroughOp(ExtendedOp.JavaSwitchFallthroughOp.NAME),
+        JavaSwitchStatementOp(ExtendedOp.JavaSwitchStatementOp.NAME),
+        MatchAllPatternOp(ExtendedOp.PatternOps.MatchAllPatternOp.NAME),
+        MatchOp(ExtendedOp.PatternOps.MatchOp.NAME),
+        RecordPatternOp(ExtendedOp.PatternOps.RecordPatternOp.NAME),
+        TypePatternOp(ExtendedOp.PatternOps.TypePatternOp.NAME);
+
+        final String opName;
+        OpTag(String opName) {
+            this.opName = opName;
+        }
+    }
+
     public static final String NAME = "CodeModel";
 
     public static final AttributeMapper MAPPER = new AttributeMapper<>() {
@@ -125,8 +186,8 @@ public String name() {
         }
 
         @Override
-        public CodeModelAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
-            return new CodeModelAttribute(readOp(new BufReader(cf, pos), false, null, null, new ArrayList<>()));
+        public CodeModelAttribute readAttribute(AttributedElement enclosing, ClassReader cr, int pos) {
+            return new CodeModelAttribute(new OpReader(cr, pos).readOp(null, null));
         }
 
         @Override
@@ -134,7 +195,7 @@ public void writeAttribute(BufWriter buf, CodeModelAttribute attr) {
             buf.writeIndex(buf.constantPool().utf8Entry(NAME));
             int lengthIndex = buf.size();
             buf.writeInt(0);
-            writeOp(buf, attr.op, new HashMap<>());
+            new OpWriter(buf).writeOp(attr.op);
             int written = buf.size() - lengthIndex - 4;
             buf.patchInt(lengthIndex, 4, written);
         }
@@ -159,303 +220,4 @@ private CodeModelAttribute(Op op) {
     public Op op() {
         return op;
     }
-
-    private static Op readOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) {
-        var extOp = readExOp(buf, terminal, ancestorBody, ancestorBodyBlocks, allValues);
-        return ExtendedOp.FACTORY.constructOpOrFail(extOp);
-    }
-
-    private static ExternalizableOp.ExternalizedOp readExOp(BufReader buf, boolean terminal, Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks, List allValues) {
-        String name = buf.readUtf8();
-        List operands = readValues(buf, allValues);
-        TypeElement rType = toType(buf.readEntryOrNull());
-        if (name.equals(CoreOp.VarOp.NAME)) rType = VarType.varType(rType);
-        Map attributes = readAttributes(buf);
-        List bodies = readNestedBodies(buf, ancestorBody, allValues);
-        return new ExternalizableOp.ExternalizedOp(
-                name,
-                operands,
-                terminal ? readSuccessors(buf, ancestorBodyBlocks, allValues) : List.of(), // successors follow terminal ops
-                rType,
-                attributes,
-                bodies);
-    }
-
-    private static void writeOp(BufWriter buf, Op op, Map valueMap) {
-        // name
-        buf.writeIndex(buf.constantPool().utf8Entry(op.opName()));
-        // operands
-        writeValues(buf, op.operands(), valueMap);
-        // result type, saving CP space by unwrapping VarType
-        buf.writeIndexOrZero(toEntry(buf.constantPool(), op.resultType() instanceof VarType vt ? vt.valueType() : op.resultType()));
-        // attributes
-        writeAttributes(buf, op instanceof ExternalizableOp extOp ? extOp.attributes() : Map.of());
-        // nested bodies
-        writeNestedBodies(buf, op.bodies(), valueMap);
-
-        if (op.result() != null) {
-            valueMap.put(op.result(), valueMap.size());
-        }
-
-        // @@@ assumption terminating op is only the last one in each block
-        if (op instanceof Op.Terminating) {
-            writeSuccessors(buf, op.successors(), valueMap);
-        }
-    }
-
-    private static Map readAttributes(BufReader buf) {
-        // number of attributes
-        int size = buf.readU2();
-        var attrs = new LinkedHashMap(size);
-        for (int i = 0; i < size; i++) {
-            // attribute name
-            String name = buf.readUtf8OrNull();
-            // attribute value
-            if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(name)) {
-                attrs.put(name, new Location(buf.readUtf8OrNull(), buf.readU2(), buf.readU2()));
-            } else {
-                attrs.put(name, buf.readUtf8OrNull());
-            }
-        }
-        return attrs;
-    }
-
-    private static void writeAttributes(BufWriter buf, Map attributes) {
-        // number of attributes
-        buf.writeU2(attributes.size());
-        for (var attre : attributes.entrySet()) {
-            // attribute name
-            buf.writeIndexOrZero(attre.getKey() == null ? null : buf.constantPool().utf8Entry(attre.getKey()));
-            // attribute value
-            if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(attre.getKey())) {
-                Location loc = switch (attre.getValue()) {
-                    case Location l -> l;
-                    case String s -> Location.fromString(s);
-                    default -> throw new IllegalArgumentException(attre.toString());
-                };
-                buf.writeIndexOrZero(loc.sourceRef() == null ? null : buf.constantPool().utf8Entry(loc.sourceRef()));
-                buf.writeU2(loc.line());
-                buf.writeU2(loc.column());
-            } else {
-                buf.writeIndexOrZero(attre.getValue() == null ? null : buf.constantPool().utf8Entry(attre.getValue().toString()));
-            }
-        }
-    }
-
-    private static List readValues(BufReader buf, List allValues) {
-        // number of values
-        var values = new Value[buf.readU2()];
-        for (int i = 0; i < values.length; i++) {
-            // value by index
-            values[i] = allValues.get(buf.readU2());
-        }
-        return List.of(values);
-    }
-
-    private static void writeValues(BufWriter buf, List values, Map valueMap) {
-        // number of values
-        buf.writeU2(values.size());
-        for (Value v : values) {
-            // value index
-            buf.writeU2(valueMap.get(v));
-        }
-    }
-
-    private static List readNestedBodies(BufReader buf, Body.Builder ancestorBody, List allValues) {
-        // number of bodies
-        var bodies = new Body.Builder[buf.readU2()];
-        for (int i = 0; i < bodies.length; i++) {
-            // body type
-            bodies[i] = Body.Builder.of(ancestorBody, toFuncType(buf.readEntryOrNull()));
-            // blocks
-            readBlocks(buf, bodies[i], allValues);
-        }
-        return List.of(bodies);
-    }
-
-    private static void writeNestedBodies(BufWriter buf, List bodies, Map valueMap) {
-        // number of bodies
-        buf.writeU2(bodies.size());
-        for (Body body : bodies) {
-            // body type
-            buf.writeIndex(toEntry(buf.constantPool(), body.bodyType()));
-            // blocks
-            writeBlocks(buf, body.blocks(), valueMap);
-        }
-    }
-
-    private static void readBlocks(BufReader buf, Body.Builder bob, List allValues) {
-        // number of blocks
-        var blocks = new Block.Builder[buf.readU2() + 1]; // entry block is mandatory
-        blocks[0] = bob.entryBlock();
-        for (int bi = 1; bi < blocks.length; bi++) {
-            blocks[bi] = bob.entryBlock().block();
-        }
-        for (Block.Builder bb : blocks) {
-            if (bb.isEntryBlock()) {
-                allValues.addAll(bob.entryBlock().parameters());
-            } else {
-                readBlockParameters(buf, bb, allValues);
-            }
-            readOps(buf, bb, blocks, allValues);
-        }
-    }
-
-    private static void writeBlocks(BufWriter buf, List blocks, Map valueMap) {
-        // number of blocks - entry block
-        buf.writeU2(blocks.size() - 1);
-        for (Block block : blocks) {
-            // parameters
-            if (block.isEntryBlock()) { // @@@ assumption entry block is the first one
-                for (var bp : block.parameters()) {
-                    valueMap.put(bp, valueMap.size());
-                }
-            } else {
-                writeBlockParameters(buf, block.parameters(), valueMap);
-            }
-            // ops
-            writeOps(buf, block.ops(), valueMap);
-        }
-    }
-
-    private static void readBlockParameters(BufReader buf, Block.Builder bb, List allValues) {
-        // number of block parameters
-        int bpnum = buf.readU2();
-        for (int i = 0; i < bpnum; i++) {
-            // block parameter type
-            allValues.add(bb.parameter(toType(buf.readEntryOrNull())));
-        }
-    }
-
-    private static void writeBlockParameters(BufWriter buf, List parameters, Map valueMap) {
-        // number of block parameters
-        buf.writeU2(parameters.size());
-        for (Block.Parameter bp : parameters) {
-            // block parameter type
-            buf.writeIndexOrZero(toEntry(buf.constantPool(), bp.type()));
-            valueMap.put(bp, valueMap.size());
-        }
-    }
-
-    private static void readOps(BufReader buf, Block.Builder bb, Block.Builder[] allBlocks, List allValues) {
-        // number of ops
-        int opnum = buf.readU2();
-        for (int i = 0; i <= opnum; i++) { // +1 terminal op
-            // op
-            Op op = readOp(buf, i == opnum, bb.parentBody(), allBlocks, allValues);
-            bb.op(op);
-            if (op.result() != null) {
-                allValues.add(op.result());
-            }
-        }
-    }
-
-    private static void writeOps(BufWriter buf, List ops, Map valueMap) {
-        // number of ops - mandatory terminal op
-        buf.writeU2(ops.size() - 1);
-        for (Op op : ops) {
-            // op
-            writeOp(buf, op, valueMap);
-        }
-    }
-
-    private static List readSuccessors(BufReader buf, Block.Builder[] ancestorBodyBlocks, List allValues) {
-        // number of successors
-        var refs = new Block.Reference[buf.readU2()];
-        for (int i = 0; i < refs.length; i++) {
-            // block from index + arguments
-            refs[i] = ancestorBodyBlocks[buf.readU2()].successor(readValues(buf, allValues));
-        }
-        return List.of(refs);
-    }
-
-    private static void writeSuccessors(BufWriter buf, List successors, Map valueMap) {
-        // number of successors
-        buf.writeU2(successors.size());
-        for (Block.Reference succ : successors) {
-            // block index
-            buf.writeU2(succ.targetBlock().index());
-            // arguments
-            writeValues(buf, succ.arguments(), valueMap);
-        }
-    }
-
-    private static FunctionType toFuncType(PoolEntry entry) {
-        return switch (entry) {
-            case Utf8Entry ue -> {
-                var mtd = MethodTypeDesc.ofDescriptor(ue.stringValue());
-                yield FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList());
-            }
-            case StringEntry se ->
-                (FunctionType)CoreTypeFactory.CORE_TYPE_FACTORY.constructType(TypeElement.ExternalizedTypeElement.ofString(se.stringValue()));
-            default ->
-                throw new IllegalArgumentException(entry.toString());
-        };
-    }
-
-    private static PoolEntry toEntry(ConstantPoolBuilder cp, FunctionType ftype) {
-        if (ftype.returnType() instanceof JavaType jret
-                && jret.erasure().equals(jret)
-                && ftype.parameterTypes().stream().allMatch(te ->
-                        te instanceof JavaType jt && jt.erasure().equals(jt))) {
-            // prefer to store as method type descriptor
-            return cp.utf8Entry(MethodTypeDesc.of(jret.toNominalDescriptor(), ftype.parameterTypes().stream().map(te -> ((JavaType)te).toNominalDescriptor()).toList()));
-        } else {
-            // fallback
-            return cp.stringEntry(ftype.externalize().toString());
-        }
-    }
-
-    private static TypeElement toType(PoolEntry entry) {
-        return switch (entry) {
-            case Utf8Entry ue ->
-                JavaType.type(ClassDesc.ofDescriptor(ue.stringValue()));
-            case StringEntry se ->
-                CoreTypeFactory.CORE_TYPE_FACTORY.constructType(TypeElement.ExternalizedTypeElement.ofString(se.stringValue()));
-            case null ->
-                JavaType.VOID;
-            default ->
-                throw new IllegalArgumentException(entry.toString());
-        };
-    }
-
-    private static PoolEntry toEntry(ConstantPoolBuilder cp, TypeElement type) {
-        if (type.equals(JavaType.VOID)) return null;
-        return type instanceof JavaType jt && jt.erasure().equals(jt)
-                ? cp.utf8Entry(jt.toNominalDescriptor())
-                : cp.stringEntry(type.externalize().toString());
-    }
-
-    private static final class BufReader {
-        private final ClassReader cr;
-        private int offset;
-        BufReader(ClassReader cr, int offset) {
-            this.cr = cr;
-            this.offset = offset;
-        }
-
-        int readU2() {
-            int i = cr.readU2(offset);
-            offset += 2;
-            return i;
-        }
-
-        String readUtf8() {
-            String s = cr.readEntry(offset, Utf8Entry.class).stringValue();
-            offset += 2;
-            return s;
-        }
-
-        String readUtf8OrNull() {
-            Utf8Entry u = cr.readEntryOrNull(offset, Utf8Entry.class);
-            offset += 2;
-            return u == null ? null : u.stringValue();
-        }
-
-        PoolEntry readEntryOrNull() {
-            PoolEntry e = cr.readEntryOrNull(offset);
-            offset += 2;
-            return e;
-        }
-    }
 }
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
new file mode 100644
index 00000000000..a3e480a0bc7
--- /dev/null
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.incubator.code.internal;
+
+import java.lang.classfile.ClassReader;
+import java.lang.classfile.constantpool.PoolEntry;
+import java.lang.classfile.constantpool.StringEntry;
+import java.lang.classfile.constantpool.Utf8Entry;
+import java.lang.constant.ClassDesc;
+import java.lang.constant.MethodTypeDesc;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import jdk.incubator.code.Block;
+import jdk.incubator.code.Body;
+import jdk.incubator.code.Location;
+import jdk.incubator.code.Op;
+import jdk.incubator.code.TypeElement;
+import jdk.incubator.code.Value;
+import jdk.incubator.code.op.CoreOp;
+import jdk.incubator.code.op.ExternalizableOp;
+import jdk.incubator.code.type.CoreTypeFactory;
+import jdk.incubator.code.type.FunctionType;
+import jdk.incubator.code.type.JavaType;
+
+final class OpReader {
+
+    private final ClassReader cr;
+    private final List allValues;
+    private int offset;
+
+    OpReader(ClassReader cr, int offset) {
+        this.cr = cr;
+        this.allValues = new ArrayList<>();
+        this.offset = offset;
+    }
+
+    Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
+        switch (CodeModelAttribute.OpTag.values()[readU1()]) {
+            case AddOp ->
+                CoreOp.add(readValue(), readValue());
+            case AndOp ->
+                CoreOp.and(readValue(), readValue());
+            case ArrayLoadOp ->
+                CoreOp.arrayLoadOp(readValue(), readValue(), readType());
+            case ArrayStoreOp ->
+                CoreOp.arrayStoreOp(readValue(), readValue(), readValue());
+            case ArrayLengthOp ->
+                CoreOp.arrayLength(readValue());
+            case AshrOp ->
+                CoreOp.ashr(readValue(), readValue());
+            case BranchOp ->
+                CoreOp.branch(readBlockReference(ancestorBodyBlocks));
+            case CastOp ->
+                CoreOp.cast(readType(), (JavaType)readType(), readValue());
+            case ClosureCallOp ->
+                CoreOp.closureCall(readValues());
+            case ClosureOp ->
+                CoreOp.closure(readNestedBody(ancestorBody));
+            case ComplOp ->
+                CoreOp.compl(readValue());
+            case ConcatOp ->
+                CoreOp.concat(readValue(), readValue());
+            case ConditionalBranchOp ->
+                CoreOp.conditionalBranch(readValue(), readBlockReference(ancestorBodyBlocks), readBlockReference(ancestorBodyBlocks));
+            case ConstantOp ->
+                CoreOp.constant(readType(), readUtf8OrNull()); // @@@ constant value is serialized as Utf8Entry
+            case ConvOp ->
+                CoreOp.conv(readType(), readValue());
+            case DivOp ->
+                CoreOp.div(readValue(), readValue());
+            case EqOp ->
+                CoreOp.eq(readValue(), readValue());
+            case ExceptionRegionEnter ->
+                CoreOp.exceptionRegionEnter(readBlockReference(ancestorBodyBlocks), readCatchers(ancestorBodyBlocks));
+            case ExceptionRegionExit ->
+                CoreOp.exceptionRegionExit(readBlockReference(ancestorBodyBlocks), readCatchers(ancestorBodyBlocks));
+            case FieldLoadOp -> {
+            }
+            case FieldStoreOp -> {
+            }
+            case FuncCallOp -> {
+            }
+            case FuncOp -> {
+            }
+            case GeOp -> {
+            }
+            case GtOp -> {
+            }
+            case InstanceOfOp -> {
+            }
+            case InvokeOp -> {
+            }
+            case LambdaOp -> {
+            }
+            case LeOp -> {
+            }
+            case LshlOp -> {
+            }
+            case LshrOp -> {
+            }
+            case LtOp -> {
+            }
+            case ModOp -> {
+            }
+            case ModuleOp -> {
+            }
+            case MonitorEnterOp -> {
+            }
+            case MonitorExitOp -> {
+            }
+            case MulOp -> {
+            }
+            case NegOp -> {
+            }
+            case NeqOp -> {
+            }
+            case NewOp -> {
+            }
+            case NotOp -> {
+            }
+            case OrOp -> {
+            }
+            case QuotedOp -> {
+            }
+            case ReturnOp -> {
+            }
+            case SubOp -> {
+            }
+            case ThrowOp -> {
+            }
+            case TupleLoadOp -> {
+            }
+            case TupleOp -> {
+            }
+            case TupleWithOp -> {
+            }
+            case UnreachableOp -> {
+            }
+            case VarLoadOp -> {
+            }
+            case VarStoreOp -> {
+            }
+            case VarOp -> {
+            }
+            case XorOp -> {
+            }
+            case YieldOp -> {
+            }
+            case JavaBlockOp -> {
+            }
+            case JavaBreakOp -> {
+            }
+            case JavaConditionalAndOp -> {
+            }
+            case JavaConditionalExpressionOp -> {
+            }
+            case JavaConditionalOrOp -> {
+            }
+            case JavaContinueOp -> {
+            }
+            case JavaDoWhileOp -> {
+            }
+            case JavaEnhancedForOp -> {
+            }
+            case JavaForOp -> {
+            }
+            case JavaIfOp -> {
+            }
+            case JavaLabeledOp -> {
+            }
+            case JavaSwitchExpressionOp -> {
+            }
+            case JavaSwitchFallthroughOp -> {
+            }
+            case JavaSwitchStatementOp -> {
+            }
+            case MatchAllPatternOp -> {
+            }
+            case MatchOp -> {
+            }
+            case RecordPatternOp -> {
+            }
+            case TypePatternOp -> {
+            }
+        }
+        return null;
+    }
+
+    private Map readAttributes()  {
+        // number of attributes
+        int size = readU2();
+        var attrs = new LinkedHashMap(size);
+        for (int i = 0; i < size; i++) {
+            // attribute name
+            String name = readUtf8OrNull();
+            // attribute value
+            if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(name)) {
+                attrs.put(name, new Location(readUtf8OrNull(), readU2(), readU2()));
+            } else {
+                attrs.put(name, readUtf8OrNull());
+            }
+        }
+        return attrs;
+    }
+
+    private List readValues() {
+        // number of values
+        var values = new Value[readU2()];
+        for (int i = 0; i < values.length; i++) {
+            // value by index
+            values[i] = allValues.get(readU2());
+        }
+        return List.of(values);
+    }
+
+    private List readNestedBodies(Body.Builder ancestorBody) {
+        // number of bodies
+        var bodies = new Body.Builder[readU2()];
+        for (int i = 0; i < bodies.length; i++) {
+            bodies[i] = readNestedBody(ancestorBody);
+        }
+        return List.of(bodies);
+    }
+
+    private Body.Builder readNestedBody(Body.Builder ancestorBody) {
+        var bb = Body.Builder.of(ancestorBody, toFuncType(readEntryOrNull()));
+        readBlocks(bb);
+        return bb;
+    }
+
+    private void readBlocks(Body.Builder bob) {
+        // number of blocks
+        var blocks = new Block.Builder[readU2() + 1]; // entry block is mandatory
+        blocks[0] = bob.entryBlock();
+        for (int bi = 1; bi < blocks.length; bi++) {
+            blocks[bi] = bob.entryBlock().block();
+        }
+        for (Block.Builder bb : blocks) {
+            if (bb.isEntryBlock()) {
+                allValues.addAll(bob.entryBlock().parameters());
+            } else {
+                readBlockParameters(bb);
+            }
+            readOps(bb, blocks);
+        }
+    }
+
+    private void readBlockParameters(Block.Builder bb) {
+        // number of block parameters
+        int bpnum = readU2();
+        for (int i = 0; i < bpnum; i++) {
+            // block parameter type
+            allValues.add(bb.parameter(toType(readEntryOrNull())));
+        }
+    }
+
+    private void readOps(Block.Builder bb, Block.Builder[] allBlocks) {
+        // number of ops
+        int opnum = readU2();
+        for (int i = 0; i <= opnum; i++) { // +1 terminal op
+            // op
+            Op op = readOp(bb.parentBody(), allBlocks);
+            bb.op(op);
+            if (op.result() != null) {
+                allValues.add(op.result());
+            }
+        }
+    }
+
+    private List readSuccessors(Block.Builder[] ancestorBodyBlocks) {
+        // number of successors
+        var refs = new Block.Reference[readU2()];
+        for (int i = 0; i < refs.length; i++) {
+            // block from index + arguments
+            refs[i] = ancestorBodyBlocks[readU2()].successor(readValues());
+        }
+        return List.of(refs);
+    }
+
+    private static FunctionType toFuncType(PoolEntry entry) {
+        return switch (entry) {
+            case Utf8Entry ue -> {
+                var mtd = MethodTypeDesc.ofDescriptor(ue.stringValue());
+                yield FunctionType.functionType(JavaType.type(mtd.returnType()), mtd.parameterList().stream().map(JavaType::type).toList());
+            }
+            case StringEntry se ->
+                (FunctionType)CoreTypeFactory.CORE_TYPE_FACTORY.constructType(TypeElement.ExternalizedTypeElement.ofString(se.stringValue()));
+            default ->
+                throw new IllegalArgumentException(entry.toString());
+        };
+    }
+
+    private static TypeElement toType(PoolEntry entry) {
+        return switch (entry) {
+            case Utf8Entry ue ->
+                JavaType.type(ClassDesc.ofDescriptor(ue.stringValue()));
+            case StringEntry se ->
+                CoreTypeFactory.CORE_TYPE_FACTORY.constructType(TypeElement.ExternalizedTypeElement.ofString(se.stringValue()));
+            case null ->
+                JavaType.VOID;
+            default ->
+                throw new IllegalArgumentException(entry.toString());
+        };
+    }
+
+    private int readU1() {
+        int i = cr.readU1(offset);
+        offset++;
+        return i;
+    }
+
+    private int readU2() {
+        int i = cr.readU2(offset);
+        offset += 2;
+        return i;
+    }
+
+    private String readUtf8() {
+        String s = cr.readEntry(offset, Utf8Entry.class).stringValue();
+        offset += 2;
+        return s;
+    }
+
+    private String readUtf8OrNull() {
+        Utf8Entry u = cr.readEntryOrNull(offset, Utf8Entry.class);
+        offset += 2;
+        return u == null ? null : u.stringValue();
+    }
+
+    private PoolEntry readEntryOrNull() {
+        PoolEntry e = cr.readEntryOrNull(offset);
+        offset += 2;
+        return e;
+    }
+
+    private Value readValue() {
+        return allValues.get(readU2());
+    }
+
+    private TypeElement readType() {
+        return toType(readEntryOrNull());
+    }
+
+    private Block.Reference readBlockReference(Block.Builder[] ancestorBodyBlocks) {
+        return ancestorBodyBlocks[readU2()].successor(readValues());
+    }
+
+    private Block.Reference[] readCatchers(Block.Builder[] ancestorBodyBlocks) {
+        var catchers = new Block.Reference[readU2()];
+        for (int i = 0; i < catchers.length; i++) {
+            catchers[i] = ancestorBodyBlocks[readU2()].successor();
+        }
+        return catchers;
+    }
+}
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
new file mode 100644
index 00000000000..b026e9fa099
--- /dev/null
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.incubator.code.internal;
+
+import java.lang.classfile.BufWriter;
+import java.lang.classfile.constantpool.ConstantPoolBuilder;
+import java.lang.classfile.constantpool.PoolEntry;
+import java.lang.constant.MethodTypeDesc;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import jdk.incubator.code.Block;
+import jdk.incubator.code.Body;
+import jdk.incubator.code.Location;
+import jdk.incubator.code.Op;
+import jdk.incubator.code.TypeElement;
+import jdk.incubator.code.Value;
+import jdk.incubator.code.op.CoreOp;
+import jdk.incubator.code.op.ExtendedOp;
+import jdk.incubator.code.op.ExternalizableOp;
+import jdk.incubator.code.type.FunctionType;
+import jdk.incubator.code.type.JavaType;
+import jdk.incubator.code.type.VarType;
+
+import static jdk.incubator.code.internal.CodeModelAttribute.OpTag.*;
+
+final class OpWriter {
+
+    private final BufWriter buf;
+    private final ConstantPoolBuilder cp;
+    private final Map valueMap;
+
+    OpWriter(BufWriter buf) {
+        this.buf = buf;
+        this.cp = buf.constantPool();
+        this.valueMap = new HashMap<>();
+    }
+
+    void writeOp(Op op) {
+       var operands = op.operands();
+       switch (op) {
+           case CoreOp.AddOp _ ->
+               writeOpWithFixedOperandValues(AddOp, op);
+           case CoreOp.AndOp _ ->
+               writeOpWithFixedOperandValues(AndOp, op);
+           case CoreOp.ArrayAccessOp.ArrayLoadOp _ -> {
+               writeOpWithFixedOperandValues(ArrayLoadOp, op);
+               writeType(op.resultType());
+           }
+           case CoreOp.ArrayAccessOp.ArrayStoreOp _ ->
+               writeOpWithFixedOperandValues(ArrayStoreOp, op);
+           case CoreOp.ArrayLengthOp _ ->
+               writeOpWithFixedOperandValues(ArrayLengthOp, op);
+           case CoreOp.AshrOp _ ->
+               writeOpWithFixedOperandValues(AshrOp, op);
+           case CoreOp.BranchOp bo -> {
+                writeTag(BranchOp);
+                writeTarget(bo.branch());
+           }
+           case CoreOp.CastOp co -> {
+               writeTag(CastOp);
+               writeType(co.resultType());
+               writeType(co.type());
+               writeValue(co.operands().getFirst());
+           }
+           case CoreOp.ClosureCallOp cco -> {
+               writeTag(ClosureCallOp);
+               writeValuesList(cco.operands());
+           }
+           case CoreOp.ClosureOp co -> {
+               writeTag(ClosureOp);
+               writeNestedBody(co.body());
+           }
+           case CoreOp.ComplOp _ ->
+               writeOpWithFixedOperandValues(ComplOp, op);
+           case CoreOp.ConcatOp _ ->
+               writeOpWithFixedOperandValues(ConcatOp, op);
+           case CoreOp.ConditionalBranchOp cbo -> {
+               writeTag(ConditionalBranchOp);
+               writeTarget(cbo.trueBranch());
+               writeTarget(cbo.falseBranch());
+           }
+           case CoreOp.ConstantOp co -> {
+               writeTag(ConstantOp);
+               writeType(co.resultType());
+               writeUtf8EntryOrZero(co.value()); // @@@ constant value serialized as Utf8Entry
+           }
+           case CoreOp.ConvOp _ -> {
+               writeTag(ConvOp);
+               writeType(op.resultType());
+               writeValue(op.operands().getFirst());
+           }
+           case CoreOp.DivOp _ ->
+               writeOpWithFixedOperandValues(DivOp, op);
+           case CoreOp.EqOp _ ->
+               writeOpWithFixedOperandValues(EqOp, op);
+           case CoreOp.ExceptionRegionEnter ere -> {
+               writeTag(ExceptionRegionEnter);
+               writeTarget(ere.start());
+               writeCatchers(ere.catchBlocks());
+           }
+           case CoreOp.ExceptionRegionExit ere -> {
+               writeTag(ExceptionRegionExit);
+               writeTarget(ere.end());
+               writeCatchers(ere.catchBlocks());
+           }
+           case CoreOp.FieldAccessOp.FieldLoadOp _ -> {
+               writeTag(FieldLoadOp);
+           }
+           case CoreOp.FieldAccessOp.FieldStoreOp _ -> {
+               writeTag(FieldStoreOp);
+           }
+           case CoreOp.FuncCallOp _ -> {
+               writeTag(FuncCallOp);
+           }
+           case CoreOp.FuncOp _ -> {
+               writeTag(FuncOp);
+           }
+           case CoreOp.GeOp _ -> {
+               writeTag(GeOp);
+           }
+           case CoreOp.GtOp _ -> {
+               writeTag(GtOp);
+           }
+           case CoreOp.InstanceOfOp _ -> {
+               writeTag(InstanceOfOp);
+           }
+           case CoreOp.InvokeOp _ -> {
+               writeTag(InvokeOp);
+           }
+           case CoreOp.LambdaOp _ -> {
+               writeTag(LambdaOp);
+           }
+           case CoreOp.LeOp _ -> {
+               writeTag(LeOp);
+           }
+           case CoreOp.LshlOp _ -> {
+               writeTag(LshlOp);
+           }
+           case CoreOp.LshrOp _ -> {
+               writeTag(LshrOp);
+           }
+           case CoreOp.LtOp _ -> {
+               writeTag(LtOp);
+           }
+           case CoreOp.ModOp _ -> {
+               writeTag(ModOp);
+           }
+           case CoreOp.ModuleOp _ -> {
+               writeTag(ModuleOp);
+           }
+           case CoreOp.MonitorOp.MonitorEnterOp _ -> {
+               writeTag(MonitorEnterOp);
+           }
+           case CoreOp.MonitorOp.MonitorExitOp _ -> {
+               writeTag(MonitorExitOp);
+           }
+           case CoreOp.MulOp _ -> {
+               writeTag(MulOp);
+           }
+           case CoreOp.NegOp _ -> {
+               writeTag(NegOp);
+           }
+           case CoreOp.NeqOp _ -> {
+               writeTag(NeqOp);
+           }
+           case CoreOp.NewOp _ -> {
+               writeTag(NewOp);
+           }
+           case CoreOp.NotOp _ -> {
+               writeTag(NotOp);
+           }
+           case CoreOp.OrOp _ -> {
+               writeTag(OrOp);
+           }
+           case CoreOp.QuotedOp _ -> {
+               writeTag(QuotedOp);
+           }
+           case CoreOp.ReturnOp _ -> {
+               writeTag(ReturnOp);
+           }
+           case CoreOp.SubOp _ -> {
+               writeTag(SubOp);
+           }
+           case CoreOp.ThrowOp _ -> {
+               writeTag(ThrowOp);
+           }
+           case CoreOp.TupleLoadOp _ -> {
+               writeTag(TupleLoadOp);
+           }
+           case CoreOp.TupleOp _ -> {
+               writeTag(TupleOp);
+           }
+           case CoreOp.TupleWithOp _ -> {
+               writeTag(TupleWithOp);
+           }
+           case CoreOp.UnreachableOp _ -> {
+               writeTag(UnreachableOp);
+           }
+           case CoreOp.VarAccessOp.VarLoadOp _ -> {
+               writeTag(VarLoadOp);
+           }
+           case CoreOp.VarAccessOp.VarStoreOp _ -> {
+               writeTag(VarStoreOp);
+           }
+           case CoreOp.VarOp _ -> {
+               writeTag(VarOp);
+           }
+           case CoreOp.XorOp _ -> {
+               writeTag(XorOp);
+           }
+           case CoreOp.YieldOp _ -> {
+               writeTag(YieldOp);
+           }
+           case ExtendedOp.JavaBlockOp _ -> {
+               writeTag(JavaBlockOp);
+           }
+           case ExtendedOp.JavaBreakOp _ -> {
+               writeTag(JavaBreakOp);
+           }
+           case ExtendedOp.JavaConditionalAndOp _ -> {
+               writeTag(JavaConditionalAndOp);
+           }
+           case ExtendedOp.JavaConditionalExpressionOp _ -> {
+               writeTag(JavaConditionalExpressionOp);
+           }
+           case ExtendedOp.JavaConditionalOrOp _ -> {
+               writeTag(JavaConditionalOrOp);
+           }
+           case ExtendedOp.JavaContinueOp _ -> {
+               writeTag(JavaContinueOp);
+           }
+           case ExtendedOp.JavaDoWhileOp _ -> {
+               writeTag(JavaDoWhileOp);
+           }
+           case ExtendedOp.JavaEnhancedForOp _ -> {
+               writeTag(JavaEnhancedForOp);
+           }
+           case ExtendedOp.JavaForOp _ -> {
+               writeTag(JavaForOp);
+           }
+           case ExtendedOp.JavaIfOp _ -> {
+               writeTag(JavaIfOp);
+           }
+           case ExtendedOp.JavaLabeledOp _ -> {
+               writeTag(JavaLabeledOp);
+           }
+           case ExtendedOp.JavaSwitchExpressionOp _ -> {
+               writeTag(JavaSwitchExpressionOp);
+           }
+           case ExtendedOp.JavaSwitchFallthroughOp _ -> {
+               writeTag(JavaSwitchFallthroughOp);
+           }
+           case ExtendedOp.JavaSwitchStatementOp _ -> {
+               writeTag(JavaSwitchStatementOp);
+           }
+           case ExtendedOp.PatternOps.MatchAllPatternOp _ -> {
+               writeTag(MatchAllPatternOp);
+           }
+           case ExtendedOp.PatternOps.MatchOp _ -> {
+               writeTag(MatchOp);
+           }
+           case ExtendedOp.PatternOps.RecordPatternOp _ -> {
+               writeTag(RecordPatternOp);
+           }
+           case ExtendedOp.PatternOps.TypePatternOp _ -> {
+               writeTag(TypePatternOp);
+           }
+           default -> {}
+       }
+   }
+
+    private void writeAttributes(Map attributes) {
+        // number of attributes
+        buf.writeU2(attributes.size());
+        for (var attre : attributes.entrySet()) {
+            // attribute name
+            buf.writeIndexOrZero(attre.getKey() == null ? null : buf.constantPool().utf8Entry(attre.getKey()));
+            // attribute value
+            if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(attre.getKey())) {
+                Location loc = switch (attre.getValue()) {
+                    case Location l -> l;
+                    case String s -> Location.fromString(s);
+                    default -> throw new IllegalArgumentException(attre.toString());
+                };
+                writeUtf8EntryOrZero(loc.sourceRef());
+                buf.writeU2(loc.line());
+                buf.writeU2(loc.column());
+            } else {
+                writeUtf8EntryOrZero(attre.getValue());
+            }
+        }
+    }
+
+    private void writeUtf8EntryOrZero(Object o) {
+        buf.writeIndexOrZero(o == null ? null : cp.utf8Entry(o.toString()));
+    }
+
+    private void writeValuesList(List values) {
+        // number of values
+        buf.writeU2(values.size());
+        for (Value v : values) {
+            writeValue(v);
+        }
+    }
+
+    private void writeNestedBodies(List bodies) {
+        // number of bodies
+        buf.writeU2(bodies.size());
+        for (Body body : bodies) {
+            writeNestedBody(body);
+        }
+    }
+
+    private void writeNestedBody(Body body) {
+        // body type
+        buf.writeIndex(toEntry(body.bodyType()));
+        // blocks
+        writeBlocks(body.blocks());
+    }
+
+    private void writeBlocks(List blocks) {
+        // number of blocks - entry block
+        buf.writeU2(blocks.size() - 1);
+        for (Block block : blocks) {
+            // parameters
+            if (block.isEntryBlock()) { // @@@ assumption entry block is the first one
+                for (var bp : block.parameters()) {
+                    valueMap.put(bp, valueMap.size());
+                }
+            } else {
+                writeBlockParameters(block.parameters());
+            }
+            // ops
+            writeOps(block.ops());
+        }
+    }
+
+    private void writeBlockParameters(List parameters) {
+        // number of block parameters
+        buf.writeU2(parameters.size());
+        for (Block.Parameter bp : parameters) {
+            // block parameter type
+            buf.writeIndexOrZero(toEntry(bp.type()));
+            valueMap.put(bp, valueMap.size());
+        }
+    }
+
+    private void writeOps(List ops) {
+        // number of ops - mandatory terminal op
+        buf.writeU2(ops.size() - 1);
+        for (Op op : ops) {
+            // op
+            writeOp(op);
+        }
+    }
+
+    private void writeSuccessors(List successors) {
+        // number of successors
+        buf.writeU2(successors.size());
+        for (Block.Reference succ : successors) {
+            // block index
+            buf.writeU2(succ.targetBlock().index());
+            // arguments
+            writeValuesList(succ.arguments());
+        }
+    }
+
+    private PoolEntry toEntry(FunctionType ftype) {
+        if (ftype.returnType() instanceof JavaType jret
+                && jret.erasure().equals(jret)
+                && ftype.parameterTypes().stream().allMatch(te ->
+                        te instanceof JavaType jt && jt.erasure().equals(jt))) {
+            // prefer to store as method type descriptor
+            return cp.utf8Entry(MethodTypeDesc.of(jret.toNominalDescriptor(), ftype.parameterTypes().stream().map(te -> ((JavaType)te).toNominalDescriptor()).toList()));
+        } else {
+            // fallback
+            return cp.stringEntry(ftype.externalize().toString());
+        }
+    }
+
+    private PoolEntry toEntry(TypeElement type) {
+        if (type.equals(JavaType.VOID)) return null;
+        return type instanceof JavaType jt && jt.erasure().equals(jt)
+                ? cp.utf8Entry(jt.toNominalDescriptor())
+                : cp.stringEntry(type.externalize().toString());
+    }
+
+    private void writeOpWithFixedOperandValues(CodeModelAttribute.OpTag tag, Op op) {
+        writeTag(tag);
+        for (Value v : op.operands()) {
+            buf.writeU2(valueMap.get(v));
+        }
+    }
+
+    private void writeTag(CodeModelAttribute.OpTag tag) {
+        buf.writeU1(tag.ordinal());
+    }
+
+    private void writeValue(Value v) {
+        buf.writeU2(valueMap.get(v));
+    }
+
+    private void writeType(TypeElement type) {
+        buf.writeIndexOrZero(toEntry(type));
+    }
+
+    private void writeTarget(Block.Reference target) {
+        buf.writeU2(target.targetBlock().index());
+        writeValuesList(target.arguments());
+    }
+
+    private void writeCatchers(List catchers) {
+        buf.writeU2(catchers.size());
+        for (var c : catchers) {
+            buf.writeU2(c.targetBlock().index());
+        }
+    }
+}
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java
index 354ad6d00be..90920d8ff61 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java
@@ -1263,7 +1263,7 @@ public JavaForOp body(Consumer c) {
             }
         }
 
-        static final String NAME = "java.for";
+        public static final String NAME = "java.for";
 
         final Body init;
         final Body cond;
@@ -1500,7 +1500,7 @@ public JavaEnhancedForOp body(Consumer c) {
             }
         }
 
-        static final String NAME = "java.enhancedFor";
+        public static final String NAME = "java.enhancedFor";
 
         final Body expression;
         final Body init;
@@ -1870,7 +1870,7 @@ public JavaDoWhileOp.PredicateBuilder body(Consumer c) {
             }
         }
 
-        private static final String NAME = "java.do.while";
+        public static final String NAME = "java.do.while";
 
         private final List bodies;
 

From b123f6a986d3a9a20097f7fcebff57f318a90b54 Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Fri, 29 Nov 2024 12:46:07 +0100
Subject: [PATCH 12/18] Hard-coded CodeModelAttribute work in progress

---
 .../jdk/incubator/code/internal/OpReader.java | 54 +++++++++++--------
 .../jdk/incubator/code/internal/OpWriter.java | 49 +++++++++++------
 .../classes/jdk/incubator/code/op/CoreOp.java | 24 +++++++++
 3 files changed, 91 insertions(+), 36 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
index a3e480a0bc7..c0d1bf64471 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
@@ -44,6 +44,7 @@
 import jdk.incubator.code.op.CoreOp;
 import jdk.incubator.code.op.ExternalizableOp;
 import jdk.incubator.code.type.CoreTypeFactory;
+import jdk.incubator.code.type.FieldRef;
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
 
@@ -56,6 +57,7 @@ final class OpReader {
     OpReader(ClassReader cr, int offset) {
         this.cr = cr;
         this.allValues = new ArrayList<>();
+        allValues.add(null); // 0-index null value
         this.offset = offset;
     }
 
@@ -100,19 +102,31 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
             case ExceptionRegionExit ->
                 CoreOp.exceptionRegionExit(readBlockReference(ancestorBodyBlocks), readCatchers(ancestorBodyBlocks));
             case FieldLoadOp -> {
+                Value receiver = readValue();
+                if (receiver == null) {
+                    CoreOp.fieldLoad(readType(), FieldRef.field(readType(), readUtf8(), readType()));
+                } else {
+                    CoreOp.fieldLoad(readType(), FieldRef.field(readType(), readUtf8(), readType()), receiver);
+                }
             }
             case FieldStoreOp -> {
-            }
-            case FuncCallOp -> {
-            }
-            case FuncOp -> {
-            }
-            case GeOp -> {
-            }
-            case GtOp -> {
-            }
-            case InstanceOfOp -> {
-            }
+                Value receiver = readValue();
+                if (receiver == null) {
+                    CoreOp.fieldStore(FieldRef.field(readType(), readUtf8(), readType()), readValue());
+                } else {
+                    CoreOp.fieldStore(FieldRef.field(readType(), readUtf8(), readType()), receiver, readValue());
+                }
+            }
+            case FuncCallOp ->
+                CoreOp.funcCall(readUtf8(), readType(), readValues());
+            case FuncOp ->
+                CoreOp.func(readUtf8(), readNestedBody(ancestorBody));
+            case GeOp ->
+                CoreOp.ge(readValue(), readValue());
+            case GtOp ->
+                CoreOp.gt(readValue(), readValue());
+            case InstanceOfOp ->
+                CoreOp.instanceOf(readType(), readValue());
             case InvokeOp -> {
             }
             case LambdaOp -> {
@@ -228,16 +242,6 @@ private Map readAttributes()  {
         return attrs;
     }
 
-    private List readValues() {
-        // number of values
-        var values = new Value[readU2()];
-        for (int i = 0; i < values.length; i++) {
-            // value by index
-            values[i] = allValues.get(readU2());
-        }
-        return List.of(values);
-    }
-
     private List readNestedBodies(Body.Builder ancestorBody) {
         // number of bodies
         var bodies = new Body.Builder[readU2()];
@@ -358,6 +362,14 @@ private PoolEntry readEntryOrNull() {
         return e;
     }
 
+    private Value[] readValues() {
+        var values = new Value[readU2()];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = allValues.get(readU2());
+        }
+        return values;
+    }
+
     private Value readValue() {
         return allValues.get(readU2());
     }
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
index b026e9fa099..52189229c76 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
@@ -41,9 +41,9 @@
 import jdk.incubator.code.op.CoreOp;
 import jdk.incubator.code.op.ExtendedOp;
 import jdk.incubator.code.op.ExternalizableOp;
+import jdk.incubator.code.type.FieldRef;
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
-import jdk.incubator.code.type.VarType;
 
 import static jdk.incubator.code.internal.CodeModelAttribute.OpTag.*;
 
@@ -57,6 +57,7 @@ final class OpWriter {
         this.buf = buf;
         this.cp = buf.constantPool();
         this.valueMap = new HashMap<>();
+        valueMap.put(null, 0); // 0-index null value
     }
 
     void writeOp(Op op) {
@@ -127,26 +128,44 @@ void writeOp(Op op) {
                writeTarget(ere.end());
                writeCatchers(ere.catchBlocks());
            }
-           case CoreOp.FieldAccessOp.FieldLoadOp _ -> {
+           case CoreOp.FieldAccessOp.FieldLoadOp flo -> {
                writeTag(FieldLoadOp);
-           }
-           case CoreOp.FieldAccessOp.FieldStoreOp _ -> {
+               writeValue(flo.receiver());
+               writeType(flo.resultType());
+               FieldRef fd = flo.fieldDescriptor();
+               writeType(fd.refType());
+               writeUtf8EntryOrZero(fd.name());
+               writeType(fd.type());
+           }
+           case CoreOp.FieldAccessOp.FieldStoreOp fso -> {
                writeTag(FieldStoreOp);
-           }
-           case CoreOp.FuncCallOp _ -> {
+               writeValue(fso.receiver());
+               writeType(fso.resultType());
+               FieldRef fd = fso.fieldDescriptor();
+               writeType(fd.refType());
+               writeUtf8EntryOrZero(fd.name());
+               writeType(fd.type());
+               writeValue(fso.value());
+           }
+           case CoreOp.FuncCallOp fco -> {
                writeTag(FuncCallOp);
+               writeUtf8EntryOrZero(fco.funcName());
+               writeType(fco.resultType());
+               writeValuesList(fco.operands());
            }
-           case CoreOp.FuncOp _ -> {
+           case CoreOp.FuncOp fo -> {
                writeTag(FuncOp);
-           }
-           case CoreOp.GeOp _ -> {
-               writeTag(GeOp);
-           }
-           case CoreOp.GtOp _ -> {
-               writeTag(GtOp);
-           }
-           case CoreOp.InstanceOfOp _ -> {
+               writeUtf8EntryOrZero(fo.funcName());
+               writeNestedBody(fo.body());
+           }
+           case CoreOp.GeOp _ ->
+               writeOpWithFixedOperandValues(GeOp, op);
+           case CoreOp.GtOp _ ->
+               writeOpWithFixedOperandValues(GtOp, op);
+           case CoreOp.InstanceOfOp ioo -> {
                writeTag(InstanceOfOp);
+               writeType(ioo.type());
+               writeValue(ioo.operands().getFirst());
            }
            case CoreOp.InvokeOp _ -> {
                writeTag(InvokeOp);
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/CoreOp.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/CoreOp.java
index 6f720d6c761..2b6d6cdd79f 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/CoreOp.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/CoreOp.java
@@ -1788,6 +1788,10 @@ public FieldLoadOp transform(CopyContext cc, OpTransformer ot) {
             public TypeElement resultType() {
                 return resultType;
             }
+
+            public Value receiver() {
+                return operands().isEmpty() ? null : operands().getFirst();
+            }
         }
 
         /**
@@ -1843,6 +1847,14 @@ public FieldStoreOp transform(CopyContext cc, OpTransformer ot) {
             public TypeElement resultType() {
                 return JavaType.VOID;
             }
+
+            public Value receiver() {
+                return operands().size() < 2 ? null : operands().getFirst();
+            }
+
+            public Value value() {
+                return operands().getLast();
+            }
         }
     }
 
@@ -3404,6 +3416,18 @@ public static FuncCallOp funcCall(String funcName, FunctionType funcType, Value.
         return funcCall(funcName, funcType, List.of(args));
     }
 
+    /**
+     * Creates a function call operation
+     *
+     * @param funcName the name of the function operation
+     * @param returnType the function return type
+     * @param args     the function arguments
+     * @return the function call operation
+     */
+    public static FuncCallOp funcCall(String funcName, TypeElement returnType, Value... args) {
+        return new FuncCallOp(funcName, returnType, List.of(args));
+    }
+
     /**
      * Creates a function call operation
      *

From 2f289ba9eb6e72bbf494125dbee26e1503018320 Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Mon, 2 Dec 2024 11:00:01 +0100
Subject: [PATCH 13/18] Hard-coded CodeModelAttribute work in progress

---
 .../jdk/incubator/code/internal/OpReader.java | 52 ++++++----
 .../jdk/incubator/code/internal/OpWriter.java | 96 +++++++++++--------
 2 files changed, 93 insertions(+), 55 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
index c0d1bf64471..3600574e4ef 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
@@ -47,6 +47,7 @@
 import jdk.incubator.code.type.FieldRef;
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
+import jdk.incubator.code.type.MethodRef;
 
 final class OpReader {
 
@@ -111,10 +112,11 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
             }
             case FieldStoreOp -> {
                 Value receiver = readValue();
+                FieldRef field = FieldRef.field(readType(), readUtf8(), readType());
                 if (receiver == null) {
-                    CoreOp.fieldStore(FieldRef.field(readType(), readUtf8(), readType()), readValue());
+                    CoreOp.fieldStore(field, readValue());
                 } else {
-                    CoreOp.fieldStore(FieldRef.field(readType(), readUtf8(), readType()), receiver, readValue());
+                    CoreOp.fieldStore(field, receiver, readValue());
                 }
             }
             case FuncCallOp ->
@@ -127,20 +129,26 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
                 CoreOp.gt(readValue(), readValue());
             case InstanceOfOp ->
                 CoreOp.instanceOf(readType(), readValue());
-            case InvokeOp -> {
-            }
-            case LambdaOp -> {
-            }
-            case LeOp -> {
-            }
-            case LshlOp -> {
-            }
-            case LshrOp -> {
-            }
-            case LtOp -> {
-            }
-            case ModOp -> {
-            }
+            case InvokeOp ->
+                CoreOp.invoke(CoreOp.InvokeOp.InvokeKind.values()[readU1()],
+                              readU1() != 0,
+                              readType(),
+                              MethodRef.method(readType(), readUtf8(), readFunctionType()),
+                              List.of(readValues()));
+            case LambdaOp ->
+                CoreOp.lambda(readNestedBody(ancestorBody),
+                              readFunctionType(),
+                              readType());
+            case LeOp ->
+                CoreOp.le(readValue(), readValue());
+            case LshlOp ->
+                CoreOp.lshl(readValue(), readValue());
+            case LshrOp ->
+                CoreOp.lshr(readValue(), readValue());
+            case LtOp ->
+                CoreOp.lt(readValue(), readValue());
+            case ModOp ->
+                CoreOp.mod(readValue(), readValue());
             case ModuleOp -> {
             }
             case MonitorEnterOp -> {
@@ -374,10 +382,22 @@ private Value readValue() {
         return allValues.get(readU2());
     }
 
+    private TypeElement[] readTypes() {
+        var types = new TypeElement[readU2()];
+        for (int i = 0; i < types.length; i++) {
+            types[i] = readType();
+        }
+        return types;
+    }
+
     private TypeElement readType() {
         return toType(readEntryOrNull());
     }
 
+    private FunctionType readFunctionType() {
+        return toFuncType(readEntryOrNull());
+    }
+
     private Block.Reference readBlockReference(Block.Builder[] ancestorBodyBlocks) {
         return ancestorBodyBlocks[readU2()].successor(readValues());
     }
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
index 52189229c76..5ad1216bc01 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
@@ -44,6 +44,7 @@
 import jdk.incubator.code.type.FieldRef;
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
+import jdk.incubator.code.type.MethodRef;
 
 import static jdk.incubator.code.internal.CodeModelAttribute.OpTag.*;
 
@@ -64,19 +65,19 @@ void writeOp(Op op) {
        var operands = op.operands();
        switch (op) {
            case CoreOp.AddOp _ ->
-               writeOpWithFixedOperandValues(AddOp, op);
+               writeOpWithFixedOperandValues(AddOp, operands);
            case CoreOp.AndOp _ ->
-               writeOpWithFixedOperandValues(AndOp, op);
+               writeOpWithFixedOperandValues(AndOp, operands);
            case CoreOp.ArrayAccessOp.ArrayLoadOp _ -> {
-               writeOpWithFixedOperandValues(ArrayLoadOp, op);
+               writeOpWithFixedOperandValues(ArrayLoadOp, operands);
                writeType(op.resultType());
            }
            case CoreOp.ArrayAccessOp.ArrayStoreOp _ ->
-               writeOpWithFixedOperandValues(ArrayStoreOp, op);
+               writeOpWithFixedOperandValues(ArrayStoreOp, operands);
            case CoreOp.ArrayLengthOp _ ->
-               writeOpWithFixedOperandValues(ArrayLengthOp, op);
+               writeOpWithFixedOperandValues(ArrayLengthOp, operands);
            case CoreOp.AshrOp _ ->
-               writeOpWithFixedOperandValues(AshrOp, op);
+               writeOpWithFixedOperandValues(AshrOp, operands);
            case CoreOp.BranchOp bo -> {
                 writeTag(BranchOp);
                 writeTarget(bo.branch());
@@ -85,20 +86,20 @@ void writeOp(Op op) {
                writeTag(CastOp);
                writeType(co.resultType());
                writeType(co.type());
-               writeValue(co.operands().getFirst());
+               writeValue(operands.getFirst());
            }
-           case CoreOp.ClosureCallOp cco -> {
+           case CoreOp.ClosureCallOp _ -> {
                writeTag(ClosureCallOp);
-               writeValuesList(cco.operands());
+               writeValuesList(operands);
            }
            case CoreOp.ClosureOp co -> {
                writeTag(ClosureOp);
                writeNestedBody(co.body());
            }
            case CoreOp.ComplOp _ ->
-               writeOpWithFixedOperandValues(ComplOp, op);
+               writeOpWithFixedOperandValues(ComplOp, operands);
            case CoreOp.ConcatOp _ ->
-               writeOpWithFixedOperandValues(ConcatOp, op);
+               writeOpWithFixedOperandValues(ConcatOp, operands);
            case CoreOp.ConditionalBranchOp cbo -> {
                writeTag(ConditionalBranchOp);
                writeTarget(cbo.trueBranch());
@@ -112,12 +113,12 @@ void writeOp(Op op) {
            case CoreOp.ConvOp _ -> {
                writeTag(ConvOp);
                writeType(op.resultType());
-               writeValue(op.operands().getFirst());
+               writeValue(operands.getFirst());
            }
            case CoreOp.DivOp _ ->
-               writeOpWithFixedOperandValues(DivOp, op);
+               writeOpWithFixedOperandValues(DivOp, operands);
            case CoreOp.EqOp _ ->
-               writeOpWithFixedOperandValues(EqOp, op);
+               writeOpWithFixedOperandValues(EqOp, operands);
            case CoreOp.ExceptionRegionEnter ere -> {
                writeTag(ExceptionRegionEnter);
                writeTarget(ere.start());
@@ -151,7 +152,7 @@ void writeOp(Op op) {
                writeTag(FuncCallOp);
                writeUtf8EntryOrZero(fco.funcName());
                writeType(fco.resultType());
-               writeValuesList(fco.operands());
+               writeValuesList(operands);
            }
            case CoreOp.FuncOp fo -> {
                writeTag(FuncOp);
@@ -159,35 +160,41 @@ void writeOp(Op op) {
                writeNestedBody(fo.body());
            }
            case CoreOp.GeOp _ ->
-               writeOpWithFixedOperandValues(GeOp, op);
+               writeOpWithFixedOperandValues(GeOp, operands);
            case CoreOp.GtOp _ ->
-               writeOpWithFixedOperandValues(GtOp, op);
+               writeOpWithFixedOperandValues(GtOp, operands);
            case CoreOp.InstanceOfOp ioo -> {
                writeTag(InstanceOfOp);
                writeType(ioo.type());
-               writeValue(ioo.operands().getFirst());
+               writeValue(operands.getFirst());
            }
-           case CoreOp.InvokeOp _ -> {
+           case CoreOp.InvokeOp io -> {
                writeTag(InvokeOp);
-           }
-           case CoreOp.LambdaOp _ -> {
+               buf.writeU1(io.invokeKind().ordinal());
+               buf.writeU1(io.isVarArgs() ? 1 : 0);
+               writeType(io.resultType());
+               MethodRef mr = io.invokeDescriptor();
+               writeType(mr.refType());
+               writeUtf8EntryOrZero(mr.name());
+               writeFunctionType(mr.type());
+               writeValuesList(operands);
+           }
+           case CoreOp.LambdaOp lo -> {
                writeTag(LambdaOp);
-           }
-           case CoreOp.LeOp _ -> {
-               writeTag(LeOp);
-           }
-           case CoreOp.LshlOp _ -> {
-               writeTag(LshlOp);
-           }
-           case CoreOp.LshrOp _ -> {
-               writeTag(LshrOp);
-           }
-           case CoreOp.LtOp _ -> {
-               writeTag(LtOp);
-           }
-           case CoreOp.ModOp _ -> {
-               writeTag(ModOp);
-           }
+               writeNestedBody(lo.body());
+               writeFunctionType(lo.invokableType());
+               writeType(lo.functionalInterface());
+           }
+           case CoreOp.LeOp _ ->
+               writeOpWithFixedOperandValues(LeOp, operands);
+           case CoreOp.LshlOp _ ->
+               writeOpWithFixedOperandValues(LshlOp, operands);
+           case CoreOp.LshrOp _ ->
+               writeOpWithFixedOperandValues(LshrOp, operands);
+           case CoreOp.LtOp _ ->
+               writeOpWithFixedOperandValues(LtOp, operands);
+           case CoreOp.ModOp _ ->
+               writeOpWithFixedOperandValues(ModOp, operands);
            case CoreOp.ModuleOp _ -> {
                writeTag(ModuleOp);
            }
@@ -428,9 +435,9 @@ private PoolEntry toEntry(TypeElement type) {
                 : cp.stringEntry(type.externalize().toString());
     }
 
-    private void writeOpWithFixedOperandValues(CodeModelAttribute.OpTag tag, Op op) {
+    private void writeOpWithFixedOperandValues(CodeModelAttribute.OpTag tag, List operands) {
         writeTag(tag);
-        for (Value v : op.operands()) {
+        for (Value v : operands) {
             buf.writeU2(valueMap.get(v));
         }
     }
@@ -447,6 +454,17 @@ private void writeType(TypeElement type) {
         buf.writeIndexOrZero(toEntry(type));
     }
 
+    private void writeFunctionType(FunctionType ftype) {
+        buf.writeIndex(toEntry(ftype));
+    }
+
+    private void writeTypesList(List types) {
+        buf.writeU2(types.size());
+        for (TypeElement type : types) {
+            writeType(type);
+        }
+    }
+
     private void writeTarget(Block.Reference target) {
         buf.writeU2(target.targetBlock().index());
         writeValuesList(target.arguments());

From 5be4e87a197e9fa753aa891a1b39e79689094e4d Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Mon, 2 Dec 2024 17:37:07 +0100
Subject: [PATCH 14/18] Hard-coded CodeModelAttribute work in progress

---
 .../code/internal/CodeModelAttribute.java     |   2 +
 .../jdk/incubator/code/internal/OpReader.java | 197 ++++---
 .../jdk/incubator/code/internal/OpWriter.java | 529 +++++++++---------
 3 files changed, 392 insertions(+), 336 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index 6a4fb422ea8..360527b990b 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -95,6 +95,8 @@
 public class CodeModelAttribute extends CustomAttribute{
 
     public enum OpTag {
+        LocationAttr(null),
+
         AddOp(CoreOp.AddOp.NAME),
         AndOp(CoreOp.AndOp.NAME),
         ArrayLoadOp(CoreOp.ArrayAccessOp.ArrayLoadOp.NAME),
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
index 3600574e4ef..718ed888cbb 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
@@ -42,6 +42,7 @@
 import jdk.incubator.code.TypeElement;
 import jdk.incubator.code.Value;
 import jdk.incubator.code.op.CoreOp;
+import jdk.incubator.code.op.CoreOp.FuncOp;
 import jdk.incubator.code.op.ExternalizableOp;
 import jdk.incubator.code.type.CoreTypeFactory;
 import jdk.incubator.code.type.FieldRef;
@@ -63,7 +64,13 @@ final class OpReader {
     }
 
     Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
-        switch (CodeModelAttribute.OpTag.values()[readU1()]) {
+        int tag = readU1();
+        Location location = Location.NO_LOCATION;
+        if (tag == CodeModelAttribute.OpTag.LocationAttr.ordinal()) { // tag for location
+            location = new Location(readUtf8OrNull(), readU2(), readU2());
+            tag = readU1();
+        }
+        Op op = switch (CodeModelAttribute.OpTag.values()[tag]) {
             case AddOp ->
                 CoreOp.add(readValue(), readValue());
             case AndOp ->
@@ -105,18 +112,18 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
             case FieldLoadOp -> {
                 Value receiver = readValue();
                 if (receiver == null) {
-                    CoreOp.fieldLoad(readType(), FieldRef.field(readType(), readUtf8(), readType()));
+                    yield CoreOp.fieldLoad(readType(), FieldRef.field(readType(), readUtf8(), readType()));
                 } else {
-                    CoreOp.fieldLoad(readType(), FieldRef.field(readType(), readUtf8(), readType()), receiver);
+                    yield CoreOp.fieldLoad(readType(), FieldRef.field(readType(), readUtf8(), readType()), receiver);
                 }
             }
             case FieldStoreOp -> {
                 Value receiver = readValue();
                 FieldRef field = FieldRef.field(readType(), readUtf8(), readType());
                 if (receiver == null) {
-                    CoreOp.fieldStore(field, readValue());
+                    yield CoreOp.fieldStore(field, readValue());
                 } else {
-                    CoreOp.fieldStore(field, receiver, readValue());
+                    yield CoreOp.fieldStore(field, receiver, readValue());
                 }
             }
             case FuncCallOp ->
@@ -136,9 +143,7 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
                               MethodRef.method(readType(), readUtf8(), readFunctionType()),
                               List.of(readValues()));
             case LambdaOp ->
-                CoreOp.lambda(readNestedBody(ancestorBody),
-                              readFunctionType(),
-                              readType());
+                CoreOp.lambda(readType(), readNestedBody(ancestorBody));
             case LeOp ->
                 CoreOp.le(readValue(), readValue());
             case LshlOp ->
@@ -150,87 +155,117 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
             case ModOp ->
                 CoreOp.mod(readValue(), readValue());
             case ModuleOp -> {
-            }
-            case MonitorEnterOp -> {
-            }
-            case MonitorExitOp -> {
-            }
-            case MulOp -> {
-            }
-            case NegOp -> {
-            }
-            case NeqOp -> {
-            }
-            case NewOp -> {
-            }
-            case NotOp -> {
-            }
-            case OrOp -> {
-            }
-            case QuotedOp -> {
-            }
+                var functions = new FuncOp[readU2()];
+                for (int i = 0; i < functions.length; i++) {
+                    functions[i] = CoreOp.func(readUtf8(), readNestedBody(ancestorBody));
+                }
+                yield CoreOp.module(functions);
+            }
+            case MonitorEnterOp ->
+                CoreOp.monitorEnter(readValue());
+            case MonitorExitOp ->
+                CoreOp.monitorExit(readValue());
+            case MulOp ->
+                CoreOp.mul(readValue(), readValue());
+            case NegOp ->
+                CoreOp.neg(readValue());
+            case NeqOp ->
+                CoreOp.neq(readValue(), readValue());
+            case NewOp ->
+                CoreOp._new(readType(), readFunctionType(), readValues());
+            case NotOp ->
+                CoreOp.not(readValue());
+            case OrOp ->
+                CoreOp.or(readValue(), readValue());
+            case QuotedOp ->
+                CoreOp.quoted(readNestedBody(ancestorBody));
             case ReturnOp -> {
+                Value v = readValue();
+                if (v == null) {
+                    yield CoreOp._return();
+                } else {
+                    yield CoreOp._return(v);
+                }
             }
-            case SubOp -> {
-            }
-            case ThrowOp -> {
-            }
-            case TupleLoadOp -> {
-            }
-            case TupleOp -> {
-            }
+            case SubOp ->
+                CoreOp.sub(readValue(), readValue());
+            case ThrowOp ->
+                CoreOp._throw(readValue());
+            case TupleLoadOp ->
+                CoreOp.tupleLoad(readValue(), readU2());
+            case TupleOp ->
+                CoreOp.tuple(readValues());
             case TupleWithOp -> {
-            }
-            case UnreachableOp -> {
-            }
-            case VarLoadOp -> {
-            }
-            case VarStoreOp -> {
-            }
+                Value t = readValue();
+                Value v = readValue();
+                yield CoreOp.tupleWith(t, readU2(), v);
+            }
+            case UnreachableOp ->
+                CoreOp.unreachable();
+            case VarLoadOp ->
+                CoreOp.varLoad(readValue());
+            case VarStoreOp ->
+                CoreOp.varStore(readValue(), readValue());
             case VarOp -> {
+                Value init = readValue();
+                if (init == null) {
+                    yield CoreOp.var(readUtf8OrNull(), readType());
+                } else {
+                    yield CoreOp.var(readUtf8OrNull(), readType(), init);
+                }
             }
-            case XorOp -> {
-            }
+            case XorOp ->
+                CoreOp.xor(readValue(), readValue());
             case YieldOp -> {
+                Value v = readValue();
+                if (v == null) {
+                    yield CoreOp._yield();
+                } else {
+                    yield CoreOp._yield(v);
+                }
             }
-            case JavaBlockOp -> {
-            }
-            case JavaBreakOp -> {
-            }
-            case JavaConditionalAndOp -> {
-            }
-            case JavaConditionalExpressionOp -> {
-            }
-            case JavaConditionalOrOp -> {
-            }
-            case JavaContinueOp -> {
-            }
-            case JavaDoWhileOp -> {
-            }
-            case JavaEnhancedForOp -> {
-            }
-            case JavaForOp -> {
-            }
-            case JavaIfOp -> {
-            }
-            case JavaLabeledOp -> {
-            }
-            case JavaSwitchExpressionOp -> {
-            }
-            case JavaSwitchFallthroughOp -> {
-            }
-            case JavaSwitchStatementOp -> {
-            }
-            case MatchAllPatternOp -> {
-            }
-            case MatchOp -> {
-            }
-            case RecordPatternOp -> {
-            }
-            case TypePatternOp -> {
-            }
+//            case JavaBlockOp -> {
+//            }
+//            case JavaBreakOp -> {
+//            }
+//            case JavaConditionalAndOp -> {
+//            }
+//            case JavaConditionalExpressionOp -> {
+//            }
+//            case JavaConditionalOrOp -> {
+//            }
+//            case JavaContinueOp -> {
+//            }
+//            case JavaDoWhileOp -> {
+//            }
+//            case JavaEnhancedForOp -> {
+//            }
+//            case JavaForOp -> {
+//            }
+//            case JavaIfOp -> {
+//            }
+//            case JavaLabeledOp -> {
+//            }
+//            case JavaSwitchExpressionOp -> {
+//            }
+//            case JavaSwitchFallthroughOp -> {
+//            }
+//            case JavaSwitchStatementOp -> {
+//            }
+//            case MatchAllPatternOp -> {
+//            }
+//            case MatchOp -> {
+//            }
+//            case RecordPatternOp -> {
+//            }
+//            case TypePatternOp -> {
+//            }
+            default -> null;
+        };
+        if (location != Location.NO_LOCATION) {
+            op.setLocation(location);
         }
-        return null;
+        return op;
     }
 
     private Map readAttributes()  {
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
index 5ad1216bc01..950848536d0 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
@@ -39,6 +39,7 @@
 import jdk.incubator.code.TypeElement;
 import jdk.incubator.code.Value;
 import jdk.incubator.code.op.CoreOp;
+import jdk.incubator.code.op.CoreOp.FuncOp;
 import jdk.incubator.code.op.ExtendedOp;
 import jdk.incubator.code.op.ExternalizableOp;
 import jdk.incubator.code.type.FieldRef;
@@ -62,261 +63,279 @@ final class OpWriter {
     }
 
     void writeOp(Op op) {
-       var operands = op.operands();
-       switch (op) {
-           case CoreOp.AddOp _ ->
-               writeOpWithFixedOperandValues(AddOp, operands);
-           case CoreOp.AndOp _ ->
-               writeOpWithFixedOperandValues(AndOp, operands);
-           case CoreOp.ArrayAccessOp.ArrayLoadOp _ -> {
-               writeOpWithFixedOperandValues(ArrayLoadOp, operands);
-               writeType(op.resultType());
-           }
-           case CoreOp.ArrayAccessOp.ArrayStoreOp _ ->
-               writeOpWithFixedOperandValues(ArrayStoreOp, operands);
-           case CoreOp.ArrayLengthOp _ ->
-               writeOpWithFixedOperandValues(ArrayLengthOp, operands);
-           case CoreOp.AshrOp _ ->
-               writeOpWithFixedOperandValues(AshrOp, operands);
-           case CoreOp.BranchOp bo -> {
-                writeTag(BranchOp);
-                writeTarget(bo.branch());
-           }
-           case CoreOp.CastOp co -> {
-               writeTag(CastOp);
-               writeType(co.resultType());
-               writeType(co.type());
-               writeValue(operands.getFirst());
-           }
-           case CoreOp.ClosureCallOp _ -> {
-               writeTag(ClosureCallOp);
-               writeValuesList(operands);
-           }
-           case CoreOp.ClosureOp co -> {
-               writeTag(ClosureOp);
-               writeNestedBody(co.body());
-           }
-           case CoreOp.ComplOp _ ->
-               writeOpWithFixedOperandValues(ComplOp, operands);
-           case CoreOp.ConcatOp _ ->
-               writeOpWithFixedOperandValues(ConcatOp, operands);
-           case CoreOp.ConditionalBranchOp cbo -> {
-               writeTag(ConditionalBranchOp);
-               writeTarget(cbo.trueBranch());
-               writeTarget(cbo.falseBranch());
-           }
-           case CoreOp.ConstantOp co -> {
-               writeTag(ConstantOp);
-               writeType(co.resultType());
-               writeUtf8EntryOrZero(co.value()); // @@@ constant value serialized as Utf8Entry
-           }
-           case CoreOp.ConvOp _ -> {
-               writeTag(ConvOp);
-               writeType(op.resultType());
-               writeValue(operands.getFirst());
-           }
-           case CoreOp.DivOp _ ->
-               writeOpWithFixedOperandValues(DivOp, operands);
-           case CoreOp.EqOp _ ->
-               writeOpWithFixedOperandValues(EqOp, operands);
-           case CoreOp.ExceptionRegionEnter ere -> {
-               writeTag(ExceptionRegionEnter);
-               writeTarget(ere.start());
-               writeCatchers(ere.catchBlocks());
-           }
-           case CoreOp.ExceptionRegionExit ere -> {
-               writeTag(ExceptionRegionExit);
-               writeTarget(ere.end());
-               writeCatchers(ere.catchBlocks());
-           }
-           case CoreOp.FieldAccessOp.FieldLoadOp flo -> {
-               writeTag(FieldLoadOp);
-               writeValue(flo.receiver());
-               writeType(flo.resultType());
-               FieldRef fd = flo.fieldDescriptor();
-               writeType(fd.refType());
-               writeUtf8EntryOrZero(fd.name());
-               writeType(fd.type());
-           }
-           case CoreOp.FieldAccessOp.FieldStoreOp fso -> {
-               writeTag(FieldStoreOp);
-               writeValue(fso.receiver());
-               writeType(fso.resultType());
-               FieldRef fd = fso.fieldDescriptor();
-               writeType(fd.refType());
-               writeUtf8EntryOrZero(fd.name());
-               writeType(fd.type());
-               writeValue(fso.value());
-           }
-           case CoreOp.FuncCallOp fco -> {
-               writeTag(FuncCallOp);
-               writeUtf8EntryOrZero(fco.funcName());
-               writeType(fco.resultType());
-               writeValuesList(operands);
-           }
-           case CoreOp.FuncOp fo -> {
-               writeTag(FuncOp);
-               writeUtf8EntryOrZero(fo.funcName());
-               writeNestedBody(fo.body());
-           }
-           case CoreOp.GeOp _ ->
-               writeOpWithFixedOperandValues(GeOp, operands);
-           case CoreOp.GtOp _ ->
-               writeOpWithFixedOperandValues(GtOp, operands);
-           case CoreOp.InstanceOfOp ioo -> {
-               writeTag(InstanceOfOp);
-               writeType(ioo.type());
-               writeValue(operands.getFirst());
-           }
-           case CoreOp.InvokeOp io -> {
-               writeTag(InvokeOp);
-               buf.writeU1(io.invokeKind().ordinal());
-               buf.writeU1(io.isVarArgs() ? 1 : 0);
-               writeType(io.resultType());
-               MethodRef mr = io.invokeDescriptor();
-               writeType(mr.refType());
-               writeUtf8EntryOrZero(mr.name());
-               writeFunctionType(mr.type());
-               writeValuesList(operands);
-           }
-           case CoreOp.LambdaOp lo -> {
-               writeTag(LambdaOp);
-               writeNestedBody(lo.body());
-               writeFunctionType(lo.invokableType());
-               writeType(lo.functionalInterface());
-           }
-           case CoreOp.LeOp _ ->
-               writeOpWithFixedOperandValues(LeOp, operands);
-           case CoreOp.LshlOp _ ->
-               writeOpWithFixedOperandValues(LshlOp, operands);
-           case CoreOp.LshrOp _ ->
-               writeOpWithFixedOperandValues(LshrOp, operands);
-           case CoreOp.LtOp _ ->
-               writeOpWithFixedOperandValues(LtOp, operands);
-           case CoreOp.ModOp _ ->
-               writeOpWithFixedOperandValues(ModOp, operands);
-           case CoreOp.ModuleOp _ -> {
-               writeTag(ModuleOp);
-           }
-           case CoreOp.MonitorOp.MonitorEnterOp _ -> {
-               writeTag(MonitorEnterOp);
-           }
-           case CoreOp.MonitorOp.MonitorExitOp _ -> {
-               writeTag(MonitorExitOp);
-           }
-           case CoreOp.MulOp _ -> {
-               writeTag(MulOp);
-           }
-           case CoreOp.NegOp _ -> {
-               writeTag(NegOp);
-           }
-           case CoreOp.NeqOp _ -> {
-               writeTag(NeqOp);
-           }
-           case CoreOp.NewOp _ -> {
-               writeTag(NewOp);
-           }
-           case CoreOp.NotOp _ -> {
-               writeTag(NotOp);
-           }
-           case CoreOp.OrOp _ -> {
-               writeTag(OrOp);
-           }
-           case CoreOp.QuotedOp _ -> {
-               writeTag(QuotedOp);
-           }
-           case CoreOp.ReturnOp _ -> {
-               writeTag(ReturnOp);
-           }
-           case CoreOp.SubOp _ -> {
-               writeTag(SubOp);
-           }
-           case CoreOp.ThrowOp _ -> {
-               writeTag(ThrowOp);
-           }
-           case CoreOp.TupleLoadOp _ -> {
-               writeTag(TupleLoadOp);
-           }
-           case CoreOp.TupleOp _ -> {
-               writeTag(TupleOp);
-           }
-           case CoreOp.TupleWithOp _ -> {
-               writeTag(TupleWithOp);
-           }
-           case CoreOp.UnreachableOp _ -> {
-               writeTag(UnreachableOp);
-           }
-           case CoreOp.VarAccessOp.VarLoadOp _ -> {
-               writeTag(VarLoadOp);
-           }
-           case CoreOp.VarAccessOp.VarStoreOp _ -> {
-               writeTag(VarStoreOp);
-           }
-           case CoreOp.VarOp _ -> {
-               writeTag(VarOp);
-           }
-           case CoreOp.XorOp _ -> {
-               writeTag(XorOp);
-           }
-           case CoreOp.YieldOp _ -> {
-               writeTag(YieldOp);
-           }
-           case ExtendedOp.JavaBlockOp _ -> {
-               writeTag(JavaBlockOp);
-           }
-           case ExtendedOp.JavaBreakOp _ -> {
-               writeTag(JavaBreakOp);
-           }
-           case ExtendedOp.JavaConditionalAndOp _ -> {
-               writeTag(JavaConditionalAndOp);
-           }
-           case ExtendedOp.JavaConditionalExpressionOp _ -> {
-               writeTag(JavaConditionalExpressionOp);
-           }
-           case ExtendedOp.JavaConditionalOrOp _ -> {
-               writeTag(JavaConditionalOrOp);
-           }
-           case ExtendedOp.JavaContinueOp _ -> {
-               writeTag(JavaContinueOp);
-           }
-           case ExtendedOp.JavaDoWhileOp _ -> {
-               writeTag(JavaDoWhileOp);
-           }
-           case ExtendedOp.JavaEnhancedForOp _ -> {
-               writeTag(JavaEnhancedForOp);
-           }
-           case ExtendedOp.JavaForOp _ -> {
-               writeTag(JavaForOp);
-           }
-           case ExtendedOp.JavaIfOp _ -> {
-               writeTag(JavaIfOp);
-           }
-           case ExtendedOp.JavaLabeledOp _ -> {
-               writeTag(JavaLabeledOp);
-           }
-           case ExtendedOp.JavaSwitchExpressionOp _ -> {
-               writeTag(JavaSwitchExpressionOp);
-           }
-           case ExtendedOp.JavaSwitchFallthroughOp _ -> {
-               writeTag(JavaSwitchFallthroughOp);
-           }
-           case ExtendedOp.JavaSwitchStatementOp _ -> {
-               writeTag(JavaSwitchStatementOp);
-           }
-           case ExtendedOp.PatternOps.MatchAllPatternOp _ -> {
-               writeTag(MatchAllPatternOp);
-           }
-           case ExtendedOp.PatternOps.MatchOp _ -> {
-               writeTag(MatchOp);
-           }
-           case ExtendedOp.PatternOps.RecordPatternOp _ -> {
-               writeTag(RecordPatternOp);
-           }
-           case ExtendedOp.PatternOps.TypePatternOp _ -> {
-               writeTag(TypePatternOp);
-           }
-           default -> {}
-       }
+        Location location = op.location();
+        if (location != Location.NO_LOCATION) {
+            writeTag(LocationAttr);
+            writeUtf8EntryOrZero(location.sourceRef());
+            buf.writeU2(location.line());
+            buf.writeU2(location.column());
+        }
+        var operands = op.operands();
+        switch (op) {
+            case CoreOp.AddOp _ ->
+                writeOpWithFixedOperandValues(AddOp, operands);
+            case CoreOp.AndOp _ ->
+                writeOpWithFixedOperandValues(AndOp, operands);
+            case CoreOp.ArrayAccessOp.ArrayLoadOp _ -> {
+                writeOpWithFixedOperandValues(ArrayLoadOp, operands);
+                writeType(op.resultType());
+            }
+            case CoreOp.ArrayAccessOp.ArrayStoreOp _ ->
+                writeOpWithFixedOperandValues(ArrayStoreOp, operands);
+            case CoreOp.ArrayLengthOp _ ->
+                writeOpWithFixedOperandValues(ArrayLengthOp, operands);
+            case CoreOp.AshrOp _ ->
+                writeOpWithFixedOperandValues(AshrOp, operands);
+            case CoreOp.BranchOp bo -> {
+                 writeTag(BranchOp);
+                 writeTarget(bo.branch());
+            }
+            case CoreOp.CastOp co -> {
+                writeTag(CastOp);
+                writeType(co.resultType());
+                writeType(co.type());
+                writeValue(operands.getFirst());
+            }
+            case CoreOp.ClosureCallOp _ -> {
+                writeTag(ClosureCallOp);
+                writeValuesList(operands);
+            }
+            case CoreOp.ClosureOp co -> {
+                writeTag(ClosureOp);
+                writeNestedBody(co.body());
+            }
+            case CoreOp.ComplOp _ ->
+                writeOpWithFixedOperandValues(ComplOp, operands);
+            case CoreOp.ConcatOp _ ->
+                writeOpWithFixedOperandValues(ConcatOp, operands);
+            case CoreOp.ConditionalBranchOp cbo -> {
+                writeOpWithFixedOperandValues(ConditionalBranchOp, operands);
+                writeTarget(cbo.trueBranch());
+                writeTarget(cbo.falseBranch());
+            }
+            case CoreOp.ConstantOp co -> {
+                writeTag(ConstantOp);
+                writeType(co.resultType());
+                writeUtf8EntryOrZero(co.value()); // @@@ constant value serialized as Utf8Entry
+            }
+            case CoreOp.ConvOp _ -> {
+                writeTag(ConvOp);
+                writeType(op.resultType());
+                writeValue(operands.getFirst());
+            }
+            case CoreOp.DivOp _ ->
+                writeOpWithFixedOperandValues(DivOp, operands);
+            case CoreOp.EqOp _ ->
+                writeOpWithFixedOperandValues(EqOp, operands);
+            case CoreOp.ExceptionRegionEnter ere -> {
+                writeTag(ExceptionRegionEnter);
+                writeTarget(ere.start());
+                writeCatchers(ere.catchBlocks());
+            }
+            case CoreOp.ExceptionRegionExit ere -> {
+                writeTag(ExceptionRegionExit);
+                writeTarget(ere.end());
+                writeCatchers(ere.catchBlocks());
+            }
+            case CoreOp.FieldAccessOp.FieldLoadOp flo -> {
+                writeTag(FieldLoadOp);
+                writeValue(flo.receiver());
+                writeType(flo.resultType());
+                FieldRef fd = flo.fieldDescriptor();
+                writeType(fd.refType());
+                writeUtf8EntryOrZero(fd.name());
+                writeType(fd.type());
+            }
+            case CoreOp.FieldAccessOp.FieldStoreOp fso -> {
+                writeTag(FieldStoreOp);
+                writeValue(fso.receiver());
+                FieldRef fd = fso.fieldDescriptor();
+                writeType(fd.refType());
+                writeUtf8EntryOrZero(fd.name());
+                writeType(fd.type());
+                writeValue(fso.value());
+            }
+            case CoreOp.FuncCallOp fco -> {
+                writeTag(FuncCallOp);
+                writeUtf8EntryOrZero(fco.funcName());
+                writeType(fco.resultType());
+                writeValuesList(operands);
+            }
+            case CoreOp.FuncOp fo -> {
+                writeTag(FuncOp);
+                writeUtf8EntryOrZero(fo.funcName());
+                writeNestedBody(fo.body());
+            }
+            case CoreOp.GeOp _ ->
+                writeOpWithFixedOperandValues(GeOp, operands);
+            case CoreOp.GtOp _ ->
+                writeOpWithFixedOperandValues(GtOp, operands);
+            case CoreOp.InstanceOfOp ioo -> {
+                writeTag(InstanceOfOp);
+                writeType(ioo.type());
+                writeValue(operands.getFirst());
+            }
+            case CoreOp.InvokeOp io -> {
+                writeTag(InvokeOp);
+                buf.writeU1(io.invokeKind().ordinal());
+                buf.writeU1(io.isVarArgs() ? 1 : 0);
+                writeType(io.resultType());
+                MethodRef mr = io.invokeDescriptor();
+                writeType(mr.refType());
+                writeUtf8EntryOrZero(mr.name());
+                writeFunctionType(mr.type());
+                writeValuesList(operands);
+            }
+            case CoreOp.LambdaOp lo -> {
+                writeTag(LambdaOp);
+                writeType(lo.functionalInterface());
+                writeNestedBody(lo.body());
+            }
+            case CoreOp.LeOp _ ->
+                writeOpWithFixedOperandValues(LeOp, operands);
+            case CoreOp.LshlOp _ ->
+                writeOpWithFixedOperandValues(LshlOp, operands);
+            case CoreOp.LshrOp _ ->
+                writeOpWithFixedOperandValues(LshrOp, operands);
+            case CoreOp.LtOp _ ->
+                writeOpWithFixedOperandValues(LtOp, operands);
+            case CoreOp.ModOp _ ->
+                writeOpWithFixedOperandValues(ModOp, operands);
+            case CoreOp.ModuleOp mo -> {
+                writeTag(ModuleOp);
+                buf.writeU2(mo.functionTable().size());
+                for (FuncOp fo : mo.functionTable().values()) {
+                     writeUtf8EntryOrZero(fo.funcName());
+                     writeNestedBody(fo.body());
+                }
+            }
+            case CoreOp.MonitorOp.MonitorEnterOp _ ->
+                writeOpWithFixedOperandValues(MonitorEnterOp, operands);
+            case CoreOp.MonitorOp.MonitorExitOp _ ->
+                writeOpWithFixedOperandValues(MonitorExitOp, operands);
+            case CoreOp.MulOp _ ->
+                writeOpWithFixedOperandValues(MulOp, operands);
+            case CoreOp.NegOp _ ->
+                writeOpWithFixedOperandValues(NegOp, operands);
+            case CoreOp.NeqOp _ ->
+                writeOpWithFixedOperandValues(NeqOp, operands);
+            case CoreOp.NewOp no -> {
+                writeTag(NewOp);
+                writeType(no.resultType());
+                writeFunctionType(no.constructorType());
+                writeValuesList(no.operands());
+            }
+            case CoreOp.NotOp _ ->
+                writeOpWithFixedOperandValues(NotOp, operands);
+            case CoreOp.OrOp _ ->
+                writeOpWithFixedOperandValues(OrOp, operands);
+            case CoreOp.QuotedOp qo -> {
+                writeTag(QuotedOp);
+                writeNestedBody(qo.bodies().getFirst());
+            }
+            case CoreOp.ReturnOp _ -> {
+                writeTag(ReturnOp);
+                if (operands.isEmpty()) {
+                    writeValue(null);
+                } else {
+                    writeValue(operands.getFirst());
+                }
+            }
+            case CoreOp.SubOp _ ->
+                writeOpWithFixedOperandValues(SubOp, operands);
+            case CoreOp.ThrowOp _ ->
+                writeOpWithFixedOperandValues(ThrowOp, operands);
+            case CoreOp.TupleLoadOp tlo -> {
+                writeOpWithFixedOperandValues(TupleLoadOp, operands);
+                buf.writeU2(tlo.index());
+            }
+            case CoreOp.TupleOp _ ->
+                writeOpWithFixedOperandValues(TupleOp, operands);
+            case CoreOp.TupleWithOp two -> {
+                writeOpWithFixedOperandValues(TupleWithOp, operands);
+                buf.writeU2(two.index());
+            }
+            case CoreOp.UnreachableOp _ ->
+                writeTag(UnreachableOp);
+            case CoreOp.VarAccessOp.VarLoadOp _ ->
+                writeOpWithFixedOperandValues(VarLoadOp, operands);
+            case CoreOp.VarAccessOp.VarStoreOp _ ->
+                writeOpWithFixedOperandValues(VarStoreOp, operands);
+            case CoreOp.VarOp vo -> {
+                writeTag(VarOp);
+                if (vo.isUninitialized()) {
+                    writeValue(null);
+                } else {
+                    writeValue(vo.initOperand());
+                }
+                writeUtf8EntryOrZero(vo.varName());
+                writeType(vo.varValueType());
+            }
+            case CoreOp.XorOp _ ->
+                writeOpWithFixedOperandValues(XorOp, operands);
+            case CoreOp.YieldOp yo -> {
+                writeTag(YieldOp);
+                writeValue(yo.yieldValue());
+            }
+            case ExtendedOp.JavaBlockOp _ -> {
+                writeTag(JavaBlockOp);
+            }
+            case ExtendedOp.JavaBreakOp _ -> {
+                writeTag(JavaBreakOp);
+            }
+            case ExtendedOp.JavaConditionalAndOp _ -> {
+                writeTag(JavaConditionalAndOp);
+            }
+            case ExtendedOp.JavaConditionalExpressionOp _ -> {
+                writeTag(JavaConditionalExpressionOp);
+            }
+            case ExtendedOp.JavaConditionalOrOp _ -> {
+                writeTag(JavaConditionalOrOp);
+            }
+            case ExtendedOp.JavaContinueOp _ -> {
+                writeTag(JavaContinueOp);
+            }
+            case ExtendedOp.JavaDoWhileOp _ -> {
+                writeTag(JavaDoWhileOp);
+            }
+            case ExtendedOp.JavaEnhancedForOp _ -> {
+                writeTag(JavaEnhancedForOp);
+            }
+            case ExtendedOp.JavaForOp _ -> {
+                writeTag(JavaForOp);
+            }
+            case ExtendedOp.JavaIfOp _ -> {
+                writeTag(JavaIfOp);
+            }
+            case ExtendedOp.JavaLabeledOp _ -> {
+                writeTag(JavaLabeledOp);
+            }
+            case ExtendedOp.JavaSwitchExpressionOp _ -> {
+                writeTag(JavaSwitchExpressionOp);
+            }
+            case ExtendedOp.JavaSwitchFallthroughOp _ -> {
+                writeTag(JavaSwitchFallthroughOp);
+            }
+            case ExtendedOp.JavaSwitchStatementOp _ -> {
+                writeTag(JavaSwitchStatementOp);
+            }
+            case ExtendedOp.PatternOps.MatchAllPatternOp _ -> {
+                writeTag(MatchAllPatternOp);
+            }
+            case ExtendedOp.PatternOps.MatchOp _ -> {
+                writeTag(MatchOp);
+            }
+            case ExtendedOp.PatternOps.RecordPatternOp _ -> {
+                writeTag(RecordPatternOp);
+            }
+            case ExtendedOp.PatternOps.TypePatternOp _ -> {
+                writeTag(TypePatternOp);
+            }
+            default -> {}
+        }
+        if (op.result() != null) {
+            valueMap.put(op.result(), valueMap.size());
+        }
    }
 
     private void writeAttributes(Map attributes) {

From 8a83e962955dd9c2b2a68dd43b16c6c8d3ffa885 Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Wed, 4 Dec 2024 11:21:07 +0100
Subject: [PATCH 15/18] Hard-coded CodeModelAttribute work in progress

---
 .../code/internal/CodeModelAttribute.java     |   4 +
 .../jdk/incubator/code/internal/OpReader.java | 130 ++++++++----------
 .../jdk/incubator/code/internal/OpWriter.java |  96 +++++++++----
 .../jdk/incubator/code/op/ExtendedOp.java     |   4 +-
 4 files changed, 134 insertions(+), 100 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index 360527b990b..2aa4e15df92 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -167,6 +167,10 @@ public enum OpTag {
         JavaSwitchExpressionOp(ExtendedOp.JavaSwitchExpressionOp.NAME),
         JavaSwitchFallthroughOp(ExtendedOp.JavaSwitchFallthroughOp.NAME),
         JavaSwitchStatementOp(ExtendedOp.JavaSwitchStatementOp.NAME),
+        JavaSynchronizedOp(ExtendedOp.JavaSynchronizedOp.NAME),
+        JavaTryOp(ExtendedOp.JavaTryOp.NAME),
+        JavaYieldOp(ExtendedOp.JavaYieldOp.NAME),
+        JavaWhileOp(ExtendedOp.JavaWhileOp.NAME),
         MatchAllPatternOp(ExtendedOp.PatternOps.MatchAllPatternOp.NAME),
         MatchOp(ExtendedOp.PatternOps.MatchOp.NAME),
         RecordPatternOp(ExtendedOp.PatternOps.RecordPatternOp.NAME),
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
index 718ed888cbb..c1dcbd21d1d 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
@@ -32,9 +32,7 @@
 import java.lang.constant.ClassDesc;
 import java.lang.constant.MethodTypeDesc;
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 import jdk.incubator.code.Block;
 import jdk.incubator.code.Body;
 import jdk.incubator.code.Location;
@@ -43,12 +41,13 @@
 import jdk.incubator.code.Value;
 import jdk.incubator.code.op.CoreOp;
 import jdk.incubator.code.op.CoreOp.FuncOp;
-import jdk.incubator.code.op.ExternalizableOp;
+import jdk.incubator.code.op.ExtendedOp;
 import jdk.incubator.code.type.CoreTypeFactory;
 import jdk.incubator.code.type.FieldRef;
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
 import jdk.incubator.code.type.MethodRef;
+import jdk.incubator.code.type.RecordTypeRef;
 
 final class OpReader {
 
@@ -224,43 +223,57 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
                     yield CoreOp._yield(v);
                 }
             }
-//            case JavaBlockOp -> {
-//            }
-//            case JavaBreakOp -> {
-//            }
-//            case JavaConditionalAndOp -> {
-//            }
-//            case JavaConditionalExpressionOp -> {
-//            }
-//            case JavaConditionalOrOp -> {
-//            }
-//            case JavaContinueOp -> {
-//            }
-//            case JavaDoWhileOp -> {
-//            }
-//            case JavaEnhancedForOp -> {
-//            }
-//            case JavaForOp -> {
-//            }
-//            case JavaIfOp -> {
-//            }
-//            case JavaLabeledOp -> {
-//            }
-//            case JavaSwitchExpressionOp -> {
-//            }
-//            case JavaSwitchFallthroughOp -> {
-//            }
-//            case JavaSwitchStatementOp -> {
-//            }
-//            case MatchAllPatternOp -> {
-//            }
-//            case MatchOp -> {
-//            }
-//            case RecordPatternOp -> {
-//            }
-//            case TypePatternOp -> {
-//            }
-            default -> null;
+            case JavaBlockOp ->
+                ExtendedOp.block(readNestedBody(ancestorBody));
+            case JavaBreakOp ->
+                ExtendedOp._break(readValue());
+            case JavaConditionalAndOp ->
+                ExtendedOp.conditionalAnd(readNestedBodies(ancestorBody));
+            case JavaConditionalExpressionOp ->
+                ExtendedOp.conditionalExpression(readType(), readNestedBodies(ancestorBody));
+            case JavaConditionalOrOp ->
+                ExtendedOp.conditionalOr(readNestedBodies(ancestorBody));
+            case JavaContinueOp ->
+                ExtendedOp._continue(readValue());
+            case JavaDoWhileOp ->
+                ExtendedOp.doWhile(readNestedBody(ancestorBody), readNestedBody(ancestorBody));
+            case JavaEnhancedForOp ->
+                ExtendedOp.enhancedFor(readNestedBody(ancestorBody), readNestedBody(ancestorBody), readNestedBody(ancestorBody));
+            case JavaForOp ->
+                ExtendedOp._for(readNestedBody(ancestorBody), readNestedBody(ancestorBody), readNestedBody(ancestorBody), readNestedBody(ancestorBody));
+            case JavaIfOp ->
+                ExtendedOp._if(readNestedBodies(ancestorBody));
+            case JavaLabeledOp ->
+                ExtendedOp.labeled(readNestedBody(ancestorBody));
+            case JavaSwitchExpressionOp ->
+                ExtendedOp.switchExpression(readType(), readValue(), readNestedBodies(ancestorBody));
+            case JavaSwitchFallthroughOp ->
+                ExtendedOp.switchFallthroughOp();
+            case JavaSwitchStatementOp ->
+                ExtendedOp.switchStatement(readValue(), readNestedBodies(ancestorBody));
+            case JavaSynchronizedOp ->
+                ExtendedOp.synchronized_(readNestedBody(ancestorBody), readNestedBody(ancestorBody));
+            case JavaTryOp ->
+                ExtendedOp._try(readNestedBody(ancestorBody), readNestedBody(ancestorBody), readNestedBodies(ancestorBody), readNestedBody(ancestorBody));
+            case JavaYieldOp -> {
+                Value v = readValue();
+                if (v == null) {
+                    yield ExtendedOp.java_yield();
+                } else {
+                    yield ExtendedOp.java_yield(v);
+                }
+            }
+            case JavaWhileOp ->
+                ExtendedOp._while(readNestedBody(ancestorBody), readNestedBody(ancestorBody));
+            case MatchAllPatternOp ->
+                ExtendedOp.matchAllPattern();
+            case MatchOp ->
+                ExtendedOp.match(readValue(), readNestedBody(ancestorBody), readNestedBody(ancestorBody));
+            case RecordPatternOp ->
+                ExtendedOp.recordPattern(RecordTypeRef.recordType(readType(), readRecordComponents()), readValues());
+            case TypePatternOp ->
+                ExtendedOp.typePattern(readType(), readUtf8());
+            default -> throw new UnsupportedOperationException("tag: " + tag);
         };
         if (location != Location.NO_LOCATION) {
             op.setLocation(location);
@@ -268,23 +281,6 @@ Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
         return op;
     }
 
-    private Map readAttributes()  {
-        // number of attributes
-        int size = readU2();
-        var attrs = new LinkedHashMap(size);
-        for (int i = 0; i < size; i++) {
-            // attribute name
-            String name = readUtf8OrNull();
-            // attribute value
-            if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(name)) {
-                attrs.put(name, new Location(readUtf8OrNull(), readU2(), readU2()));
-            } else {
-                attrs.put(name, readUtf8OrNull());
-            }
-        }
-        return attrs;
-    }
-
     private List readNestedBodies(Body.Builder ancestorBody) {
         // number of bodies
         var bodies = new Body.Builder[readU2()];
@@ -295,7 +291,9 @@ private List readNestedBodies(Body.Builder ancestorBody) {
     }
 
     private Body.Builder readNestedBody(Body.Builder ancestorBody) {
-        var bb = Body.Builder.of(ancestorBody, toFuncType(readEntryOrNull()));
+        var type = readEntryOrNull();
+        if (type == null) return null;
+        var bb = Body.Builder.of(ancestorBody, toFuncType(type));
         readBlocks(bb);
         return bb;
     }
@@ -339,16 +337,6 @@ private void readOps(Block.Builder bb, Block.Builder[] allBlocks) {
         }
     }
 
-    private List readSuccessors(Block.Builder[] ancestorBodyBlocks) {
-        // number of successors
-        var refs = new Block.Reference[readU2()];
-        for (int i = 0; i < refs.length; i++) {
-            // block from index + arguments
-            refs[i] = ancestorBodyBlocks[readU2()].successor(readValues());
-        }
-        return List.of(refs);
-    }
-
     private static FunctionType toFuncType(PoolEntry entry) {
         return switch (entry) {
             case Utf8Entry ue -> {
@@ -417,10 +405,10 @@ private Value readValue() {
         return allValues.get(readU2());
     }
 
-    private TypeElement[] readTypes() {
-        var types = new TypeElement[readU2()];
+    private RecordTypeRef.ComponentRef[] readRecordComponents() {
+        var types = new RecordTypeRef.ComponentRef[readU2()];
         for (int i = 0; i < types.length; i++) {
-            types[i] = readType();
+            types[i] = new RecordTypeRef.ComponentRef(readType(), readUtf8());
         }
         return types;
     }
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
index 950848536d0..161a1b5d4f5 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
@@ -46,6 +46,7 @@
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
 import jdk.incubator.code.type.MethodRef;
+import jdk.incubator.code.type.RecordTypeRef;
 
 import static jdk.incubator.code.internal.CodeModelAttribute.OpTag.*;
 
@@ -277,61 +278,95 @@ void writeOp(Op op) {
                 writeTag(YieldOp);
                 writeValue(yo.yieldValue());
             }
-            case ExtendedOp.JavaBlockOp _ -> {
+            case ExtendedOp.JavaBlockOp jbo -> {
                 writeTag(JavaBlockOp);
+                writeNestedBody(jbo.body());
             }
             case ExtendedOp.JavaBreakOp _ -> {
                 writeTag(JavaBreakOp);
+                writeValue(operands.isEmpty() ? null : operands.getFirst());
             }
             case ExtendedOp.JavaConditionalAndOp _ -> {
                 writeTag(JavaConditionalAndOp);
+                writeNestedBodies(op.bodies());
             }
             case ExtendedOp.JavaConditionalExpressionOp _ -> {
                 writeTag(JavaConditionalExpressionOp);
+                writeType(op.resultType());
+                writeNestedBodies(op.bodies());
             }
             case ExtendedOp.JavaConditionalOrOp _ -> {
                 writeTag(JavaConditionalOrOp);
+                writeNestedBodies(op.bodies());
             }
             case ExtendedOp.JavaContinueOp _ -> {
                 writeTag(JavaContinueOp);
-            }
-            case ExtendedOp.JavaDoWhileOp _ -> {
-                writeTag(JavaDoWhileOp);
-            }
-            case ExtendedOp.JavaEnhancedForOp _ -> {
-                writeTag(JavaEnhancedForOp);
-            }
-            case ExtendedOp.JavaForOp _ -> {
-                writeTag(JavaForOp);
-            }
+                writeValue(operands.isEmpty() ? null : operands.getFirst());
+            }
+            case ExtendedOp.JavaDoWhileOp _ ->
+                writeOpWithFixedNestedBodies(JavaDoWhileOp, op);
+            case ExtendedOp.JavaEnhancedForOp _ ->
+                writeOpWithFixedNestedBodies(JavaEnhancedForOp, op);
+            case ExtendedOp.JavaForOp _ ->
+                writeOpWithFixedNestedBodies(JavaForOp, op);
             case ExtendedOp.JavaIfOp _ -> {
                 writeTag(JavaIfOp);
+                writeNestedBodies(op.bodies());
             }
-            case ExtendedOp.JavaLabeledOp _ -> {
-                writeTag(JavaLabeledOp);
-            }
+            case ExtendedOp.JavaLabeledOp _ ->
+                writeOpWithFixedNestedBodies(JavaLabeledOp, op);
             case ExtendedOp.JavaSwitchExpressionOp _ -> {
                 writeTag(JavaSwitchExpressionOp);
+                writeType(op.resultType());
+                writeValue(operands.getFirst());
+                writeNestedBodies(op.bodies());
             }
-            case ExtendedOp.JavaSwitchFallthroughOp _ -> {
+            case ExtendedOp.JavaSwitchFallthroughOp _ ->
                 writeTag(JavaSwitchFallthroughOp);
-            }
             case ExtendedOp.JavaSwitchStatementOp _ -> {
-                writeTag(JavaSwitchStatementOp);
-            }
-            case ExtendedOp.PatternOps.MatchAllPatternOp _ -> {
+                writeOpWithFixedOperandValues(JavaSwitchStatementOp, operands);
+                writeNestedBodies(op.bodies());
+            }
+            case ExtendedOp.JavaSynchronizedOp _ ->
+                writeOpWithFixedNestedBodies(JavaSynchronizedOp, op);
+            case ExtendedOp.JavaTryOp jto -> {
+                writeTag(JavaTryOp);
+                writeNestedBody(jto.resources());
+                writeNestedBody(jto.body());
+                writeNestedBodies(jto.catchers());
+                writeNestedBody(jto.finalizer());
+            }
+            case ExtendedOp.JavaYieldOp _ -> {
+                writeTag(JavaYieldOp);
+                writeValue(operands.isEmpty() ? null : operands.getFirst());
+            }
+            case ExtendedOp.JavaWhileOp _ ->
+                writeOpWithFixedNestedBodies(JavaWhileOp, op);
+            case ExtendedOp.PatternOps.MatchAllPatternOp _ ->
                 writeTag(MatchAllPatternOp);
+            case ExtendedOp.PatternOps.MatchOp mo -> {
+                writeOpWithFixedOperandValues(MatchOp, operands);
+                writeNestedBody(mo.pattern());
+                writeNestedBody(mo.match());
             }
-            case ExtendedOp.PatternOps.MatchOp _ -> {
-                writeTag(MatchOp);
-            }
-            case ExtendedOp.PatternOps.RecordPatternOp _ -> {
+            case ExtendedOp.PatternOps.RecordPatternOp rpo -> {
                 writeTag(RecordPatternOp);
+                RecordTypeRef rd = rpo.recordDescriptor();
+                writeType(rd.recordType());
+                buf.writeU2(rd.components().size());
+                for (RecordTypeRef.ComponentRef rc : rd.components()) {
+                    writeType(rc.type());
+                    writeUtf8EntryOrZero(rc.name());
+                }
+                 writeUtf8EntryOrZero(rpo.recordDescriptor().toString());
             }
-            case ExtendedOp.PatternOps.TypePatternOp _ -> {
+            case ExtendedOp.PatternOps.TypePatternOp tpo -> {
                 writeTag(TypePatternOp);
+                writeType(tpo.targetType());
+                writeUtf8EntryOrZero(tpo.bindingName());
             }
-            default -> {}
+            default ->
+                throw new IllegalArgumentException(op.toText());
         }
         if (op.result() != null) {
             valueMap.put(op.result(), valueMap.size());
@@ -382,9 +417,9 @@ private void writeNestedBodies(List bodies) {
 
     private void writeNestedBody(Body body) {
         // body type
-        buf.writeIndex(toEntry(body.bodyType()));
+        buf.writeIndexOrZero(body == null ? null : toEntry(body.bodyType()));
         // blocks
-        writeBlocks(body.blocks());
+        if (body != null) writeBlocks(body.blocks());
     }
 
     private void writeBlocks(List blocks) {
@@ -461,6 +496,13 @@ private void writeOpWithFixedOperandValues(CodeModelAttribute.OpTag tag, List c) {
             }
         }
 
-        private static final String NAME = "java.while";
+        public static final String NAME = "java.while";
 
         private final List bodies;
 
@@ -2366,7 +2366,7 @@ public JavaTryOp noFinalizer() {
             }
         }
 
-        static final String NAME = "java.try";
+        public static final String NAME = "java.try";
 
         final Body resources;
         final Body body;

From cc859f020695aac1bfdca2db1722cc43a7bd8989 Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Wed, 4 Dec 2024 11:49:01 +0100
Subject: [PATCH 16/18] code cleanup

---
 .../code/internal/CodeModelAttribute.java     | 164 ++----------------
 .../jdk/incubator/code/internal/OpReader.java |   4 +-
 .../jdk/incubator/code/internal/OpWriter.java |  49 +-----
 3 files changed, 23 insertions(+), 194 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index 2aa4e15df92..c24c2f40de2 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -31,155 +31,25 @@
 import java.lang.classfile.ClassReader;
 import java.lang.classfile.CustomAttribute;
 import jdk.incubator.code.Op;
-import jdk.incubator.code.op.CoreOp;
-import jdk.incubator.code.op.ExtendedOp;
-
-/**
- * 
- * CodeModel_attribute {
- *     u2 attribute_name_index;
- *     u4 attribute_length;
- *     op_info;
- * }
- *
- * op_info {
- *     u2 op_name_index;
- *     u2 op_operands_length;
- *     u2 op_operands[op_operands_length];
- *     u2 op_result_type_index;
- *     u2 op_attributes_length;
- *     op_attribute_info op_attributes_table[op_attributes_length];
- *     u2 nested_bodies_length;
- *     {   u2 body_func_type_index;
- *         block_content_info; // entry block
- *         u2 blocks_length;
- *         {   u2 block_parameters_length;
- *             u2 block_parameter_type_index[block_parameters_length];
- *             block_content_info;
- *         } blocks_table[blocks_length];
- *     } nested_bodies_table[nested_bodies_length];
- * }
- *
- * union op_attribute_info {
- *     value_attribute_info;
- *     location_attribute_info;
- * }
- *
- * value_attribute_info {
- *     u2 attribute_name_index;
- *     u2 attribute_value_index;
- * }
- *
- * location_attribute_info {
- *     u2 location_attribute_name_index;
- *     u2 source_index;
- *     u2 line_number;
- *     u2 column_number;
- * }
- *
- * block_content_info {
- *     u2 ops_length;
- *     op_info ops_table[ops_length];
- *     terminal_op_info;
- * } blocks_table[blocks_length];
- *
- * terminal_op_info {
- *     op_info;
- *     u2 successors_length;
- *     {   u2 successor_block_index;
- *         u2 block_arguments_length;
- *         u2 block_arguments[block_arguments_length];
- *     } successors_table[successors_length]
- * }
- */
+
 public class CodeModelAttribute extends CustomAttribute{
 
-    public enum OpTag {
-        LocationAttr(null),
-
-        AddOp(CoreOp.AddOp.NAME),
-        AndOp(CoreOp.AndOp.NAME),
-        ArrayLoadOp(CoreOp.ArrayAccessOp.ArrayLoadOp.NAME),
-        ArrayStoreOp(CoreOp.ArrayAccessOp.ArrayStoreOp.NAME),
-        ArrayLengthOp(CoreOp.ArrayLengthOp.NAME),
-        AshrOp(CoreOp.AshrOp.NAME),
-        AssertOp(CoreOp.AssertOp.NAME),
-        BranchOp(CoreOp.BranchOp.NAME),
-        CastOp(CoreOp.CastOp.NAME),
-        ClosureCallOp(CoreOp.ClosureCallOp.NAME),
-        ClosureOp(CoreOp.ClosureOp.NAME),
-        ComplOp(CoreOp.ComplOp.NAME),
-        ConcatOp(CoreOp.ConcatOp.NAME),
-        ConditionalBranchOp(CoreOp.ConditionalBranchOp.NAME),
-        ConstantOp(CoreOp.ConstantOp.NAME),
-        ConvOp(CoreOp.ConvOp.NAME),
-        DivOp(CoreOp.DivOp.NAME),
-        EqOp(CoreOp.EqOp.NAME),
-        ExceptionRegionEnter(CoreOp.ExceptionRegionEnter.NAME),
-        ExceptionRegionExit(CoreOp.ExceptionRegionExit.NAME),
-        FieldLoadOp(CoreOp.FieldAccessOp.FieldLoadOp.NAME),
-        FieldStoreOp(CoreOp.FieldAccessOp.FieldStoreOp.NAME),
-        FuncCallOp(CoreOp.FuncCallOp.NAME),
-        FuncOp(CoreOp.FuncOp.NAME),
-        GeOp(CoreOp.GeOp.NAME),
-        GtOp(CoreOp.GtOp.NAME),
-        InstanceOfOp(CoreOp.InstanceOfOp.NAME),
-        InvokeOp(CoreOp.InvokeOp.NAME),
-        LambdaOp(CoreOp.LambdaOp.NAME),
-        LeOp(CoreOp.LeOp.NAME),
-        LshlOp(CoreOp.LshlOp.NAME),
-        LshrOp(CoreOp.LshrOp.NAME),
-        LtOp(CoreOp.LtOp.NAME),
-        ModOp(CoreOp.ModOp.NAME),
-        ModuleOp(CoreOp.ModuleOp.NAME),
-        MonitorEnterOp(CoreOp.MonitorOp.MonitorEnterOp.NAME),
-        MonitorExitOp(CoreOp.MonitorOp.MonitorExitOp.NAME),
-        MulOp(CoreOp.MulOp.NAME),
-        NegOp(CoreOp.NegOp.NAME),
-        NeqOp(CoreOp.NeqOp.NAME),
-        NewOp(CoreOp.NewOp.NAME),
-        NotOp(CoreOp.NotOp.NAME),
-        OrOp(CoreOp.OrOp.NAME),
-        QuotedOp(CoreOp.QuotedOp.NAME),
-        ReturnOp(CoreOp.ReturnOp.NAME),
-        SubOp(CoreOp.SubOp.NAME),
-        ThrowOp(CoreOp.ThrowOp.NAME),
-        TupleLoadOp(CoreOp.TupleLoadOp.NAME),
-        TupleOp(CoreOp.TupleOp.NAME),
-        TupleWithOp(CoreOp.TupleWithOp.NAME),
-        UnreachableOp(CoreOp.UnreachableOp.NAME),
-        VarLoadOp(CoreOp.VarAccessOp.VarLoadOp.NAME),
-        VarStoreOp(CoreOp.VarAccessOp.VarStoreOp.NAME),
-        VarOp(CoreOp.VarOp.NAME),
-        XorOp(CoreOp.XorOp.NAME),
-        YieldOp(CoreOp.YieldOp.NAME),
-        JavaBlockOp(ExtendedOp.JavaBlockOp.NAME),
-        JavaBreakOp(ExtendedOp.JavaBreakOp.NAME),
-        JavaConditionalAndOp(ExtendedOp.JavaConditionalAndOp.NAME),
-        JavaConditionalExpressionOp(ExtendedOp.JavaConditionalExpressionOp.NAME),
-        JavaConditionalOrOp(ExtendedOp.JavaConditionalOrOp.NAME),
-        JavaContinueOp(ExtendedOp.JavaContinueOp.NAME),
-        JavaDoWhileOp(ExtendedOp.JavaDoWhileOp.NAME),
-        JavaEnhancedForOp(ExtendedOp.JavaEnhancedForOp.NAME),
-        JavaForOp(ExtendedOp.JavaForOp.NAME),
-        JavaIfOp(ExtendedOp.JavaIfOp.NAME),
-        JavaLabeledOp(ExtendedOp.JavaLabeledOp.NAME),
-        JavaSwitchExpressionOp(ExtendedOp.JavaSwitchExpressionOp.NAME),
-        JavaSwitchFallthroughOp(ExtendedOp.JavaSwitchFallthroughOp.NAME),
-        JavaSwitchStatementOp(ExtendedOp.JavaSwitchStatementOp.NAME),
-        JavaSynchronizedOp(ExtendedOp.JavaSynchronizedOp.NAME),
-        JavaTryOp(ExtendedOp.JavaTryOp.NAME),
-        JavaYieldOp(ExtendedOp.JavaYieldOp.NAME),
-        JavaWhileOp(ExtendedOp.JavaWhileOp.NAME),
-        MatchAllPatternOp(ExtendedOp.PatternOps.MatchAllPatternOp.NAME),
-        MatchOp(ExtendedOp.PatternOps.MatchOp.NAME),
-        RecordPatternOp(ExtendedOp.PatternOps.RecordPatternOp.NAME),
-        TypePatternOp(ExtendedOp.PatternOps.TypePatternOp.NAME);
-
-        final String opName;
-        OpTag(String opName) {
-            this.opName = opName;
-        }
+    public enum Tag {
+        LocationAttr,
+
+        // CoreOp
+        AddOp, AndOp, ArrayLoadOp, ArrayStoreOp, ArrayLengthOp, AshrOp, AssertOp, BranchOp, CastOp, ClosureCallOp,
+        ClosureOp, ComplOp, ConcatOp, ConditionalBranchOp, ConstantOp, ConvOp, DivOp, EqOp, ExceptionRegionEnter,
+        ExceptionRegionExit, FieldLoadOp, FieldStoreOp, FuncCallOp, FuncOp, GeOp, GtOp, InstanceOfOp, InvokeOp,
+        LambdaOp, LeOp, LshlOp, LshrOp, LtOp, ModOp, ModuleOp, MonitorEnterOp, MonitorExitOp, MulOp, NegOp, NeqOp,
+        NewOp, NotOp, OrOp, QuotedOp, ReturnOp, SubOp, ThrowOp, TupleLoadOp, TupleOp, TupleWithOp, UnreachableOp,
+        VarLoadOp, VarStoreOp, VarOp, XorOp, YieldOp,
+
+        // ExtendedOp
+        JavaBlockOp, JavaBreakOp, JavaConditionalAndOp, JavaConditionalExpressionOp, JavaConditionalOrOp,
+        JavaContinueOp, JavaDoWhileOp, JavaEnhancedForOp, JavaForOp, JavaIfOp, JavaLabeledOp, JavaSwitchExpressionOp,
+        JavaSwitchFallthroughOp, JavaSwitchStatementOp, JavaSynchronizedOp, JavaTryOp, JavaYieldOp, JavaWhileOp,
+        MatchAllPatternOp, MatchOp, RecordPatternOp, TypePatternOp;
     }
 
     public static final String NAME = "CodeModel";
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
index c1dcbd21d1d..33496242134 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpReader.java
@@ -65,11 +65,11 @@ final class OpReader {
     Op readOp(Body.Builder ancestorBody, Block.Builder[] ancestorBodyBlocks) {
         int tag = readU1();
         Location location = Location.NO_LOCATION;
-        if (tag == CodeModelAttribute.OpTag.LocationAttr.ordinal()) { // tag for location
+        if (tag == CodeModelAttribute.Tag.LocationAttr.ordinal()) { // tag for location
             location = new Location(readUtf8OrNull(), readU2(), readU2());
             tag = readU1();
         }
-        Op op = switch (CodeModelAttribute.OpTag.values()[tag]) {
+        Op op = switch (CodeModelAttribute.Tag.values()[tag]) {
             case AddOp ->
                 CoreOp.add(readValue(), readValue());
             case AndOp ->
diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
index 161a1b5d4f5..7ba9e99d9fd 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/OpWriter.java
@@ -41,14 +41,13 @@
 import jdk.incubator.code.op.CoreOp;
 import jdk.incubator.code.op.CoreOp.FuncOp;
 import jdk.incubator.code.op.ExtendedOp;
-import jdk.incubator.code.op.ExternalizableOp;
 import jdk.incubator.code.type.FieldRef;
 import jdk.incubator.code.type.FunctionType;
 import jdk.incubator.code.type.JavaType;
 import jdk.incubator.code.type.MethodRef;
 import jdk.incubator.code.type.RecordTypeRef;
 
-import static jdk.incubator.code.internal.CodeModelAttribute.OpTag.*;
+import static jdk.incubator.code.internal.CodeModelAttribute.Tag.*;
 
 final class OpWriter {
 
@@ -373,28 +372,6 @@ void writeOp(Op op) {
         }
    }
 
-    private void writeAttributes(Map attributes) {
-        // number of attributes
-        buf.writeU2(attributes.size());
-        for (var attre : attributes.entrySet()) {
-            // attribute name
-            buf.writeIndexOrZero(attre.getKey() == null ? null : buf.constantPool().utf8Entry(attre.getKey()));
-            // attribute value
-            if (ExternalizableOp.ATTRIBUTE_LOCATION.equals(attre.getKey())) {
-                Location loc = switch (attre.getValue()) {
-                    case Location l -> l;
-                    case String s -> Location.fromString(s);
-                    default -> throw new IllegalArgumentException(attre.toString());
-                };
-                writeUtf8EntryOrZero(loc.sourceRef());
-                buf.writeU2(loc.line());
-                buf.writeU2(loc.column());
-            } else {
-                writeUtf8EntryOrZero(attre.getValue());
-            }
-        }
-    }
-
     private void writeUtf8EntryOrZero(Object o) {
         buf.writeIndexOrZero(o == null ? null : cp.utf8Entry(o.toString()));
     }
@@ -458,17 +435,6 @@ private void writeOps(List ops) {
         }
     }
 
-    private void writeSuccessors(List successors) {
-        // number of successors
-        buf.writeU2(successors.size());
-        for (Block.Reference succ : successors) {
-            // block index
-            buf.writeU2(succ.targetBlock().index());
-            // arguments
-            writeValuesList(succ.arguments());
-        }
-    }
-
     private PoolEntry toEntry(FunctionType ftype) {
         if (ftype.returnType() instanceof JavaType jret
                 && jret.erasure().equals(jret)
@@ -489,21 +455,21 @@ private PoolEntry toEntry(TypeElement type) {
                 : cp.stringEntry(type.externalize().toString());
     }
 
-    private void writeOpWithFixedOperandValues(CodeModelAttribute.OpTag tag, List operands) {
+    private void writeOpWithFixedOperandValues(CodeModelAttribute.Tag tag, List operands) {
         writeTag(tag);
         for (Value v : operands) {
             buf.writeU2(valueMap.get(v));
         }
     }
 
-    private void writeOpWithFixedNestedBodies(CodeModelAttribute.OpTag tag, Op op) {
+    private void writeOpWithFixedNestedBodies(CodeModelAttribute.Tag tag, Op op) {
         writeTag(tag);
         for (var body : op.bodies()) {
             writeNestedBody(body);
         }
     }
 
-    private void writeTag(CodeModelAttribute.OpTag tag) {
+    private void writeTag(CodeModelAttribute.Tag tag) {
         buf.writeU1(tag.ordinal());
     }
 
@@ -519,13 +485,6 @@ private void writeFunctionType(FunctionType ftype) {
         buf.writeIndex(toEntry(ftype));
     }
 
-    private void writeTypesList(List types) {
-        buf.writeU2(types.size());
-        for (TypeElement type : types) {
-            writeType(type);
-        }
-    }
-
     private void writeTarget(Block.Reference target) {
         buf.writeU2(target.targetBlock().index());
         writeValuesList(target.arguments());

From 7a94d8dbee7fb23999fbb45975938373c2bcd1a6 Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Wed, 4 Dec 2024 11:53:17 +0100
Subject: [PATCH 17/18] code cleanup

---
 .../classes/jdk/incubator/code/internal/CodeModelAttribute.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
index c24c2f40de2..aa5778a662e 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/CodeModelAttribute.java
@@ -34,7 +34,7 @@
 
 public class CodeModelAttribute extends CustomAttribute{
 
-    public enum Tag {
+    enum Tag {
         LocationAttr,
 
         // CoreOp

From 15f241e383e5135f1c112a04d692b797ba091a69 Mon Sep 17 00:00:00 2001
From: Adam Sotona 
Date: Wed, 4 Dec 2024 11:57:55 +0100
Subject: [PATCH 18/18] code cleanup

---
 .../classes/jdk/incubator/code/op/ExtendedOp.java      | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java
index 20191e517a5..354ad6d00be 100644
--- a/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java
+++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/op/ExtendedOp.java
@@ -1263,7 +1263,7 @@ public JavaForOp body(Consumer c) {
             }
         }
 
-        public static final String NAME = "java.for";
+        static final String NAME = "java.for";
 
         final Body init;
         final Body cond;
@@ -1500,7 +1500,7 @@ public JavaEnhancedForOp body(Consumer c) {
             }
         }
 
-        public static final String NAME = "java.enhancedFor";
+        static final String NAME = "java.enhancedFor";
 
         final Body expression;
         final Body init;
@@ -1724,7 +1724,7 @@ public JavaWhileOp body(Consumer c) {
             }
         }
 
-        public static final String NAME = "java.while";
+        private static final String NAME = "java.while";
 
         private final List bodies;
 
@@ -1870,7 +1870,7 @@ public JavaDoWhileOp.PredicateBuilder body(Consumer c) {
             }
         }
 
-        public static final String NAME = "java.do.while";
+        private static final String NAME = "java.do.while";
 
         private final List bodies;
 
@@ -2366,7 +2366,7 @@ public JavaTryOp noFinalizer() {
             }
         }
 
-        public static final String NAME = "java.try";
+        static final String NAME = "java.try";
 
         final Body resources;
         final Body body;