fix some code style issues
This commit is contained in:
@@ -19,6 +19,15 @@ public class CodeWriter {
|
||||
public static final String NL = System.getProperty("line.separator");
|
||||
public static final String INDENT = "\t";
|
||||
|
||||
private static final String[] INDENT_CACHE = {
|
||||
"",
|
||||
INDENT,
|
||||
INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT + INDENT + INDENT,
|
||||
};
|
||||
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
private String indentStr;
|
||||
private int indent;
|
||||
@@ -66,6 +75,11 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter add(Object obj) {
|
||||
buf.append(obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter add(String str) {
|
||||
buf.append(str);
|
||||
return this;
|
||||
@@ -116,18 +130,9 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
private static final String[] INDENT_CACHE = {
|
||||
"",
|
||||
INDENT,
|
||||
INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT + INDENT,
|
||||
INDENT + INDENT + INDENT + INDENT + INDENT,
|
||||
};
|
||||
|
||||
private void updateIndent() {
|
||||
int curIndent = indent;
|
||||
if (curIndent < 6) {
|
||||
if (curIndent < INDENT_CACHE.length) {
|
||||
this.indentStr = INDENT_CACHE[curIndent];
|
||||
} else {
|
||||
StringBuilder s = new StringBuilder(curIndent * INDENT.length());
|
||||
|
||||
@@ -81,8 +81,7 @@ public class ConditionGen {
|
||||
}
|
||||
InsnNode insn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
if (insn.getType() == InsnType.ARITH) {
|
||||
ArithNode arith = ((ArithNode) insn);
|
||||
switch (arith.getOp()) {
|
||||
switch (((ArithNode) insn).getOp()) {
|
||||
case ADD:
|
||||
case SUB:
|
||||
case MUL:
|
||||
|
||||
@@ -255,14 +255,14 @@ public class InsnGen {
|
||||
case CAST: {
|
||||
boolean wrap = state.contains(IGState.BODY_ONLY);
|
||||
if (wrap) {
|
||||
code.add("(");
|
||||
code.add('(');
|
||||
}
|
||||
code.add("(");
|
||||
code.add(useType(((ArgType) ((IndexInsnNode) insn).getIndex())));
|
||||
code.add('(');
|
||||
code.add(useType((ArgType) ((IndexInsnNode) insn).getIndex()));
|
||||
code.add(") ");
|
||||
addArg(code, insn.getArg(0), true);
|
||||
if (wrap) {
|
||||
code.add(")");
|
||||
code.add(')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -461,10 +461,11 @@ public class InsnGen {
|
||||
code.add("switch(").add(arg(insn, 0)).add(") {");
|
||||
code.incIndent();
|
||||
for (int i = 0; i < sw.getCasesCount(); i++) {
|
||||
code.startLine("case " + sw.getKeys()[i]
|
||||
+ ": goto " + MethodGen.getLabelName(sw.getTargets()[i]) + ";");
|
||||
code.startLine("case ").add(sw.getKeys()[i]).add(": goto ");
|
||||
code.add(MethodGen.getLabelName(sw.getTargets()[i])).add(';');
|
||||
}
|
||||
code.startLine("default: goto " + MethodGen.getLabelName(sw.getDefaultCaseOffset()) + ";");
|
||||
code.startLine("default: goto ");
|
||||
code.add(MethodGen.getLabelName(sw.getDefaultCaseOffset())).add(';');
|
||||
code.decIndent();
|
||||
code.startLine('}');
|
||||
state.add(IGState.NO_SEMICOLON);
|
||||
@@ -719,7 +720,7 @@ public class InsnGen {
|
||||
code.add(cond);
|
||||
} else {
|
||||
if (state.contains(IGState.BODY_ONLY)) {
|
||||
code.add("((").add(cond).add(')').add(" ? ").add(th).add(" : ").add(els).add(")");
|
||||
code.add("((").add(cond).add(')').add(" ? ").add(th).add(" : ").add(els).add(')');
|
||||
} else {
|
||||
code.add('(').add(cond).add(')').add(" ? ").add(th).add(" : ").add(els);
|
||||
}
|
||||
|
||||
@@ -168,18 +168,14 @@ public class MethodGen {
|
||||
if (fallback) {
|
||||
if (name != null) {
|
||||
return base + "_" + name;
|
||||
} else {
|
||||
return base;
|
||||
}
|
||||
return base;
|
||||
} else {
|
||||
if (name != null) {
|
||||
if (name.equals("this")) {
|
||||
return name;
|
||||
} else if (Consts.DEBUG) {
|
||||
return name + "_" + base;
|
||||
} else {
|
||||
return name;
|
||||
if (Consts.DEBUG) {
|
||||
return base + "_" + name;
|
||||
}
|
||||
return name;
|
||||
} else {
|
||||
ArgType type = arg.getType();
|
||||
if (type.isPrimitive()) {
|
||||
@@ -274,7 +270,7 @@ public class MethodGen {
|
||||
|
||||
public void addFallbackMethodCode(CodeWriter code) {
|
||||
if (mth.getInstructions() == null) {
|
||||
// loadFile original instructions
|
||||
// load original instructions
|
||||
try {
|
||||
mth.load();
|
||||
DepthTraverser.visit(new FallbackModeVisitor(), mth);
|
||||
|
||||
@@ -34,6 +34,10 @@ public enum AttributeType {
|
||||
private static final int NOT_UNIQ_COUNT;
|
||||
private final boolean uniq;
|
||||
|
||||
private AttributeType(boolean isUniq) {
|
||||
this.uniq = isUniq;
|
||||
}
|
||||
|
||||
static {
|
||||
// place all not unique attributes at first
|
||||
int last = -1;
|
||||
@@ -51,10 +55,6 @@ public enum AttributeType {
|
||||
return NOT_UNIQ_COUNT;
|
||||
}
|
||||
|
||||
private AttributeType(boolean isUniq) {
|
||||
this.uniq = isUniq;
|
||||
}
|
||||
|
||||
public boolean isUniq() {
|
||||
return uniq;
|
||||
}
|
||||
|
||||
@@ -109,10 +109,7 @@ public final class AttributesList {
|
||||
|
||||
public Annotation getAnnotation(String cls) {
|
||||
AnnotationsList aList = (AnnotationsList) get(AttributeType.ANNOTATION_LIST);
|
||||
if (aList == null || aList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
return aList.get(cls);
|
||||
return aList == null ? null : aList.get(cls);
|
||||
}
|
||||
|
||||
public List<IAttribute> getAll(AttributeType type) {
|
||||
|
||||
@@ -13,6 +13,20 @@ public final class ClassInfo {
|
||||
|
||||
private static final Map<ArgType, ClassInfo> CLASSINFO_CACHE = new WeakHashMap<ArgType, ClassInfo>();
|
||||
|
||||
private final ArgType type;
|
||||
private String pkg;
|
||||
private String name;
|
||||
private String fullName;
|
||||
// for inner class not equals null
|
||||
private ClassInfo parentClass;
|
||||
|
||||
private ClassInfo(ArgType type) {
|
||||
assert type.isObject() : "Not class type: " + type;
|
||||
this.type = type;
|
||||
|
||||
splitNames(true);
|
||||
}
|
||||
|
||||
public static ClassInfo fromDex(DexNode dex, int clsIndex) {
|
||||
if (clsIndex == DexNode.NO_INDEX) {
|
||||
return null;
|
||||
@@ -41,19 +55,6 @@ public final class ClassInfo {
|
||||
CLASSINFO_CACHE.clear();
|
||||
}
|
||||
|
||||
private final ArgType type;
|
||||
private String pkg;
|
||||
private String name;
|
||||
private String fullName;
|
||||
private ClassInfo parentClass; // not equals null if this is inner class
|
||||
|
||||
private ClassInfo(ArgType type) {
|
||||
assert type.isObject() : "Not class type: " + type;
|
||||
this.type = type;
|
||||
|
||||
splitNames(true);
|
||||
}
|
||||
|
||||
private void splitNames(boolean canBeInner) {
|
||||
String fullObjectName = type.getObject();
|
||||
assert fullObjectName.indexOf('/') == -1 : "Raw type: " + type;
|
||||
|
||||
@@ -11,6 +11,12 @@ public class FieldInfo {
|
||||
private final String name;
|
||||
private final ArgType type;
|
||||
|
||||
public FieldInfo(ClassInfo declClass, String name, ArgType type) {
|
||||
this.declClass = declClass;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static FieldInfo fromDex(DexNode dex, int index) {
|
||||
FieldId field = dex.getFieldId(index);
|
||||
return new FieldInfo(
|
||||
@@ -19,12 +25,6 @@ public class FieldInfo {
|
||||
dex.getType(field.getTypeIndex()));
|
||||
}
|
||||
|
||||
public FieldInfo(ClassInfo declClass, String name, ArgType type) {
|
||||
this.declClass = declClass;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static String getNameById(DexNode dex, int ind) {
|
||||
return dex.getString(dex.getFieldId(ind).getNameIndex());
|
||||
}
|
||||
|
||||
@@ -18,10 +18,6 @@ public final class MethodInfo {
|
||||
private final ClassInfo declClass;
|
||||
private final String shortId;
|
||||
|
||||
public static MethodInfo fromDex(DexNode dex, int mthIndex) {
|
||||
return new MethodInfo(dex, mthIndex);
|
||||
}
|
||||
|
||||
private MethodInfo(DexNode dex, int mthIndex) {
|
||||
MethodId mthId = dex.getMethodId(mthIndex);
|
||||
name = dex.getString(mthId.getNameIndex());
|
||||
@@ -43,6 +39,10 @@ public final class MethodInfo {
|
||||
shortId = signature.toString();
|
||||
}
|
||||
|
||||
public static MethodInfo fromDex(DexNode dex, int mthIndex) {
|
||||
return new MethodInfo(dex, mthIndex);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ public enum ArithOp {
|
||||
SHR(">>"),
|
||||
USHR(">>>");
|
||||
|
||||
private final String symbol;
|
||||
|
||||
private ArithOp(String symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
private final String symbol;
|
||||
|
||||
public String getSymbol() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ public abstract class ArgType {
|
||||
types.add(type);
|
||||
}
|
||||
}
|
||||
if (types.size() == 0) {
|
||||
if (types.isEmpty()) {
|
||||
return null;
|
||||
} else if (types.size() == 1) {
|
||||
PrimitiveType nt = types.get(0);
|
||||
@@ -479,7 +479,7 @@ public abstract class ArgType {
|
||||
String aObj = a.getObject();
|
||||
String bObj = b.getObject();
|
||||
if (aObj.equals(bObj)) {
|
||||
return (a.getGenericTypes() != null ? a : b);
|
||||
return a.getGenericTypes() != null ? a : b;
|
||||
} else if (aObj.equals(Consts.CLASS_OBJECT)) {
|
||||
return b;
|
||||
} else if (bObj.equals(Consts.CLASS_OBJECT)) {
|
||||
@@ -487,7 +487,7 @@ public abstract class ArgType {
|
||||
} else {
|
||||
// different objects
|
||||
String obj = clsp.getCommonAncestor(aObj, bObj);
|
||||
return (obj == null ? null : object(obj));
|
||||
return obj == null ? null : object(obj);
|
||||
}
|
||||
}
|
||||
if (a.isArray()) {
|
||||
@@ -498,7 +498,7 @@ public abstract class ArgType {
|
||||
return OBJECT;
|
||||
} else {
|
||||
ArgType res = merge(ea, eb);
|
||||
return (res == null ? null : ArgType.array(res));
|
||||
return res == null ? null : ArgType.array(res);
|
||||
}
|
||||
} else if (b.equals(OBJECT)) {
|
||||
return OBJECT;
|
||||
|
||||
@@ -7,8 +7,8 @@ public final class InsnWrapArg extends InsnArg {
|
||||
private final InsnNode wrappedInsn;
|
||||
|
||||
public InsnWrapArg(InsnNode insn) {
|
||||
ArgType type = (insn.getResult() == null ? ArgType.VOID : insn.getResult().getType());
|
||||
this.typedVar = new TypedVar(type);
|
||||
RegisterArg result = insn.getResult();
|
||||
this.typedVar = new TypedVar((result != null ? result.getType() : ArgType.VOID));
|
||||
this.wrappedInsn = insn;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
return (nodes.size() == sucList.size() ? sucList : nodes);
|
||||
return nodes.size() == sucList.size() ? sucList : nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
private Map<Object, FieldNode> constFields = Collections.emptyMap();
|
||||
private List<ClassNode> innerClasses = Collections.emptyList();
|
||||
|
||||
private CodeWriter code; // generated code
|
||||
private CodeWriter code;
|
||||
|
||||
public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
|
||||
this.dex = dex;
|
||||
|
||||
@@ -20,6 +20,12 @@ import com.android.dx.io.DexBuffer.Section;
|
||||
|
||||
public class AnnotationsParser {
|
||||
|
||||
private static final Annotation.Visibility[] VISIBILITIES = {
|
||||
Visibility.BUILD,
|
||||
Visibility.RUNTIME,
|
||||
Visibility.SYSTEM
|
||||
};
|
||||
|
||||
private final DexNode dex;
|
||||
private final ClassNode cls;
|
||||
|
||||
@@ -76,12 +82,6 @@ public class AnnotationsParser {
|
||||
return new AnnotationsList(list);
|
||||
}
|
||||
|
||||
private static final Annotation.Visibility[] VISIBILITIES = {
|
||||
Visibility.BUILD,
|
||||
Visibility.RUNTIME,
|
||||
Visibility.SYSTEM
|
||||
};
|
||||
|
||||
public static Annotation readAnnotation(DexNode dex, Section s, boolean readVisibility) throws DecodeException {
|
||||
EncValueParser parser = new EncValueParser(dex, s);
|
||||
Visibility visibility = null;
|
||||
|
||||
@@ -17,31 +17,6 @@ import java.util.List;
|
||||
|
||||
public final class IfCondition {
|
||||
|
||||
public static IfCondition fromIfBlock(BlockNode header) {
|
||||
if (header == null) {
|
||||
return null;
|
||||
}
|
||||
return fromIfNode((IfNode) header.getInstructions().get(0));
|
||||
}
|
||||
|
||||
public static IfCondition fromIfNode(IfNode insn) {
|
||||
return new IfCondition(new Compare(insn));
|
||||
}
|
||||
|
||||
public static IfCondition merge(Mode mode, IfCondition a, IfCondition b) {
|
||||
if (a.getMode() == mode) {
|
||||
IfCondition n = new IfCondition(a);
|
||||
n.addArg(b);
|
||||
return n;
|
||||
} else if (b.getMode() == mode) {
|
||||
IfCondition n = new IfCondition(b);
|
||||
n.addArg(a);
|
||||
return n;
|
||||
} else {
|
||||
return new IfCondition(mode, Arrays.asList(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Mode {
|
||||
COMPARE,
|
||||
NOT,
|
||||
@@ -75,6 +50,31 @@ public final class IfCondition {
|
||||
}
|
||||
}
|
||||
|
||||
public static IfCondition fromIfBlock(BlockNode header) {
|
||||
if (header == null) {
|
||||
return null;
|
||||
}
|
||||
return fromIfNode((IfNode) header.getInstructions().get(0));
|
||||
}
|
||||
|
||||
public static IfCondition fromIfNode(IfNode insn) {
|
||||
return new IfCondition(new Compare(insn));
|
||||
}
|
||||
|
||||
public static IfCondition merge(Mode mode, IfCondition a, IfCondition b) {
|
||||
if (a.getMode() == mode) {
|
||||
IfCondition n = new IfCondition(a);
|
||||
n.addArg(b);
|
||||
return n;
|
||||
} else if (b.getMode() == mode) {
|
||||
IfCondition n = new IfCondition(b);
|
||||
n.addArg(a);
|
||||
return n;
|
||||
} else {
|
||||
return new IfCondition(mode, Arrays.asList(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -94,15 +94,15 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
|
||||
private boolean canMove(int from, int to) {
|
||||
List<RegisterArg> movedArgs = argsList.get(from).getArgs();
|
||||
from++;
|
||||
if (from == to) {
|
||||
int start = from + 1;
|
||||
if (start == to) {
|
||||
// previous instruction or on edge of inline border
|
||||
return true;
|
||||
}
|
||||
if (from > to) {
|
||||
throw new JadxRuntimeException("Invalid inline insn positions: " + from + " - " + to);
|
||||
if (start > to) {
|
||||
throw new JadxRuntimeException("Invalid inline insn positions: " + start + " - " + to);
|
||||
}
|
||||
for (int i = from; i < to; i++) {
|
||||
for (int i = start; i < to; i++) {
|
||||
ArgsInfo argsInfo = argsList.get(i);
|
||||
if (argsInfo.getInlinedInsn() == this) {
|
||||
continue;
|
||||
|
||||
@@ -21,7 +21,7 @@ public class CheckRegions extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (mth.isNoCode() || mth.getBasicBlocks().size() == 0) {
|
||||
if (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,11 @@ import org.slf4j.LoggerFactory;
|
||||
public class CleanRegions {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CleanRegions.class);
|
||||
|
||||
private CleanRegions() {
|
||||
}
|
||||
|
||||
public static void process(MethodNode mth) {
|
||||
if (mth.isNoCode() || mth.getBasicBlocks().size() == 0) {
|
||||
if (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
IRegionVisitor removeEmptyBlocks = new AbstractRegionVisitor() {
|
||||
|
||||
@@ -15,6 +15,9 @@ import java.util.Set;
|
||||
|
||||
public class BlockUtils {
|
||||
|
||||
private BlockUtils() {
|
||||
}
|
||||
|
||||
public static BlockNode getBlockByOffset(int offset, Iterable<BlockNode> casesBlocks) {
|
||||
for (BlockNode block : casesBlocks) {
|
||||
if (block.getStartOffset() == offset) {
|
||||
|
||||
@@ -8,6 +8,12 @@ import java.util.List;
|
||||
|
||||
public final class InsnList implements Iterable<InsnNode> {
|
||||
|
||||
private final List<InsnNode> list;
|
||||
|
||||
public InsnList(List<InsnNode> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public static void remove(List<InsnNode> list, InsnNode insn) {
|
||||
for (Iterator<InsnNode> iterator = list.iterator(); iterator.hasNext(); ) {
|
||||
InsnNode next = iterator.next();
|
||||
@@ -33,12 +39,6 @@ public final class InsnList implements Iterable<InsnNode> {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private final List<InsnNode> list;
|
||||
|
||||
public InsnList(List<InsnNode> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public int getIndex(InsnNode insn) {
|
||||
return getIndex(list, insn);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import com.android.dx.io.instructions.DecodedInstruction;
|
||||
|
||||
public class InsnUtils {
|
||||
|
||||
private InsnUtils() {
|
||||
}
|
||||
|
||||
public static int getArg(DecodedInstruction insn, int arg) {
|
||||
switch (arg) {
|
||||
case 0:
|
||||
|
||||
@@ -14,6 +14,9 @@ import java.util.List;
|
||||
|
||||
public class RegionUtils {
|
||||
|
||||
private RegionUtils() {
|
||||
}
|
||||
|
||||
public static boolean hasExitEdge(IContainer container) {
|
||||
if (container instanceof BlockNode) {
|
||||
BlockNode block = (BlockNode) container;
|
||||
|
||||
@@ -2,6 +2,9 @@ package jadx.core.utils;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
private StringUtils() {
|
||||
}
|
||||
|
||||
public static String unescapeString(String str) {
|
||||
int len = str.length();
|
||||
StringBuilder res = new StringBuilder();
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.io.StringWriter;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class Utils {
|
||||
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestLineNumbers extends InternalJadxTest {
|
||||
|
||||
public static class TestCls extends Exception {
|
||||
public static class TestCls {
|
||||
int field;
|
||||
|
||||
public void func() {
|
||||
|
||||
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestConditions2 extends InternalJadxTest {
|
||||
|
||||
public static class TestCls extends Exception {
|
||||
public static class TestCls {
|
||||
int c;
|
||||
String d;
|
||||
String f;
|
||||
|
||||
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestInline extends InternalJadxTest {
|
||||
|
||||
public static class TestCls extends Exception {
|
||||
public static class TestCls {
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("Test: " + new TestCls().testRun());
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestInline2 extends InternalJadxTest {
|
||||
|
||||
public static class TestCls extends Exception {
|
||||
public static class TestCls {
|
||||
public int simple_loops() throws InterruptedException {
|
||||
int[] a = new int[]{1, 2, 4, 6, 8};
|
||||
int b = 0;
|
||||
|
||||
@@ -24,7 +24,6 @@ public class TestInline6 extends InternalJadxTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
setOutputCFG();
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
System.out.println(code);
|
||||
|
||||
@@ -3,6 +3,9 @@ package jadx.gui.treemodel;
|
||||
import javax.swing.Icon;
|
||||
|
||||
public class TextNode extends JNode {
|
||||
|
||||
private static final long serialVersionUID = 2342749142368352232L;
|
||||
|
||||
private final String label;
|
||||
|
||||
public TextNode(String str) {
|
||||
|
||||
Reference in New Issue
Block a user