com.gargoylesoftware.js.nashorn.internal.tools.nasgen.MethodGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.gargoylesoftware.js.nashorn.internal.tools.nasgen.MethodGenerator.java

Source

/*
 * Copyright (c) 2015 Gargoyle Software Inc.
 *
 * 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 (http://www.gnu.org/licenses/).
 */
/*
 * Copyright (c) 2010, 2013, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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 com.gargoylesoftware.js.nashorn.internal.tools.nasgen;

import static com.gargoylesoftware.js.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static com.gargoylesoftware.js.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT2;
import static com.gargoylesoftware.js.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT3;
import static com.gargoylesoftware.js.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_TYPE;
import static com.gargoylesoftware.js.nashorn.internal.tools.nasgen.StringConstants.TYPE_SPECIALIZATION;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.BALOAD;
import static org.objectweb.asm.Opcodes.BASTORE;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CALOAD;
import static org.objectweb.asm.Opcodes.CASTORE;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DALOAD;
import static org.objectweb.asm.Opcodes.DASTORE;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.DUP2;
import static org.objectweb.asm.Opcodes.FALOAD;
import static org.objectweb.asm.Opcodes.FASTORE;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static org.objectweb.asm.Opcodes.IALOAD;
import static org.objectweb.asm.Opcodes.IASTORE;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.LALOAD;
import static org.objectweb.asm.Opcodes.LASTORE;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SALOAD;
import static org.objectweb.asm.Opcodes.SASTORE;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.SWAP;

import java.util.List;

import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import com.gargoylesoftware.js.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;

/**
 * Base class for all method generating classes.
 *
 */
public class MethodGenerator extends MethodVisitor {
    private final int access;
    private final String name;
    private final String descriptor;
    private final Type returnType;
    private final Type[] argumentTypes;

    static final Type EMPTY_LINK_LOGIC_TYPE = Type.getType(LinkLogic.getEmptyLinkLogicClass());

    MethodGenerator(final MethodVisitor mv, final int access, final String name, final String descriptor) {
        super(Main.ASM_VERSION, mv);
        this.access = access;
        this.name = name;
        this.descriptor = descriptor;
        this.returnType = Type.getReturnType(descriptor);
        this.argumentTypes = Type.getArgumentTypes(descriptor);
    }

    int getAccess() {
        return access;
    }

    final String getName() {
        return name;
    }

    final String getDescriptor() {
        return descriptor;
    }

    final Type getReturnType() {
        return returnType;
    }

    final Type[] getArgumentTypes() {
        return argumentTypes;
    }

    /**
     * Check whether access for this method is static
     * @return true if static
     */
    protected final boolean isStatic() {
        return (getAccess() & ACC_STATIC) != 0;
    }

    /**
     * Check whether this method is a constructor
     * @return true if constructor
     */
    protected final boolean isConstructor() {
        return "<init>".equals(name);
    }

    void newObject(final String type) {
        super.visitTypeInsn(NEW, type);
    }

    void newObjectArray(final String type) {
        super.visitTypeInsn(ANEWARRAY, type);
    }

    void loadThis() {
        if ((access & ACC_STATIC) != 0) {
            throw new IllegalStateException("no 'this' inside static method");
        }
        super.visitVarInsn(ALOAD, 0);
    }

    void returnValue() {
        super.visitInsn(returnType.getOpcode(IRETURN));
    }

    void returnVoid() {
        super.visitInsn(RETURN);
    }

    // load, store
    void arrayLoad(final Type type) {
        super.visitInsn(type.getOpcode(IALOAD));
    }

    void arrayLoad() {
        super.visitInsn(AALOAD);
    }

    void arrayStore(final Type type) {
        super.visitInsn(type.getOpcode(IASTORE));
    }

    void arrayStore() {
        super.visitInsn(AASTORE);
    }

    void loadLiteral(final Object value) {
        super.visitLdcInsn(value);
    }

    void classLiteral(final String className) {
        super.visitLdcInsn(className);
    }

    void loadLocal(final Type type, final int index) {
        super.visitVarInsn(type.getOpcode(ILOAD), index);
    }

    void loadLocal(final int index) {
        super.visitVarInsn(ALOAD, index);
    }

    void storeLocal(final Type type, final int index) {
        super.visitVarInsn(type.getOpcode(ISTORE), index);
    }

    void storeLocal(final int index) {
        super.visitVarInsn(ASTORE, index);
    }

    void checkcast(final String type) {
        super.visitTypeInsn(CHECKCAST, type);
    }

    // push constants/literals
    void pushNull() {
        super.visitInsn(ACONST_NULL);
    }

