-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathClassNodeHandle.java
More file actions
162 lines (143 loc) · 5.67 KB
/
ClassNodeHandle.java
File metadata and controls
162 lines (143 loc) · 5.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* FalsePatternLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.lib.turboasm;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
/** A simple handle to a mutable ClassNode and flags for ClassWriter. */
public final class ClassNodeHandle {
private final byte @Nullable [] originalBytes;
private final @Nullable ClassHeaderMetadata originalMetadata;
private final int readerOptions;
private boolean initialized = false;
private @Nullable ClassNode node = null;
private @Nullable FastClassAccessor accessor = null;
private int writerFlags = 0;
/** Parse the class data with no reader options (for fastest speed). */
public ClassNodeHandle(byte @Nullable [] classData) {
this(classData, 0);
}
/** Parse the class data with custom reader options. */
public ClassNodeHandle(
byte @Nullable [] classData, @MagicConstant(flagsFromClass = ClassReader.class) int readerOptions) {
@Nullable ClassHeaderMetadata originalMetadata;
this.originalBytes = classData;
if (classData == null) {
originalMetadata = null;
} else {
try {
originalMetadata = new ClassHeaderMetadata(classData);
} catch (Exception e) {
originalMetadata = null;
}
}
this.originalMetadata = originalMetadata;
this.accessor = originalMetadata;
this.readerOptions = 0;
}
/** Gets the original pre-transformer-phase bytes of the class. */
public byte @Nullable [] getOriginalBytes() {
return originalBytes;
}
/** Gets the original pre-transformer-phase header metadata of the class, or null if invalid/not present. */
public @Nullable ClassHeaderMetadata getOriginalMetadata() {
return originalMetadata;
}
/** Gets the fast class metadata accessor of the class, that can access the current state of various class attributes without (re)parsing. */
public @Nullable FastClassAccessor getFastAccessor() {
return accessor;
}
/** @return If the class was not yet turned into a ClassNode object, and the original bytes still represent the class. */
public boolean isOriginal() {
return !initialized;
}
/** If the class currently has any bytes or a node associated with it. */
public boolean isPresent() {
if (initialized) {
return node != null;
} else {
return originalBytes != null;
}
}
/** Gets the parsed node of the currently processed class. This can cause full class parsing! */
public @Nullable ClassNode getNode() {
ensureInitialized();
return node;
}
/** Overwrites the parsed node of the currently processed class. */
public void setNode(@Nullable ClassNode node) {
initialized = true;
this.node = node;
if (node == null) {
this.accessor = null;
} else {
this.accessor = FastClassAccessor.ofAsmNode(node);
}
}
/** Computes the byte[] array of the transformed class. Returns the original bytes if {@link ClassNodeHandle#getNode()} was never called. */
public byte @Nullable [] computeBytes() {
if (!initialized) {
return originalBytes;
}
if (node == null) {
return null;
}
final ClassWriter writer = new ClassWriter(writerFlags);
node.accept(writer);
return writer.toByteArray();
}
/** Gets the ClassWriter flags for the current class. */
public int getWriterFlags() {
return writerFlags;
}
/** Set the ClassWriter flags for the current class. */
public void setWriterFlags(@MagicConstant(flagsFromClass = ClassWriter.class) int flags) {
this.writerFlags = flags;
}
/** Combine the currently set writer flags with the given flags using bitwise OR. */
public void orWriterFlags(@MagicConstant(flagsFromClass = ClassWriter.class) int flags) {
this.writerFlags |= flags;
}
/** Set ClassWriter.COMPUTE_MAXS on the writer flags. */
public void computeMaxs() {
this.writerFlags |= ClassWriter.COMPUTE_MAXS;
}
/** Set ClassWriter.COMPUTE_FRAMES on the writer flags. */
public void computeFrames() {
this.writerFlags |= ClassWriter.COMPUTE_FRAMES;
}
private void ensureInitialized() {
if (!initialized) {
if (originalBytes == null) {
node = null;
accessor = null;
} else {
node = new ClassNode();
new ClassReader(originalBytes).accept(node, readerOptions);
accessor = FastClassAccessor.ofAsmNode(node);
}
initialized = true;
}
}
}