blob: 3d968e8e1920599ce3419510dc1f4adbf46f83de [file] [log] [blame]
/*
* 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);
}
}