From 4dd3098ab2a7fbfb20dc85dce3e26ed21dbac845 Mon Sep 17 00:00:00 2001 From: volodya-lombrozo Date: Wed, 3 Apr 2024 13:01:39 +0300 Subject: [PATCH] feat(#528): suggest ugly solution --- .../bytecode/BytecodeClass.java | 30 +++++------ .../bytecode/BytecodeMethod.java | 41 +++++++++----- .../bytecode/BytecodeMethodProperties.java | 23 +++++++- .../bytecode/CustomClassVisitor.java | 54 +++++++++++++++++++ 4 files changed, 117 insertions(+), 31 deletions(-) create mode 100644 src/main/java/org/eolang/jeo/representation/bytecode/CustomClassVisitor.java diff --git a/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeClass.java b/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeClass.java index 555d6d020..925eadf96 100644 --- a/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeClass.java +++ b/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeClass.java @@ -54,7 +54,7 @@ public final class BytecodeClass implements Testable { * Class writer. */ @ToString.Exclude - private final CustomClassWriter visitor; + private final CustomClassVisitor visitor; /** * Methods. @@ -116,15 +116,6 @@ public BytecodeClass(final String name, final BytecodeClassProperties properties this(name, properties, true); } - public BytecodeClass( - final String name, - final BytecodeClassProperties properties, - final boolean verify, - final boolean frames - ) { - this(name, BytecodeClass.writer(verify, frames), new ArrayList<>(0), properties); - } - /** * Constructor. * Has real usages. @@ -137,7 +128,16 @@ public BytecodeClass( final BytecodeClassProperties properties, final boolean verify ) { - this(name, BytecodeClass.writer(verify, true), new ArrayList<>(0), properties); + this(name, BytecodeClass.writer(verify, false), new ArrayList<>(0), properties); + } + + public BytecodeClass( + final String name, + final BytecodeClassProperties properties, + final boolean verify, + final boolean frames + ) { + this(name, BytecodeClass.writer(verify, frames), new ArrayList<>(0), properties); } /** @@ -151,7 +151,7 @@ public BytecodeClass( */ public BytecodeClass( final String name, - final CustomClassWriter writer, + final CustomClassVisitor writer, final Collection methods, final BytecodeClassProperties properties ) { @@ -192,7 +192,7 @@ public Bytecode bytecode() { this.fields.forEach(field -> field.write(this.visitor)); this.methods.forEach(BytecodeMethod::write); this.visitor.visitEnd(); - return new Bytecode(this.visitor.toByteArray()); + return this.visitor.bytecode(); } catch (final IllegalArgumentException exception) { throw new IllegalArgumentException( String.format("Can't create bytecode for the class '%s' ", this.name), @@ -389,7 +389,7 @@ public BytecodeClass helloWorldMethod() { * @param verify Verify bytecode. * @return Verified class writer if verify is true, otherwise custom class writer. */ - private static CustomClassWriter writer(final boolean verify, final boolean frames) { + private static CustomClassVisitor writer(final boolean verify, final boolean frames) { final CustomClassWriter result; final int flags; if (frames) { @@ -402,6 +402,6 @@ private static CustomClassWriter writer(final boolean verify, final boolean fram } else { result = new CustomClassWriter(flags); } - return result; + return new CustomClassVisitor(result); } } diff --git a/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethod.java b/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethod.java index 1a31d007f..ff7604f66 100644 --- a/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethod.java +++ b/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethod.java @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.List; import org.eolang.jeo.representation.xmir.AllLabels; -import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; @@ -39,7 +38,7 @@ public final class BytecodeMethod implements Testable { /** * ASM class visitor. */ - private final ClassVisitor visitor; + private final CustomClassVisitor visitor; /** * Original class. @@ -92,7 +91,7 @@ public final class BytecodeMethod implements Testable { */ BytecodeMethod( final String name, - final ClassVisitor visitor, + final CustomClassVisitor visitor, final BytecodeClass clazz, final String descriptor, final int... modifiers @@ -108,7 +107,7 @@ public final class BytecodeMethod implements Testable { */ BytecodeMethod( final BytecodeMethodProperties properties, - final ClassVisitor visitor, + final CustomClassVisitor visitor, final BytecodeClass clazz ) { this(properties, visitor, clazz, 0, 0); @@ -125,7 +124,7 @@ public final class BytecodeMethod implements Testable { */ public BytecodeMethod( final BytecodeMethodProperties properties, - final ClassVisitor visitor, + final CustomClassVisitor visitor, final BytecodeClass clazz, final int stack, final int locals @@ -243,16 +242,30 @@ public String testCode() { @SuppressWarnings("PMD.AvoidCatchingGenericException") void write() { try { - final MethodVisitor mvisitor = this.properties.writeMethod(this.visitor); - this.annotations.forEach(annotation -> annotation.write(mvisitor)); - this.defvalues.forEach(defvalue -> defvalue.writeTo(mvisitor)); - if (!this.properties.isAbstract()) { - mvisitor.visitCode(); - this.tryblocks.forEach(block -> block.writeTo(mvisitor)); - this.instructions.forEach(instruction -> instruction.writeTo(mvisitor)); - mvisitor.visitMaxs(this.stack, this.locals); + if (this.stack == 0 && this.locals == 0) { + final MethodVisitor mvisitor = this.properties.writeCustomMethodWithComputation( + this.visitor); + this.annotations.forEach(annotation -> annotation.write(mvisitor)); + this.defvalues.forEach(defvalue -> defvalue.writeTo(mvisitor)); + if (!this.properties.isAbstract()) { + mvisitor.visitCode(); + this.tryblocks.forEach(block -> block.writeTo(mvisitor)); + this.instructions.forEach(instruction -> instruction.writeTo(mvisitor)); + mvisitor.visitMaxs(this.stack, this.locals); + } + mvisitor.visitEnd(); + } else { + final MethodVisitor mvisitor = this.properties.writeMethod(this.visitor); + this.annotations.forEach(annotation -> annotation.write(mvisitor)); + this.defvalues.forEach(defvalue -> defvalue.writeTo(mvisitor)); + if (!this.properties.isAbstract()) { + mvisitor.visitCode(); + this.tryblocks.forEach(block -> block.writeTo(mvisitor)); + this.instructions.forEach(instruction -> instruction.writeTo(mvisitor)); + mvisitor.visitMaxs(this.stack, this.locals); + } + mvisitor.visitEnd(); } - mvisitor.visitEnd(); } catch (final NegativeArraySizeException exception) { throw new IllegalStateException( String.format( diff --git a/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethodProperties.java b/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethodProperties.java index 07d92c858..71fa54b52 100644 --- a/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethodProperties.java +++ b/src/main/java/org/eolang/jeo/representation/bytecode/BytecodeMethodProperties.java @@ -28,7 +28,6 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.eolang.jeo.representation.JavaName; -import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -133,12 +132,32 @@ public boolean isAbstract() { return (this.access & Opcodes.ACC_ABSTRACT) != 0; } + + /** + * Add method to class writer. + * @param writer Class writer. + * @return Method visitor. + */ + MethodVisitor writeCustomMethodWithComputation(final CustomClassVisitor writer) { + Logger.debug( + this, + String.format("Creating method visitor with the following properties %s", this) + ); + return writer.visitCustomMethodWithComputation( + this.access, + new JavaName(this.name).decode(), + this.descriptor, + this.signature, + this.exceptions + ); + } + /** * Add method to class writer. * @param writer Class writer. * @return Method visitor. */ - MethodVisitor writeMethod(final ClassVisitor writer) { + MethodVisitor writeMethod(final CustomClassVisitor writer) { Logger.debug( this, String.format("Creating method visitor with the following properties %s", this) diff --git a/src/main/java/org/eolang/jeo/representation/bytecode/CustomClassVisitor.java b/src/main/java/org/eolang/jeo/representation/bytecode/CustomClassVisitor.java new file mode 100644 index 000000000..0245f8a26 --- /dev/null +++ b/src/main/java/org/eolang/jeo/representation/bytecode/CustomClassVisitor.java @@ -0,0 +1,54 @@ +package org.eolang.jeo.representation.bytecode; + +import java.lang.reflect.Field; +import org.eolang.jeo.representation.DefaultVersion; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.tree.ClassNode; + +public final class CustomClassVisitor extends ClassVisitor { + + private final CustomClassWriter writer; + + + public CustomClassVisitor(final int api) { + this(api, new CustomClassWriter()); + } + + public CustomClassVisitor(final CustomClassWriter writer) { + this(new DefaultVersion().api(), writer); + } + + public CustomClassVisitor(final int api, final CustomClassWriter writer) { + super(api, writer); + this.writer = writer; + } + + public MethodVisitor visitCustomMethodWithComputation( + final int access, + final String name, + final String descriptor, + final String signature, + final String[] exceptions + ) { + + try { + final Field field = ClassWriter.class.getDeclaredField("compute"); + field.setAccessible(true); + field.setInt(this.writer, 4); + final MethodVisitor original = this.visitMethod( + access, name, descriptor, signature, exceptions + ); + field.setInt(this.writer, 0); + return original; + } catch (final NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + public Bytecode bytecode() { + return new Bytecode(this.writer.toByteArray()); + } +}