Skip to content

Commit

Permalink
General code cleanup for future development
Browse files Browse the repository at this point in the history
  • Loading branch information
P3ridot committed Apr 1, 2024
1 parent 5b164c2 commit e24cc75
Show file tree
Hide file tree
Showing 16 changed files with 443 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class InstanceConstructionWithFieldsBenchmark {

private static class Entity {
public static class Entity {

private final int id;
private final String name;

@Inject
private EntityData data;
public EntityData data;
@Inject
private EntityStorage storage;
public EntityStorage storage;

public Entity(int id, String name) {
this.name = name;
Expand All @@ -50,7 +50,7 @@ public Entity(int id, String name) {

}

private static class EntityData {
public static class EntityData {

private final int coins;
private final float health;
Expand Down Expand Up @@ -130,6 +130,8 @@ public void setup() throws Exception {
resources.on(EntityData.class).assignInstance(this.entityDataSupplier);
resources.on(EntityStorage.class).assignInstance(this.entityStorage);
});

CodegenFieldsInjector<Entity> entityCodegenFieldsInjector = new CodegenFieldsInjector<>(new InjectorProcessor(this.entityInjector), this.entityInjector.forConstructor(Entity.class));
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.panda_lang.utilities.inject;

import java.util.Map;

final class CodegenCache {

private CodegenCache() { }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2020 Dzikoysk
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.panda_lang.utilities.inject;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Function;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatchers;
import panda.utilities.ObjectUtils;

final class CodegenConstructorInjector<T> implements ConstructorInjector<T> {

private static final Object[] EMPTY = new Object[0];

Check warning on line 34 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L34

Added line #L34 was not covered by tests

private static final AtomicInteger ID = new AtomicInteger();

Check warning on line 36 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L36

Added line #L36 was not covered by tests

private final InjectorProcessor processor;
private final Constructor<T> constructor;
private final boolean empty;
private final InjectorCache cache;

private final Function<Object[], Object> generated;

CodegenConstructorInjector(InjectorProcessor processor, Constructor<T> constructor) throws Exception {
this.processor = processor;
this.constructor = constructor;

Check warning on line 47 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L45-L47

Added lines #L45 - L47 were not covered by tests
this.empty = constructor.getParameterCount() == 0;
this.cache = InjectorCache.of(processor, constructor);

Check warning on line 49 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L49

Added line #L49 was not covered by tests

this.generated = generate(constructor);
}

Check warning on line 52 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L51-L52

Added lines #L51 - L52 were not covered by tests

@SuppressWarnings("unchecked")
@Override
public T newInstance(Object... injectorArgs) throws Exception {
return (T) this.generated.apply(

Check warning on line 57 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L57

Added line #L57 was not covered by tests
this.empty
? EMPTY
: this.processor.fetchValues(this.cache, injectorArgs)

Check warning on line 60 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L59-L60

Added lines #L59 - L60 were not covered by tests
);
}

private static Function<Object[], Object> generate(Constructor<?> constructor) throws Exception {
Class<?> declaringClass = constructor.getDeclaringClass();

Check warning on line 65 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L65

Added line #L65 was not covered by tests
if (!Modifier.isPublic(declaringClass.getModifiers())) {
throw new IllegalStateException(declaringClass + " has to be public");

Check warning on line 67 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L67

Added line #L67 was not covered by tests
}

if (!Modifier.isPublic(constructor.getModifiers())) {
throw new IllegalStateException(constructor + " has to be public");

Check warning on line 71 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L71

Added line #L71 was not covered by tests
}

ByteBuddy byteBuddy = new ByteBuddy();
DynamicType.Unloaded<?> classPackage = byteBuddy
.makePackage(declaringClass.getPackage().getName())
.make();

Check warning on line 77 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L74-L77

Added lines #L74 - L77 were not covered by tests

Class<?> loaded = byteBuddy.subclass(Object.class)
.implement(GeneratedFunction.class)
.name(declaringClass.getName() + "$" + constructor.getName() + "$" + ID.incrementAndGet())
.method(ElementMatchers.named("apply"))
.intercept(MethodCall.invoke(constructor)
.onArgument(0)
.withArgumentArrayElements(1)
.withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC))
.make()
.include(classPackage)
.load(declaringClass.getClassLoader())
.getLoaded();

Check warning on line 90 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L79-L90

Added lines #L79 - L90 were not covered by tests

return ObjectUtils.cast(loaded.getDeclaredConstructor().newInstance());

Check warning on line 92 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L92

Added line #L92 was not covered by tests
}

@Override
public Constructor<T> getConstructor() {
return this.constructor;

Check warning on line 97 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenConstructorInjector.java#L97

Added line #L97 was not covered by tests
}

@FunctionalInterface
public interface GeneratedFunction extends Function<Object[], Object> {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.panda_lang.utilities.inject;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.matcher.ElementMatchers;
import panda.utilities.ArrayUtils;

public class CodegenFieldsInjector<T> implements FieldsInjector<T> {

private static final AtomicInteger ID = new AtomicInteger();

Check warning on line 21 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L21

Added line #L21 was not covered by tests

private final InjectorProcessor processor;
private final ConstructorInjector<T> constructorInjector;
private final Class<?> declaringClass;

private final BiConsumer<Object, Object[]> generated;

CodegenFieldsInjector(InjectorProcessor processor, ConstructorInjector<T> constructorInjector) throws Exception {
this.processor = processor;
this.constructorInjector = constructorInjector;
this.declaringClass = constructorInjector.getConstructor().getDeclaringClass();

Check warning on line 32 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L29-L32

Added lines #L29 - L32 were not covered by tests

this.generated = generate(this.declaringClass);
}

Check warning on line 35 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L34-L35

Added lines #L34 - L35 were not covered by tests

@Override
public T newInstance(Object... injectorArgs) throws Exception {
T instance = this.constructorInjector.newInstance(injectorArgs);

Check warning on line 39 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L39

Added line #L39 was not covered by tests

Field[] fields = ClassCache.getInjectorFields(this.declaringClass);
Object[] values = new Object[fields.length];

Check warning on line 42 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L41-L42

Added lines #L41 - L42 were not covered by tests
for (int i = 0; i < fields.length; i++) {
values[i] = this.processor.fetchValue(new PropertyField(fields[i]), injectorArgs);

Check warning on line 44 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L44

Added line #L44 was not covered by tests
}
this.generated.accept(instance, values);

Check warning on line 46 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L46

Added line #L46 was not covered by tests

return instance;

Check warning on line 48 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L48

Added line #L48 was not covered by tests
}

@Override
public Constructor<T> getConstructor() {
return this.constructorInjector.getConstructor();

Check warning on line 53 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L53

Added line #L53 was not covered by tests
}

private static BiConsumer<Object, Object[]> generate(Class<?> declaringClass) throws Exception {
if (!Modifier.isPublic(declaringClass.getModifiers())) {
throw new IllegalStateException(declaringClass + " has to be public");

Check warning on line 58 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L58

Added line #L58 was not covered by tests
}

Field[] fields = ClassCache.getInjectorFields(declaringClass);

Check warning on line 61 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L61

Added line #L61 was not covered by tests
for (Field field : fields) {
int modifiers = field.getModifiers();

Check warning on line 63 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L63

Added line #L63 was not covered by tests
if (!Modifier.isPublic(modifiers)) {
throw new IllegalStateException(field + " has to be public");

Check warning on line 65 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L65

Added line #L65 was not covered by tests
} else if (Modifier.isFinal(modifiers)) {
throw new IllegalStateException(field + " cannot be final");

Check warning on line 67 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L67

Added line #L67 was not covered by tests
}
}

ByteBuddy byteBuddy = new ByteBuddy();
DynamicType.Unloaded<?> classPackage = byteBuddy
.makePackage(declaringClass.getPackage().getName())
.make();

Check warning on line 74 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L71-L74

Added lines #L71 - L74 were not covered by tests


/*DynamicType.Builder.MethodDefinition.ExceptionDefinition<Object> init = byteBuddy.subclass(Object.class)
.implement(GeneratedConsumer.class)
.name(declaringClass.getName() + "$init$" + ID.incrementAndGet())
.method(ElementMatchers.named("accept"))
.withoutCode()
.defineMethod("init", void.class, Modifier.PUBLIC)
.withParameters(ArrayUtils.merge(new Type[]{declaringClass}, Arrays.stream(fields).map(Field::getType).toArray(Class[]::new)))
.intercept(FieldAccessor.ofField("instance").setsArgumentAt(0));
DynamicType.Unloaded<Object> unloaded = init
.intercept(MethodDelegation.to(FieldAccessor.ofField("instance")))
.make();*/

return (instance, values) -> {

Check warning on line 90 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L90

Added line #L90 was not covered by tests

};

Check warning on line 92 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L92

Added line #L92 was not covered by tests
}

public static class Interceptor {

private final Field[] fields;

public Interceptor(Field[] fields) {
this.fields = fields;
}

Check warning on line 101 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L99-L101

Added lines #L99 - L101 were not covered by tests

public void intercept(@AllArguments Object[] args) throws IllegalAccessException {
Object instance = args[0];
Object[] values = Arrays.copyOfRange(args, 1, args.length);

Check warning on line 105 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L104-L105

Added lines #L104 - L105 were not covered by tests

for (int i = 0; i < values.length; i++) {
Field field = this.fields[i];
field.setAccessible(true);
field.set(instance, values[i]);

Check warning on line 110 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L108-L110

Added lines #L108 - L110 were not covered by tests
}

}

Check warning on line 113 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenFieldsInjector.java#L113

Added line #L113 was not covered by tests

}

@FunctionalInterface
public interface GeneratedConsumer extends BiConsumer<Object, Object[]> { }

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,36 @@
import net.bytebuddy.matcher.ElementMatchers;
import panda.utilities.ObjectUtils;

final class GeneratedMethodInjector implements MethodInjector {
final class CodegenMethodInjector implements MethodInjector {

private static final Object[] EMPTY = new Object[0];

private static final AtomicInteger ID = new AtomicInteger();

private final InjectorProcessor processor;
private final Method method;
private final BiFunction<Object, Object[], Object> function;
private final InjectorCache cache;
private final boolean empty;

GeneratedMethodInjector(InjectorProcessor processor, Method method) throws Exception {
private final BiFunction<Object, Object[], Object> generated;

CodegenMethodInjector(InjectorProcessor processor, Method method) throws Exception {
this.processor = processor;
this.method = method;
this.function = generate(method);
this.cache = InjectorCache.of(processor, method);
this.empty = method.getParameterCount() == 0;

this.generated = generate(method);
}

@SuppressWarnings("unchecked")
@Override
public <T> T invoke(Object instance, Object... injectorArgs) throws Exception {
return (T) this.function.apply(instance, this.empty ? EMPTY : this.processor.fetchValues(this.cache, injectorArgs));
return (T) this.generated.apply(instance,
this.empty
? EMPTY

Check warning on line 57 in di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenMethodInjector.java

View check run for this annotation

Codecov / codecov/patch

di-codegen/src/main/java/org/panda_lang/utilities/inject/CodegenMethodInjector.java#L57

Added line #L57 was not covered by tests
: this.processor.fetchValues(this.cache, injectorArgs)
);
}

private static BiFunction<Object, Object[], Object> generate(Method method) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public final class CodegenMethodInjectorFactory implements MethodInjectorFactory

@Override
public MethodInjector createMethodInjector(InjectorProcessor processor, Method method) throws Exception {
return new GeneratedMethodInjector(processor, method);
return new CodegenMethodInjector(processor, method);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void testInjector() {
assertEquals(DYNAMIC, (Integer) injector.fork(resources -> resources.on(int.class).assignInstance(DYNAMIC)).invokeMethod(testForkedInjector, instance));

MethodInjector generatedMethodInjector = injector.fork(resources -> resources.on(int.class).assignInstance(DYNAMIC)).forGeneratedMethod(testForkedInjector);
assertTrue(generatedMethodInjector instanceof GeneratedMethodInjector);
assertTrue(generatedMethodInjector instanceof CodegenMethodInjector);
assertEquals(DYNAMIC, (Integer) generatedMethodInjector.invoke(instance));
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.ApiStatus;
import org.panda_lang.utilities.inject.annotations.AutoConstruct;
import org.panda_lang.utilities.inject.annotations.Inject;
import panda.std.Pair;
Expand All @@ -16,6 +17,7 @@
/**
* Utility class for caching class data (fields, methods, etc.) to improve performance.
*/
@ApiStatus.Internal
final class ClassCache {

private static final Map<Class<?>, Constructor<?>> CACHED_CONSTRUCTORS = new ConcurrentHashMap<>();
Expand Down
Loading

0 comments on commit e24cc75

Please sign in to comment.