mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-12 16:37:38 +00:00
build: compile directly for Java 8
Keep modern java out of that for now, it make build script too complicated as long as scala still here to force J8 path
This commit is contained in:
1 parent
d3f1b561a3
commit
12eb146d02
14 files changed
+376
-330
No files matched your search
@@ -26,9 +26,7 @@ jobs:
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: |
|
||||
8
|
||||
25
|
||||
java-version: 8
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
|
||||
@@ -6,8 +6,7 @@ An API for dynamically handling different functional parts in the one block spac
|
||||
Requirements:
|
||||
* The latest version of CCL (files.minecraftforge.net/CodeChickenLib/)
|
||||
* Forge
|
||||
* JDK 8 and JDK 25. The mod targets Java 8, but a small set of helpers is compiled at Java 21 by the
|
||||
`compileModernJava` task and downgraded to Java 8, so both toolchains must be resolvable to build.
|
||||
* JDK 8. The mod compiles directly for Java 8.
|
||||
* A scala compatible IDE and project (For eclipse, download the scala eclipse plugin and right click on the project -> Add Scala Nature).
|
||||
Most of the codebase is Java now; the Java sources live under `src/main/scala` so they can be joint-compiled
|
||||
with the Scala files that remain.
|
||||
|
||||
+1
-46
@@ -25,52 +25,7 @@ tasks.test {
|
||||
}
|
||||
}
|
||||
|
||||
// Scope modern syntax to these helpers; retained Scala and joint Java stay on Java 8.
|
||||
apply plugin: xyz.wagyourtail.jvmdg.gradle.JVMDowngraderPlugin
|
||||
|
||||
// Single source of truth for the modern-syntax helpers. These are compiled by compileModernJava
|
||||
// and must be excluded from compileScala; keep one list, because a file excluded from the joint
|
||||
// Scala compile but absent from this list is never compiled at all, and nothing reports it.
|
||||
def modernJavaPaths = [
|
||||
'codechicken/multipart/asm/StackAnalyserLogic.java',
|
||||
'codechicken/multipart/asm/JavaTraitRegistration.java',
|
||||
'codechicken/multipart/asm/ClassInfoLookup.java',
|
||||
'codechicken/multipart/asm/ScalaSignatureParser.java',
|
||||
'codechicken/microblock/HollowMicroblockTraitLogic.java',
|
||||
'codechicken/microblock/PostMicroblockClientLogic.java',
|
||||
'codechicken/microblock/HollowMicroblockClientLogic.java',
|
||||
'codechicken/microblock/TMicroOcclusionLogic.java',
|
||||
]
|
||||
def modernJavaSources = sourceSets.main.scala.matching { include modernJavaPaths }
|
||||
def legacyScala = tasks.named('compileScala') {
|
||||
tasks.named('compileScala') {
|
||||
// Zinc does not invalidate joint-compiled Java annotations when Tags.VERSION changes.
|
||||
scalaCompileOptions.force = true
|
||||
// Scalac reads the Java declaration without asking Java 8 javac to compile its body.
|
||||
exclude modernJavaPaths
|
||||
scalaCompileOptions.additionalParameters = ['-sourcepath', file('src/main/scala').absolutePath]
|
||||
inputs.files(modernJavaSources).withPropertyName('modernJavaDeclarations').withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
}
|
||||
|
||||
def compileModernJava = tasks.register('compileModernJava', JavaCompile) {
|
||||
source(modernJavaSources)
|
||||
classpath = sourceSets.main.compileClasspath + files(
|
||||
tasks.named('compileJava').flatMap { it.destinationDirectory },
|
||||
legacyScala.flatMap { it.destinationDirectory })
|
||||
destinationDirectory = layout.buildDirectory.dir('classes/java/modern')
|
||||
javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(25) }
|
||||
options.release = 21
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
def downgradeModernJava = tasks.register('downgradeModernJava', xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles) {
|
||||
inputCollection = files(compileModernJava.flatMap { it.destinationDirectory })
|
||||
classpath = compileModernJava.get().classpath
|
||||
downgradeTo = JavaVersion.VERSION_1_8
|
||||
multiReleaseOriginal = false
|
||||
multiReleaseVersions = [] as Set
|
||||
// The bundled plugin otherwise declares outputs only when inputs already exist.
|
||||
outputs.dirs(outputMap.values())
|
||||
}
|
||||
|
||||
sourceSets.main.output.classesDirs.from(downgradeModernJava.map { it.outputCollection })
|
||||
tasks.named('classes') { dependsOn(downgradeModernJava) }
|
||||
@@ -205,14 +205,6 @@ These are accepted once for the migration, not repeated for every otherwise equi
|
||||
the retained getter and old Iguana reflection. Its name, type and visibility remain unchanged.
|
||||
- TickScheduler's private Scala scheduler types become package-private static nested Java classes, retaining their
|
||||
binary names.
|
||||
- The modern-syntax helpers that have nested or anonymous classes carry `xyz.wagyourtail.jvmdg.j11.NestHost` and
|
||||
`NestMembers` annotations on 23 packaged classes, recording nest relationships Java 8 class files cannot express.
|
||||
These annotation types are deliberately not shipped and JVM Downgrader is not a runtime dependency: the packaged
|
||||
classes contain no executable Downgrader API reference, and reflection skips an annotation whose type is absent
|
||||
instead of throwing, so `getAnnotations()` on these classes returns an empty array on a Java 8 runtime. Forge and
|
||||
FMP's own scanners read annotation tables through ASM without loading the types. Do not shade the Downgrader API
|
||||
jar to resolve them; that would place FMP classes in a namespace it does not own and duplicate types other
|
||||
installed mods already provide.
|
||||
|
||||
## Updating this ledger
|
||||
|
||||
|
||||
@@ -10,29 +10,16 @@ These constraints explain the retained build and runtime machinery. Consumer mig
|
||||
modern-runtime policy spans Java 17–26; validate packaged releases on the selected supported runtime. Equal
|
||||
throughput across Java versions is not required. The deobfuscated Forge test runner uses Java 8; the current
|
||||
modern dedicated-server run tasks are not a substitute for packaged validation.
|
||||
- Keep `enableModernJavaSyntax = false` while Scala 2.11.5 remains. The convention plugin's global JVM Downgrader
|
||||
mode forces a modern toolchain that Scala cannot run on. With gtnhgradle 2.0.29, forcing Java 8 also conflicts
|
||||
with the mode's multi-release version constraints. Revisit the supported global mode after Scala removal,
|
||||
including an explicit stubs-provider decision; `gtnhlib` adds a runtime dependency and shading is rejected in
|
||||
[DIVERGENCES.md](DIVERGENCES.md).
|
||||
- Normal Java and joint Scala/Java compilation use Java 8. `modernJavaPaths` in `build.gradle` is the single list
|
||||
that both excludes modern helpers from joint javac and includes them in `compileModernJava`. Scalac resolves
|
||||
their declarations through `-sourcepath`; JDK 25 compiles their bodies with `--release 21` after Scala compilation.
|
||||
Only `downgradeModernJava`'s Java 8 output joins main outputs, tests, Forge and packaged jars.
|
||||
- Keep declaration inputs and downgrade output directories declared to Gradle. A modern-source exclusion without
|
||||
a matching inclusion can silently omit a class. Helpers importing retained Scala models cannot simply move to
|
||||
`src/main/java`: `compileJava` runs before those Scala types exist. Retire per-file routing when the dependency
|
||||
cycle permits a source-set boundary, and remove the special routing after Scala removal.
|
||||
- Keep `enableModernJavaSyntax = false` while Scala 2.11.5 remains. Normal Java and joint Scala/Java compilation
|
||||
use Java 8. Helpers importing retained Scala models belong in the joint `src/main/scala` source set because
|
||||
`compileJava` runs before those Scala types exist.
|
||||
- `scalaCompileOptions.force = true` prevents stale `Tags.VERSION` constants in joint-compiled Java `@Mod`
|
||||
annotations. Zinc otherwise misses those consumers when the generated constant changes. Verify all five mod
|
||||
annotations in dev/release artifacts against their artifact version; replace the guard only with equivalent coverage.
|
||||
- Modernize bounded helpers only after checking declaration visibility, compile order, ABI and generated output.
|
||||
Java 21 is the validated source ceiling. Records/sealed types are not automatic replacements for ABI-sensitive
|
||||
models. New library APIs may need runtime stubs even when syntax downgrades successfully.
|
||||
- Keep registered trait inputs and compatibility facades conservative. Moving `RedstoneInteractions$` or
|
||||
`BlockMultipart` pulls transformer-sensitive dependencies; `RenderPartResolver` is needed by joint compilation
|
||||
before the modern stage. Bootstrap and mixin source sets are not downgraded by this task. The explicit
|
||||
`Microblock` cast in `PostMicroblockTraitLogic` is required by the retained Scala declaration.
|
||||
before Java helpers that use it. The explicit `Microblock` cast in `PostMicroblockTraitLogic` is required by the
|
||||
retained Scala declaration.
|
||||
- GTNHLib is compile-only here. Its fastutil compile classpath does not grant a runtime dependency. Declare that
|
||||
dependency explicitly before using fastutil in ordinary code; require profiling evidence for such a change.
|
||||
|
||||
@@ -106,7 +93,7 @@ mechanical ports. Coverage percentages and signature counts alone are insufficie
|
||||
|
||||
A Scala-to-Java replacement needs clean verification: Zinc can retain the removed Scala class and package it
|
||||
instead of exercising the new source. Stop the daemon if Windows holds build outputs open. Verify packaged
|
||||
class version 52, unexpected downgrader API references and mod-version freshness, including after the release commit.
|
||||
class version 52 and mod-version freshness, including after the release commit.
|
||||
|
||||
JUnit reports are under `build/test-results/test/` and `run/server/junit-out/`; inspect counts, failures, errors and
|
||||
skips. The Forge task rejects missing/failed reports. Keep that CI gate required. Generated dumps are under
|
||||
|
||||
+1
-22
@@ -39,34 +39,13 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
|
||||
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
|
||||
developmentEnvironmentUserName = Developer
|
||||
|
||||
# Enables modern Java syntax support. Valid values:
|
||||
# - false: No modern syntax, Java 8 only
|
||||
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
|
||||
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
|
||||
# - modern: Native modern Java bytecode, no downgrading
|
||||
# Retained Scala and Java sources compile directly for Java 8.
|
||||
enableModernJavaSyntax = false
|
||||
|
||||
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
|
||||
# and should *not* be used in most situations. -1 disables this.
|
||||
# forceToolchainVersion = -1
|
||||
|
||||
# Target JVM version for JVM Downgrader bytecode downgrading.
|
||||
# Only used when enableModernJavaSyntax = jvmDowngrader
|
||||
# downgradeTargetVersion = 8
|
||||
|
||||
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
|
||||
# Classes will be available in META-INF/versions/N/ for each version N in this list.
|
||||
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
|
||||
# jvmDowngraderMultiReleaseVersions = 21,25
|
||||
|
||||
# Specifies how JVM Downgrader API stubs are provided. Options:
|
||||
# - shade: Shade minimized stubs into the jar
|
||||
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
|
||||
# - external: Another dependency provides stubs (no constraint, no warning)
|
||||
# - (empty): Warning reminding you to configure stubs
|
||||
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
|
||||
# jvmDowngraderStubsProvider =
|
||||
|
||||
# Enables injecting missing generics into the decompiled source code for a better coding experience.
|
||||
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
|
||||
enableGenericInjection = false
|
||||
|
||||
@@ -75,7 +75,8 @@ final class HollowMicroblockClientLogic {
|
||||
int slot = part.getSlot();
|
||||
int internalMask = 0;
|
||||
switch (slot) {
|
||||
case 0, 1 -> {
|
||||
case 0:
|
||||
case 1:
|
||||
if (face) internalMask = 0x3c;
|
||||
f.apply(pos, material, pass, new Cuboid6(d1, y1, d2, d2, y2, z2), 0x3b | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(d1, y1, z1, d2, y2, d1), 0x37 | internalMask);
|
||||
@@ -83,8 +84,9 @@ final class HollowMicroblockClientLogic {
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, y1, d1, d1, y2, d2), sideMask & 0x13 | 0xc | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, y1, d2, x2, y2, z2), sideMask & 0x3b | 4 | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, y1, z1, x2, y2, d1), sideMask & 0x37 | 8 | internalMask);
|
||||
}
|
||||
case 2, 3 -> {
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if (face) internalMask = 0x33;
|
||||
f.apply(pos, material, pass, new Cuboid6(d2, d1, z1, x2, d2, z2), 0x2f | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, d1, z1, d1, d2, z2), 0x1f | internalMask);
|
||||
@@ -102,8 +104,9 @@ final class HollowMicroblockClientLogic {
|
||||
pass,
|
||||
new Cuboid6(x1, y1, z1, d1, y2, z2),
|
||||
sideMask & 0x1f | 0x20 | internalMask);
|
||||
}
|
||||
case 4, 5 -> {
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (face) internalMask = 0xf;
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, d2, d1, x2, y2, d2), 0x3e | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, y1, d1, x2, d1, d2), 0x3d | internalMask);
|
||||
@@ -111,8 +114,9 @@ final class HollowMicroblockClientLogic {
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, d1, z1, x2, d2, d1), sideMask & 0x34 | 3 | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, d2, z1, x2, y2, z2), sideMask & 0x3e | 1 | internalMask);
|
||||
f.apply(pos, material, pass, new Cuboid6(x1, y1, z1, x2, d1, z2), sideMask & 0x3d | 2 | internalMask);
|
||||
}
|
||||
default -> throw new MatchError(slot);
|
||||
break;
|
||||
default:
|
||||
throw new MatchError(slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ final class HollowMicroblockTraitLogic {
|
||||
}
|
||||
// The second virtual lookup is observable when a part overrides tile().
|
||||
TMultiPart center = ((TMultiPart) part).tile().partMap(6);
|
||||
if (center instanceof ISidedHollowConnect connection) {
|
||||
return connection.getHollowSize(part.getSlot());
|
||||
if (center instanceof ISidedHollowConnect) {
|
||||
return ((ISidedHollowConnect) center).getHollowSize(part.getSlot());
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
@@ -60,15 +60,26 @@ final class HollowMicroblockTraitLogic {
|
||||
double z1 = c.min.z;
|
||||
double z2 = c.max.z;
|
||||
int slot = part.getSlot();
|
||||
Cuboid6[] boxes = switch (slot) {
|
||||
case 0, 1 -> new Cuboid6[] { new Cuboid6(d2, y1, d1, x2, y2, d2), new Cuboid6(x1, y1, d1, d1, y2, d2),
|
||||
new Cuboid6(x1, y1, d2, x2, y2, z2), new Cuboid6(x1, y1, z1, x2, y2, d1) };
|
||||
case 2, 3 -> new Cuboid6[] { new Cuboid6(d1, d2, z1, d2, y2, z2), new Cuboid6(d1, y1, z1, d2, d1, z2),
|
||||
new Cuboid6(d2, y1, z1, x2, y2, z2), new Cuboid6(x1, y1, z1, d1, y2, z2) };
|
||||
case 4, 5 -> new Cuboid6[] { new Cuboid6(x1, d1, d2, x2, d2, z2), new Cuboid6(x1, d1, z1, x2, d2, d1),
|
||||
new Cuboid6(x1, d2, z1, x2, y2, z2), new Cuboid6(x1, y1, z1, x2, d1, z2) };
|
||||
default -> throw new MatchError(slot);
|
||||
};
|
||||
Cuboid6[] boxes;
|
||||
switch (slot) {
|
||||
case 0:
|
||||
case 1:
|
||||
boxes = new Cuboid6[] { new Cuboid6(d2, y1, d1, x2, y2, d2), new Cuboid6(x1, y1, d1, d1, y2, d2),
|
||||
new Cuboid6(x1, y1, d2, x2, y2, z2), new Cuboid6(x1, y1, z1, x2, y2, d1) };
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
boxes = new Cuboid6[] { new Cuboid6(d1, d2, z1, d2, y2, z2), new Cuboid6(d1, y1, z1, d2, d1, z2),
|
||||
new Cuboid6(d2, y1, z1, x2, y2, z2), new Cuboid6(x1, y1, z1, d1, y2, z2) };
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
boxes = new Cuboid6[] { new Cuboid6(x1, d1, d2, x2, d2, z2), new Cuboid6(x1, d1, z1, x2, d2, d1),
|
||||
new Cuboid6(x1, d2, z1, x2, y2, z2), new Cuboid6(x1, y1, z1, x2, d1, z2) };
|
||||
break;
|
||||
default:
|
||||
throw new MatchError(slot);
|
||||
}
|
||||
return JavaConversions.seqAsJavaList(sequence(boxes));
|
||||
}
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@ final class PostMicroblockClientLogic {
|
||||
|
||||
@Override
|
||||
public BoxedUnit apply(TMultiPart other) {
|
||||
if (other instanceof PostMicroblock post && !other.equals(part)) {
|
||||
part.shrinkPost(post);
|
||||
if (other instanceof PostMicroblock && !other.equals(part)) {
|
||||
part.shrinkPost((PostMicroblock) other);
|
||||
}
|
||||
return BoxedUnit.UNIT;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,10 @@ final class TMicroOcclusionLogic {
|
||||
private TMicroOcclusionLogic() {}
|
||||
|
||||
static boolean occlusionTest(TMicroOcclusion part, TMultiPart next) {
|
||||
if (!(next instanceof TMicroOcclusion other)) {
|
||||
if (!(next instanceof TMicroOcclusion)) {
|
||||
return true;
|
||||
}
|
||||
TMicroOcclusion other = (TMicroOcclusion) next;
|
||||
int shape1 = MicroOcclusion$.MODULE$.shapePriority(part.getSlot());
|
||||
int shape2 = MicroOcclusion$.MODULE$.shapePriority(other.getSlot());
|
||||
if (other.getSize() + part.getSize() > 8) {
|
||||
|
||||
@@ -191,7 +191,7 @@ final class ClassInfoLookup {
|
||||
|
||||
// Preserve the compiler's argument-count indexing, including its behavior for wide stack values.
|
||||
StackEntry receiver = stack.peek(Type.getType(call.desc).getArgumentTypes().length);
|
||||
if (!(receiver instanceof Load load) || !(load.e() instanceof This)) return Option.empty();
|
||||
if (!(receiver instanceof Load) || !(((Load) receiver).e() instanceof This)) return Option.empty();
|
||||
|
||||
return ASMMixinCompiler$.MODULE$.getClassInfo(stack.owner().getInternalName()).superClass()
|
||||
.flatMap(new AbstractFunction1<ClassInfo, Option<MethodInfo>>() {
|
||||
|
||||
@@ -189,7 +189,8 @@ final class JavaTraitRegistration {
|
||||
InsnList instructions = method.instructions;
|
||||
AbstractInsnNode instruction = instructions.getFirst();
|
||||
while (instruction != null) {
|
||||
if (instruction instanceof FieldInsnNode field) {
|
||||
if (instruction instanceof FieldInsnNode) {
|
||||
FieldInsnNode field = (FieldInsnNode) instruction;
|
||||
if (instruction.getOpcode() == GETFIELD) instruction = replace(
|
||||
instructions,
|
||||
instruction,
|
||||
@@ -208,7 +209,8 @@ final class JavaTraitRegistration {
|
||||
fieldName(field.name) + "_$eq",
|
||||
"(" + field.desc + ")V",
|
||||
true));
|
||||
} else if (instruction instanceof MethodInsnNode call) {
|
||||
} else if (instruction instanceof MethodInsnNode) {
|
||||
MethodInsnNode call = (MethodInsnNode) instruction;
|
||||
if (instruction.getOpcode() == INVOKESPECIAL) {
|
||||
if (ASMMixinCompiler$.MODULE$.getSuper(call, stack).isDefined())
|
||||
instruction = replace(instructions, instruction, superInstruction(call));
|
||||
@@ -284,12 +286,14 @@ final class JavaTraitRegistration {
|
||||
StackAnalyser stack = new StackAnalyser(Type.getObjectType(input.name), source);
|
||||
AbstractInsnNode instruction = constructor.instructions.getFirst();
|
||||
while (instruction != null) {
|
||||
if (instruction.getOpcode() == INVOKESPECIAL && instruction instanceof MethodInsnNode call) {
|
||||
if (instruction.getOpcode() == INVOKESPECIAL && instruction instanceof MethodInsnNode) {
|
||||
MethodInsnNode call = (MethodInsnNode) instruction;
|
||||
if (Objects.equals(call.owner, input.superName) && Objects.equals(call.name, "<init>")) {
|
||||
int argumentWidth = 0;
|
||||
for (Type argument : Type.getArgumentTypes(call.desc)) argumentWidth += argument.getSize();
|
||||
StackAnalyser.StackEntry receiver = stack.peek(argumentWidth);
|
||||
if (receiver instanceof StackAnalyser.Load load && load.e() instanceof StackAnalyser.This) {
|
||||
if (receiver instanceof StackAnalyser.Load
|
||||
&& ((StackAnalyser.Load) receiver).e() instanceof StackAnalyser.This) {
|
||||
removeRange(constructor.instructions, receiver.insn(), instruction);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,18 +157,22 @@ final class ScalaSignatureParser {
|
||||
Bytes bytes = entry.bytes();
|
||||
ByteCodeReader reader = bytes.reader();
|
||||
byte id = entry.id();
|
||||
return switch (id) {
|
||||
case 1, 2 -> reader.readString(bytes.len());
|
||||
case 3 -> sig.NoSymbol().full();
|
||||
case 9, 10 -> {
|
||||
switch (id) {
|
||||
case 1:
|
||||
case 2:
|
||||
return reader.readString(bytes.len());
|
||||
case 3:
|
||||
return sig.NoSymbol().full();
|
||||
case 9:
|
||||
case 10:
|
||||
String name = sig.evalS(reader.readNat());
|
||||
if (bytes.pos() + bytes.len() > reader.pos()) {
|
||||
name = sig.evalS(reader.readNat()) + "." + name;
|
||||
}
|
||||
yield name;
|
||||
}
|
||||
default -> throw new MatchError(Byte.valueOf(id));
|
||||
};
|
||||
return name;
|
||||
default:
|
||||
throw new MatchError(Byte.valueOf(id));
|
||||
}
|
||||
}
|
||||
|
||||
static List<?> evalList(ScalaSignature sig, ByteCodeReader reader) {
|
||||
@@ -181,36 +185,61 @@ final class ScalaSignatureParser {
|
||||
|
||||
static Object eval(ScalaSignature sig, int index, Object entry, ByteCodeReader reader, byte id) {
|
||||
// The Scala optimizer constructs case classes directly, without initializing their companions.
|
||||
return switch (id) {
|
||||
case 1, 2 -> sig.evalS(index);
|
||||
case 3 -> sig.NoSymbol();
|
||||
case 6 -> sig.new ClassSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
|
||||
reader.readNat());
|
||||
case 7 -> sig.new ObjectSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
|
||||
reader.readNat());
|
||||
case 8 -> sig.new MethodSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
|
||||
reader.readNat());
|
||||
case 9, 10 -> sig.new ExternalSymbol(sig.evalS(index));
|
||||
// NoPrefixType is treated as NoType in the reference.
|
||||
case 11, 12 -> sig.NoType();
|
||||
case 13 -> sig.new ThisType(sig.evalT(reader.readNat()));
|
||||
case 14 -> sig.new SingleType(sig.evalT(reader.readNat()), sig.evalT(reader.readNat()));
|
||||
// Retain the reference's parameterless interpretation of a bounded super type.
|
||||
case 21, 48 -> sig.new ParameterlessType(sig.evalT(reader.readNat()));
|
||||
case 25 -> sig.new BooleanLiteral(reader.readLong() != 0);
|
||||
case 26 -> sig.new ByteLiteral((byte) reader.readLong());
|
||||
case 27 -> sig.new ShortLiteral((short) reader.readLong());
|
||||
case 28 -> sig.new CharLiteral((char) reader.readLong());
|
||||
case 29 -> sig.new IntLiteral((int) reader.readLong());
|
||||
case 30 -> sig.new LongLiteral(reader.readLong());
|
||||
case 31 -> sig.new FloatLiteral(Float.intBitsToFloat((int) reader.readLong()));
|
||||
case 32 -> sig.new DoubleLiteral(Double.longBitsToDouble(reader.readLong()));
|
||||
case 33 -> sig.new StringLiteral(sig.evalS(reader.readNat()));
|
||||
case 34 -> sig.NullLiteral();
|
||||
case 35 -> sig.new TypeLiteral(sig.evalT(reader.readNat()));
|
||||
case 36 -> sig.new EnumLiteral(sig.evalT(reader.readNat()));
|
||||
default -> entry;
|
||||
};
|
||||
switch (id) {
|
||||
case 1:
|
||||
case 2:
|
||||
return sig.evalS(index);
|
||||
case 3:
|
||||
return sig.NoSymbol();
|
||||
case 6:
|
||||
return sig.new ClassSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
|
||||
reader.readNat());
|
||||
case 7:
|
||||
return sig.new ObjectSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
|
||||
reader.readNat());
|
||||
case 8:
|
||||
return sig.new MethodSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
|
||||
reader.readNat());
|
||||
case 9:
|
||||
case 10:
|
||||
return sig.new ExternalSymbol(sig.evalS(index));
|
||||
case 11:
|
||||
case 12: // NoPrefixType is treated as NoType in the reference.
|
||||
return sig.NoType();
|
||||
case 13:
|
||||
return sig.new ThisType(sig.evalT(reader.readNat()));
|
||||
case 14:
|
||||
return sig.new SingleType(sig.evalT(reader.readNat()), sig.evalT(reader.readNat()));
|
||||
case 21:
|
||||
case 48: // Retain the reference's parameterless interpretation of a bounded super type.
|
||||
return sig.new ParameterlessType(sig.evalT(reader.readNat()));
|
||||
case 25:
|
||||
return sig.new BooleanLiteral(reader.readLong() != 0);
|
||||
case 26:
|
||||
return sig.new ByteLiteral((byte) reader.readLong());
|
||||
case 27:
|
||||
return sig.new ShortLiteral((short) reader.readLong());
|
||||
case 28:
|
||||
return sig.new CharLiteral((char) reader.readLong());
|
||||
case 29:
|
||||
return sig.new IntLiteral((int) reader.readLong());
|
||||
case 30:
|
||||
return sig.new LongLiteral(reader.readLong());
|
||||
case 31:
|
||||
return sig.new FloatLiteral(Float.intBitsToFloat((int) reader.readLong()));
|
||||
case 32:
|
||||
return sig.new DoubleLiteral(Double.longBitsToDouble(reader.readLong()));
|
||||
case 33:
|
||||
return sig.new StringLiteral(sig.evalS(reader.readNat()));
|
||||
case 34:
|
||||
return sig.NullLiteral();
|
||||
case 35:
|
||||
return sig.new TypeLiteral(sig.evalT(reader.readNat()));
|
||||
case 36:
|
||||
return sig.new EnumLiteral(sig.evalT(reader.readNat()));
|
||||
default:
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
static <T> IndexedSeq<T> collect(ScalaSignature sig, int id) {
|
||||
|
||||
@@ -56,20 +56,18 @@ final class StackAnalyserLogic {
|
||||
|
||||
static Type constType(Const constant) {
|
||||
Object value = constant.c();
|
||||
return switch (value) {
|
||||
case Byte ignored -> BYTE_TYPE;
|
||||
case Short ignored -> SHORT_TYPE;
|
||||
case Integer ignored -> INT_TYPE;
|
||||
case Long ignored -> LONG_TYPE;
|
||||
case Float ignored -> FLOAT_TYPE;
|
||||
case Double ignored -> DOUBLE_TYPE;
|
||||
case Character ignored -> CHAR_TYPE;
|
||||
case Boolean ignored -> BOOLEAN_TYPE;
|
||||
case String ignored -> getObjectType("java/lang/String");
|
||||
case null -> getObjectType("java/lang/Object");
|
||||
// The failure message rereads the virtual accessor after classification.
|
||||
default -> throw new IllegalArgumentException("Unknown const " + constant.c());
|
||||
};
|
||||
if (value instanceof Byte) return BYTE_TYPE;
|
||||
if (value instanceof Short) return SHORT_TYPE;
|
||||
if (value instanceof Integer) return INT_TYPE;
|
||||
if (value instanceof Long) return LONG_TYPE;
|
||||
if (value instanceof Float) return FLOAT_TYPE;
|
||||
if (value instanceof Double) return DOUBLE_TYPE;
|
||||
if (value instanceof Character) return CHAR_TYPE;
|
||||
if (value instanceof Boolean) return BOOLEAN_TYPE;
|
||||
if (value instanceof String) return getObjectType("java/lang/String");
|
||||
if (value == null) return getObjectType("java/lang/Object");
|
||||
// The failure message rereads the virtual accessor after classification.
|
||||
throw new IllegalArgumentException("Unknown const " + constant.c());
|
||||
}
|
||||
|
||||
static void initialize(StackAnalyser a, Type owner, MethodNode method,
|
||||
@@ -126,167 +124,256 @@ final class StackAnalyserLogic {
|
||||
}
|
||||
|
||||
static void visitInsn(StackAnalyser a, AbstractInsnNode insn) {
|
||||
switch (insn) {
|
||||
case InsnNode ignored -> {
|
||||
int op = insn.getOpcode();
|
||||
switch (op) {
|
||||
case ACONST_NULL -> a.push(new Const(null, insn));
|
||||
case ICONST_M1 -> a.push(new Const(-1, insn));
|
||||
case ICONST_0 -> a.push(new Const(0, insn));
|
||||
case ICONST_1 -> a.push(new Const(1, insn));
|
||||
case ICONST_2 -> a.push(new Const(2, insn));
|
||||
case ICONST_3 -> a.push(new Const(3, insn));
|
||||
case ICONST_4 -> a.push(new Const(4, insn));
|
||||
case ICONST_5 -> a.push(new Const(5, insn));
|
||||
case LCONST_0 -> a.push(new Const(0L, insn));
|
||||
case LCONST_1 -> a.push(new Const(1L, insn));
|
||||
case FCONST_0 -> a.push(new Const(0f, insn));
|
||||
case FCONST_1 -> a.push(new Const(1f, insn));
|
||||
case FCONST_2 -> a.push(new Const(2f, insn));
|
||||
case DCONST_0 -> a.push(new Const(0d, insn));
|
||||
case DCONST_1 -> a.push(new Const(1d, insn));
|
||||
case POP -> rawPop(a);
|
||||
case POP2 -> {
|
||||
rawPop(a);
|
||||
rawPop(a);
|
||||
}
|
||||
case DUP -> a.push(peek(a));
|
||||
case DUP_X1 -> a.insert(2, peek(a));
|
||||
case DUP_X2 -> a.insert(3, peek(a));
|
||||
case DUP2 -> {
|
||||
a.push(a.peek(1));
|
||||
a.push(a.peek(1));
|
||||
}
|
||||
case DUP2_X1 -> {
|
||||
a.insert(3, a.peek(1));
|
||||
a.insert(3, peek(a));
|
||||
}
|
||||
case DUP2_X2 -> {
|
||||
a.insert(4, a.peek(1));
|
||||
a.insert(4, peek(a));
|
||||
}
|
||||
case SWAP -> a.push(a.pop(1));
|
||||
// Preserve the reference's inferred types, including conversions to int reporting double.
|
||||
case L2I, F2I, D2I -> a.push(new PrimitiveCast(pop(a), DOUBLE_TYPE, insn));
|
||||
case I2L, F2L, D2L -> a.push(new PrimitiveCast(pop(a), LONG_TYPE, insn));
|
||||
case I2F, L2F, D2F -> a.push(new PrimitiveCast(pop(a), FLOAT_TYPE, insn));
|
||||
case I2D, L2D, F2D -> a.push(new PrimitiveCast(pop(a), DOUBLE_TYPE, insn));
|
||||
case I2B -> a.push(new PrimitiveCast(pop(a), BYTE_TYPE, insn));
|
||||
case I2C -> a.push(new PrimitiveCast(pop(a), CHAR_TYPE, insn));
|
||||
case I2S -> a.push(new PrimitiveCast(pop(a), SHORT_TYPE, insn));
|
||||
case ARRAYLENGTH -> a.push(new ArrayLength(pop(a), insn));
|
||||
case ATHROW, MONITORENTER, MONITOREXIT -> pop(a);
|
||||
default -> {
|
||||
if (op >= IALOAD && op <= SALOAD) {
|
||||
a.push(new ArrayLoad(pop(a), pop(a), insn));
|
||||
} else if (op >= IASTORE && op <= SASTORE) {
|
||||
pop(a);
|
||||
pop(a);
|
||||
pop(a);
|
||||
} else if (op >= IADD && op <= DREM || op >= ISHL && op <= LXOR || op >= LCMP && op <= DCMPG) {
|
||||
a.push(new BinaryOp(op, pop(a), pop(a), insn));
|
||||
} else if (op >= INEG && op <= DNEG) {
|
||||
a.push(new UnaryOp(op, pop(a), insn));
|
||||
} else if (op >= IRETURN && op <= ARETURN) {
|
||||
pop(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case IntInsnNode intInsn -> {
|
||||
switch (insn.getOpcode()) {
|
||||
case BIPUSH -> a.push(new Const((byte) intInsn.operand, insn));
|
||||
case SIPUSH -> a.push(new Const((short) intInsn.operand, insn));
|
||||
default -> throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
}
|
||||
case LdcInsnNode ldc -> {
|
||||
if (insn.getOpcode() != LDC) throw new MatchError(insn.getOpcode());
|
||||
a.push(new Const(ldc.cst, insn));
|
||||
}
|
||||
case VarInsnNode varInsn -> {
|
||||
int op = insn.getOpcode();
|
||||
if (op >= ILOAD && op <= ALOAD) {
|
||||
a.push(new Load(a.locals().apply(varInsn.var), insn));
|
||||
} else if (op >= ISTORE && op <= ASTORE) {
|
||||
a.setL(varInsn.var, new Store(pop(a), insn));
|
||||
} else {
|
||||
throw new MatchError(op);
|
||||
}
|
||||
}
|
||||
case IincInsnNode incInsn -> {
|
||||
if (insn.getOpcode() != IINC) throw new MatchError(insn.getOpcode());
|
||||
a.setL(
|
||||
incInsn.var,
|
||||
new Store(
|
||||
new BinaryOp(
|
||||
IINC,
|
||||
new Const(incInsn.incr, insn),
|
||||
new Load(a.locals().apply(incInsn.var), insn),
|
||||
insn),
|
||||
insn));
|
||||
}
|
||||
case JumpInsnNode ignored -> {
|
||||
int op = insn.getOpcode();
|
||||
if (op >= IFEQ && op <= IFLE || op == IFNULL || op == IFNONNULL) {
|
||||
if (insn instanceof InsnNode) {
|
||||
int op = insn.getOpcode();
|
||||
switch (op) {
|
||||
case ACONST_NULL:
|
||||
a.push(new Const(null, insn));
|
||||
break;
|
||||
case ICONST_M1:
|
||||
a.push(new Const(-1, insn));
|
||||
break;
|
||||
case ICONST_0:
|
||||
a.push(new Const(0, insn));
|
||||
break;
|
||||
case ICONST_1:
|
||||
a.push(new Const(1, insn));
|
||||
break;
|
||||
case ICONST_2:
|
||||
a.push(new Const(2, insn));
|
||||
break;
|
||||
case ICONST_3:
|
||||
a.push(new Const(3, insn));
|
||||
break;
|
||||
case ICONST_4:
|
||||
a.push(new Const(4, insn));
|
||||
break;
|
||||
case ICONST_5:
|
||||
a.push(new Const(5, insn));
|
||||
break;
|
||||
case LCONST_0:
|
||||
a.push(new Const(0L, insn));
|
||||
break;
|
||||
case LCONST_1:
|
||||
a.push(new Const(1L, insn));
|
||||
break;
|
||||
case FCONST_0:
|
||||
a.push(new Const(0f, insn));
|
||||
break;
|
||||
case FCONST_1:
|
||||
a.push(new Const(1f, insn));
|
||||
break;
|
||||
case FCONST_2:
|
||||
a.push(new Const(2f, insn));
|
||||
break;
|
||||
case DCONST_0:
|
||||
a.push(new Const(0d, insn));
|
||||
break;
|
||||
case DCONST_1:
|
||||
a.push(new Const(1d, insn));
|
||||
break;
|
||||
case POP:
|
||||
rawPop(a);
|
||||
break;
|
||||
case POP2:
|
||||
rawPop(a);
|
||||
rawPop(a);
|
||||
break;
|
||||
case DUP:
|
||||
a.push(peek(a));
|
||||
break;
|
||||
case DUP_X1:
|
||||
a.insert(2, peek(a));
|
||||
break;
|
||||
case DUP_X2:
|
||||
a.insert(3, peek(a));
|
||||
break;
|
||||
case DUP2:
|
||||
a.push(a.peek(1));
|
||||
a.push(a.peek(1));
|
||||
break;
|
||||
case DUP2_X1:
|
||||
a.insert(3, a.peek(1));
|
||||
a.insert(3, peek(a));
|
||||
break;
|
||||
case DUP2_X2:
|
||||
a.insert(4, a.peek(1));
|
||||
a.insert(4, peek(a));
|
||||
break;
|
||||
case SWAP:
|
||||
a.push(a.pop(1));
|
||||
break;
|
||||
// Preserve the reference's inferred types, including conversions to int reporting double.
|
||||
case L2I:
|
||||
case F2I:
|
||||
case D2I:
|
||||
a.push(new PrimitiveCast(pop(a), DOUBLE_TYPE, insn));
|
||||
break;
|
||||
case I2L:
|
||||
case F2L:
|
||||
case D2L:
|
||||
a.push(new PrimitiveCast(pop(a), LONG_TYPE, insn));
|
||||
break;
|
||||
case I2F:
|
||||
case L2F:
|
||||
case D2F:
|
||||
a.push(new PrimitiveCast(pop(a), FLOAT_TYPE, insn));
|
||||
break;
|
||||
case I2D:
|
||||
case L2D:
|
||||
case F2D:
|
||||
a.push(new PrimitiveCast(pop(a), DOUBLE_TYPE, insn));
|
||||
break;
|
||||
case I2B:
|
||||
a.push(new PrimitiveCast(pop(a), BYTE_TYPE, insn));
|
||||
break;
|
||||
case I2C:
|
||||
a.push(new PrimitiveCast(pop(a), CHAR_TYPE, insn));
|
||||
break;
|
||||
case I2S:
|
||||
a.push(new PrimitiveCast(pop(a), SHORT_TYPE, insn));
|
||||
break;
|
||||
case ARRAYLENGTH:
|
||||
a.push(new ArrayLength(pop(a), insn));
|
||||
break;
|
||||
case ATHROW:
|
||||
case MONITORENTER:
|
||||
case MONITOREXIT:
|
||||
pop(a);
|
||||
} else if (op >= IF_ICMPEQ && op <= IF_ACMPNE) {
|
||||
pop(a);
|
||||
pop(a);
|
||||
} else if (op == JSR) {
|
||||
a.push(new ReturnAddress(insn));
|
||||
} else if (op != GOTO) {
|
||||
throw new MatchError(op);
|
||||
}
|
||||
}
|
||||
case TableSwitchInsnNode ignored -> pop(a);
|
||||
case LookupSwitchInsnNode ignored -> pop(a);
|
||||
case FieldInsnNode field -> {
|
||||
switch (insn.getOpcode()) {
|
||||
case GETSTATIC -> a.push(new GetField(null, field, insn));
|
||||
case PUTSTATIC -> pop(a);
|
||||
case GETFIELD -> a.push(new GetField(pop(a), field, insn));
|
||||
case PUTFIELD -> {
|
||||
break;
|
||||
default:
|
||||
if (op >= IALOAD && op <= SALOAD) {
|
||||
a.push(new ArrayLoad(pop(a), pop(a), insn));
|
||||
} else if (op >= IASTORE && op <= SASTORE) {
|
||||
pop(a);
|
||||
pop(a);
|
||||
pop(a);
|
||||
} else if (op >= IADD && op <= DREM || op >= ISHL && op <= LXOR || op >= LCMP && op <= DCMPG) {
|
||||
a.push(new BinaryOp(op, pop(a), pop(a), insn));
|
||||
} else if (op >= INEG && op <= DNEG) {
|
||||
a.push(new UnaryOp(op, pop(a), insn));
|
||||
} else if (op >= IRETURN && op <= ARETURN) {
|
||||
pop(a);
|
||||
}
|
||||
default -> throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
}
|
||||
case MethodInsnNode method -> {
|
||||
switch (insn.getOpcode()) {
|
||||
case INVOKEVIRTUAL, INVOKESPECIAL, INVOKEINTERFACE ->
|
||||
a.push(new Invoke(insn.getOpcode(), a.popArgs(method.desc), pop(a), method, insn));
|
||||
case INVOKESTATIC ->
|
||||
a.push(new Invoke(insn.getOpcode(), a.popArgs(method.desc), null, method, insn));
|
||||
default -> throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
} else if (insn instanceof IntInsnNode) {
|
||||
IntInsnNode intInsn = (IntInsnNode) insn;
|
||||
switch (insn.getOpcode()) {
|
||||
case BIPUSH:
|
||||
a.push(new Const((byte) intInsn.operand, insn));
|
||||
break;
|
||||
case SIPUSH:
|
||||
a.push(new Const((short) intInsn.operand, insn));
|
||||
break;
|
||||
default:
|
||||
throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
case TypeInsnNode typeInsn -> {
|
||||
switch (insn.getOpcode()) {
|
||||
case NEW -> a.push(new New(getObjectType(typeInsn.desc), insn));
|
||||
case NEWARRAY -> a.push(new NewArray(pop(a), getObjectType(typeInsn.desc), insn));
|
||||
case ANEWARRAY -> a.push(new NewArray(pop(a), getObjectType("[" + typeInsn.desc), insn));
|
||||
case CHECKCAST -> a.push(new Cast(pop(a), getObjectType(typeInsn.desc), insn));
|
||||
case INSTANCEOF -> a.push(new UnaryOp(INSTANCEOF, pop(a), insn));
|
||||
default -> throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
} else if (insn instanceof LdcInsnNode) {
|
||||
if (insn.getOpcode() != LDC) throw new MatchError(insn.getOpcode());
|
||||
a.push(new Const(((LdcInsnNode) insn).cst, insn));
|
||||
} else if (insn instanceof VarInsnNode) {
|
||||
VarInsnNode varInsn = (VarInsnNode) insn;
|
||||
int op = insn.getOpcode();
|
||||
if (op >= ILOAD && op <= ALOAD) {
|
||||
a.push(new Load(a.locals().apply(varInsn.var), insn));
|
||||
} else if (op >= ISTORE && op <= ASTORE) {
|
||||
a.setL(varInsn.var, new Store(pop(a), insn));
|
||||
} else {
|
||||
throw new MatchError(op);
|
||||
}
|
||||
case MultiANewArrayInsnNode multi -> {
|
||||
StackEntry[] sizes = new StackEntry[multi.dims];
|
||||
for (int i = 0; i < sizes.length; i++) sizes[i] = pop(a);
|
||||
a.push(new NewMultiArray(sizes, getType(multi.desc), insn));
|
||||
} else if (insn instanceof IincInsnNode) {
|
||||
IincInsnNode incInsn = (IincInsnNode) insn;
|
||||
if (insn.getOpcode() != IINC) throw new MatchError(insn.getOpcode());
|
||||
a.setL(
|
||||
incInsn.var,
|
||||
new Store(
|
||||
new BinaryOp(
|
||||
IINC,
|
||||
new Const(incInsn.incr, insn),
|
||||
new Load(a.locals().apply(incInsn.var), insn),
|
||||
insn),
|
||||
insn));
|
||||
} else if (insn instanceof JumpInsnNode) {
|
||||
int op = insn.getOpcode();
|
||||
if (op >= IFEQ && op <= IFLE || op == IFNULL || op == IFNONNULL) {
|
||||
pop(a);
|
||||
} else if (op >= IF_ICMPEQ && op <= IF_ACMPNE) {
|
||||
pop(a);
|
||||
pop(a);
|
||||
} else if (op == JSR) {
|
||||
a.push(new ReturnAddress(insn));
|
||||
} else if (op != GOTO) {
|
||||
throw new MatchError(op);
|
||||
}
|
||||
case LabelNode label -> {
|
||||
Option<TryCatchBlockNode> handler = a.codechicken$multipart$asm$StackAnalyser$$catchHandlers()
|
||||
.get(label);
|
||||
if (handler instanceof Some) {
|
||||
a.push(new CaughtException(Type.getType(((Some<TryCatchBlockNode>) handler).x().type), insn));
|
||||
} else if (!None$.MODULE$.equals(handler)) {
|
||||
throw new MatchError(handler);
|
||||
}
|
||||
} else if (insn instanceof TableSwitchInsnNode || insn instanceof LookupSwitchInsnNode) {
|
||||
pop(a);
|
||||
} else if (insn instanceof FieldInsnNode) {
|
||||
FieldInsnNode field = (FieldInsnNode) insn;
|
||||
switch (insn.getOpcode()) {
|
||||
case GETSTATIC:
|
||||
a.push(new GetField(null, field, insn));
|
||||
break;
|
||||
case PUTSTATIC:
|
||||
pop(a);
|
||||
break;
|
||||
case GETFIELD:
|
||||
a.push(new GetField(pop(a), field, insn));
|
||||
break;
|
||||
case PUTFIELD:
|
||||
pop(a);
|
||||
pop(a);
|
||||
break;
|
||||
default:
|
||||
throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
} else if (insn instanceof MethodInsnNode) {
|
||||
MethodInsnNode method = (MethodInsnNode) insn;
|
||||
switch (insn.getOpcode()) {
|
||||
case INVOKEVIRTUAL:
|
||||
case INVOKESPECIAL:
|
||||
case INVOKEINTERFACE:
|
||||
a.push(new Invoke(insn.getOpcode(), a.popArgs(method.desc), pop(a), method, insn));
|
||||
break;
|
||||
case INVOKESTATIC:
|
||||
a.push(new Invoke(insn.getOpcode(), a.popArgs(method.desc), null, method, insn));
|
||||
break;
|
||||
default:
|
||||
throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
} else if (insn instanceof TypeInsnNode) {
|
||||
TypeInsnNode typeInsn = (TypeInsnNode) insn;
|
||||
switch (insn.getOpcode()) {
|
||||
case NEW:
|
||||
a.push(new New(getObjectType(typeInsn.desc), insn));
|
||||
break;
|
||||
case NEWARRAY:
|
||||
a.push(new NewArray(pop(a), getObjectType(typeInsn.desc), insn));
|
||||
break;
|
||||
case ANEWARRAY:
|
||||
a.push(new NewArray(pop(a), getObjectType("[" + typeInsn.desc), insn));
|
||||
break;
|
||||
case CHECKCAST:
|
||||
a.push(new Cast(pop(a), getObjectType(typeInsn.desc), insn));
|
||||
break;
|
||||
case INSTANCEOF:
|
||||
a.push(new UnaryOp(INSTANCEOF, pop(a), insn));
|
||||
break;
|
||||
default:
|
||||
throw new MatchError(insn.getOpcode());
|
||||
}
|
||||
} else if (insn instanceof MultiANewArrayInsnNode) {
|
||||
MultiANewArrayInsnNode multi = (MultiANewArrayInsnNode) insn;
|
||||
StackEntry[] sizes = new StackEntry[multi.dims];
|
||||
for (int i = 0; i < sizes.length; i++) sizes[i] = pop(a);
|
||||
a.push(new NewMultiArray(sizes, getType(multi.desc), insn));
|
||||
} else if (insn instanceof LabelNode) {
|
||||
Option<TryCatchBlockNode> handler = a.codechicken$multipart$asm$StackAnalyser$$catchHandlers()
|
||||
.get((LabelNode) insn);
|
||||
if (handler instanceof Some) {
|
||||
a.push(new CaughtException(Type.getType(((Some<TryCatchBlockNode>) handler).x().type), insn));
|
||||
} else if (!None$.MODULE$.equals(handler)) {
|
||||
throw new MatchError(handler);
|
||||
}
|
||||
case null, default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user