| /* |
| * 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, new Type[0]), 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, 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(); |
| } |
| |
| } |