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, 2021 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.internal.libraries.asm.ClassWriter;
import org.eclipse.persistence.internal.libraries.asm.MethodVisitor;
import org.eclipse.persistence.internal.libraries.asm.Opcodes;
import org.eclipse.persistence.internal.libraries.asm.Type;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import commonj.sdo.helper.HelperContext;
import org.eclipse.persistence.internal.libraries.asm.EclipseLinkASMClassWriter;
/*
* 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 = (Integer) 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() {
EclipseLinkASMClassWriter cw = new EclipseLinkASMClassWriter();
if (null == type.getInstanceClass()) {
cw.visit(Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, typeImplClassDescriptor, null, Type.getType(parentClass).getInternalName(), null);
} else {
String[] interfaces = new String[1];
interfaces[0] = type.getInstanceClassName().replace('.', '/');
cw.visit(Opcodes.ACC_PUBLIC + Opcodes.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.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.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.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, END_PROPERTY_INDEX, "I", null, endPropertyIndex).visitEnd();
}
private void addConstructors(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]), null, new String[] { Type.getInternalName(Serializable.class) });
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getType(parentClass).getInternalName(), "", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]), false);
mv.visitInsn(Opcodes.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.ACC_PUBLIC, outerGetMethodName, "()" + propertyInstanceClassDescriptor, null, null);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitIntInsn(Opcodes.BIPUSH, startPropertyIndex + property.getIndexInType());
String builtIn = SDOUtil.getBuiltInType(returnType);
if (null != builtIn) {
if (property.getType().isDataType() && !builtIn.equals(LIST)) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeImplClassDescriptor, GET + builtIn, "(I)" + propertyInstanceClassDescriptor, false);
int iReturnOpcode = Type.getType(property.getType().getInstanceClass()).getOpcode(Opcodes.IRETURN);
mv.visitInsn(iReturnOpcode);
} else {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeImplClassDescriptor, GET, "(I)Ljava/lang/Object;", false);
mv.visitInsn(Opcodes.ARETURN);
}
} else {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeImplClassDescriptor, GET, "(I)Ljava/lang/Object;", false);
mv.visitInsn(Opcodes.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.ACC_PUBLIC, outerSetMethodName, "(" + propertyInstanceClassDescriptor + ")V", null, null);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitIntInsn(Opcodes.BIPUSH, startPropertyIndex + property.getIndexInType());
String builtIn = SDOUtil.getBuiltInType(returnType);
int iLoadOpcode = Opcodes.ALOAD;
if (null != builtIn) {
if (property.getType().isDataType() && !builtIn.equals(LIST)) {
iLoadOpcode = Type.getType(property.getType().getInstanceClass()).getOpcode(Opcodes.ILOAD);
mv.visitVarInsn(iLoadOpcode, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeImplClassDescriptor, SET + builtIn, "(I" + propertyInstanceClassDescriptor + ")V", false);
} else {
mv.visitVarInsn(iLoadOpcode, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeImplClassDescriptor, SET, "(ILjava/lang/Object;)V", false);
}
} else {
mv.visitVarInsn(iLoadOpcode, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeImplClassDescriptor, SET, "(ILjava/lang/Object;)V", false);
}
mv.visitInsn(Opcodes.RETURN);
if (iLoadOpcode == Opcodes.DLOAD || iLoadOpcode == Opcodes.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, new Class[0]);
} catch (NoSuchMethodException e) {
return;
}
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, method.getName(), Type.getMethodDescriptor(method), null, new String[] { Type.getInternalName(ObjectStreamException.class) });
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentClass), method.getName(), Type.getMethodDescriptor(method), false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
}