blob: b319253d6734419c804befd60ab04b03be2818ca [file] [log] [blame]
// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
import com.android.tools.r8.assistant.ClassInjectionHelper;
import com.android.tools.r8.assistant.runtime.EmptyReflectiveOperationReceiver;
import com.android.tools.r8.assistant.runtime.ReflectiveOperationReceiver;
import com.android.tools.r8.assistant.runtime.ReflectiveOracle;
import com.android.tools.r8.assistant.runtime.ReflectiveOracle.ReflectiveOperationLogger;
import com.android.tools.r8.assistant.runtime.ReflectiveOracle.Stack;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Backend;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DumpInputFlags;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.Collections;
/**
* This is an experimental API for injecting reflective identification callbacks into dex code. This
* API is subject to change.
*/
@KeepForApi
public class R8AssistantCommand extends BaseCompilerCommand {
private final String reflectiveReceiverDescriptor;
public R8AssistantCommand(
AndroidApp app,
CompilationMode mode,
ProgramConsumer programConsumer,
int minApiLevel,
Reporter reporter,
String reflectiveReceiverDescriptor) {
super(
app,
mode,
programConsumer,
StringConsumer.emptyConsumer(),
minApiLevel,
reporter,
DesugarState.ON,
false,
false,
(a, b) -> true,
Collections.emptyList(),
Collections.emptyList(),
ThreadUtils.NOT_SPECIFIED,
DumpInputFlags.noDump(),
null,
null,
false,
Collections.emptyList(),
Collections.emptyList(),
null,
null);
this.reflectiveReceiverDescriptor = reflectiveReceiverDescriptor;
}
public static Builder builder(DiagnosticsHandler reporter) {
return new Builder(reporter);
}
public static Builder builder() {
return new Builder();
}
@Override
InternalOptions getInternalOptions() {
DexItemFactory factory = new DexItemFactory();
InternalOptions options = new InternalOptions(factory, getReporter());
options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getMinApiLevel()));
options.passthroughDexCode = true;
options.tool = Tool.R8Assistant;
Marker marker = new Marker(Tool.R8Assistant);
marker.setBackend(Backend.DEX);
marker.setMinApi(getMinApiLevel());
options.setMarker(marker);
options.programConsumer = getProgramConsumer();
return options;
}
public String getReflectiveReceiverDescriptor() {
return reflectiveReceiverDescriptor;
}
/**
* This is an experimental API for injecting reflective identification callbacks into dex code.
* This API is subject to change.
*/
@KeepForApi
public static class Builder extends BaseCompilerCommand.Builder<R8AssistantCommand, Builder> {
private String reflectiveReceiverDescriptor;
private Builder() {
this(new DiagnosticsHandler() {});
}
@Override
void validate() {
if (!(getProgramConsumer() instanceof DexIndexedConsumer)) {
getReporter().error("R8 assistant does not support CF output.");
}
if (!hasNativeMultidex()) {
getReporter().error("R8 assistant requires min api >= 21");
}
}
public Builder addReflectiveOperationReceiverInput(ProgramResourceProvider provider) {
// This code is simply added to the program input, will be called by the ReflectiveOracle.
addProgramResourceProvider(provider);
return self();
}
public Builder setReflectiveReceiverClassDescriptor(String descriptor) {
if (!DescriptorUtils.isClassDescriptor(descriptor)) {
getReporter().error("Not a valid descriptor " + descriptor);
}
this.reflectiveReceiverDescriptor = descriptor;
return self();
}
@Override
CompilationMode defaultCompilationMode() {
return CompilationMode.RELEASE;
}
private Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
}
@Override
Builder self() {
return this;
}
@Override
R8AssistantCommand makeCommand() {
injectClasses(
EmptyReflectiveOperationReceiver.class,
ReflectiveOperationLogger.class,
ReflectiveOperationReceiver.NameLookupType.class,
ReflectiveOperationReceiver.class,
ReflectiveOracle.class,
Stack.class);
return new R8AssistantCommand(
getAppBuilder().build(),
getMode(),
getProgramConsumer(),
getMinApiLevel(),
getReporter(),
reflectiveReceiverDescriptor);
}
private void injectClasses(Class<?>... classes) {
ClassInjectionHelper injectionHelper = new ClassInjectionHelper(getReporter());
String reason = "Reflective instrumentation";
for (Class<?> clazz : classes) {
addClassProgramData(
injectionHelper.getClassBytes(clazz), new SynthesizedOrigin(reason, clazz));
}
}
}
}