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

Java tutorial

Introduction

Here is the source code for com.tencent.tinker.build.auxiliaryclass.AuxiliaryClassGenerator.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.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.regex.Pattern;

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

public final class AuxiliaryClassGenerator {
    private static final String JAVA_IDENTIFIER_PATTERN_STR = "(?:\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)";

    private static final String JAVA_FULL_CLASSNAME_PATTERN_STR = String.format("(%s(?:\\.%s)*)",
            JAVA_IDENTIFIER_PATTERN_STR, JAVA_IDENTIFIER_PATTERN_STR);

    private static final Pattern JAVA_FULL_CLASSNAME_PATTERN = Pattern.compile(JAVA_FULL_CLASSNAME_PATTERN_STR);

    public static void generateAuxiliaryClass(File dirOutput, String dotClassName) throws IOException {
        if (!JAVA_FULL_CLASSNAME_PATTERN.matcher(dotClassName).matches()) {
            throw new IllegalArgumentException("Bad dotClassName: " + dotClassName);
        }
        if (isPrimitiveClass(dotClassName)) {
            throw new UnsupportedOperationException("Cannot generate primitive class.");
        }
        if (isArrayClass(dotClassName)) {
            throw new UnsupportedOperationException("Cannot generate array class.");
        }

        final int lastDotSepPos = dotClassName.lastIndexOf('.');
        final String classPkgPart = (lastDotSepPos >= 0 ? dotClassName.substring(0, lastDotSepPos) : "");
        final String classNamePart = dotClassName.substring(lastDotSepPos + 1);

        final File realDirOutput = new File(dirOutput, classPkgPart.replace('.', '/'));
        if (!realDirOutput.exists()) {
            realDirOutput.mkdirs();
        }
        final File fileOut = new File(realDirOutput, classNamePart + ".class");

        generateClass(dotClassName, fileOut);
    }

    private static void generateClass(String dotClassName, File fileOut) throws IOException {
        final String classDesc = dotClassName.replace('.', '/');
        ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, classDesc, null, "java/lang/Object", null);
        cw.visitSource(fileOut.getName(), null);
        {
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        cw.visitEnd();
        byte[] classBytes = cw.toByteArray();

        OutputStream os = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(fileOut));
            os.write(classBytes);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (Exception e) {
                    // Ignored.
                }
            }
        }
    }

    private static boolean isPrimitiveClass(String className) {
        try {
            return Class.forName(className).isPrimitive();
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    private static boolean isArrayClass(String className) {
        try {
            return Class.forName(className).isArray();
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}