diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 3d9806e7481dcc0f575f5cdf437f4249bc87488c..fcfa30c9b35c43fcd3964b1f8d4e29825f7a7578 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -146,7 +146,7 @@ public class InsnGen { private String sfield(FieldInfo field) { String thisClass = mth.getParentClass().getFullName(); - if (field.getDeclClass().getFullName().equals(thisClass)) { + if (thisClass.startsWith(field.getDeclClass().getFullName())) { return field.getName(); } else { return useClass(field.getDeclClass()) + '.' + field.getName(); diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index 4b4a7666afa3bc04e453c5e0c5750c610614da05..54df22b122b544532a949be36ad661e7a013384b 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -5,8 +5,10 @@ import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.attributes.DeclareVariableAttr; import jadx.core.dex.attributes.ForceReturnAttr; import jadx.core.dex.attributes.IAttribute; +import jadx.core.dex.info.FieldInfo; import jadx.core.dex.instructions.ArithNode; import jadx.core.dex.instructions.IfOp; +import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.SwitchNode; import jadx.core.dex.instructions.args.ArgType; @@ -232,15 +234,19 @@ public class RegionGen extends InsnGen { SwitchNode insn = (SwitchNode) sw.getHeader().getInstructions().get(0); InsnArg arg = insn.getArg(0); code.startLine("switch(").add(arg(arg)).add(") {"); - code.incIndent(); int size = sw.getKeys().size(); for (int i = 0; i < size; i++) { - List keys = sw.getKeys().get(i); + List keys = sw.getKeys().get(i); IContainer c = sw.getCases().get(i); - for (Integer k : keys) { + for (Object k : keys) { code.startLine("case "); - code.add(TypeGen.literalToString(k, arg.getType())); + if (k instanceof IndexInsnNode) { + code.add(sfield((FieldInfo) ((IndexInsnNode) k).getIndex())); + } + else { + code.add(TypeGen.literalToString((Integer) k, arg.getType())); + } code.add(':'); } makeCaseBlock(c, code); @@ -249,13 +255,12 @@ public class RegionGen extends InsnGen { code.startLine("default:"); makeCaseBlock(sw.getDefaultCase(), code); } - code.decIndent(); + code.startLine('}'); return code; } private void makeCaseBlock(IContainer c, CodeWriter code) throws CodegenException { - code.add(" {"); if (RegionUtils.notEmpty(c)) { makeRegionIndent(code, c); if (RegionUtils.hasExitEdge(c)) { @@ -264,7 +269,6 @@ public class RegionGen extends InsnGen { } else { code.startLine(1, "break;"); } - code.startLine('}'); } private void makeTryCatch(IContainer region, TryCatchBlock tryCatchBlock, CodeWriter code) @@ -305,4 +309,13 @@ public class RegionGen extends InsnGen { } } + // FIXME: !!code from InsnGen.sfield + private String sfield(FieldInfo field) { + String thisClass = mth.getParentClass().getFullName(); + if (thisClass.startsWith(field.getDeclClass().getFullName())) { + return field.getName(); + } else { + return useClass(field.getDeclClass()) + '.' + field.getName(); + } + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java index 99d038d1d060264555b4899288c1be0292a82ae6..18815080fa3d0e286dd000793ae1a4242a522219 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java @@ -571,19 +571,21 @@ public class InsnDecoder { private InsnNode decodeSwitch(DecodedInstruction insn, int offset, boolean packed) { int payloadOffset = insn.getTarget(); DecodedInstruction payload = insnArr[payloadOffset]; - int[] keys; + Object[] keys; int[] targets; if (packed) { PackedSwitchPayloadDecodedInstruction ps = (PackedSwitchPayloadDecodedInstruction) payload; targets = ps.getTargets(); - keys = new int[targets.length]; + keys = new Object[targets.length]; int k = ps.getFirstKey(); for (int i = 0; i < keys.length; i++) keys[i] = k++; } else { SparseSwitchPayloadDecodedInstruction ss = (SparseSwitchPayloadDecodedInstruction) payload; targets = ss.getTargets(); - keys = ss.getKeys(); + keys = new Object[targets.length]; + for (int i = 0; i < keys.length; i++) + keys[i] = ss.getKeys()[i]; } // convert from relative to absolute offsets for (int i = 0; i < targets.length; i++) { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java index 82cd9b648e8c7c3313b197e1a0766030add281e3..d68be585452c3acde8f77a1655fad6dcfc00bce0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java @@ -8,11 +8,11 @@ import java.util.Arrays; public class SwitchNode extends InsnNode { - private final int[] keys; + private final Object[] keys; private final int[] targets; private final int def; // next instruction - public SwitchNode(InsnArg arg, int[] keys, int[] targets, int def) { + public SwitchNode(InsnArg arg, Object[] keys, int[] targets, int def) { super(InsnType.SWITCH, 1); this.keys = keys; this.targets = targets; @@ -24,7 +24,7 @@ public class SwitchNode extends InsnNode { return keys.length; } - public int[] getKeys() { + public Object[] getKeys() { return keys; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 7ec8fe206717fc4cfa2ca95002100281ac5388ca..9ee9a729b30c28f497601449bc81c6ff071ce56b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -12,6 +12,7 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.nodes.parser.AnnotationsParser; import jadx.core.dex.nodes.parser.FieldValueAttr; import jadx.core.dex.nodes.parser.StaticValuesParser; @@ -237,12 +238,37 @@ public class ClassNode extends LineAttrNode implements ILoadable { } public FieldNode getConstField(Object o) { - FieldNode field = constFields.get(o); + ClassNode cn = this; + FieldNode field; + do { + field = cn.constFields.get(o); + } + while (field == null + && (cn.clsInfo.getParentClass() != null) + && (cn = dex.resolveClass(cn.clsInfo.getParentClass())) != null); + if (field == null) field = dex.getConstFields().get(o); return field; } + public FieldNode getConstFieldByLiteralArg(LiteralArg arg) { + ArgType type = arg.getType(); + long literal = arg.getLiteral(); + + if (type.equals(ArgType.DOUBLE)) + return getConstField(Double.longBitsToDouble(literal)); + else if (type.equals(ArgType.FLOAT)) + return getConstField(Float.intBitsToFloat((int) literal)); + else if (Math.abs(literal) > 0x1) { + if (type.equals(ArgType.INT)) + return getConstField((int) literal); + else if (type.equals(ArgType.LONG)) + return getConstField(literal); + } + return null; + } + public FieldNode searchFieldById(int id) { String name = FieldInfo.getNameById(dex, id); for (FieldNode f : fields) { diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java index 9eb52d76a07eb925a9ffadea18da6dc6a4e13ebf..debd0434d19a237c7d5942ea8d77b159876803d2 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java @@ -12,14 +12,14 @@ public final class SwitchRegion extends AbstractRegion { private final BlockNode header; - private final List> keys; + private final List> keys; private final List cases; private IContainer defCase; public SwitchRegion(IRegion parent, BlockNode header) { super(parent); this.header = header; - this.keys = new ArrayList>(); + this.keys = new ArrayList>(); this.cases = new ArrayList(); } @@ -27,7 +27,7 @@ public final class SwitchRegion extends AbstractRegion { return header; } - public void addCase(List keysList, IContainer c) { + public void addCase(List keysList, IContainer c) { keys.add(keysList); cases.add(c); } @@ -40,7 +40,7 @@ public final class SwitchRegion extends AbstractRegion { return defCase; } - public List> getKeys() { + public List> getKeys() { return keys; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java index c79ecb782423e5adb852c9db1e80f1f1b05c4c9d..220bdbb1dc5f6b90ed14bc9cebbd80a75b1fa801 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java @@ -349,44 +349,70 @@ public class BlockMakerVisitor extends AbstractVisitor { return true; } } - - // splice return block if several precessors presents - if (false && block.getAttributes().contains(AttributeFlag.RETURN) - && block.getPredecessors().size() > 1 - && !block.getInstructions().get(0).getAttributes().contains(AttributeType.CATCH_BLOCK)) { + } + // splice return block if several predecessors presents + for (BlockNode block : mth.getExitBlocks()) { + if (block.getInstructions().size() == 1 + && block.getInstructions().get(0).getArgsCount() > 0 + && !block.getInstructions().get(0).getAttributes().contains(AttributeType.CATCH_BLOCK) + && !block.getAttributes().contains(AttributeFlag.SYNTHETIC)) { List preds = new ArrayList(block.getPredecessors()); - - BlockNode origRetBlock = block; - origRetBlock.getPredecessors().clear(); - origRetBlock.getPredecessors().add(preds.get(0)); - preds.remove(0); - - InsnNode origReturnInsn = origRetBlock.getInstructions().get(0); + InsnNode origReturnInsn = block.getInstructions().get(0); RegisterArg retArg = null; if (origReturnInsn.getArgsCount() != 0) retArg = (RegisterArg) origReturnInsn.getArg(0); for (BlockNode pred : preds) { - pred.getSuccessors().remove(origRetBlock); - // make copy of return block and connect to predecessor - BlockNode newRetBlock = startNewBlock(mth, origRetBlock.getStartOffset()); + BlockNode newRetBlock; + InsnNode predInsn = pred.getInstructions().get(0); + + switch (predInsn.getType()) { + case IF: + // make copy of return block and connect to predecessor + newRetBlock = startNewBlock(mth, block.getStartOffset()); + newRetBlock.getAttributes().add(AttributeFlag.SYNTHETIC); + + if (pred.getSuccessors().get(0) == block) { + pred.getSuccessors().set(0, newRetBlock); + } else if (pred.getSuccessors().get(1) == block){ + pred.getSuccessors().set(1, newRetBlock); + } + block.getPredecessors().remove(pred); + newRetBlock.getPredecessors().add(pred); + break; + + case SWITCH: + // TODO: is it ok to just skip this predecessor? + block.getAttributes().add(AttributeFlag.SYNTHETIC); + continue; + + default: + removeConnection(pred, block); + newRetBlock = pred; + break; + } InsnNode ret = new InsnNode(InsnType.RETURN, 1); - if (retArg != null) + if (retArg != null) { ret.addArg(InsnArg.reg(retArg.getRegNum(), retArg.getType())); + ret.getArg(0).forceSetTypedVar(retArg.getTypedVar()); + } ret.getAttributes().addAll(origReturnInsn.getAttributes()); newRetBlock.getInstructions().add(ret); newRetBlock.getAttributes().add(AttributeFlag.RETURN); - connect(pred, newRetBlock); mth.addExitBlock(newRetBlock); } - return true; + if (block.getPredecessors().size() == 0) { + mth.getBasicBlocks().remove(block); + mth.getExitBlocks().remove(block); + return true; + } + return block.getAttributes().contains(AttributeFlag.SYNTHETIC); } - - // TODO detect ternary operator } + // TODO detect ternary operator return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java index f58a8ece64b8dca710e2c8a8927ef853e856d57d..efeaf1beb4ddb77f52938ececcb3191535458b9d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java @@ -81,13 +81,17 @@ public class CodeShrinker extends AbstractVisitor { } } if (wrap) { -// if (useInsn.getType() == InsnType.MOVE) { -// // TODO -// // remover.add(useInsn); -// } else { - useInsnArg.wrapInstruction(insn); + if (insn.getType() == InsnType.MOVE) { + for (int r = 0; r < useInsn.getArgsCount(); r++) { + if (useInsn.getArg(r).getTypedVar() == insn.getResult().getTypedVar()) { + useInsn.setArg(r, insn.getArg(0)); + break; + } + } + } else { + useInsnArg.wrapInstruction(insn); + } remover.add(insn); -// } } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index 36621fae0ab1d5693fecd19a8ce821ed00874fb5..8cfea235f13ae459d845e1e8104e412c4278088c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -3,22 +3,10 @@ package jadx.core.dex.visitors; import jadx.core.deobf.NameMapper; import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.info.MethodInfo; -import jadx.core.dex.instructions.ConstClassNode; -import jadx.core.dex.instructions.ConstStringNode; -import jadx.core.dex.instructions.FillArrayNode; -import jadx.core.dex.instructions.IndexInsnNode; -import jadx.core.dex.instructions.InsnType; -import jadx.core.dex.instructions.InvokeNode; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.instructions.args.LiteralArg; -import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.*; +import jadx.core.dex.instructions.args.*; import jadx.core.dex.instructions.mods.ConstructorInsn; -import jadx.core.dex.nodes.BlockNode; -import jadx.core.dex.nodes.ClassNode; -import jadx.core.dex.nodes.FieldNode; -import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.*; import jadx.core.dex.trycatch.ExcHandlerAttr; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.utils.BlockUtils; @@ -59,6 +47,8 @@ public class ModVisitor extends AbstractVisitor { int size = block.getInstructions().size(); for (int i = 0; i < size; i++) { InsnNode insn = block.getInstructions().get(i); + ClassNode parentClass = mth.getParentClass(); + FieldNode f = null; switch (insn.getType()) { case INVOKE: @@ -102,8 +92,6 @@ public class ModVisitor extends AbstractVisitor { case CONST: case CONST_STR: case CONST_CLASS: - ClassNode parentClass = mth.getParentClass(); - FieldNode f = null; if (insn.getType() == InsnType.CONST_STR) { String s = ((ConstStringNode) insn).getString(); f = parentClass.getConstField(s); @@ -111,19 +99,7 @@ public class ModVisitor extends AbstractVisitor { ArgType t = ((ConstClassNode) insn).getClsType(); f = parentClass.getConstField(t); } else { - LiteralArg arg = (LiteralArg) insn.getArg(0); - ArgType type = arg.getType(); - long lit = arg.getLiteral(); - if (Math.abs(lit) > 0xFF) { - if (type.equals(ArgType.INT)) - f = parentClass.getConstField((int) lit); - else if (type.equals(ArgType.LONG)) - f = parentClass.getConstField(lit); - } - if (type.equals(ArgType.DOUBLE)) - f = parentClass.getConstField(Double.longBitsToDouble(lit)); - else if (type.equals(ArgType.FLOAT)) - f = parentClass.getConstField(Float.intBitsToFloat((int) lit)); + f = parentClass.getConstFieldByLiteralArg((LiteralArg) insn.getArg(0)); } if (f != null) { InsnNode inode = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0); @@ -132,6 +108,27 @@ public class ModVisitor extends AbstractVisitor { } break; + case SWITCH: + SwitchNode sn = (SwitchNode) insn; + for (int k = 0; k < sn.getCasesCount(); k++) { + f = parentClass.getConstField(sn.getKeys()[k]); + if (f != null) { + sn.getKeys()[k] = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0); + } + } + break; + + case RETURN: + if (insn.getArgsCount() > 0 + && insn.getArg(0).isLiteral()) { + LiteralArg arg = (LiteralArg) insn.getArg(0); + f = parentClass.getConstFieldByLiteralArg(arg); + if (f != null) { + arg.wrapInstruction(new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0)); + } + } + break; + default: break; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index fb7f77018d7c9c9585463e4ab6c7dc5ca76b5062..dbe80836d707d0f377810c3f6fb9941273c9fd1c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -583,21 +583,21 @@ public class RegionMaker { int len = insn.getTargets().length; // sort by target - Map> casesMap = new LinkedHashMap>(len); + Map> casesMap = new LinkedHashMap>(len); for (int i = 0; i < len; i++) { - int key = insn.getKeys()[i]; + Object key = insn.getKeys()[i]; int targ = insn.getTargets()[i]; - List keys = casesMap.get(targ); + List keys = casesMap.get(targ); if (keys == null) { - keys = new ArrayList(1); + keys = new ArrayList(2); casesMap.put(targ, keys); } keys.add(key); } - Map> blocksMap = new LinkedHashMap>(len); - for (Entry> entry : casesMap.entrySet()) { - BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors()); + Map> blocksMap = new LinkedHashMap>(len); + for (Entry> entry : casesMap.entrySet()) { + BlockNode c = getBlockByOffset((int) entry.getKey(), block.getSuccessors()); assert c != null; blocksMap.put(c, entry.getValue()); } @@ -650,7 +650,7 @@ public class RegionMaker { if (!stack.containsExit(defCase)) { sw.setDefaultCase(makeRegion(defCase, stack)); } - for (Entry> entry : blocksMap.entrySet()) { + for (Entry> entry : blocksMap.entrySet()) { BlockNode c = entry.getKey(); if (stack.containsExit(c)) { // empty case block diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestReturnWrapping.java b/jadx-core/src/test/java/jadx/tests/internal/TestReturnWrapping.java new file mode 100644 index 0000000000000000000000000000000000000000..ec04fb8640e7dcd1fdcb27135b9091e7cf5f4b2c --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestReturnWrapping.java @@ -0,0 +1,63 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestReturnWrapping extends InternalJadxTest { + public static class TestCls { + /**/ + public static int f1(int arg0) { + switch (arg0) { + case 1: + return 255; + } + return arg0 + 1; + }/**/ + + /**/ + public static Object f2(Object arg0, int arg1) { + Object ret = null; + int i = arg1; + if (arg0 == null) { + return ret + Integer.toHexString(i); + } else { + i++; + try { + ret = new Object().getClass(); + } catch (Exception e) { + ret = "Qwerty"; + } + return i > 128 ? arg0.toString() + ret.toString() : i; + } + }/**/ + + /**/ + public static int f3(int arg0) { + while (arg0 > 10) { + int abc = 951; + if (arg0 == 255) { + return arg0 + 2; + } + arg0 -= abc; + } + return arg0; + }/**/ + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + assertThat(code, containsString("return 255;")); + assertThat(code, containsString("return arg0 + 1;")); + //assertThat(code, containsString("return Integer.toHexString(i);")); + assertThat(code, containsString("return arg0.toString() + ret.toString();")); + assertThat(code, containsString("return arg0 + 2;")); + assertThat(code, containsString("arg0 -= 951;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestSwitchLabels.java b/jadx-core/src/test/java/jadx/tests/internal/TestSwitchLabels.java new file mode 100644 index 0000000000000000000000000000000000000000..e88df55390bd1d08aa244d8f96afdf8afa0e8ca6 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestSwitchLabels.java @@ -0,0 +1,49 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.AdditionalMatchers.or; + +public class TestSwitchLabels extends InternalJadxTest { + public static class TestCls { + public static final int CONST_ABC = 0xABC; + public static final int CONST_CDE = 0xCDE; + + public static class Inner { + private static final int CONST_CDE_PRIVATE = 0xCDE; + public int f1(int arg0) { + switch (arg0) { + case CONST_CDE_PRIVATE: + return CONST_ABC; + } + return 0; + } + } + + public static int f1(int arg0) { + switch (arg0) { + case CONST_ABC: + return CONST_CDE; + } + return 0; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + assertThat(code, containsString("case CONST_ABC:")); + assertThat(code, containsString("return CONST_CDE;")); + + cls.addInnerClass(getClassNode(TestCls.Inner.class)); + assertThat(code, containsString("case CONST_CDE_PRIVATE:")); + assertThat(code, containsString(".CONST_ABC;")); + } +}