    void push(final int value) {
        if (value >= -1 && value <= 5) {
            super.visitInsn(ICONST_0 + value);
        } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
            super.visitIntInsn(BIPUSH, value);
        } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
            super.visitIntInsn(SIPUSH, value);
        } else {
            super.visitLdcInsn(value);
        }
    }

    void loadClass(final String className) {
        super.visitLdcInsn(Type.getObjectType(className));
    }

    void pop() {
        super.visitInsn(POP);
    }

    // various "dups"
    void dup() {
        super.visitInsn(DUP);
    }

    void dup2() {
        super.visitInsn(DUP2);
    }

    void swap() {
        super.visitInsn(SWAP);
    }

    void dupArrayValue(final int arrayOpcode) {
        switch (arrayOpcode) {
        case IALOAD:
        case FALOAD:
        case AALOAD:
        case BALOAD:
        case CALOAD:
        case SALOAD:
        case IASTORE:
        case FASTORE:
        case AASTORE:
        case BASTORE:
        case CASTORE:
        case SASTORE:
            dup();
            break;

        case LALOAD:
        case DALOAD:
        case LASTORE:
        case DASTORE:
            dup2();
            break;
        default:
            throw new AssertionError("invalid dup");
        }
    }

    void dupReturnValue(final int returnOpcode) {
        switch (returnOpcode) {
        case IRETURN:
        case FRETURN:
        case ARETURN:
            super.visitInsn(DUP);
            return;
        case LRETURN:
        case DRETURN:
            super.visitInsn(DUP2);
            return;
        case RETURN:
            return;
        default:
            throw new IllegalArgumentException("not return");
        }
    }

    void dupValue(final Type type) {
        switch (type.getSize()) {
        case 1:
            dup();
            break;
        case 2:
            dup2();
            break;
        default:
            throw new AssertionError("invalid dup");
        }
    }

    void dupValue(final String desc) {
        final int typeCode = desc.charAt(0);
        switch (typeCode) {
        case '[':
        case 'L':
        case 'Z':
        case 'C':
        case 'B':
        case 'S':
        case 'I':
            super.visitInsn(DUP);
            break;
        case 'J':
        case 'D':
            super.visitInsn(DUP2);
            break;
        default:
            throw new RuntimeException("invalid signature");
        }
    }

    // push default value of given type desc
    void defaultValue(final String desc) {
        final int typeCode = desc.charAt(0);
        switch (typeCode) {
        case '[':
        case 'L':
            super.visitInsn(ACONST_NULL);
            break;
        case 'Z':
        case 'C':
        case 'B':
        case 'S':
        case 'I':
            super.visitInsn(ICONST_0);
            break;
        case 'J':
            super.visitInsn(LCONST_0);
            break;
        case 'F':
            super.visitInsn(FCONST_0);
            break;
        case 'D':
            super.visitInsn(DCONST_0);
            break;
        default:
            throw new AssertionError("invalid desc " + desc);
        }
    }

    // invokes, field get/sets
    void invokeInterface(final String owner, final String method, final String desc) {
        super.visitMethodInsn(INVOKEINTERFACE, owner, method, desc, true);
    }

    void invokeVirtual(final String owner, final String method, final String desc) {
        super.visitMethodInsn(INVOKEVIRTUAL, owner, method, desc, false);
    }

    void invokeSpecial(final String owner, final String method, final String desc) {
        super.visitMethodInsn(INVOKESPECIAL, owner, method, desc, false);
    }

    void invokeStatic(final String owner, final String method, final String desc) {
        super.visitMethodInsn(INVOKESTATIC, owner, method, desc, false);
    }

    void putStatic(final String owner, final String field, final String desc) {
        super.visitFieldInsn(PUTSTATIC, owner, field, desc);
    }

    void getStatic(final String owner, final String field, final String desc) {
        super.visitFieldInsn(GETSTATIC, owner, field, desc);
    }

    void putField(final String owner, final String field, final String desc) {
        super.visitFieldInsn(PUTFIELD, owner, field, desc);
    }

    void getField(final String owner, final String field, final String desc) {
        super.visitFieldInsn(GETFIELD, owner, field, desc);
    }

    private static boolean linkLogicIsEmpty(final Type type) {
        assert EMPTY_LINK_LOGIC_TYPE != null; //type is ok for null if we are a @SpecializedFunction without any attribs
        return EMPTY_LINK_LOGIC_TYPE.equals(type);
    }

    void memberInfoArray(final String className, final List<MemberInfo> mis) {
        if (mis.isEmpty()) {
            pushNull();
            return;
        }

        int pos = 0;
        push(mis.size());
        newObjectArray(SPECIALIZATION_TYPE);
        for (final MemberInfo mi : mis) {
            dup();
            push(pos++);
            visitTypeInsn(NEW, SPECIALIZATION_TYPE);
            dup();
            visitLdcInsn(new Handle(H_INVOKESTATIC, className, mi.getJavaName(), mi.getJavaDesc()));
            final Type linkLogicClass = mi.getLinkLogicClass();
            final boolean linkLogic = !linkLogicIsEmpty(linkLogicClass);
            final String ctor = linkLogic ? SPECIALIZATION_INIT3 : SPECIALIZATION_INIT2;
            if (linkLogic) {
                visitLdcInsn(linkLogicClass);
            }
            visitInsn(mi.isOptimistic() ? ICONST_1 : ICONST_0);
            visitMethodInsn(INVOKESPECIAL, SPECIALIZATION_TYPE, INIT, ctor, false);
            arrayStore(TYPE_SPECIALIZATION);
        }
    }

    void computeMaxs() {
        // These values are ignored as we create class writer
        // with ClassWriter.COMPUTE_MAXS flag.
        super.visitMaxs(Short.MAX_VALUE, Short.MAX_VALUE);
    }

    // debugging support - print calls
    void println(final String msg) {
        super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        super.visitLdcInsn(msg);
        super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    }

    // print the object on the top of the stack
    void printObject() {
        super.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        super.visitInsn(SWAP);
        super.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
    }
}