diff --git a/dex-tools/src/main/java/com/googlecode/dex2jar/tools/DecryptStringCmd.java b/dex-tools/src/main/java/com/googlecode/dex2jar/tools/DecryptStringCmd.java index e2b7470e9..54e8fdb1f 100644 --- a/dex-tools/src/main/java/com/googlecode/dex2jar/tools/DecryptStringCmd.java +++ b/dex-tools/src/main/java/com/googlecode/dex2jar/tools/DecryptStringCmd.java @@ -292,7 +292,10 @@ private boolean decryptByIr(ClassNode cn, Map map) { // convert ir to m3 MethodNode m3 = new MethodNode(); m3.tryCatchBlocks = new ArrayList<>(); - new IR2JConverter(true).convert(irMethod, m3); + new IR2JConverter() + .ir(irMethod) + .asm(m3) + .convert(); // copy back m3 to m m.maxLocals = -1; diff --git a/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java b/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java index d8f880aea..892b8c8f4 100755 --- a/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java @@ -34,24 +34,44 @@ import org.objectweb.asm.*; import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; @SuppressWarnings("incomplete-switch") public class IR2JConverter implements Opcodes { + public static final int MAX_FILL_ARRAY_BYTES = 500; private boolean optimizeSynchronized = false; - + Dex2Asm.ClzCtx clzCtx; + IrMethod ir; + MethodVisitor asm; public IR2JConverter() { super(); } - public IR2JConverter(boolean optimizeSynchronized) { - super(); + public IR2JConverter optimizeSynchronized(boolean optimizeSynchronized) { this.optimizeSynchronized = optimizeSynchronized; + return this; + } + + public IR2JConverter clzCtx(Dex2Asm.ClzCtx clzCtx) { + this.clzCtx = clzCtx; + return this; + } + + public IR2JConverter ir(IrMethod ir) { + this.ir = ir; + return this; + } + + public IR2JConverter asm(MethodVisitor asm) { + this.asm = asm; + return this; } - public void convert(IrMethod ir, MethodVisitor asm) { + public void convert() { mapLabelStmt(ir); reBuildInstructions(ir, asm); reBuildTryCatchBlocks(ir, asm); @@ -229,15 +249,45 @@ private void reBuildInstructions(IrMethod ir, MethodVisitor asm) { } else { elementType = "I"; } - int iastoreOP = getOpcode(elementType, IASTORE); - accept(e2.getOp1(), asm); - for (int i = 0; i < arraySize; i++) { - asm.visitInsn(DUP); - asm.visitLdcInsn(i); - asm.visitLdcInsn(Array.get(arrayData, i)); - asm.visitInsn(iastoreOP); + boolean genBig = false; + try { + if (this.clzCtx != null + && "BSIJ".contains(elementType)) { + + byte[] data = toLittleEndianArray(arrayData); + + if (data != null && data.length > MAX_FILL_ARRAY_BYTES) { + accept(e2.getOp1(), asm); + asm.visitLdcInsn(0); + constLargeArray(asm, data, elementType); + asm.visitLdcInsn(0); + asm.visitLdcInsn(arraySize); + + asm.visitMethodInsn(Opcodes.INVOKESTATIC, + "java/lang/System", + "arraycopy", + "(Ljava/lang/Object;ILjava/lang/Object;II)V", + false + ); + genBig = true; + } + + } + } catch (Exception ignore) { + // any exception, revert to normal + } + + if (!genBig) { + int iastoreOP = getOpcode(elementType, IASTORE); + accept(e2.getOp1(), asm); + for (int i = 0; i < arraySize; i++) { + asm.visitInsn(DUP); + asm.visitLdcInsn(i); + asm.visitLdcInsn(Array.get(arrayData, i)); + asm.visitInsn(iastoreOP); + } + asm.visitInsn(POP); } - asm.visitInsn(POP); } else { FilledArrayExpr filledArrayExpr = (FilledArrayExpr) e2.getOp2(); int arraySize = filledArrayExpr.ops.length; @@ -248,15 +298,47 @@ private void reBuildInstructions(IrMethod ir, MethodVisitor asm) { } else { elementType = "I"; } - int iastoreOP = getOpcode(elementType, IASTORE); - accept(e2.getOp1(), asm); - for (int i = 0; i < arraySize; i++) { - asm.visitInsn(DUP); - asm.visitLdcInsn(i); - accept(filledArrayExpr.ops[i], asm); - asm.visitInsn(iastoreOP); + + boolean genBig = false; + try { + if (this.clzCtx != null + && "BSIJ".contains(elementType) + && isConstant(filledArrayExpr.ops)) { + // create a 500-len byte array, may cause 'Method code too large!' + // convert it to a base64 decoding + byte[] data = collectDataAsByteArray(filledArrayExpr.ops, elementType); + if (data != null && data.length > MAX_FILL_ARRAY_BYTES) { + accept(e2.getOp1(), asm); + asm.visitLdcInsn(0); + constLargeArray(asm, data, elementType); + asm.visitLdcInsn(0); + asm.visitLdcInsn(arraySize); + + asm.visitMethodInsn(INVOKESTATIC, + "java/lang/System", + "arraycopy", + "(Ljava/lang/Object;ILjava/lang/Object;II)V", + false + ); + + genBig = true; + } + } + } catch (Exception ignore) { + // any exception, revert to normal + } + + if (!genBig) { + int iastoreOP = getOpcode(elementType, IASTORE); + accept(e2.getOp1(), asm); + for (int i = 0; i < arraySize; i++) { + asm.visitInsn(DUP); + asm.visitLdcInsn(i); + accept(filledArrayExpr.ops[i], asm); + asm.visitInsn(iastoreOP); + } + asm.visitInsn(POP); } - asm.visitInsn(POP); } } break; @@ -390,6 +472,35 @@ private void reBuildInstructions(IrMethod ir, MethodVisitor asm) { } } + private void constLargeArray(MethodVisitor asm, byte[] data, String elementType) { + String cst = hexEncode(data); + if (cst.length() > 65535) { // asm have the limit + asm.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); + asm.visitInsn(Opcodes.DUP); + asm.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "", "()V", false); + + for (int i = 0; i < cst.length(); i += 65500) { + int a = Math.min(65500, cst.length() - i); + asm.visitLdcInsn(cst.substring(i, i + a)); + asm.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", + "append", + "(Ljava/lang/String;)Ljava/lang/StringBuilder;", + false + ); + } + asm.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", + "toString", + "()Ljava/lang/String;", + false + ); + } else { + asm.visitLdcInsn(cst); + } + + asm.visitMethodInsn(Opcodes.INVOKESTATIC, toInternal(this.clzCtx.classDescriptor), + this.clzCtx.buildHexDecodeMethodName(elementType), "(Ljava/lang/String;)[" + elementType, false); + } + private static boolean isLocalWithIndex(Value v, int i) { return v.vt == VT.LOCAL && ((Local) v)._ls_index == i; } @@ -556,7 +667,7 @@ static int getOpcode(String v, int op) { } } - private static void accept(Value value, MethodVisitor asm) { + private void accept(Value value, MethodVisitor asm) { switch (value.et) { case E0: @@ -595,10 +706,17 @@ private static void accept(Value value, MethodVisitor asm) { } } - private static void reBuildEnExpression(EnExpr value, MethodVisitor asm) { + public static String hexEncode(byte[] data) { + StringBuilder sb = new StringBuilder(); + for (byte b : data) { + sb.append(String.format("%02x", b & 0xFF)); + } + return sb.toString(); + } + + private void reBuildEnExpression(EnExpr value, MethodVisitor asm) { if (value.vt == VT.FILLED_ARRAY) { FilledArrayExpr fae = (FilledArrayExpr) value; - reBuildE1Expression(Exprs.nNewArray(fae.type, Exprs.nInt(fae.ops.length)), asm); String tp1 = fae.valueType; int xastore = IASTORE; String elementType = null; @@ -607,6 +725,25 @@ private static void reBuildEnExpression(EnExpr value, MethodVisitor asm) { xastore = getOpcode(elementType, IASTORE); } + try { + if (this.clzCtx != null + && elementType != null + && "BSIJ".contains(elementType) + && isConstant(fae.ops)) { + + byte[] data = collectDataAsByteArray(fae.ops, elementType); + if (data != null && data.length > MAX_FILL_ARRAY_BYTES) { + constLargeArray(asm, data, elementType); + return; + } + } + } catch (Exception ignore) { + // any exception, revert to normal + } + + reBuildE1Expression(Exprs.nNewArray(fae.type, Exprs.nInt(fae.ops.length)), asm); + + for (int i = 0; i < fae.ops.length; i++) { if (fae.ops[i] == null) continue; @@ -720,6 +857,91 @@ private static void reBuildEnExpression(EnExpr value, MethodVisitor asm) { } } + private static byte[] collectDataAsByteArray(Value[] ops, String t) { + switch (t) { + case "B": { + byte[] d = new byte[ops.length]; + for (int i = 0, opsLength = ops.length; i < opsLength; i++) { + Value op = ops[i]; + Constant cst = (Constant) op; + d[i] = ((Number) cst.value).byteValue(); + } + return d; + } + case "S": { + short[] d = new short[ops.length]; + for (int i = 0, opsLength = ops.length; i < opsLength; i++) { + Value op = ops[i]; + Constant cst = (Constant) op; + d[i] = ((Number) cst.value).shortValue(); + } + return toLittleEndianArray(d); + } + case "I": { + int[] d = new int[ops.length]; + for (int i = 0, opsLength = ops.length; i < opsLength; i++) { + Value op = ops[i]; + Constant cst = (Constant) op; + d[i] = ((Number) cst.value).intValue(); + } + return toLittleEndianArray(d); + } + case "J": { + long[] d = new long[ops.length]; + for (int i = 0, opsLength = ops.length; i < opsLength; i++) { + Value op = ops[i]; + Constant cst = (Constant) op; + d[i] = ((Number) cst.value).longValue(); + } + return toLittleEndianArray(d); + } + } + return null; + } + + private static byte[] toLittleEndianArray(Object d) { + if (d instanceof byte[]) { + return (byte[]) d; + } else if (d instanceof short[]) { + return toLittleEndianArray((short[]) d); + } else if (d instanceof int[]) { + return toLittleEndianArray((int[]) d); + } else if (d instanceof long[]) { + return toLittleEndianArray((long[]) d); + } + return null; + } + + private static byte[] toLittleEndianArray(long[] d) { + ByteBuffer b = ByteBuffer.allocate(d.length *8); + b.order(ByteOrder.LITTLE_ENDIAN); + b.asLongBuffer().put(d); + return b.array(); + } + + private static byte[] toLittleEndianArray(int[] d) { + ByteBuffer b = ByteBuffer.allocate(d.length *4); + b.order(ByteOrder.LITTLE_ENDIAN); + b.asIntBuffer().put(d); + return b.array(); + } + + private static byte[] toLittleEndianArray(short[] d) { + ByteBuffer b = ByteBuffer.allocate(d.length *2); + b.order(ByteOrder.LITTLE_ENDIAN); + b.asShortBuffer().put(d); + return b.array(); + } + + private static boolean isConstant(Value[] ops) { + for (Value op : ops) { + if (op.vt != VT.CONSTANT) { + return false; + } + } + return true; + } + private static void box(String provideType, String expectedType, MethodVisitor asm) { if(provideType.equals(expectedType)){ return; @@ -848,7 +1070,7 @@ private static void box(String provideType, String expectedType, MethodVisitor a } } - private static void reBuildE1Expression(E1Expr e1, MethodVisitor asm) { + private void reBuildE1Expression(E1Expr e1, MethodVisitor asm) { accept(e1.getOp(), asm); switch (e1.vt) { case STATIC_FIELD: { @@ -924,7 +1146,7 @@ private static void reBuildE1Expression(E1Expr e1, MethodVisitor asm) { } } - private static void reBuildE2Expression(E2Expr e2, MethodVisitor asm) { + private void reBuildE2Expression(E2Expr e2, MethodVisitor asm) { String type = e2.op2.valueType; accept(e2.op1, asm); if ((e2.vt == VT.ADD || e2.vt == VT.SUB) && e2.op2.vt == VT.CONSTANT) { diff --git a/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java b/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java index e75f2fdbe..f37b4fad5 100644 --- a/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java @@ -1,5 +1,7 @@ package com.googlecode.d2j.dex; +import java.io.IOException; +import java.io.InputStream; import java.util.*; import com.googlecode.d2j.converter.Dex2IRConverter; @@ -14,7 +16,19 @@ import com.googlecode.dex2jar.ir.ts.array.FillArrayTransformer; public class Dex2Asm { - + public static class ClzCtx { + public String classDescriptor; + public String hexDecodeMethodNamePrefix; + + public String buildHexDecodeMethodName(String x) { + if (hexDecodeMethodNamePrefix == null) { + byte[] d = new byte[4]; + new Random().nextBytes(d); + hexDecodeMethodNamePrefix = "$d2j$hex$" + IR2JConverter.hexEncode(d); + } + return hexDecodeMethodNamePrefix + "$decode_" + x; + } + } protected static class Clz { public int access; public Clz enclosingClass; @@ -440,17 +454,42 @@ public void convertClass(int dexVersion, DexClassNode classNode, ClassVisitorFac } } if (classNode.methods != null) { + ClzCtx clzCtx = new ClzCtx(); + clzCtx.classDescriptor = classNode.className; for (DexMethodNode methodNode : classNode.methods) { - convertMethod(classNode, methodNode, cv); + convertMethod(classNode, methodNode, cv, clzCtx); + } + if (clzCtx.hexDecodeMethodNamePrefix != null) { + addHexDecodeMethod(cv, clzCtx.hexDecodeMethodNamePrefix); } } cv.visitEnd(); } - - public void convertCode(DexMethodNode methodNode, MethodVisitor mv) { + private void addHexDecodeMethod(ClassVisitor outCV, String hexDecodeMethodNameBase) { + // the .data is a class file compiled from res.Hex + try (InputStream is = Dex2Asm.class.getResourceAsStream("/d2j_hex_decode_stub.data")) { + ClassReader cr = new ClassReader(is); + cr.accept(new ClassVisitor(Opcodes.ASM5) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.startsWith("decode")) { + return outCV.visitMethod(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, + hexDecodeMethodNameBase + "$" + name, + desc, signature, exceptions + ); + } else { + return super.visitMethod(access, name, desc, signature, exceptions); + } + } + }, ClassReader.EXPAND_FRAMES); + } catch (IOException e) { + throw new RuntimeException("fail to add hex.decode", e); + } + } + public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) { IrMethod irMethod = dex2ir(methodNode); optimize(irMethod); - ir2j(irMethod, mv); + ir2j(irMethod, mv, clzCtx); } public void convertDex(DexFileNode fileNode, ClassVisitorFactory cvf) { @@ -546,7 +585,7 @@ public static Object convertConstantValue(Object ele) { return ele; } - public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, ClassVisitor cv) { + public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, ClassVisitor cv, ClzCtx clzCtx) { MethodVisitor mv = collectBasicMethodInfo(methodNode, cv); @@ -593,7 +632,7 @@ public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, Clas if ((NO_CODE_MASK & methodNode.access) == 0) { // has code if (methodNode.codeNode != null) { mv.visitCode(); - convertCode(methodNode, mv); + convertCode(methodNode, mv, clzCtx); } } @@ -624,8 +663,13 @@ private static Clz get(Map classes, String name) { return clz; } - public void ir2j(IrMethod irMethod, MethodVisitor mv) { - new IR2JConverter(false).convert(irMethod, mv); + public void ir2j(IrMethod irMethod, MethodVisitor mv, ClzCtx clzCtx) { + new IR2JConverter() + .optimizeSynchronized(false) + .clzCtx(clzCtx) + .ir(irMethod) + .asm(mv) + .convert(); mv.visitMaxs(-1, -1); } @@ -645,15 +689,16 @@ public void optimize(IrMethod irMethod) { T_agg.transform(irMethod); T_multiArray.transform(irMethod); T_voidInvoke.transform(irMethod); - T_type.transform(irMethod); + { // https://github.com/pxb1988/dex2jar/issues/477 // dead code found in unssa, clean up T_deadCode.transform(irMethod); T_removeLocal.transform(irMethod); T_removeConst.transform(irMethod); - T_unssa.transform(irMethod); } + T_type.transform(irMethod); + T_unssa.transform(irMethod); T_trimEx.transform(irMethod); T_ir2jRegAssign.transform(irMethod); } diff --git a/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2jar.java b/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2jar.java index a9d8d96f9..6694fb335 100644 --- a/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2jar.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2jar.java @@ -124,12 +124,12 @@ public void visitEnd() { }; new ExDex2Asm(exceptionHandler) { - public void convertCode(DexMethodNode methodNode, MethodVisitor mv) { + public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) { if ((readerConfig & DexFileReader.SKIP_CODE) != 0 && methodNode.method.getName().equals("")) { // also skip clinit return; } - super.convertCode(methodNode, mv); + super.convertCode(methodNode, mv, clzCtx); } @Override @@ -169,8 +169,13 @@ public void optimize(IrMethod irMethod) { } @Override - public void ir2j(IrMethod irMethod, MethodVisitor mv) { - new IR2JConverter(0 != (V3.OPTIMIZE_SYNCHRONIZED & v3Config)).convert(irMethod, mv); + public void ir2j(IrMethod irMethod, MethodVisitor mv, ClzCtx clzCtx) { + new IR2JConverter() + .optimizeSynchronized(0 != (V3.OPTIMIZE_SYNCHRONIZED & v3Config)) + .clzCtx(clzCtx) + .ir(irMethod) + .asm(mv) + .convert(); } }.convertDex(fileNode, cvf); diff --git a/dex-translator/src/main/java/com/googlecode/d2j/dex/ExDex2Asm.java b/dex-translator/src/main/java/com/googlecode/d2j/dex/ExDex2Asm.java index 75ef1c2cb..de30fc814 100644 --- a/dex-translator/src/main/java/com/googlecode/d2j/dex/ExDex2Asm.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/dex/ExDex2Asm.java @@ -32,12 +32,12 @@ public ExDex2Asm(DexExceptionHandler exceptionHandler) { } @Override - public void convertCode(DexMethodNode methodNode, MethodVisitor mv) { + public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) { MethodVisitor mw = AsmBridge.searchMethodWriter(mv); MethodNode mn = new MethodNode(Opcodes.ASM5, methodNode.access, methodNode.method.getName(), methodNode.method.getDesc(), null, null); try { - super.convertCode(methodNode, mn); + super.convertCode(methodNode, mn, clzCtx); } catch (Exception ex) { if (exceptionHandler == null) { throw new DexException(ex, "fail convert code for %s", methodNode.method); diff --git a/dex-translator/src/main/resources/d2j_hex_decode_stub.data b/dex-translator/src/main/resources/d2j_hex_decode_stub.data new file mode 100644 index 000000000..7cd3159c1 Binary files /dev/null and b/dex-translator/src/main/resources/d2j_hex_decode_stub.data differ diff --git a/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java b/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java index 9d572418f..f75531a8a 100644 --- a/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java +++ b/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java @@ -296,9 +296,9 @@ public static byte[] translateAndCheck(DexFileNode fileNode, DexClassNode clzNod // 1. convert to .class Dex2Asm dex2Asm = new Dex2Asm() { @Override - public void convertCode(DexMethodNode methodNode, MethodVisitor mv) { + public void convertCode(DexMethodNode methodNode, MethodVisitor mv, ClzCtx clzCtx) { try { - super.convertCode(methodNode, mv); + super.convertCode(methodNode, mv, clzCtx); } catch (Exception ex) { BaksmaliDumper d = new BaksmaliDumper(); try { diff --git a/dex-translator/src/test/java/res/Hex.java b/dex-translator/src/test/java/res/Hex.java new file mode 100644 index 000000000..b53eac2f3 --- /dev/null +++ b/dex-translator/src/test/java/res/Hex.java @@ -0,0 +1,65 @@ +package res; + +import java.nio.*; + +public class Hex { + public static long[] decode_J(String src) { + byte[] d = decode_B(src); + ByteBuffer b = ByteBuffer.wrap(d); + b.order(ByteOrder.LITTLE_ENDIAN); + LongBuffer s = b.asLongBuffer(); + long[] data = new long[d.length / 8]; + s.get(data); + return data; + } + public static int[] decode_I(String src) { + byte[] d = decode_B(src); + ByteBuffer b = ByteBuffer.wrap(d); + b.order(ByteOrder.LITTLE_ENDIAN); + IntBuffer s = b.asIntBuffer(); + int[] data = new int[d.length / 4]; + s.get(data); + return data; + } + + public static short[] decode_S(String src) { + byte[] d = decode_B(src); + ByteBuffer b = ByteBuffer.wrap(d); + b.order(ByteOrder.LITTLE_ENDIAN); + ShortBuffer s = b.asShortBuffer(); + short[] data = new short[d.length / 2]; + s.get(data); + return data; + } + + public static byte[] decode_B(String src) { + char[] d = src.toCharArray(); + byte[] ret = new byte[src.length() / 2]; + for (int i = 0; i < ret.length; i++) { + char h = d[2 * i]; + char l = d[2 * i + 1]; + int hh = 0; + if (h >= '0' && h <= '9') { + hh = h - '0'; + } else if (h >= 'a' && h <= 'f') { + hh = h - 'a' + 10; + } else if (h >= 'A' && h <= 'F') { + hh = h - 'A' + 10; + } else { + throw new RuntimeException(); + } + int ll = 0; + if (l >= '0' && l <= '9') { + ll = h - '0'; + } else if (l >= 'a' && l <= 'f') { + ll = h - 'a' + 10; + } else if (l >= 'A' && l <= 'F') { + ll = h - 'A' + 10; + } else { + throw new RuntimeException(); + } + d[i] = (char) ((hh << 4) | ll); + } + return ret; + } +} diff --git a/dex-translator/src/test/resources/smalis/mayfail-method-code-too-large.smali b/dex-translator/src/test/resources/smalis/method-code-too-large.smali similarity index 99% rename from dex-translator/src/test/resources/smalis/mayfail-method-code-too-large.smali rename to dex-translator/src/test/resources/smalis/method-code-too-large.smali index ef11e0b28..ecdb7a4c5 100644 --- a/dex-translator/src/test/resources/smalis/mayfail-method-code-too-large.smali +++ b/dex-translator/src/test/resources/smalis/method-code-too-large.smali @@ -1,5 +1,6 @@ -.class Lcode.Large; +.class Lcode/Large; +.super Lcode/LargeS; .method private static constructor ()V .catchall { :L2 .. :L3 } :L0