org.eclipse.persistence.sdo.helper.DynamicClassWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// bdoughan - Mar 18/2009 - 2.0 - Dynamically generated impl classes now
// implement correct interface.
package org.eclipse.persistence.sdo.helper;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.eclipse.persistence.sdo.SDOConstants;
import org.eclipse.persistence.sdo.SDODataObject;
import org.eclipse.persistence.sdo.SDOProperty;
import org.eclipse.persistence.sdo.SDOType;
import org.eclipse.persistence.sdo.helper.extension.SDOUtil;
import org.eclipse.persistence.asm.ASMFactory;
import org.eclipse.persistence.asm.ClassWriter;
import org.eclipse.persistence.asm.MethodVisitor;
import org.eclipse.persistence.asm.Opcodes;
import org.eclipse.persistence.asm.Type;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import commonj.sdo.helper.HelperContext;
/*
* Dynamically generate the implementation class for the SDO type. If the type has an instance
* (interface) class then the dynamically generated impl class must fully implement it. Additionally
* a constructor and a writeReplace() method are added.
*/
public class DynamicClassWriter {
private static final String START_PROPERTY_INDEX = "START_PROPERTY_INDEX";
private static final String END_PROPERTY_INDEX = "END_PROPERTY_INDEX";
private static final String GET = "get";
private static final String SET = "set";
private static final String LIST = "List";
private static final String WRITE_REPLACE = "writeReplace";
private Class> parentClass;
private String typeImplClassDescriptor;
private SDOType type;
private Integer startPropertyIndex;
// hold the context containing all helpers so that we can preserve
// inter-helper relationships
private HelperContext aHelperContext;
public DynamicClassWriter(String className, SDOType type, HelperContext aContext) {
this.aHelperContext = aContext;
this.parentClass = SDODataObject.class;
this.typeImplClassDescriptor = className.replace('.', '/');
this.type = type;
initializeParentClass();
if (type.isSubType()) {
try {
Field parentEndPropertyIndexField = PrivilegedAccessHelper.getField(parentClass, END_PROPERTY_INDEX, true);
Integer parentEndPropertyIndex = PrivilegedAccessHelper.getValueFromField(parentEndPropertyIndexField, parentClass);
startPropertyIndex = parentEndPropertyIndex + 1;
} catch (NoSuchFieldException e) {
startPropertyIndex = 0;
} catch (IllegalAccessException e) {
startPropertyIndex = 0;
}
} else {
startPropertyIndex = 0;
}
}
private void initializeParentClass() {
if (type.isSubType()) {
SDOType parentSDOType = (SDOType) type.getBaseTypes().get(0);
String parentClassName = parentSDOType.getInstanceClassName() + SDOConstants.SDO_IMPL_NAME;
try {
parentClass = ((SDOXMLHelper) aHelperContext.getXMLHelper()).getLoader().loadClass(parentClassName, parentSDOType);
} catch (Exception e) {
parentClass = null;
}
if (parentClass == null) {
parentClass = SDODataObject.class;
}
} else {
parentClass = SDODataObject.class;
}
}
public Class> getParentClass() {
return this.parentClass;
}
/**
* This is where the byte codes for the generic subclass are defined and the
* class is created dynamically from them.
*/
public byte[] createClass() {
ClassWriter cw = ASMFactory.createClassWriter();
if (null == type.getInstanceClass()) {
cw.visit(Opcodes.valueInt("ACC_PUBLIC") + Opcodes.valueInt("ACC_SUPER"), typeImplClassDescriptor, null, Type.getType(parentClass).getInternalName(), null);
} else {
String[] interfaces = new String[1];
interfaces[0] = type.getInstanceClassName().replace('.', '/');
cw.visit(Opcodes.valueInt("ACC_PUBLIC") + Opcodes.valueInt("ACC_SUPER"), typeImplClassDescriptor, null, Type.getType(parentClass).getInternalName(), interfaces);
addPropertyIndices(cw);
for (Object object : type.getDeclaredProperties()) {
SDOProperty sdoProperty = (SDOProperty) object;
addPropertyGetMethod(cw, sdoProperty);
addPropertySetMethod(cw, sdoProperty);
}
}
addConstructors(cw);
addWriteReplace(cw);
cw.visitEnd();
return cw.toByteArray();
}
private void addPropertyIndices(ClassWriter cw) {
cw.visitField(Opcodes.valueInt("ACC_PUBLIC") + Opcodes.valueInt("ACC_FINAL") + Opcodes.valueInt("ACC_STATIC"), START_PROPERTY_INDEX, "I", null, startPropertyIndex).visitEnd();
int declaredPropsSize = type.getDeclaredProperties().size();
Integer endPropertyIndex;
if (declaredPropsSize > 0) {
endPropertyIndex = startPropertyIndex + declaredPropsSize - 2;
} else {
endPropertyIndex = startPropertyIndex - 1;
}
cw.visitField(Opcodes.valueInt("ACC_PUBLIC") + Opcodes.valueInt("ACC_FINAL") + Opcodes.valueInt("ACC_STATIC"), END_PROPERTY_INDEX, "I", null, endPropertyIndex).visitEnd();
}
private void addConstructors(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(Opcodes.valueInt("ACC_PUBLIC"), "", Type.getMethodDescriptor(Type.VOID_TYPE), null, new String[] { Type.getInternalName(Serializable.class) });
mv.visitVarInsn(Opcodes.valueInt("ALOAD"), 0);
mv.visitMethodInsn(Opcodes.valueInt("INVOKESPECIAL"), Type.getType(parentClass).getInternalName(), "", Type.getMethodDescriptor(Type.VOID_TYPE), false);
mv.visitInsn(Opcodes.valueInt("RETURN"));
mv.visitMaxs(1, 1);
mv.visitEnd();
}
private void addPropertyGetMethod(ClassWriter cw, SDOProperty property) {
String returnType = SDOUtil.getJavaTypeForProperty(property);
String outerGetMethodName = SDOUtil.getMethodName(property.getName(), returnType);
if (property.getType() == SDOConstants.SDO_BOOLEAN || property.getType() == SDOConstants.SDO_BOOLEANOBJECT) {
String booleanGetterName = SDOUtil.getBooleanGetMethodName(property.getName(), returnType);
addPropertyGetMethodInternal(cw, property, booleanGetterName, returnType);
}
addPropertyGetMethodInternal(cw, property, outerGetMethodName, returnType);
}
private void addPropertyGetMethodInternal(ClassWriter cw, SDOProperty property, String outerGetMethodName, String returnType) {
String propertyInstanceClassDescriptor;
if (property.isMany()) {
propertyInstanceClassDescriptor = Type.getDescriptor(List.class);
} else if (property.getType().isDataType()) {
propertyInstanceClassDescriptor = Type.getDescriptor(property.getType().getInstanceClass());
} else {
propertyInstanceClassDescriptor = "L" + returnType.replace('.', '/') + ";";
}
MethodVisitor mv = cw.visitMethod(Opcodes.valueInt("ACC_PUBLIC"), outerGetMethodName, "()" + propertyInstanceClassDescriptor, null, null);
mv.visitVarInsn(Opcodes.valueInt("ALOAD"), 0);
mv.visitIntInsn(Opcodes.valueInt("BIPUSH"), startPropertyIndex + property.getIndexInType());
String builtIn = SDOUtil.getBuiltInType(returnType);
if (null != builtIn) {
if (property.getType().isDataType() && !builtIn.equals(LIST)) {
mv.visitMethodInsn(Opcodes.valueInt("INVOKEVIRTUAL"), typeImplClassDescriptor, GET + builtIn, "(I)" + propertyInstanceClassDescriptor, false);
int iReturnOpcode = Type.getType(property.getType().getInstanceClass()).getOpcode(Opcodes.valueInt("IRETURN"));
mv.visitInsn(iReturnOpcode);
} else {
mv.visitMethodInsn(Opcodes.valueInt("INVOKEVIRTUAL"), typeImplClassDescriptor, GET, "(I)Ljava/lang/Object;", false);
mv.visitInsn(Opcodes.valueInt("ARETURN"));
}
} else {
mv.visitMethodInsn(Opcodes.valueInt("INVOKEVIRTUAL"), typeImplClassDescriptor, GET, "(I)Ljava/lang/Object;", false);
mv.visitInsn(Opcodes.valueInt("ARETURN"));
}
mv.visitMaxs(2, 1);
mv.visitEnd();
}
private void addPropertySetMethod(ClassWriter cw, SDOProperty property) {
String returnType = SDOUtil.getJavaTypeForProperty(property);
String outerSetMethodName = SDOUtil.setMethodName(property.getName());
String propertyInstanceClassDescriptor;
if (property.isMany()) {
propertyInstanceClassDescriptor = Type.getDescriptor(List.class);
} else if (property.getType().isDataType()) {
propertyInstanceClassDescriptor = Type.getDescriptor(property.getType().getInstanceClass());
} else {
propertyInstanceClassDescriptor = "L" + returnType.replace('.', '/') + ";";
}
MethodVisitor mv = cw.visitMethod(Opcodes.valueInt("ACC_PUBLIC"), outerSetMethodName, "(" + propertyInstanceClassDescriptor + ")V", null, null);
mv.visitVarInsn(Opcodes.valueInt("ALOAD"), 0);
mv.visitIntInsn(Opcodes.valueInt("BIPUSH"), startPropertyIndex + property.getIndexInType());
String builtIn = SDOUtil.getBuiltInType(returnType);
int iLoadOpcode = Opcodes.valueInt("ALOAD");
if (null != builtIn) {
if (property.getType().isDataType() && !builtIn.equals(LIST)) {
iLoadOpcode = Type.getType(property.getType().getInstanceClass()).getOpcode(Opcodes.valueInt("ILOAD"));
mv.visitVarInsn(iLoadOpcode, 1);
mv.visitMethodInsn(Opcodes.valueInt("INVOKEVIRTUAL"), typeImplClassDescriptor, SET + builtIn, "(I" + propertyInstanceClassDescriptor + ")V", false);
} else {
mv.visitVarInsn(iLoadOpcode, 1);
mv.visitMethodInsn(Opcodes.valueInt("INVOKEVIRTUAL"), typeImplClassDescriptor, SET, "(ILjava/lang/Object;)V", false);
}
} else {
mv.visitVarInsn(iLoadOpcode, 1);
mv.visitMethodInsn(Opcodes.valueInt("INVOKEVIRTUAL"), typeImplClassDescriptor, SET, "(ILjava/lang/Object;)V", false);
}
mv.visitInsn(Opcodes.valueInt("RETURN"));
if (iLoadOpcode == Opcodes.valueInt("DLOAD") || iLoadOpcode == Opcodes.valueInt("LLOAD")) {
mv.visitMaxs(4, 3);
} else {
mv.visitMaxs(3, 2);
}
mv.visitEnd();
}
private void addWriteReplace(ClassWriter cw) {
Method method;
try {
method = parentClass.getDeclaredMethod(WRITE_REPLACE);
} catch (NoSuchMethodException e) {
return;
}
MethodVisitor mv = cw.visitMethod(Opcodes.valueInt("ACC_PROTECTED"), method.getName(), Type.getMethodDescriptor(method), null, new String[] { Type.getInternalName(ObjectStreamException.class) });
mv.visitVarInsn(Opcodes.valueInt("ALOAD"), 0);
mv.visitMethodInsn(Opcodes.valueInt("INVOKESPECIAL"), Type.getInternalName(parentClass), method.getName(), Type.getMethodDescriptor(method), false);
mv.visitInsn(Opcodes.valueInt("ARETURN"));
mv.visitMaxs(1, 1);
mv.visitEnd();
}
}