Skip to content

Commit

Permalink
Eliminate reflection and serialization from initial boot
Browse files Browse the repository at this point in the history
  • Loading branch information
dmlloyd committed Jun 25, 2019
1 parent c02a3d8 commit 84b2caf
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 383 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
import io.quarkus.deployment.QuarkusClassWriter;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.ExecutionChainBuildItem;
import io.quarkus.deployment.builditem.QuarkusApplicationBuildItem;
import io.quarkus.deployment.builditem.substrate.SubstrateOutputBuildItem;
import io.quarkus.gizmo.NullWriter;
Expand Down Expand Up @@ -321,7 +320,6 @@ public Writer writeSource(final String className) {
builder.setOutput(classOutput);
builder.addFinal(BytecodeTransformerBuildItem.class)
.addFinal(ApplicationArchivesBuildItem.class)
.addFinal(ExecutionChainBuildItem.class)
.addFinal(QuarkusApplicationBuildItem.class)
.addFinal(SubstrateOutputBuildItem.class);
result = builder.build().run();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,39 @@
import org.wildfly.common.Assert;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.runtime.execution.ExecutionHandler;
import io.quarkus.gizmo.MethodDescriptor;

/**
* A build item for execution handlers.
*/
public final class ExecutionHandlerBuildItem extends MultiBuildItem {
private final ExecutionHandler handler;
private final MethodDescriptor methodDescriptor;

/**
* Construct a new instance.
*
* @param handler the handler (must not be {@code null})
* @param methodDescriptor the method descriptor to use to construct the handler (must not be {@code null})
*/
public ExecutionHandlerBuildItem(final ExecutionHandler handler) {
this.handler = Assert.checkNotNullParam("handler", handler);
public ExecutionHandlerBuildItem(final MethodDescriptor methodDescriptor) {
Assert.checkNotNullParam("methodDescriptor", methodDescriptor);
this.methodDescriptor = methodDescriptor;
}

/**
* Get the execution handler.
* Construct a new instance. The handler will be instantiated by its no-arg constructor.
*
* @return the execution handler (not {@code null})
* @param handlerClassName the handler class name (must not be {@code null})
*/
public ExecutionHandler getHandler() {
return handler;
public ExecutionHandlerBuildItem(final String handlerClassName) {
this(MethodDescriptor.ofConstructor(handlerClassName));
}

/**
* Get the factory method descriptor for this handler.
*
* @return the method descriptor
*/
public MethodDescriptor getMethodDescriptor() {
return methodDescriptor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import static io.quarkus.gizmo.MethodDescriptor.ofConstructor;
import static io.quarkus.gizmo.MethodDescriptor.ofMethod;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
Expand All @@ -21,16 +19,13 @@
import io.quarkus.deployment.annotations.Produce;
import io.quarkus.deployment.builditem.BytecodeRecorderObjectLoaderBuildItem;
import io.quarkus.deployment.builditem.ClassOutputBuildItem;
import io.quarkus.deployment.builditem.ExecutionChainBuildItem;
import io.quarkus.deployment.builditem.ExecutionHandlerBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem;
import io.quarkus.deployment.builditem.ObjectSubstitutionBuildItem;
import io.quarkus.deployment.builditem.QuarkusApplicationBuildItem;
import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem;
import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem;
import io.quarkus.deployment.recording.BytecodeRecorderImpl;
import io.quarkus.gizmo.CatchBlockCreator;
import io.quarkus.gizmo.ClassCreator;
Expand All @@ -44,7 +39,6 @@
import io.quarkus.runtime.execution.ExecutionChain;
import io.quarkus.runtime.execution.ExecutionContext;
import io.quarkus.runtime.execution.ExecutionHandler;
import io.quarkus.runtime.execution.SupplierExecutionHandler;

/**
* A build step which assembles all of the execution handlers into a serialized chain.
Expand All @@ -64,64 +58,13 @@ public final class ExecutionHandlersBuildStep {
private static final MethodDescriptor WITH_VALUES = MethodDescriptor.ofMethod(ExecutionContext.class, "withValues",
ExecutionContext.class, String.class, Object.class, String.class, Object.class);
static final MethodDescriptor PROCEED = ofMethod(ExecutionChain.class, "proceed", int.class, ExecutionContext.class);

@BuildStep
@Produce(QuarkusApplicationBuildItem.class)
public void generateStaticInitRecorders(
ClassOutputBuildItem outputItem,
List<StaticBytecodeRecorderBuildItem> staticInitTasks,
List<ObjectSubstitutionBuildItem> substitutions,
List<BytecodeRecorderObjectLoaderBuildItem> loaders) {

try (ClassCreator cc = ClassCreator.builder().className(INIT_CLASS_NAME)
.classOutput(ClassOutput.gizmoAdaptor(outputItem.getClassOutput(), true))
.setFinal(true).superClass(Object.class).build()) {

try (MethodCreator mv = cc.getMethodCreator("getInitialContext", ExecutionContext.class)) {
mv.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);

// set sys props
mv.invokeStaticMethod(ofMethod(SYSTEM_PROPS_CLASS_NAME, "setProperties", void.class));

// call generated steps
ResultHandle startupContext = mv.newInstance(ofConstructor(StartupContext.class));
try (TryBlock tryBlock = mv.tryBlock()) {
for (StaticBytecodeRecorderBuildItem recItem : staticInitTasks) {
final BytecodeRecorderImpl recorder = recItem.getBytecodeRecorder();
if (!recorder.isEmpty()) {
// Register substitutions in all recorders
for (ObjectSubstitutionBuildItem sub : substitutions) {
ObjectSubstitutionBuildItem.Holder holder1 = sub.holder;
recorder.registerSubstitution(holder1.from, holder1.to, holder1.substitution);
}
for (BytecodeRecorderObjectLoaderBuildItem item : loaders) {
recorder.registerObjectLoader(item.getObjectLoader());
}
recorder.writeBytecode(outputItem.getClassOutput());

tryBlock.invokeStaticMethod(
ofMethod(recorder.getClassName(), "initialize", void.class, StartupContext.class),
startupContext);
}
}
final ResultHandle empty = tryBlock.readStaticField(EMPTY);
final ResultHandle ctx1 = tryBlock.invokeVirtualMethod(ADD_VALUES_TO, startupContext, empty);
final ResultHandle ctx2 = tryBlock.invokeVirtualMethod(WITH_CLOSEABLE, ctx1, startupContext);
tryBlock.returnValue(ctx2);
try (CatchBlockCreator cb = tryBlock.addCatch(Throwable.class)) {
cb.invokeVirtualMethod(ofMethod(StartupContext.class, "close", void.class), startupContext);
cb.throwException(RuntimeException.class, "Failed to start quarkus", cb.getCaughtException());
}
}
}
}
}
static final MethodDescriptor CTOR_EXECUTION_CHAIN = MethodDescriptor.ofConstructor(ExecutionChain.class,
ExecutionChain.class, ExecutionHandler.class);

@BuildStep
@Produce(QuarkusApplicationBuildItem.class)
public ExecutionHandlerBuildItem versionAndFeatures(
List<FeatureBuildItem> featuresItems,
Consumer<ReflectiveClassBuildItem> reflectiveClasses,
ClassOutputBuildItem outputItem) {
try (ClassCreator cc = ClassCreator.builder().className(VERSION_AND_FEATURES_CLASS_NAME)
.classOutput(ClassOutput.gizmoAdaptor(outputItem.getClassOutput(), true))
Expand All @@ -140,8 +83,7 @@ public ExecutionHandlerBuildItem versionAndFeatures(
final ResultHandle result = mv.invokeVirtualMethod(PROCEED, mv.getMethodParam(0), newCtx);
mv.returnValue(result);
}
reflectiveClasses.accept(new ReflectiveClassBuildItem(true, false, false, VERSION_AND_FEATURES_CLASS_NAME));
return new ExecutionHandlerBuildItem(new SupplierExecutionHandler(VERSION_AND_FEATURES_CLASS_NAME));
return new ExecutionHandlerBuildItem(VERSION_AND_FEATURES_CLASS_NAME);
}
}

Expand All @@ -152,7 +94,6 @@ public void generateMainRecorders(
List<MainBytecodeRecorderBuildItem> mainTasks,
List<ObjectSubstitutionBuildItem> substitutions,
List<BytecodeRecorderObjectLoaderBuildItem> loaders,
Consumer<ReflectiveClassBuildItem> reflectiveClasses,
Consumer<ExecutionHandlerBuildItem> executionHandlers) {

for (MainBytecodeRecorderBuildItem recItem : mainTasks) {
Expand All @@ -167,48 +108,36 @@ public void generateMainRecorders(
recorder.registerObjectLoader(item.getObjectLoader());
}
recorder.writeBytecode(outputItem.getClassOutput());
executionHandlers.accept(new ExecutionHandlerBuildItem(new SupplierExecutionHandler(recorder.getClassName())));
reflectiveClasses.accept(new ReflectiveClassBuildItem(true, false, false, recorder.getClassName()));
}
}
}

@BuildStep
@Produce(QuarkusApplicationBuildItem.class)
public GeneratedResourceBuildItem serializeExecutionChain(ExecutionChainBuildItem chainBuildItem) throws IOException {
ExecutionChain chain = chainBuildItem.getChain();

// Serialize the chain
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos = new ObjectOutputStream(os)) {
oos.writeObject(chain);
executionHandlers.accept(new ExecutionHandlerBuildItem(recorder.getClassName()));
}
return new GeneratedResourceBuildItem("META-INF/quarkus.init", os.toByteArray());
}
}

@BuildStep
@Produce(QuarkusApplicationBuildItem.class)
public ExecutionChainBuildItem assembleHandlers(
public void assembleHandlers(
List<ExecutionHandlerBuildItem> handlerItems,
List<SystemPropertyBuildItem> propertyItems,
Consumer<ReflectiveClassBuildItem> reflectiveClasses,
List<ObjectSubstitutionBuildItem> substitutions,
List<StaticBytecodeRecorderBuildItem> staticInitTasks,
List<BytecodeRecorderObjectLoaderBuildItem> loaders,
ClassOutputBuildItem outputItem) {
ExecutionChain chain = null;
final ListIterator<ExecutionHandlerBuildItem> itr = handlerItems.listIterator(handlerItems.size());
while (itr.hasPrevious()) {
final ExecutionHandlerBuildItem item = itr.previous();
final ExecutionHandler handler = item.getHandler();
chain = new ExecutionChain(chain, handler);

final List<MethodDescriptor> descriptors = new ArrayList<>();

descriptors.add(MethodDescriptor.ofConstructor(SYSTEM_PROPS_CLASS_NAME));
descriptors.add(MethodDescriptor.ofMethod(ConfigurationHandler.class, "getInstance", ConfigurationHandler.class));

for (ExecutionHandlerBuildItem handlerItem : handlerItems) {
descriptors.add(handlerItem.getMethodDescriptor());
}

Map<String, String> properties = new TreeMap<>();
for (SystemPropertyBuildItem propertyItem : propertyItems) {
if (properties.putIfAbsent(propertyItem.getKey(), propertyItem.getValue()) != null) {
throw new IllegalArgumentException("Multiple values for property \"" + propertyItem.getKey() + "\" given");
}
}
// TODO: temporary
chain = new ExecutionChain(chain, ConfigurationHandler.getInstance());

// System properties
try (ClassCreator cc = ClassCreator.builder().className(SYSTEM_PROPS_CLASS_NAME)
Expand All @@ -231,8 +160,67 @@ public ExecutionChainBuildItem assembleHandlers(
}
}

chain = new ExecutionChain(chain, new SupplierExecutionHandler(SYSTEM_PROPS_CLASS_NAME));
reflectiveClasses.accept(new ReflectiveClassBuildItem(true, false, false, SYSTEM_PROPS_CLASS_NAME));
return new ExecutionChainBuildItem(chain);
try (ClassCreator cc = ClassCreator.builder().className(INIT_CLASS_NAME)
.classOutput(ClassOutput.gizmoAdaptor(outputItem.getClassOutput(), true))
.setFinal(true).superClass(Object.class).build()) {

// Static initializers
try (MethodCreator mv = cc.getMethodCreator("getInitialContext", ExecutionContext.class)) {
mv.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);

// set sys props
mv.invokeStaticMethod(ofMethod(SYSTEM_PROPS_CLASS_NAME, "setProperties", void.class));

// call generated steps
ResultHandle startupContext = mv.newInstance(ofConstructor(StartupContext.class));
try (TryBlock tryBlock = mv.tryBlock()) {
for (StaticBytecodeRecorderBuildItem recItem : staticInitTasks) {
final BytecodeRecorderImpl recorder = recItem.getBytecodeRecorder();
if (!recorder.isEmpty()) {
// Register substitutions in all recorders
for (ObjectSubstitutionBuildItem sub : substitutions) {
ObjectSubstitutionBuildItem.Holder holder1 = sub.holder;
recorder.registerSubstitution(holder1.from, holder1.to, holder1.substitution);
}
for (BytecodeRecorderObjectLoaderBuildItem item : loaders) {
recorder.registerObjectLoader(item.getObjectLoader());
}
recorder.writeBytecode(outputItem.getClassOutput());

tryBlock.invokeStaticMethod(
ofMethod(recorder.getClassName(), "initialize", void.class, StartupContext.class),
startupContext);
}
}
final ResultHandle empty = tryBlock.readStaticField(EMPTY);
final ResultHandle ctx1 = tryBlock.invokeVirtualMethod(ADD_VALUES_TO, startupContext, empty);
final ResultHandle ctx2 = tryBlock.invokeVirtualMethod(WITH_CLOSEABLE, ctx1, startupContext);
tryBlock.returnValue(ctx2);
try (CatchBlockCreator cb = tryBlock.addCatch(Throwable.class)) {
cb.invokeVirtualMethod(ofMethod(StartupContext.class, "close", void.class), startupContext);
cb.throwException(RuntimeException.class, "Failed to start quarkus", cb.getCaughtException());
}
}
}

// Initial chain construction
try (MethodCreator mv = cc.getMethodCreator("getInitialChain", ExecutionChain.class)) {
mv.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);

ResultHandle item = mv.loadNull();

ListIterator<MethodDescriptor> itr = descriptors.listIterator(descriptors.size());
while (itr.hasPrevious()) {
MethodDescriptor desc = itr.previous();
if ("<init>".equals(desc.getName())) {
item = mv.newInstance(CTOR_EXECUTION_CHAIN, item, mv.newInstance(desc));
} else {
item = mv.newInstance(CTOR_EXECUTION_CHAIN, item, mv.invokeStaticMethod(desc));
}
}

mv.returnValue(item);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import io.quarkus.deployment.ClassOutput;
import io.quarkus.deployment.QuarkusAugmentor;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.ExecutionChainBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.builditem.QuarkusApplicationBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ProfileManager;
import io.quarkus.runtime.execution.AsynchronousExitException;
Expand Down Expand Up @@ -95,7 +95,7 @@ public void run() {
builder.addBuildChainCustomizer(i);
}
builder.addFinal(BytecodeTransformerBuildItem.class)
.addFinal(ExecutionChainBuildItem.class);
.addFinal(QuarkusApplicationBuildItem.class);

BuildResult result = builder.build().run();
List<BytecodeTransformerBuildItem> bytecodeTransformerBuildItems = result
Expand All @@ -111,11 +111,12 @@ public void run() {

ClassLoader old = Thread.currentThread().getContextClassLoader();
final ExecutionContext ctxt;
final ExecutionChain chain;
try {
Thread.currentThread().setContextClassLoader(loader);
ExecutionChain chain = result.consume(ExecutionChainBuildItem.class).getChain();
ctxt = (ExecutionContext) Class.forName("io.quarkus.runtime.generated.Init", true, loader)
.getDeclaredMethod("getInitialContext").invoke(null);
final Class<?> initClass = Class.forName("io.quarkus.runtime.generated.Init", true, loader);
chain = (ExecutionChain) initClass.getDeclaredMethod("getInitialChain").invoke(null);
ctxt = (ExecutionContext) initClass.getDeclaredMethod("getInitialContext").invoke(null);
try {
chain.startAsynchronously(ctxt);
} catch (Throwable t) {
Expand Down
Loading

0 comments on commit 84b2caf

Please sign in to comment.