ActiveJ framework learning -- specialization tool reference

Posted by curtis_b on Mon, 20 Dec 2021 20:39:06 +0100

2021SC@SDUSC

catalogue

1, The foregoing undertaking

2, Specialization tool

(1) Act as a method for calling a tool

docall method

lookupField method

lookupMethod method

(2) Util method class

3, Summary

4, Reference

5, Past review

1, The foregoing undertaking

The previous article mainly analyzed the transformMethod method of the Specialization module in the Specialization module. The transformMethod method is the main method in the Specialization module. Almost all the codes of the whole Specialization module are composed of the transformMethod method. The transformMethod method penetrates into the assembly level (such as node and instruction classes in asm package). It optimizes the JVM level by decomposing the specific instructions of an object method, so as to accelerate ActiveJ serialization to generate code and improve the running speed of the project.

After analyzing Specialization, the code of the Specialization class is basically analyzed. In this blog, like the analysis of Codegen in the previous part, the last blog is used to analyze all relevant tool classes used by Specialization and other methods for Specialization except the internal classes of Specialization, These methods also serve as tools for Specialization.

2, Specialization tool

(1) Act as a method for calling a tool

docall method

First, let's look at the parameters received by the docall method,

private void doCall(GeneratorAdapter g,
		Type ownerType, Type[] paramTypes,
		Function<Specialization, Optional<Runnable>> staticCallSupplier,
		Runnable defaultCall) {

It accepts a GeneratorAdapter, which often appears in previous blogs. We already know that this class is used to add and generate methods for dynamic classes when creating dynamic classes during program operation. The next is Type ownerType, which is used as a type definer to describe the type of caller executing docall, accompanied by paraTypes, From the name, we can see that it describes the type of parameter, but the parameter should be used for Function.

The next one is Function < Specialization, optional < Runnable > >. Here is a Function type and uses the internal class of Specialization analyzed in our last blog. At the same time, the other is a Runnable. This type is often used in multithreading. The whole Function is represented as a generic template.

How to understand the type of Function?

According to the explanation of the rookie tutorial, Function (functional interface) has only one abstract method, but there can be many interfaces of non abstract methods, which can be implicitly transformed into lambda expressions. Like stream and filter we have encountered before, Function was introduced in java8.

In short, it is an application of a Lambda expression. For example, the explanation example given in the rookie tutorial is as follows:

Define a function interface:

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

The Lambda expression is as follows:

GreetingService greetService1 = message -> System.out.println("Hello " + message);

The Function interface type corresponding to Function is:

Therefore, function < Specialization, optional < Runnable > > here means to accept a Specialization as a parameter and return a Runnable type as a return value.

Then analyze the parameters of docall. The last parameter is Runnable defaultCall. Combined with the Function < specialization, optional < runnable > >, we can easily know that this defaultCall should correspond to the return value of the Function interface.

After analyzing the parameters, let's analyze the flow of the doCall method

Class<?> ownerClazz = loadClass(classLoader, ownerType);

The first line defines a parameter of the loadClass method and uses the classloader. However, the classloader here refers to the BytecodeClassLoader class redefined by the specialization package. You can use bytecode to define a dynamic class and add attributes. The other parameter of the latter is ownerType, that is, a class attribute. This line of statement creates a class of the same type as ownerType.

for (int j = paramTypes.length - 1; j >= 0; j--) {
	Type type = paramTypes[j];
	int paramLocal = g.newLocal(type);
	paramLocals[j] = paramLocal;
	g.storeLocal(paramLocal);
}

Next, for the passed in parameter array paraTypes, traverse and take out its parameters one by one. After obtaining its type, use the Generator Adapter to create a variable, and store a paraLocal variable locally for subsequent operations.

In the initial analysis of the Specialization class, we mentioned that whenever a specialFields is read, a corresponding Specialization variable needs to be generated to record the optimization operations taken on this file. Therefore, after processing paraTypes, we need to perform corresponding operations on the read para.

for (Specialization s : relatedSpecializations) {
	    if (!ownerClazz.isAssignableFrom(s.instance.getClass())) continue;
		Optional<Runnable> staticCall = staticCallSupplier.apply(s);
		if (!staticCall.isPresent()) continue;

		Label labelNext = g.newLabel();

		g.dup();
		g.getStatic(s.specializedType, THIS, getType(s.instanceClass));
		g.ifCmp(getType(Object.class), NE, labelNext);

		g.pop();

		for (int paramLocal : paramLocals) {
			g.loadLocal(paramLocal);
		}
		staticCall.get().run();
		g.goTo(labelExit);

		g.mark(labelNext);
}

Here, we need to traverse all the Specialization stored before. First, we need to judge the risk, that is

if (!ownerClazz.isAssignableFrom(s.instance.getClass())) continue;

If the type is not the data type specified by Specialization, the following parsing and optimization operations cannot be continued.

Note the following statement:

Optional<Runnable> staticCall = staticCallSupplier.apply(s);

Here we use the Function function interface programming of java8 mentioned above. The apply operation adds the staticCallSupplier operation to Specialization and returns a return value of Runnable type. This should correspond to the parameters of the entire doCall method we saw at the beginning.

Next, judge the legitimacy of Runnable. If it is, continue. Otherwise, continue to exit this iteration.

Next, I went to the JVM instruction operation mentioned in the previous blog.

dup();
g.getStatic(s.specializedType, THIS, getType(s.instanceClass));
g.ifCmp(getType(Object.class), NE, labelNext);

g. The getstatic method is described as follows:

Generates the instruction to push the value of a static field on the stack. 

This method pushes the field corresponding to Specialization to the static field of the whole class to make it a static variable. This shows why ActiveJ mentioned the operation of converting object variables into class variables when introducing Specializer.

Then it was implemented again

g.ifCmp(getType(Object.class), NE, labelNext);

g.pop();

ifCmp operation is to jump the instruction to labelNext, which means to push the value to the pointer before the static field to continue execution. However, at this time, this variable has been pushed to the static field processed by Specialization. Finally, g.pop is executed to pop up the dup() instruction previously executed to execute this method, which copies the top of the stack and pushes it into the stack.

After the static domain is operated, the next step is to actually generate this variable in the GeneratorAdapter, because in the previous step, it is only pressed into the static domain, and there is no actual generation, which is equivalent to just registration.

for (int paramLocal : paramLocals) {
	g.loadLocal(paramLocal);
}
staticCall.get().run();
g.goTo(labelExit);

g.mark(labelNext);

Static call is executed after loading get(). As mentioned earlier, we accept a Runnable parameter. Here, we use multithreading to let the above loader run in another thread, so as not to affect the continuous execution of the main program and optimize the processing time.

lookupField method

The lookupField method is mainly used to help SpecializedField obtain its own element. Since the HashMap method is used, it is necessary to use the keySet to compare whether the obtained result is the Type defined in the Type of the incoming owner. If they are all corresponding, or even if the types are different, but there is an inheritance relationship between parent and child classes, the result can be returned.

@Nullable String lookupField(Class<?> owner, String field) {
	java.lang.reflect.Field result = null;
	for (java.lang.reflect.Field originalField : specializedFields.keySet()) {
		if (true &&
				Objects.equals(originalField.getName(), field) &&
				originalField.getDeclaringClass().isAssignableFrom(owner) &&
				(result == null || result.getDeclaringClass() .isAssignableFrom(originalField.getDeclaringClass()))) {
			result = originalField;
		}
	}
    return specializedFields.get(result);
}

lookupMethod method

From the name and our previous experience in analyzing the field, with the field extraction method, there will be the method extraction method. The two are highly related objects in the specialization.

@Nullable String lookupMethod(Class<?> owner, Method method) {
	java.lang.reflect.Method result = null;
	for (java.lang.reflect.Method originalMethod : specializedMethods.keySet()) {
		if (true &&
				Objects.equals(originalMethod.getName(), method.getName()) &&
				Objects.equals(
				Arrays.stream(originalMethod.getParameters()).map(p -> getType(p.getType())).collect(toList()),
				Arrays.asList(method.getArgumentTypes())) &&
				originalMethod.getDeclaringClass().isAssignableFrom(owner) &&
				(result == null ||
								result.getDeclaringClass().isAssignableFrom(originalMethod.getDeclaringClass()))) {
			result = originalMethod;
		}
	}
	return specializedMethods.get(result);
}

As in the previous blog, the reflection mechanism of java needs to be used to obtain the corresponding method in the metaclass. Due to the existence of object-oriented inheritance and rewriting of the method, whether the corresponding method is the method to be obtained is different from the direct name and type comparison of field. We need to compare the signature of the method one by one, Only when the signature is correct can the corresponding read method be returned in specializedMethods.

(2) Util method class

Because there are many reflection operations in Specializer, type judgment is often used when using optimization operations to optimize an attribute. This method is encapsulated in Util class. In many places, a comparison method is used to determine whether the type matches the base type

static Class<?> getBoxedType(Class<?> type) {
	if (byte.class == type) return Byte.class;
	if (boolean.class == type) return Boolean.class;
	if (short.class == type) return Short.class;
	if (char.class == type) return Character.class;
	if (int.class == type) return Integer.class;
	if (float.class == type) return Float.class;
	if (long.class == type) return Long.class;
	if (double.class == type) return Double.class;
	throw new IllegalArgumentException();
}

After that, common and simple methods such as hashEqual methods will not write more analysis. They are basically the methods of each class. This habit of writing corresponding unique hashEqual methods for each class is also one of the places where we need to learn ActiveJ project. When building a good project, we should also pay attention to whether these common but simple methods are built correctly.

There is also a Function interface in Util, which is why we saw the apply method in the previous doCall method. InjectorSpecializer is an entity class that implements Function and is used by the Specializer

private static class InjectorSpecializer implements Function<Object, Object> {
	private final Specializer specializer;

	public InjectorSpecializer() {
		try {
			Class<?> compiledBindingClass = Class.forName("io.activej.inject.impl.CompiledBinding");
			this.specializer = Specializer.create(Thread.currentThread().getContextClassLoader())
					.withPredicate(cls -> compiledBindingClass.isAssignableFrom(cls) &&
								!cls.getName().startsWith("io.activej.inject.binding.Multibinders$"));
		} catch (ClassNotFoundException e) {
			throw new IllegalStateException("Can not access ActiveJ Inject", e);
		}
	}

	@Override
	public Object apply(Object o) {
		return specializer.specialize(o);
	}
}

The accepted parameters of the Function interface here are not particularly accurate (even using Object type...), we need to pay more attention to what type is passed in as a parameter, otherwise it will be affected by polymorphism.

The construction method of this class uses a try structure, and there is only one try structure. Determine where a created class is located according to the prefix Name: "io. activej. inject. impl. CompiledBinding". Then directly use the Create method of the specialization class to create a class. Here, we also use the predict statement analyzed in our last blog, This is equivalent to a strong true judgment, which is used to judge whether a list meets the requirements. This shows whether the injected class is "binding.Multibinders $".

3, Summary

This blog mainly analyzes other classes in the specialization package except the specialization class, which basically serve the function area of the specialization class, including annotation, static attribute conversion and other functions. For method acceleration, Specializer mainly penetrates the JVM level by reading instruction s one by one for optimization.

For attribute optimization, specialization takes out an instance that needs to be optimized with specialization, reads and accesses all attributes of the instance, and copies these attributes to the Field for storage. Before optimizing the method, first copy these attributes, and then press them into the static stack area of the program, so as to speed up access and operation.

4, Reference

Java 8 functional interface | rookie tutorial (runoob.com)

5, Past review

ActiveJ framework learning (XI) -- blog of Specializer_m0_56367233 - CSDN blog

ActiveJ framework learning (x) -- blog of Specializer_m0_56367233 - CSDN blog

ActiveJ framework learning (IX) -- blog of Specializer [connecting the preceding and the following] _m0_56367233 - CSDN blog

ActiveJ framework learning (8) -- Record class function analysis (II) _m0_56367233 blog - CSDN blog

ActiveJ framework learning (VII) -- Record class function analysis _m0_56367233 blog - CSDN blog

ActiveJ framework learning (VI) -- blog of Util other scattered tools analysis _m0_56367233 - CSDN blog

ActiveJ framework learning (V) -- classbuilder class analysis _m0_56367233 blog - CSDN blog _classbuilder

ActiveJ framework learning (IV) -- Context function analysis _m0_56367233 blog - CSDN blog

ActiveJ framework learning (III) -- expression test class thousand line source code analysis _m0_56367233 blog - CSDN blog

ActiveJ framework learning (II) -- Codegen's preliminary understanding _m0_56367233 blog - CSDN blog

ActiveJ framework learning (I) -- starting _m0_56367233 blog - CSDN blog

Topics: Java