Skip to content

Commit

Permalink
Домашняя работа №11 (#15)
Browse files Browse the repository at this point in the history
* Done homework 11

* Fixes homework 11

* Fix jacoco problem

* Fix jacoco problem
  • Loading branch information
arhostcode authored Jan 4, 2024
1 parent c995f60 commit 1cdd777
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 7 deletions.
33 changes: 26 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
<maven-site-plugin.version>4.0.0-M9</maven-site-plugin.version>

<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
<bytebuddy.version>1.14.9</bytebuddy.version>
<asm.version>9.6</asm.version>
</properties>

<dependencyManagement>
Expand All @@ -63,17 +65,26 @@
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- For resolve assertJ`s and mockito`s byte-buddy version conflict -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.9</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<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>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
Expand Down Expand Up @@ -198,6 +209,14 @@
</goals>
</execution>
</executions>
<configuration>
<excludes>
<!-- Resolve problem with compatibility bytebuddy and jacoco -->
<!-- Without this statement crash with exception. Primitive solving problem -->
<!-- More: https://github.com/raphw/byte-buddy/issues/1248 -->
<exclude>*ArithmeticUtils*</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/edu/hw11/task1/HelloWorldCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package edu.hw11.task1;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;

public final class HelloWorldCreator {

private HelloWorldCreator() {
}

public static Object createHelloWorld() {
try (var unloaded = new ByteBuddy()
.subclass(Object.class)
.method(ElementMatchers.isToString())
.intercept(FixedValue.value("Hello, ByteBuddy!"))
.make()
) {
return unloaded.load(HelloWorldCreator.class.getClassLoader()).getLoaded().getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

}
10 changes: 10 additions & 0 deletions src/main/java/edu/hw11/task2/ArithmeticUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package edu.hw11.task2;

public final class ArithmeticUtils {
private ArithmeticUtils() {
}

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

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

public final class ClassBehaviourReloader {

private ClassBehaviourReloader() {
}

/**
* Should be called after installing the agent {@link net.bytebuddy.agent.ByteBuddyAgent ByteBuddyAgent}
*/
public static void reload() {
new ByteBuddy()
.redefine(ArithmeticUtils.class)
.method(ElementMatchers.named("sum"))
.intercept(MethodDelegation.to(Delegate.class))
.make()
.load(ArithmeticUtils.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
}

private final static class Delegate {
public static int sum(int a, int b) {
return a * b;
}
}

}
99 changes: 99 additions & 0 deletions src/main/java/edu/hw11/task3/FibClassGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package edu.hw11.task3;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import org.jetbrains.annotations.NotNull;

public final class FibClassGenerator {

private static final String FIB_CLASS_NAME = "Fibonacci";
private static final String FUNCTION_NAME = "fib";
private static final int FUNCTION_OPERANDS_STACK_SIZE = 5;
private static final String FUNCTION_SIGNATURE = "(I)J";

private FibClassGenerator() {
}

public static Object generate() {
try (var unloaded = new ByteBuddy()
.subclass(Object.class)
.name(FIB_CLASS_NAME)
.defineMethod(FUNCTION_NAME, long.class, Ownership.MEMBER, Visibility.PUBLIC)
.withParameter(int.class, "n")
.intercept(createFibImplementation())
.make()
) {
return unloaded.load(FibClassGenerator.class.getClassLoader()).getLoaded().getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static Implementation createFibImplementation() {
return new Implementation.Simple(new FibByteCodeAppender());
}

private final static class FibByteCodeAppender implements ByteCodeAppender {

@Override
@NotNull
public Size apply(
MethodVisitor methodVisitor,
@NotNull Implementation.Context context,
@NotNull MethodDescription methodDescription
) {
Label moreTwoLabel = new Label();

methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
methodVisitor.visitInsn(Opcodes.ICONST_2);
methodVisitor.visitJumpInsn(Opcodes.IF_ICMPGE, moreTwoLabel);
methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
methodVisitor.visitInsn(Opcodes.I2L);
methodVisitor.visitInsn(Opcodes.LRETURN);

// more than 2
methodVisitor.visitLabel(moreTwoLabel);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
methodVisitor.visitInsn(Opcodes.ICONST_1);
methodVisitor.visitInsn(Opcodes.ISUB);

// fib(n - 1)
methodVisitor.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
FIB_CLASS_NAME,
FUNCTION_NAME,
FUNCTION_SIGNATURE,
false
);

methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
methodVisitor.visitInsn(Opcodes.ICONST_2);
methodVisitor.visitInsn(Opcodes.ISUB);

// fib(n - 2)
methodVisitor.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
FIB_CLASS_NAME,
FUNCTION_NAME,
FUNCTION_SIGNATURE,
false
);
methodVisitor.visitInsn(Opcodes.LADD);
methodVisitor.visitInsn(Opcodes.LRETURN);
// localVariableSize = 0 because Implementation.Simple creates variables
// for parameters and I don`t use anything else
return new ByteCodeAppender.Size(FUNCTION_OPERANDS_STACK_SIZE, 0);
}
}

}
15 changes: 15 additions & 0 deletions src/test/java/edu/hw11/task1/HelloWorldCreatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package edu.hw11.task1;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class HelloWorldCreatorTest {

@Test
@DisplayName("Тестирование HelloWorldCreator#createHelloWorld")
public void createHelloWorld_shouldReturnObjectReturnsHelloWorld() {
var helloWorld = HelloWorldCreator.createHelloWorld();
Assertions.assertThat(helloWorld.toString()).isEqualTo("Hello, ByteBuddy!");
}
}
18 changes: 18 additions & 0 deletions src/test/java/edu/hw11/task2/ClassBehaviourReloaderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package edu.hw11.task2;

import net.bytebuddy.agent.ByteBuddyAgent;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class ClassBehaviourReloaderTest {

@Test
@DisplayName("Тестирование ClassBehaviourReloader#reload")
public void reload() {
ByteBuddyAgent.install();
ClassBehaviourReloader.reload();
Assertions.assertThat(ArithmeticUtils.sum(5, 5)).isEqualTo(25);
}

}
20 changes: 20 additions & 0 deletions src/test/java/edu/hw11/task3/FibClassGeneratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package edu.hw11.task3;

import java.lang.reflect.Method;
import lombok.SneakyThrows;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class FibClassGeneratorTest {

@SneakyThrows @Test
@DisplayName("Тестирование FibClassGenerator#generate")
public void generate() {
Object object = FibClassGenerator.generate();
Class<?> clazz = object.getClass();
Method method = clazz.getMethod("fib", int.class);
Object result = method.invoke(object, 10);
Assertions.assertThat(result).isEqualTo(55L);
}
}

0 comments on commit 1cdd777

Please sign in to comment.