Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@

<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
<kotlin.version>1.9.20</kotlin.version>
<bytebuddy.version>1.14.8</bytebuddy.version>
<asm.version>9.6</asm.version>
<jmh.version>1.37</jmh.version>
</properties>

Expand Down Expand Up @@ -114,6 +116,22 @@
<version>3.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>${asm.version}</version>
</dependency>

<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<!-- JMH -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/edu/hw_11/ArithmeticUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package edu.hw_11;

public class ArithmeticUtils {

private ArithmeticUtils() {
}

public static int sum(int a, int b) {
return a + b;
}
}
11 changes: 11 additions & 0 deletions src/main/java/edu/hw_11/Multiplier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package edu.hw_11;

public class Multiplier {

private Multiplier() {
}

public static int multiply(int a, int b) {
return a * b;
}
}
18 changes: 18 additions & 0 deletions src/main/java/edu/hw_11/task3/Fib.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package edu.hw_11.task3;

public class Fib {

private Fib() {
}

public static long fib(int n) {
long last = 0;
long next = 1;
for (int i = 0; i < n; i++) {
long oldLast = last;
last = next;
next = oldLast + next;
}
return last;
}
}
20 changes: 20 additions & 0 deletions src/main/java/edu/hw_11/task3/FibImplementation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package edu.hw_11.task3;

import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import org.jetbrains.annotations.NotNull;

public enum FibImplementation implements Implementation {
INSTANCE;

@Override
public @NotNull ByteCodeAppender appender(@NotNull Target target) {
return FibMethod.INSTANCE;
}

@Override
public @NotNull InstrumentedType prepare(@NotNull InstrumentedType instrumentedType) {
return instrumentedType;
}
}
31 changes: 31 additions & 0 deletions src/main/java/edu/hw_11/task3/FibMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package edu.hw_11.task3;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.jar.asm.MethodVisitor;
import org.jetbrains.annotations.NotNull;

public enum FibMethod implements ByteCodeAppender {
INSTANCE;

@Override
public @NotNull Size apply(
@NotNull MethodVisitor mv,
Implementation.@NotNull Context implementationContext,
MethodDescription instrumentedMethod
) {
if (!instrumentedMethod.getReturnType().asErasure().represents(long.class)) {
throw new IllegalArgumentException(instrumentedMethod + " must return long");
}
StackManipulation.Size operandStackSize = new StackManipulation.Compound(
FibNumber.INSTANCE
).apply(mv, implementationContext);
return new Size(
operandStackSize.getMaximalSize(),
instrumentedMethod.getStackSize()
);
}
}

55 changes: 55 additions & 0 deletions src/main/java/edu/hw_11/task3/FibNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package edu.hw_11.task3;

import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings("MagicNumber")
enum FibNumber implements StackManipulation {

INSTANCE; // singleton

@Override
public boolean isValid() {
return true;
}

@SuppressWarnings({"LineLength", "MultipleStringLiterals"})
@Override
public @NotNull Size apply(
MethodVisitor methodVisitor,
Implementation.@NotNull Context implementationContext
) {
methodVisitor.visitCode();
// if (n <= 1)
Label l1 = new Label();
methodVisitor.visitVarInsn(Opcodes.ILOAD, 0);
methodVisitor.visitInsn(Opcodes.ICONST_1);
methodVisitor.visitJumpInsn(Opcodes.IF_ICMPGT, l1);
// return n
methodVisitor.visitVarInsn(Opcodes.ILOAD, 0);
methodVisitor.visitInsn(Opcodes.I2L);
methodVisitor.visitInsn(Opcodes.LRETURN);
// if (n > 1)
methodVisitor.visitLabel(l1);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
// fib(n - 1)
methodVisitor.visitVarInsn(Opcodes.ILOAD, 0);
methodVisitor.visitInsn(Opcodes.ICONST_1);
methodVisitor.visitInsn(Opcodes.ISUB);
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "edu/hw_11/FibExample", "fib", "(I)J", false);
// fib(n - 2)
methodVisitor.visitVarInsn(Opcodes.ILOAD, 0);
methodVisitor.visitInsn(Opcodes.ICONST_2);
methodVisitor.visitInsn(Opcodes.ISUB);
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "edu/hw_11/FibExample", "fib", "(I)J", false);
// fib(n - 1) + fib(n - 2);
methodVisitor.visitInsn(Opcodes.LADD);
methodVisitor.visitInsn(Opcodes.LRETURN);
methodVisitor.visitEnd();
return new Size(-1, 4);
}
}
19 changes: 19 additions & 0 deletions src/test/java/edu/hw_11/FibExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package edu.hw_11;

public class FibExample {
private FibExample() {
}

@SuppressWarnings("MagicNumber")
public static long fib(int n) {
long last = 10;
long next = 111;
for (int i = 0; i < n; i++) {
long oldLast = last;
last = next;
next = oldLast - next;
}
return last;
}

}
27 changes: 27 additions & 0 deletions src/test/java/edu/hw_11/Task1Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package edu.hw_11;

import java.lang.reflect.InvocationTargetException;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import org.junit.jupiter.api.Test;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class Task1Test {
@Test
void generateClassThatPrintsHello()
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
String hello = new ByteBuddy()
.subclass(Object.class)
.name("example.Type")
.method(named("toString")).intercept(FixedValue.value("Hello ByteBuddy!"))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.getConstructor()
.newInstance()
.toString();

assertThat(hello).isEqualTo("Hello ByteBuddy!");
}
}
32 changes: 32 additions & 0 deletions src/test/java/edu/hw_11/Task2Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package edu.hw_11;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.pool.TypePool;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class Task2Test {
@Test
void multipliesInsteadOfAdding() {
TypeDescription typeDescription = TypePool.Default.ofSystemLoader()
.describe("edu.hw_11.ArithmeticUtils")
.resolve();
new ByteBuddy()
.redefine(typeDescription, ClassFileLocator.ForClassLoader.ofSystemLoader())
.method(named("sum")).intercept(MethodDelegation.to(Multiplier.class))
.make()
.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION);

int result = ArithmeticUtils
.sum(1, 2);

assertThat(result).isEqualTo(2);
}
}
30 changes: 30 additions & 0 deletions src/test/java/edu/hw_11/Task3Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package edu.hw_11;

import edu.hw_11.task3.FibImplementation;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.pool.TypePool;
import org.junit.jupiter.api.Test;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class Task3Test {
@Test
void countsFibGenerated() {
TypeDescription typeDescription = TypePool.Default.ofSystemLoader()
.describe("edu.hw_11.FibExample")
.resolve();


new ByteBuddy()
.redefine(typeDescription, ClassFileLocator.ForClassLoader.ofSystemLoader())
.method(named("fib")).intercept(FibImplementation.INSTANCE)
.make()
.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION);

assertThat(FibExample.fib(6)).isEqualTo(8L);

}
}