blob: c6fd54da898bc757c7fd5fca11e2a65f996b90d0 [file] [log] [blame]
/*
* 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 = 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, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), null, new String[] { Type.getInternalName(Serializable.class) });
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getType(parentClass).getInternalName(), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), 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);
} 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();
}
}