test/hotspot/jtreg/runtime/InvocationTests/shared/GenericClassGenerator.java
author hseigel
Wed, 26 Jun 2019 09:06:32 -0400
changeset 55497 d3a33953b936
permissions -rw-r--r--
8224137: Analyze and port invocation tests to jtreg and co-locate to jdk repo Summary: Add JTReg compatible main programs to run tests for various invoke* instructions Reviewed-by: lfoltan, coleenp

/*
 * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

package shared;

import jdk.internal.org.objectweb.asm.ClassWriter;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import static shared.AccessCheck.*;

public class GenericClassGenerator<T extends GenericClassGenerator> {
    private static final String targetMethodName = Utils.TARGET_METHOD_NAME;

    private int flags = 0;
    private ClassWriter writer;
    private String fullClassName = null;
    private String parentClassName = null;

    /*******************************************************************/
    public GenericClassGenerator(String fullClassName) {
        this(fullClassName, "java/lang/Object");
    }

    /*******************************************************************/
    public GenericClassGenerator(String fullClassName, String parentClassName ) {
        this(fullClassName, parentClassName, ACC_PUBLIC);
    }

    /*******************************************************************/
    public GenericClassGenerator(String fullClassName, String parentClassName, int flags) {
        this(fullClassName, parentClassName, flags, new String[0]);
    }

    /*******************************************************************/
    public GenericClassGenerator(String fullClassName, String parentClassName, int flags, String[] implementedInterfaces) {
        writer = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);

        this.fullClassName = fullClassName;
        this.flags = flags;

        // Construct simple class
        if (parentClassName != null) {
            this.parentClassName = getInternalName(parentClassName);
        } else {
            this.parentClassName = "java/lang/Object";
        }

        String parent = this.parentClassName;
        String name = getInternalName(fullClassName);

        if (Utils.isACC_SUPER) {
            flags = flags | ACC_SUPER;
        }

        writer.visit(Utils.version, flags, name, null, parent, implementedInterfaces);

        // Add constructor
        if ( !isInterface(flags) ) {
            MethodVisitor m =
                    writer.visitMethod(
                            ACC_PUBLIC
                            , "<init>"
                            , "()V"
                            , null
                            , null
                    );

            m.visitCode();
            m.visitVarInsn(ALOAD, 0);
            m.visitMethodInsn(
                      INVOKESPECIAL
                    , getInternalName(parent)
                    , "<init>"
                    , "()V"
            );
            m.visitInsn(RETURN);
            m.visitEnd();
            m.visitMaxs(0,0);
        }
    }

    /*******************************************************************/
    protected static String getInternalName(String fullClassName) {
        return fullClassName.replaceAll("\\.", "/");
    }

    /*******************************************************************/
    public T addTargetConstructor(AccessType access) {
        // AccessType.UNDEF means that the target method isn't defined, so do nothing
        if (access == AccessType.UNDEF || isInterface(flags) ) {
            return (T)this;
        }

        // Add target constructor
        int methodAccessType = access.value();

        MethodVisitor m =
                writer.visitMethod(
                        methodAccessType
                        , "<init>"
                        , "(I)V"
                        , null
                        , null
                );

        // Add a call to parent constructor
        m.visitCode();
        m.visitVarInsn(ALOAD, 0);
        m.visitMethodInsn(
                  INVOKESPECIAL
                , getInternalName(parentClassName)
                , "<init>"
                , "()V"
        );

        // Add result reporting
        String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
        m.visitLdcInsn(shortName+".<init>");
        m.visitFieldInsn(
                  PUTSTATIC
                , "Result"
                , "value"
                , "Ljava/lang/String;"
        );

        m.visitInsn(RETURN);
        m.visitEnd();
        m.visitMaxs(0,0);

        return (T)this;

    }

    /*******************************************************************/
    public T addTargetMethod(AccessType access) {
        return addTargetMethod(access, 0);
    }

    /*******************************************************************/
    public T addTargetMethod(AccessType access, int additionalFlags) {
        // AccessType.UNDEF means that the target method isn't defined, so do nothing
        if (access == AccessType.UNDEF) {
            return (T)this;
        }

        // Add target method
        int methodAccessType = access.value();
        if ( isInterface(flags) || isAbstract(flags) ) {
            methodAccessType |= ACC_ABSTRACT;
        }

        // Skip method declaration for abstract private case, which doesn't pass
        // classfile verification stage
        if ( isPrivate(methodAccessType) && isAbstract(methodAccessType) ) {
            return (T)this;
        }

        MethodVisitor m =
                writer.visitMethod(
                        methodAccessType | additionalFlags
                        , targetMethodName
                        , "()Ljava/lang/String;"
                        , null
                        , null
                );

        // Don't generate body if the method is abstract
        if ( (methodAccessType & ACC_ABSTRACT) == 0 ) {
            String shortName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);

            // Simply returns info about itself
            m.visitCode();
            m.visitLdcInsn(shortName+"."+targetMethodName);
            m.visitInsn(ARETURN);
            m.visitEnd();
            m.visitMaxs(0,0);
        }

        return (T)this;
    }

    /*******************************************************************/
    public T addField(int access, String name, String type) {
        writer.visitField(
                access
                , name
                , getInternalName(type)
                , null
                , null
        )
                .visitEnd();

        return (T)this;
    }

    /*******************************************************************/
    // Add target method call site into current class
    public T addCaller(String targetClass, int callType) {
        MethodVisitor m = writer.visitMethod(
                ACC_PUBLIC | ACC_STATIC
                , "call"
                , String.format( "(L%s;)Ljava/lang/String;" , getInternalName(targetClass))
                , null
                , null
        );

        m.visitCode();
        m.visitVarInsn(ALOAD, 0);
        m.visitMethodInsn(
                  callType
                , getInternalName(targetClass)
                , targetMethodName
                , "()Ljava/lang/String;"
        );
        m.visitInsn(ARETURN);
        m.visitEnd();
        m.visitMaxs(0,0);

        return (T)this;
    }

    /*******************************************************************/
    public byte[] getClassFile() {
        writer.visitEnd();
        return writer.toByteArray();
    }

    /*******************************************************************/
    public String getFullClassName() {
        return fullClassName;
    }
}