implements PR-O4.2
This commit is contained in:
parent
d4f6d96437
commit
3449771f33
@ -18,6 +18,7 @@ import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||
import p.studio.compiler.source.tables.CallableTable;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.compiler.source.tables.IntrinsicTable;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
@ -124,7 +125,7 @@ public final class PbsFrontendCompiler {
|
||||
: lowerFunctions(fileId, ast);
|
||||
final var loweringErrorBaseline = diagnostics.errorCount();
|
||||
final var executableLowering = sourceKind == SourceKind.SDK_INTERFACE
|
||||
? new ExecutableLoweringResult(ReadOnlyList.empty(), ReadOnlyList.empty())
|
||||
? new ExecutableLoweringResult(ReadOnlyList.empty(), ReadOnlyList.empty(), ReadOnlyList.empty())
|
||||
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, diagnostics);
|
||||
if (diagnostics.errorCount() > loweringErrorBaseline) {
|
||||
return IRBackendFile.empty(fileId);
|
||||
@ -134,7 +135,8 @@ public final class PbsFrontendCompiler {
|
||||
functions,
|
||||
executableLowering.executableFunctions(),
|
||||
reservedMetadata,
|
||||
executableLowering.callableSignatures());
|
||||
executableLowering.callableSignatures(),
|
||||
executableLowering.intrinsicPool());
|
||||
}
|
||||
|
||||
private ReadOnlyList<IRFunction> lowerFunctions(final FileId fileId, final PbsAst.File ast) {
|
||||
@ -312,9 +314,14 @@ public final class PbsFrontendCompiler {
|
||||
for (final var callableId : callableIdTable.identifiers()) {
|
||||
callableSignatures.add(callableIdTable.get(callableId));
|
||||
}
|
||||
final var intrinsicPool = new ArrayList<IntrinsicReference>(intrinsicIdTable.size());
|
||||
for (final var intrinsicId : intrinsicIdTable.identifiers()) {
|
||||
intrinsicPool.add(intrinsicIdTable.get(intrinsicId));
|
||||
}
|
||||
return new ExecutableLoweringResult(
|
||||
ReadOnlyList.wrap(executableFunctions),
|
||||
ReadOnlyList.wrap(callableSignatures));
|
||||
ReadOnlyList.wrap(callableSignatures),
|
||||
ReadOnlyList.wrap(intrinsicPool));
|
||||
}
|
||||
|
||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||
@ -550,6 +557,7 @@ public final class PbsFrontendCompiler {
|
||||
|
||||
private record ExecutableLoweringResult(
|
||||
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures,
|
||||
ReadOnlyList<IntrinsicReference> intrinsicPool) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package p.studio.compiler.backend.irvm;
|
||||
public enum IRVMLoweringErrorCode {
|
||||
LOWER_IRVM_EMPTY_EXECUTABLE_INPUT,
|
||||
LOWER_IRVM_MISSING_CALLEE,
|
||||
LOWER_IRVM_INVALID_INTRINSIC_ID,
|
||||
LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,
|
||||
LOWER_IRVM_CALL_RET_SLOTS_MISMATCH,
|
||||
LOWER_IRVM_MISSING_JUMP_TARGET,
|
||||
|
||||
@ -121,6 +121,11 @@ public class LowerToIRVMService {
|
||||
}
|
||||
case CALL_INTRINSIC -> {
|
||||
final var intrinsic = instr.intrinsicCall();
|
||||
if (intrinsic.intrinsicId() < 0 || intrinsic.intrinsicId() >= backend.getIntrinsicPool().size()) {
|
||||
throw new IRVMLoweringException(
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_INVALID_INTRINSIC_ID,
|
||||
"invalid intrinsic id: " + intrinsic.intrinsicId());
|
||||
}
|
||||
instructions.add(new IRVMInstruction(IRVMOp.INTRINSIC, intrinsic.intrinsicId()));
|
||||
operations.add(BytecodeEmitter.Operation.intrinsic(intrinsic.intrinsicId(), sourceSpan));
|
||||
functionPc += IRVMOp.INTRINSIC.immediateSize() + 2;
|
||||
|
||||
@ -5,6 +5,7 @@ import p.studio.compiler.models.IRBackend;
|
||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@ -39,8 +40,9 @@ class LowerToIRVMServiceTest {
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("main", "app", 10, ReadOnlyList.from(
|
||||
callHost("gfx", "draw_pixel", 1, 2, 0),
|
||||
callIntrinsic("core.color.pack", 1, 0x2001),
|
||||
callIntrinsic("core.color.pack", 1, 0),
|
||||
ret()))))
|
||||
.intrinsicPool(ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1)))
|
||||
.build();
|
||||
|
||||
final var lowered = new LowerToIRVMService().lower(backend);
|
||||
@ -48,7 +50,7 @@ class LowerToIRVMServiceTest {
|
||||
final var instructions = lowered.module().functions().getFirst().instructions();
|
||||
assertEquals(IRVMOp.HOSTCALL, instructions.get(0).op());
|
||||
assertEquals(IRVMOp.INTRINSIC, instructions.get(1).op());
|
||||
assertEquals(0x2001, instructions.get(1).immediate());
|
||||
assertEquals(0, instructions.get(1).immediate());
|
||||
final var emissionOps = lowered.emissionPlan().functions().getFirst().operations();
|
||||
assertEquals(p.studio.compiler.backend.bytecode.BytecodeEmitter.OperationKind.HOSTCALL, emissionOps.get(0).kind());
|
||||
assertEquals(p.studio.compiler.backend.bytecode.BytecodeEmitter.OperationKind.INTRINSIC, emissionOps.get(1).kind());
|
||||
|
||||
@ -11,6 +11,7 @@ import p.studio.compiler.models.IRBackend;
|
||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.compiler.workspaces.stages.EmitBytecodePipelineStage;
|
||||
import p.studio.compiler.workspaces.stages.LowerToIRVMPipelineStage;
|
||||
import p.studio.compiler.workspaces.stages.OptimizeIRVMPipelineStage;
|
||||
@ -141,8 +142,9 @@ class BackendGateIIntegrationTest {
|
||||
void gateI_validIntrinsicPath() {
|
||||
final var module = emitFromBackend(backendWithSingleFunction(
|
||||
fn("main", ReadOnlyList.from(
|
||||
callIntrinsic("core.color.pack", 1, 0x2000),
|
||||
ret()))));
|
||||
callIntrinsic("core.color.pack", 1, 0),
|
||||
ret())),
|
||||
ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1))));
|
||||
|
||||
final var check = compatibilityAdapter.check(module);
|
||||
assertEquals(CompatibilityError.NONE, check.error());
|
||||
@ -166,8 +168,15 @@ class BackendGateIIntegrationTest {
|
||||
}
|
||||
|
||||
private IRBackend backendWithSingleFunction(final IRBackendExecutableFunction function) {
|
||||
return backendWithSingleFunction(function, ReadOnlyList.empty());
|
||||
}
|
||||
|
||||
private IRBackend backendWithSingleFunction(
|
||||
final IRBackendExecutableFunction function,
|
||||
final ReadOnlyList<IntrinsicReference> intrinsicPool) {
|
||||
return IRBackend.builder()
|
||||
.executableFunctions(ReadOnlyList.from(function))
|
||||
.intrinsicPool(intrinsicPool)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||
import p.studio.compiler.source.tables.CallableTable;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.compiler.source.tables.IntrinsicTable;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -19,6 +21,8 @@ public class IRBackend {
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<CallableSignatureRef> callableSignatures = ReadOnlyList.empty();
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<IntrinsicReference> intrinsicPool = ReadOnlyList.empty();
|
||||
@Builder.Default
|
||||
private final IRReservedMetadata reservedMetadata = IRReservedMetadata.empty();
|
||||
|
||||
public static IRBackendAggregator aggregator() {
|
||||
@ -29,6 +33,7 @@ public class IRBackend {
|
||||
private final ArrayList<IRFunction> functions = new ArrayList<>();
|
||||
private final ArrayList<IRBackendExecutableFunction> executableFunctions = new ArrayList<>();
|
||||
private final CallableTable callableTable = new CallableTable();
|
||||
private final IntrinsicTable intrinsicTable = new IntrinsicTable();
|
||||
private final ArrayList<IRReservedMetadata.HostMethodBinding> hostMethodBindings = new ArrayList<>();
|
||||
private final ArrayList<IRReservedMetadata.BuiltinTypeSurface> builtinTypeSurfaces = new ArrayList<>();
|
||||
private final ArrayList<IRReservedMetadata.BuiltinConstSurface> builtinConstSurfaces = new ArrayList<>();
|
||||
@ -40,8 +45,9 @@ public class IRBackend {
|
||||
}
|
||||
functions.addAll(backendFile.functions().asList());
|
||||
final var callableRemap = reindexCallables(backendFile.callableSignatures());
|
||||
final var intrinsicRemap = reindexIntrinsics(backendFile.intrinsicPool());
|
||||
for (final var function : backendFile.executableFunctions()) {
|
||||
executableFunctions.add(remapExecutableFunction(function, callableRemap));
|
||||
executableFunctions.add(remapExecutableFunction(function, callableRemap, intrinsicRemap));
|
||||
}
|
||||
final var metadata = backendFile.reservedMetadata();
|
||||
hostMethodBindings.addAll(metadata.hostMethodBindings().asList());
|
||||
@ -63,7 +69,8 @@ public class IRBackend {
|
||||
|
||||
private IRBackendExecutableFunction remapExecutableFunction(
|
||||
final IRBackendExecutableFunction function,
|
||||
final int[] callableRemap) {
|
||||
final int[] callableRemap,
|
||||
final int[] intrinsicRemap) {
|
||||
final var remappedInstructions = new ArrayList<IRBackendExecutableFunction.Instruction>(function.instructions().size());
|
||||
for (final var instruction : function.instructions()) {
|
||||
final Integer remappedCallee;
|
||||
@ -72,13 +79,23 @@ public class IRBackend {
|
||||
} else {
|
||||
remappedCallee = remapCallableId(instruction.calleeCallableId(), callableRemap, "callee");
|
||||
}
|
||||
final IRBackendExecutableFunction.IntrinsicCallMetadata remappedIntrinsic;
|
||||
if (instruction.kind() == IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC && instruction.intrinsicCall() != null) {
|
||||
final var intrinsic = instruction.intrinsicCall();
|
||||
remappedIntrinsic = new IRBackendExecutableFunction.IntrinsicCallMetadata(
|
||||
intrinsic.canonicalName(),
|
||||
intrinsic.canonicalVersion(),
|
||||
remapIntrinsicId(intrinsic.intrinsicId(), intrinsicRemap));
|
||||
} else {
|
||||
remappedIntrinsic = instruction.intrinsicCall();
|
||||
}
|
||||
remappedInstructions.add(new IRBackendExecutableFunction.Instruction(
|
||||
instruction.kind(),
|
||||
instruction.calleeModuleKey(),
|
||||
instruction.calleeCallableName(),
|
||||
remappedCallee,
|
||||
instruction.hostCall(),
|
||||
instruction.intrinsicCall(),
|
||||
remappedIntrinsic,
|
||||
instruction.label(),
|
||||
instruction.targetLabel(),
|
||||
instruction.expectedArgSlots(),
|
||||
@ -110,6 +127,26 @@ public class IRBackend {
|
||||
return callableRemap[localCallableId];
|
||||
}
|
||||
|
||||
private int[] reindexIntrinsics(final ReadOnlyList<IntrinsicReference> localIntrinsicPool) {
|
||||
if (localIntrinsicPool == null || localIntrinsicPool.isEmpty()) {
|
||||
return new int[0];
|
||||
}
|
||||
final var remap = new int[localIntrinsicPool.size()];
|
||||
for (var i = 0; i < localIntrinsicPool.size(); i++) {
|
||||
remap[i] = intrinsicTable.register(localIntrinsicPool.get(i)).getId();
|
||||
}
|
||||
return remap;
|
||||
}
|
||||
|
||||
private int remapIntrinsicId(
|
||||
final int localIntrinsicId,
|
||||
final int[] intrinsicRemap) {
|
||||
if (localIntrinsicId < 0 || localIntrinsicId >= intrinsicRemap.length) {
|
||||
throw new IllegalArgumentException("invalid local intrinsic id: " + localIntrinsicId);
|
||||
}
|
||||
return intrinsicRemap[localIntrinsicId];
|
||||
}
|
||||
|
||||
private ReadOnlyList<CallableSignatureRef> emitCallableSignatures() {
|
||||
final var signatures = new ArrayList<CallableSignatureRef>(callableTable.size());
|
||||
for (final var callableId : callableTable.identifiers()) {
|
||||
@ -118,12 +155,21 @@ public class IRBackend {
|
||||
return ReadOnlyList.wrap(signatures);
|
||||
}
|
||||
|
||||
private ReadOnlyList<IntrinsicReference> emitIntrinsicPool() {
|
||||
final var pool = new ArrayList<IntrinsicReference>(intrinsicTable.size());
|
||||
for (final var intrinsicId : intrinsicTable.identifiers()) {
|
||||
pool.add(intrinsicTable.get(intrinsicId));
|
||||
}
|
||||
return ReadOnlyList.wrap(pool);
|
||||
}
|
||||
|
||||
public IRBackend emit() {
|
||||
return IRBackend
|
||||
.builder()
|
||||
.functions(ReadOnlyList.wrap(functions))
|
||||
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
|
||||
.callableSignatures(emitCallableSignatures())
|
||||
.intrinsicPool(emitIntrinsicPool())
|
||||
.reservedMetadata(new IRReservedMetadata(
|
||||
ReadOnlyList.wrap(hostMethodBindings),
|
||||
ReadOnlyList.wrap(builtinTypeSurfaces),
|
||||
@ -139,6 +185,7 @@ public class IRBackend {
|
||||
sb.append("IRBackend{functions=").append(functions.size())
|
||||
.append(", executableFunctions=").append(executableFunctions.size())
|
||||
.append(", callableSignatures=").append(callableSignatures.size())
|
||||
.append(", intrinsicPool=").append(intrinsicPool.size())
|
||||
.append(", hostBindings=").append(reservedMetadata.hostMethodBindings().size())
|
||||
.append(", builtinTypes=").append(reservedMetadata.builtinTypeSurfaces().size())
|
||||
.append(", builtinConsts=").append(reservedMetadata.builtinConstSurfaces().size())
|
||||
|
||||
@ -2,6 +2,7 @@ package p.studio.compiler.models;
|
||||
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -11,29 +12,31 @@ public record IRBackendFile(
|
||||
ReadOnlyList<IRFunction> functions,
|
||||
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||
IRReservedMetadata reservedMetadata,
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures,
|
||||
ReadOnlyList<IntrinsicReference> intrinsicPool) {
|
||||
public IRBackendFile {
|
||||
fileId = Objects.requireNonNull(fileId, "fileId");
|
||||
functions = functions == null ? ReadOnlyList.empty() : functions;
|
||||
executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions;
|
||||
reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata;
|
||||
callableSignatures = callableSignatures == null ? ReadOnlyList.empty() : callableSignatures;
|
||||
intrinsicPool = intrinsicPool == null ? ReadOnlyList.empty() : intrinsicPool;
|
||||
}
|
||||
|
||||
public IRBackendFile(
|
||||
final FileId fileId,
|
||||
final ReadOnlyList<IRFunction> functions) {
|
||||
this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty());
|
||||
this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty(), ReadOnlyList.empty());
|
||||
}
|
||||
|
||||
public static IRBackendFile empty(final FileId fileId) {
|
||||
return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty());
|
||||
return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty(), ReadOnlyList.empty());
|
||||
}
|
||||
|
||||
public IRBackendFile(
|
||||
final FileId fileId,
|
||||
final ReadOnlyList<IRFunction> functions,
|
||||
final IRReservedMetadata reservedMetadata) {
|
||||
this(fileId, functions, ReadOnlyList.empty(), reservedMetadata, ReadOnlyList.empty());
|
||||
this(fileId, functions, ReadOnlyList.empty(), reservedMetadata, ReadOnlyList.empty(), ReadOnlyList.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||
import p.studio.compiler.source.tables.IntrinsicReference;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@ -139,7 +140,8 @@ class IRBackendExecutableContractTest {
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
IRReservedMetadata.empty(),
|
||||
ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit")));
|
||||
ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit")),
|
||||
ReadOnlyList.empty());
|
||||
final var fileB = new IRBackendFile(
|
||||
new FileId(2),
|
||||
ReadOnlyList.empty(),
|
||||
@ -164,7 +166,8 @@ class IRBackendExecutableContractTest {
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
IRReservedMetadata.empty(),
|
||||
ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit")));
|
||||
ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit")),
|
||||
ReadOnlyList.empty());
|
||||
|
||||
final var aggregator = IRBackend.aggregator();
|
||||
aggregator.merge(fileA);
|
||||
@ -176,4 +179,69 @@ class IRBackendExecutableContractTest {
|
||||
assertEquals("aux", backend.getExecutableFunctions().get(1).callableName());
|
||||
assertEquals(2, backend.getCallableSignatures().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void aggregatorMustReindexIntrinsicsToBuildGlobalPool() {
|
||||
final var fileA = new IRBackendFile(
|
||||
new FileId(1),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction(
|
||||
new FileId(1),
|
||||
"app/main",
|
||||
"entry",
|
||||
0,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata("core.color.pack", 1, 0),
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
IRReservedMetadata.empty(),
|
||||
ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit")),
|
||||
ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1)));
|
||||
final var fileB = new IRBackendFile(
|
||||
new FileId(2),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction(
|
||||
new FileId(2),
|
||||
"app/main",
|
||||
"aux",
|
||||
0,
|
||||
11,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata("core.color.pack", 1, 0),
|
||||
Span.none())),
|
||||
Span.none())),
|
||||
IRReservedMetadata.empty(),
|
||||
ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit")),
|
||||
ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1)));
|
||||
|
||||
final var aggregator = IRBackend.aggregator();
|
||||
aggregator.merge(fileA);
|
||||
aggregator.merge(fileB);
|
||||
final var backend = aggregator.emit();
|
||||
|
||||
final var firstIntrinsicId = backend.getExecutableFunctions().get(0).instructions().getFirst().intrinsicCall().intrinsicId();
|
||||
final var secondIntrinsicId = backend.getExecutableFunctions().get(1).instructions().getFirst().intrinsicCall().intrinsicId();
|
||||
assertEquals(1, backend.getIntrinsicPool().size());
|
||||
assertEquals(firstIntrinsicId, secondIntrinsicId);
|
||||
assertEquals("core.color.pack", backend.getIntrinsicPool().getFirst().canonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user