com.tencent.tinker.build.auxiliaryclass.AuxiliaryClassInjectAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.tencent.tinker.build.auxiliaryclass.AuxiliaryClassInjectAdapter.java

Source

/*
 * Tencent is pleased to support the open source community by making Tinker available.
 *
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tencent.tinker.build.auxiliaryclass;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
 * Created by tangyinsheng on 2016/10/10.
 */

public final class AuxiliaryClassInjectAdapter extends ClassVisitor {
    private final String auxiliaryClassDesc;
    private boolean isClInitExists;
    private boolean isInitExists;
    private boolean isTargetClass;
    private boolean isInjected;

    public AuxiliaryClassInjectAdapter(String auxiliaryClassName, ClassWriter cw) {
        super(Opcodes.ASM5, cw);
        this.auxiliaryClassDesc = fastClassNameToDesc(auxiliaryClassName);
    }

    private String fastClassNameToDesc(String className) {
        if (className.startsWith("L") && className.endsWith(";")) {
            return className;
        }
        if ("boolean".equals(className)) {
            return "Z";
        } else if ("byte".equals(className)) {
            return "B";
        } else if ("char".equals(className)) {
            return "C";
        } else if ("short".equals(className)) {
            return "S";
        } else if ("int".equals(className)) {
            return "I";
        } else if ("long".equals(className)) {
            return "J";
        } else if ("float".equals(className)) {
            return "F";
        } else if ("double".equals(className)) {
            return "D";
        } else {
            className = className.replace('.', '/');
            return "L" + className + ";";
        }
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this.isClInitExists = false;
        this.isInitExists = false;
        this.isTargetClass = ((access & Opcodes.ACC_INTERFACE) == 0);
        this.isInjected = false;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if (mv != null && this.isTargetClass && !this.isInjected) {
            if ("<clinit>".equals(name)) {
                this.isClInitExists = true;
                this.isInjected = true;
                mv = new InjectImplMethodVisitor(mv);
            } else if ("<init>".equals(name)) {
                this.isInitExists = true;
                this.isInjected = true;
                mv = new InjectImplMethodVisitor(mv);
            }
        }
        return mv;
    }

    @Override
    public void visitEnd() {
        // If method <clinit> and <init> are not found, we should generate a <clinit>.
        if (!this.isClInitExists && !this.isInitExists) {
            MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "lineSeparator", "()Ljava/lang/String;",
                    false);
            Label lblSkipInvalidInsn = new Label();
            mv.visitJumpInsn(Opcodes.IFNONNULL, lblSkipInvalidInsn);
            mv.visitLdcInsn(Type.getType(this.auxiliaryClassDesc));
            mv.visitVarInsn(Opcodes.ASTORE, 0);
            mv.visitLabel(lblSkipInvalidInsn);
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        super.visitEnd();
    }

    private class InjectImplMethodVisitor extends MethodVisitor {
        InjectImplMethodVisitor(MethodVisitor mv) {
            super(Opcodes.ASM5, mv);
        }

        @Override
        public void visitInsn(int opcode) {
            if (opcode == Opcodes.RETURN) {
                super.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "lineSeparator",
                        "()Ljava/lang/String;", false);
                Label lblSkipInvalidInsn = new Label();
                super.visitJumpInsn(Opcodes.IFNONNULL, lblSkipInvalidInsn);
                super.visitLdcInsn(Type.getType(AuxiliaryClassInjectAdapter.this.auxiliaryClassDesc));
                super.visitVarInsn(Opcodes.ASTORE, 0);
                super.visitLabel(lblSkipInvalidInsn);
            }
            super.visitInsn(opcode);
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            if (maxStack < 1) {
                maxStack = 1;
            }
            if (maxLocals < 1) {
                maxLocals = 1;
            }
            super.visitMaxs(maxStack, maxLocals);
        }
    }
}