/**
* Copyright (C) 2001-2005 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.generation.enhancer.pc;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.CodeVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.generation.api.SpeedoCompilerParameter;
import org.objectweb.speedo.generation.generator.lib.AbstractSpeedoGenerator;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.speedo.mim.lib.SpeedoPOSerializer;
import org.objectweb.speedo.naming.api.NamingManager;
import org.objectweb.util.monolog.api.Logger;
/**
* Is an ASM visitor adding the implementation of the PersistenceCapable
* interface.
*
* @author S.Chassande-Barrioz
*/
public class POAdder
extends AbstractPCModifier
implements POVariableNames {
public POAdder(ClassVisitor classVisitor,
Logger logger,
SpeedoClass sc,
SpeedoCompilerParameter cp,
Personality p) {
super(classVisitor, logger, sc, cp, p);
}
public void visit(final int version,
final int access,
final String name,
final String superName,
final String[] interfaces,
final String sourceFile) {
//indicate the number of new interface to add
String[] itfs;
if (interfaces != null && interfaces.length > 0) {
itfs = new String[interfaces.length + 1];
System.arraycopy(interfaces, 0, itfs, 1, interfaces.length);
} else {
itfs = new String[1];
}
itfs[0] = Type.getInternalName(PersistentObjectItf.class);
cv.visit(version, access, name, superName, itfs, sourceFile);
}
public void visitEnd() {
if (speedoClass.getSuper() == null) {
generateFields();
generateSpeedoIsActiveMethod();
generateSpeedoIsActiveZMethod();
generateSpeedoGetHomeMethod();
generateSpeedoGetReferenceStateMethod();
generateSpeedoSetReferenceStateMethod();
generateSpeedoGetStateMethod();
generateSpeedoCopyStateMethod();
generateSpeedoReadIntentionMethod();
generateSpeedoWriteIntentionMethod();
generateSpeedoGetEncodedPNameMethod();
generateSpeedoSetEncodedPNameMethod();
generateSpeedoGetPNameHintsMethod();
generateSpeedoGetPOManagerMethod();
generateSpeedoIsPersistentMethod();
}
generateSpeedoCreateStateMethod();
final int nbfields = speedoClass.computeFieldNumbers();
if (speedoClass.isSerializable) {
generateReadObjectMethod(nbfields);
generateWriteObjectMethod(nbfields);
}
super.visitEnd();
}
private void generateFields() {
//transient protected boolean speedoIsActive = false;
cv.visitField(ACC_TRANSIENT + ACC_PROTECTED, ISACTIVE_FIELD_NAME,
Type.BOOLEAN_TYPE.getDescriptor(), null, null);
//transient protected $classNameFields speedoReferenceState = null;
cv.visitField(ACC_TRANSIENT + ACC_PROTECTED, REFSTATE_FIELD_NAME,
xfieldsJT, null, null);
//protected Object encodedPName = null;
cv.visitField(ACC_PROTECTED, ENCODEDPNAME_FIELD_NAME,
"Ljava/lang/Object;", null, null);
}
private void generateSpeedoIsActiveMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, ISACTIVE_FIELD_NAME, "()Z", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
mv.visitInsn(IRETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoIsActiveZMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, ISACTIVE_FIELD_NAME, "(Z)V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitFieldInsn(PUTFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoGetHomeMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, M_SPEEDO_GET_HOME, "()" + JT_HOME, null, null);
mv.visitVarInsn(ALOAD, 0);
//return (HomeItf) getPClassMapping();
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite, "getPClassMapping",
"()Lorg/objectweb/jorm/api/PClassMapping;");
mv.visitTypeInsn(CHECKCAST, JCN_HOME);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoGetReferenceStateMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, M_SPEEDO_GET_REF_STATE, "()" + JT_STATE, null, null);
//return speedoReferenceState
generateGetRefState(mv, false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoGetStateMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, M_SPEEDO_GET_STATE, "()" + JT_STATE, null, null);
//if (!speedoIsActive) {
// return speedoReferenceState;
//}
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
Label l1 = new Label();
mv.visitJumpInsn(IFNE, l1);
generateGetRefState(mv, false);
mv.visitInsn(ARETURN);
mv.visitLabel(l1);
//return speedoGetHome().getSpeedoAccessor(this);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite, M_SPEEDO_GET_HOME, "()" + JT_HOME);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEINTERFACE, JCN_HOME, "getState",
"(" + JT_PO + ")" + JT_STATE);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoReadIntentionMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, "speedoReadIntention",
"([J)" + JT_STATE, null, null);
//if (!speedoIsActive) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
Label l1 = new Label();
mv.visitJumpInsn(IFNE, l1);
{
//if (speedoReferenceState == null) {
generateGetRefState(mv, false);
Label l3 = new Label();
mv.visitJumpInsn(IFNONNULL, l3);
{
//speedoSetReferenceState(speedoCreateState());
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite,
M_SPEEDO_CREATE_STATE, "()" + JT_STATE);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite,
M_SPEEDO_SET_REF_STATE, "(" + JT_STATE + ")V");
}
//return speedoReferenceState;
mv.visitLabel(l3);
generateGetRefState(mv, false);
mv.visitInsn(ARETURN);
}
//return speedoGetHome().readIntention(this, fields);
mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite, M_SPEEDO_GET_HOME, "()" + JT_HOME);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, JCN_HOME, "readIntention",
"(" + JT_PO + "[J)" + JT_STATE);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoWriteIntentionMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, "speedoWriteIntention",
"([J)" + JT_STATE, null, null);
//if (!speedoIsActive) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
Label l1 = new Label();
mv.visitJumpInsn(IFNE, l1);
{
//if (speedoReferenceState == null) {
generateGetRefState(mv, false);
Label l3 = new Label();
mv.visitJumpInsn(IFNONNULL, l3);
{
//speedoSetReferenceState(speedoCreateState());
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite,
M_SPEEDO_CREATE_STATE, "()" + JT_STATE);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite,
M_SPEEDO_SET_REF_STATE, "(" + JT_STATE + ")V");
}
//if(speedoReferenceState.getDetachedStatus() == DetachedLifeCycle.DETACHED_CLEAN){
mv.visitLabel(l3);
generateGetRefState(mv, false);
mv.visitMethodInsn(INVOKEVIRTUAL, xfieldsAncestorJCN, "getDetachedStatus", "()B");
mv.visitInsn(ICONST_1);
Label l5 = new Label();
mv.visitJumpInsn(IF_ICMPNE, l5);
{
//speedoReferenceState.setDetachedStatus(DetachedLifeCycle.DETACHED_DIRTY);
generateGetRefState(mv, false);
mv.visitInsn(ICONST_2);
mv.visitMethodInsn(INVOKEVIRTUAL, xfieldsAncestorJCN, "setDetachedStatus", "(B)V");
}
//return speedoReferenceState;
mv.visitLabel(l5);
generateGetRefState(mv, false);
mv.visitInsn(ARETURN);
}
//return speedoGetHome().writeIntention(this, fields);
mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite, M_SPEEDO_GET_HOME, "()" + JT_HOME);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, JCN_HOME, "writeIntention",
"(" + JT_PO + "[J)" + JT_STATE);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoGetEncodedPNameMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, "speedoGetEncodedPName", "()Ljava/lang/Object;", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ENCODEDPNAME_FIELD_NAME, "Ljava/lang/Object;");
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoSetEncodedPNameMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, "speedoSetEncodedPName", "(Ljava/lang/Object;)V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, classToWrite, ENCODEDPNAME_FIELD_NAME, "Ljava/lang/Object;");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoGetPNameHintsMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, "speedoGetPNameHints", "()Ljava/lang/Object;", null, null);
final Object[] actions;
try {
actions = scp.nmf.getNamingManager(speedoClass).getPNameHints2(
speedoClass,
AbstractSpeedoGenerator.getClassNameDef(speedoClass.jormclass, scp));
} catch (SpeedoException e) {
throw personality.newRuntimeException(e.getMessage(), e);
}
if (actions == null || actions.length == 0) {
mv.visitInsn(ACONST_NULL);
} else if (actions[0].equals(NamingManager.PNH_NULL_VALUE)) {
mv.visitInsn(ACONST_NULL);
} else if (actions[0].equals(NamingManager.PNH_REF_STATE)) {
generateGetRefState(mv, false);
} else if (actions[0].equals(NamingManager.PNH_PFIELD)) {
final PType pt = (PType) actions[2];
final String fieldName = (String) actions[1];
final String jvmFieldType = getJVMType(pt);
final String wrapper = getJavaWrapper(pt);
if (wrapper != null) {
mv.visitTypeInsn(NEW, wrapper);
mv.visitInsn(DUP);
}
generateGetRefState(mv, true);
mv.visitFieldInsn(GETFIELD, xfieldsJCN, fieldName, jvmFieldType);
if (wrapper != null) {
mv.visitMethodInsn(INVOKESPECIAL, wrapper, "<init>",
"(" + jvmFieldType + ")V");
}
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoGetPOManagerMethod() {
CodeVisitor mv = cv.visitMethod(ACC_PUBLIC, "speedoGetPOManager",
"()Lorg/objectweb/speedo/pm/api/POManagerItf;", null, null);
//return (speedoIsActive
//? speedoGetHome().getPOManagerFactory().lookup()
//: null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite, "speedoGetHome",
"()" + JT_HOME);
mv.visitMethodInsn(INVOKEINTERFACE, JCN_HOME, "getPOManagerFactory",
"()" + JT_POMF);
mv.visitMethodInsn(INVOKEINTERFACE, JCN_POMF, "lookup", "()" + JT_POM);
Label l2 = new Label();
mv.visitJumpInsn(GOTO, l2);
mv.visitLabel(l1);
mv.visitInsn(ACONST_NULL);
mv.visitLabel(l2);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private String getJavaWrapper(PType pt) {
switch (pt.getTypeCode()) {
case PType.TYPECODE_BOOLEAN:
return getJVMClassName(Boolean.class);
case PType.TYPECODE_CHAR:
return getJVMClassName(Character.class);
case PType.TYPECODE_BYTE:
return getJVMClassName(Byte.class);
case PType.TYPECODE_SHORT:
return getJVMClassName(Short.class);
case PType.TYPECODE_INT:
return getJVMClassName(Integer.class);
case PType.TYPECODE_LONG:
return getJVMClassName(Long.class);
case PType.TYPECODE_FLOAT:
return getJVMClassName(Float.class);
case PType.TYPECODE_DOUBLE:
return getJVMClassName(Double.class);
default:
return null;
}
}
private void generateSpeedoCreateStateMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, M_SPEEDO_CREATE_STATE, "()" + JT_STATE, null, null);
//return new $classNameFields(this);
mv.visitTypeInsn(NEW, xfieldsJCN);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, xfieldsJCN, "<init>", "(" + JT_PO + ")V");
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoSetReferenceStateMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, M_SPEEDO_SET_REF_STATE, "("+ JT_STATE + ")V", null, null);
//speedoReferenceState = ($classNameFields) refAcc;
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, xfieldsAncestorJCN);
mv.visitFieldInsn(PUTFIELD, ancestorJCN, REFSTATE_FIELD_NAME, xfieldsAncestorJT);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoCopyStateMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, M_SPEEDO_COPY_STATE,
"(" + JT_STATE + JT_STATE + ")V", null, null);
//(($classNameFields) src).speedoCopyState(src, dest);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, xfieldsJCN);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, xfieldsJCN, M_SPEEDO_COPY_STATE,
"(" + JT_STATE + JT_STATE + ")V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
private void generateReadObjectMethod(int nbField) {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PRIVATE, "readObject",
"(Ljava/io/ObjectInputStream;)V",
new String[] { "java/io/IOException", "java/lang/ClassNotFoundException" }, null);
mv.visitVarInsn(ALOAD, 1); //param
mv.visitVarInsn(ALOAD, 0); //this
mv.visitMethodInsn(INVOKESTATIC, getJVMClassName(SpeedoPOSerializer.class),
"readObject",
"(Ljava/io/ObjectInputStream;" + JT_PO + ")V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
private void generateWriteObjectMethod(int nbField) {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PRIVATE, "writeObject",
"(Ljava/io/ObjectOutputStream;)V",
new String[] { "java/io/IOException" }, null);
mv.visitVarInsn(ALOAD, 1); //param
mv.visitVarInsn(ALOAD, 0); //this
// new long[]{0xffffffffffffffffL, ..., 0xffffffffffffffffL}
generateFieldIdAsLongArray(null, nbField, mv);
mv.visitMethodInsn(INVOKESTATIC, getJVMClassName(SpeedoPOSerializer.class),
"writeObject",
"(Ljava/io/ObjectOutputStream;" + JT_PO + "[J)V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
private void generateSpeedoIsPersistentMethod() {
CodeVisitor mv;
mv = cv.visitMethod(ACC_PUBLIC, "speedoIsPersistent", "()Z", null, null);
//if (!speedoIsActive) {
// return false;
//}
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classToWrite, ISACTIVE_FIELD_NAME, "Z");
Label l1 = new Label();
mv.visitJumpInsn(IFNE, l1);
mv.visitInsn(ICONST_0);
mv.visitInsn(IRETURN);
mv.visitLabel(l1);
//StateItf sa = speedoGetState();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classToWrite, M_SPEEDO_GET_STATE, "()" + JT_STATE);
mv.visitVarInsn(ASTORE, 1);
//return sa == null || LifeCycle.isPersistent(sa.speedoGetStatus());
mv.visitVarInsn(ALOAD, 1);
Label l4 = new Label();
mv.visitJumpInsn(IFNULL, l4);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, JCN_STATE, M_SPEEDO_GET_STATUS, "()B");
mv.visitMethodInsn(INVOKESTATIC, JCN_LIFE_CYCLE, "isPersistent", "(B)Z");
Label l5 = new Label();
mv.visitJumpInsn(IFEQ, l5);
mv.visitLabel(l4);
mv.visitInsn(ICONST_1);
Label l6 = new Label();
mv.visitJumpInsn(GOTO, l6);
mv.visitLabel(l5);
mv.visitInsn(ICONST_0);
mv.visitLabel(l6);
mv.visitInsn(IRETURN);
mv.visitMaxs(0, 0);
}
}