From 3cd8a018602ce3bbb46dc9c007f8da75d37f8a17 Mon Sep 17 00:00:00 2001 From: Adam Sotona Date: Mon, 25 Nov 2024 17:33:53 +0100 Subject: [PATCH 01/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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{