| /* |
| * 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 jdk.nashorn.internal.tools.nasgen; |
| |
| import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.AASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ANEWARRAY; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.BALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.BASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH; |
| import static jdk.internal.org.objectweb.asm.Opcodes.CALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.CASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DCONST_0; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DUP; |
| import static jdk.internal.org.objectweb.asm.Opcodes.DUP2; |
| import static jdk.internal.org.objectweb.asm.Opcodes.FALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.FASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.FCONST_0; |
| import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; |
| import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0; |
| import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.NEW; |
| import static jdk.internal.org.objectweb.asm.Opcodes.POP; |
| import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; |
| import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; |
| import static jdk.internal.org.objectweb.asm.Opcodes.SALOAD; |
| import static jdk.internal.org.objectweb.asm.Opcodes.SASTORE; |
| import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH; |
| import static jdk.internal.org.objectweb.asm.Opcodes.SWAP; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT2; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT3; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_TYPE; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_SPECIALIZATION; |
| import java.util.List; |
| import jdk.internal.org.objectweb.asm.Handle; |
| import jdk.internal.org.objectweb.asm.MethodVisitor; |
| import jdk.internal.org.objectweb.asm.Type; |
| |
| /** |
| * 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("Ljdk/nashorn/internal/objects/annotations/SpecializedFunction$LinkLogic$Empty;"); |
| |
| 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); |
| } |
| } |