/*
* Copyright (c) 2014, 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.sun.btrace.runtime;
import com.sun.btrace.org.objectweb.asm.Label;
import com.sun.btrace.org.objectweb.asm.MethodVisitor;
import com.sun.btrace.org.objectweb.asm.Opcodes;
import static com.sun.btrace.org.objectweb.asm.Opcodes.*;
import static com.sun.btrace.runtime.Constants.*;
import com.sun.btrace.org.objectweb.asm.Type;
import com.sun.btrace.util.Interval;
/**
* Convenient fluent wrapper over the ASM method visitor
*
* @author Jaroslav Bachorik
*/
final public class Assembler {
private final MethodVisitor mv;
public Assembler(MethodVisitor mv) {
this.mv = mv;
}
public Assembler push(int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, value);
} else {
mv.visitLdcInsn(value);
}
return this;
}
public Assembler arrayLoad(Type type) {
mv.visitInsn(type.getOpcode(IALOAD));
return this;
}
public Assembler arrayStore(Type type) {
mv.visitInsn(type.getOpcode(IASTORE));
return this;
}
public Assembler jump(int opcode, Label l) {
mv.visitJumpInsn(opcode, l);
return this;
}
public Assembler ldc(Object o) {
if (o == null) {
return loadNull();
}
if (o instanceof Integer) {
int i = (int)o;
if (i >= -1 && i <= 5) {
int opcode = -1;
switch(i) {
case 0: {
opcode = ICONST_0;
break;
}
case 1: {
opcode = ICONST_1;
break;
}
case 2: {
opcode = ICONST_2;
break;
}
case 3: {
opcode = ICONST_3;
break;
}
case 4: {
opcode = ICONST_4;
break;
}
case 5: {
opcode = ICONST_5;
break;
}
case -1: {
opcode = ICONST_M1;
break;
}
}
mv.visitInsn(opcode);
return this;
}
}
if (o instanceof Long) {
long l = (long)o;
if (l >= 0 && l <= 1) {
int opcode = -1;
switch((int)l) {
case 0: {
opcode = LCONST_0;
break;
}
case 1: {
opcode = LCONST_1;
break;
}
}
mv.visitInsn(opcode);
return this;
}
}
mv.visitLdcInsn(o);
return this;
}
public Assembler sub(Type t) {
int opcode = -1;
switch (t.getSort()) {
case Type.SHORT:
case Type.BYTE:
case Type.INT: {
opcode = ISUB;
break;
}
case Type.LONG: {
opcode = LSUB;
break;
}
case Type.FLOAT: {
opcode = FSUB;
break;
}
case Type.DOUBLE: {
opcode = DSUB;
break;
}
}
if (opcode != -1) {
mv.visitInsn(opcode);
}
return this;
}
public Assembler loadNull() {
mv.visitInsn(ACONST_NULL);
return this;
}
public Assembler loadLocal(Type type, int index) {
mv.visitVarInsn(type.getOpcode(ILOAD), index);
return this;
}
public Assembler storeLocal(Type type, int index) {
mv.visitVarInsn(type.getOpcode(ISTORE), index);
return this;
}
public Assembler storeField(Type owner, String name, Type t) {
mv.visitFieldInsn(Opcodes.PUTFIELD, owner.getInternalName(), name, t.getDescriptor());
return this;
}
public Assembler storeStaticField(Type owner, String name, Type t) {
mv.visitFieldInsn(Opcodes.PUTSTATIC, owner.getInternalName(), name, t.getDescriptor());
return this;
}
public Assembler loadField(Type owner, String name, Type t) {
mv.visitFieldInsn(Opcodes.GETFIELD, owner.getInternalName(), name, t.getDescriptor());
return this;
}
public Assembler loadStaticField(Type owner, String name, Type t) {
mv.visitFieldInsn(Opcodes.GETSTATIC, owner.getInternalName(), name, t.getDescriptor());
return this;
}
public Assembler pop() {
mv.visitInsn(POP);
return this;
}
public Assembler dup() {
mv.visitInsn(DUP);
return this;
}
public Assembler dup_x1() {
mv.visitInsn(DUP_X1);
return this;
}
public Assembler dup_x2() {
mv.visitInsn(DUP_X2);
return this;
}
public Assembler dup2() {
mv.visitInsn(DUP2);
return this;
}
public Assembler dup2_x1() {
mv.visitInsn(DUP2_X1);
return this;
}
public Assembler dup2_x2() {
mv.visitInsn(DUP2_X2);
return this;
}
public Assembler swap() {
mv.visitInsn(SWAP);
return this;
}
public Assembler newInstance(Type t) {
mv.visitTypeInsn(NEW, t.getInternalName());
return this;
}
public Assembler newArray(Type t) {
mv.visitTypeInsn(ANEWARRAY, t.getInternalName());
return this;
}
public Assembler dupArrayValue(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;
}
return this;
}
public Assembler dupReturnValue(int returnOpcode) {
switch (returnOpcode) {
case IRETURN:
case FRETURN:
case ARETURN:
mv.visitInsn(DUP);
break;
case LRETURN:
case DRETURN:
mv.visitInsn(DUP2);
break;
case RETURN:
break;
default:
throw new IllegalArgumentException("not return");
}
return this;
}
public Assembler dupValue(Type type) {
switch (type.getSize()) {
case 1:
dup();
break;
case 2:
dup2();
break;
}
return this;
}
public Assembler dupValue(String desc) {
int typeCode = desc.charAt(0);
switch (typeCode) {
case '[':
case 'L':
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
mv.visitInsn(DUP);
break;
case 'J':
case 'D':
mv.visitInsn(DUP2);
break;
default:
throw new RuntimeException("invalid signature");
}
return this;
}
public Assembler box(Type type) {
return box(type.getDescriptor());
}
public Assembler box(String desc) {
int typeCode = desc.charAt(0);
switch (typeCode) {
case '[':
case 'L':
break;
case 'Z':
invokeStatic(BOOLEAN_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_BOOLEAN_DESC);
break;
case 'C':
invokeStatic(CHARACTER_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_CHARACTER_DESC);
break;
case 'B':
invokeStatic(BYTE_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_BYTE_DESC);
break;
case 'S':
invokeStatic(SHORT_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_SHORT_DESC);
break;
case 'I':
invokeStatic(INTEGER_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_INTEGER_DESC);
break;
case 'J':
invokeStatic(LONG_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_LONG_DESC);
break;
case 'F':
invokeStatic(FLOAT_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_FLOAT_DESC);
break;
case 'D':
invokeStatic(DOUBLE_BOXED_INTERNAL,
BOX_VALUEOF,
BOX_DOUBLE_DESC);
break;
}
return this;
}
public Assembler unbox(Type type) {
return unbox(type.getDescriptor());
}
public Assembler unbox(String desc) {
int typeCode = desc.charAt(0);
switch (typeCode) {
case '[':
case 'L':
mv.visitTypeInsn(CHECKCAST, Type.getType(desc).getInternalName());
break;
case 'Z':
mv.visitTypeInsn(CHECKCAST, BOOLEAN_BOXED_INTERNAL);
invokeVirtual(BOOLEAN_BOXED_INTERNAL,
BOOLEAN_VALUE,
BOOLEAN_VALUE_DESC);
break;
case 'C':
mv.visitTypeInsn(CHECKCAST, CHARACTER_BOXED_INTERNAL);
invokeVirtual(CHARACTER_BOXED_INTERNAL,
CHAR_VALUE,
CHAR_VALUE_DESC);
break;
case 'B':
mv.visitTypeInsn(CHECKCAST, NUMBER_INTERNAL);
invokeVirtual(NUMBER_INTERNAL,
BYTE_VALUE,
BYTE_VALUE_DESC);
break;
case 'S':
mv.visitTypeInsn(CHECKCAST, NUMBER_INTERNAL);
invokeVirtual(NUMBER_INTERNAL,
SHORT_VALUE,
SHORT_VALUE_DESC);
break;
case 'I':
mv.visitTypeInsn(CHECKCAST, NUMBER_INTERNAL);
invokeVirtual(NUMBER_INTERNAL,
INT_VALUE,
INT_VALUE_DESC);
break;
case 'J':
mv.visitTypeInsn(CHECKCAST, NUMBER_INTERNAL);
invokeVirtual(NUMBER_INTERNAL,
LONG_VALUE,
LONG_VALUE_DESC);
break;
case 'F':
mv.visitTypeInsn(CHECKCAST, NUMBER_INTERNAL);
invokeVirtual(NUMBER_INTERNAL,
FLOAT_VALUE,
FLOAT_VALUE_DESC);
break;
case 'D':
mv.visitTypeInsn(CHECKCAST, NUMBER_INTERNAL);
invokeVirtual(NUMBER_INTERNAL,
DOUBLE_VALUE,
DOUBLE_VALUE_DESC);
break;
}
return this;
}
public Assembler defaultValue(String desc) {
int typeCode = desc.charAt(0);
switch (typeCode) {
case '[':
case 'L':
mv.visitInsn(ACONST_NULL);
break;
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
mv.visitInsn(ICONST_0);
break;
case 'J':
mv.visitInsn(LCONST_0);
break;
case 'F':
mv.visitInsn(FCONST_0);
break;
case 'D':
mv.visitInsn(DCONST_0);
break;
}
return this;
}
public Assembler println(String msg) {
mv.visitFieldInsn(GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn(msg);
invokeVirtual("java/io/PrintStream",
"println",
"(Ljava/lang/String;)V");
return this;
}
// print the object on the top of the stack
public Assembler printObject() {
mv.visitFieldInsn(GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;");
mv.visitInsn(SWAP);
mv.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/Object;)V",
false);
return this;
}
public Assembler invokeVirtual(String owner, String method, String desc) {
mv.visitMethodInsn(INVOKEVIRTUAL, owner, method, desc, false);
return this;
}
public Assembler invokeSpecial(String owner, String method, String desc) {
mv.visitMethodInsn(INVOKESPECIAL, owner, method, desc, false);
return this;
}
public Assembler invokeStatic(String owner, String method, String desc) {
mv.visitMethodInsn(INVOKESTATIC, owner, method, desc, false);
return this;
}
public Assembler invokeInterface(String owner, String method, String desc) {
mv.visitMethodInsn(INVOKEVIRTUAL, owner, method, desc, true);
return this;
}
public Assembler getStatic(String owner, String name, String desc) {
mv.visitFieldInsn(GETSTATIC, owner, name, desc);
return this;
}
public Assembler putStatic(String owner, String name, String desc) {
mv.visitFieldInsn(PUTSTATIC, owner, name, desc);
return this;
}
public Assembler label(Label l) {
mv.visitLabel(l);
return this;
}
public Assembler addLevelCheck(String clsName, Level level, Label jmp) {
return addLevelCheck(clsName, level.getValue(), jmp);
}
public Assembler addLevelCheck(String clsName, Interval itv, Label jmp) {
getStatic(clsName, "$btrace$$level", INT_DESC);
if (itv.getA() <= 0) {
if (itv.getB() != Integer.MAX_VALUE) {
ldc(itv.getB());
jump(Opcodes.IF_ICMPGT, jmp);
}
} else if (itv.getA() < itv.getB()) {
if (itv.getB() == Integer.MAX_VALUE) {
ldc(itv.getA());
jump(Opcodes.IF_ICMPLT, jmp);
} else {
ldc(itv.getA());
jump(Opcodes.IF_ICMPLT, jmp);
getStatic(clsName, "$btrace$$level", INT_DESC);
ldc(itv.getB());
jump(Opcodes.IF_ICMPGT, jmp);
}
}
return this;
}
/**
* Compares the instrumentation level interval against the runtime value.
* <p>
* If the runtime value is fitting the level interval there will be 0
* on stack upon return from this method. Otherwise there will be -1.
*
* @param clsName The probe class name
* @param level The probe instrumentation level
* @return itself
*/
public Assembler compareLevel(String clsName, Level level) {
Interval itv = level.getValue();
if (itv.getA() <= 0) {
if (itv.getB() != Integer.MAX_VALUE) {
ldc(itv.getB());
getStatic(clsName, BTRACE_LEVEL_FLD, INT_DESC);
sub(Type.INT_TYPE);
}
} else if (itv.getA() < itv.getB()) {
if (itv.getB() == Integer.MAX_VALUE) {
getStatic(clsName, BTRACE_LEVEL_FLD, INT_DESC);
ldc(itv.getA());
sub(Type.INT_TYPE);
} else {
Label l1 = new Label();
Label l2 = new Label();
ldc(itv.getA());
jump(Opcodes.IF_ICMPLT, l1);
getStatic(clsName, BTRACE_LEVEL_FLD, INT_DESC);
ldc(itv.getB());
jump(Opcodes.IF_ICMPGT, l1);
ldc(0);
jump(Opcodes.GOTO, l2);
label(l1);
ldc(-1);
label(l2);
}
}
return this;
}
}