mirror of https://github.com/apache/openjpa.git
OPENJPA-2911 move more BCClass usage to ASM
This commit is contained in:
parent
eabceb69f4
commit
eba1637454
|
@ -100,6 +100,8 @@ import org.apache.openjpa.util.StringId;
|
|||
import org.apache.openjpa.util.UserException;
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.asm.ClassNodeTracker;
|
||||
import org.apache.openjpa.util.asm.RedefinedAttribute;
|
||||
import org.apache.xbean.asm9.Attribute;
|
||||
import org.apache.xbean.asm9.Opcodes;
|
||||
import org.apache.xbean.asm9.Type;
|
||||
import org.apache.xbean.asm9.tree.*;
|
||||
|
@ -132,6 +134,7 @@ public class PCEnhancer {
|
|||
public static final String ISDETACHEDSTATEDEFINITIVE = PRE + "isDetachedStateDefinitive";
|
||||
|
||||
private static final Class<?> PCTYPE = PersistenceCapable.class;
|
||||
private static final Type TYPE_PCTYPE = Type.getType(PersistenceCapable.class);
|
||||
private static final String SM = PRE + "StateManager";
|
||||
private static final Class<?> SMTYPE = StateManager.class;
|
||||
private static final String INHERIT = PRE + "InheritedFieldCount";
|
||||
|
@ -146,7 +149,6 @@ public class PCEnhancer {
|
|||
private static final String VERSION_INIT_STR = PRE + "VersionInit";
|
||||
|
||||
private static final Localizer _loc = Localizer.forPackage(PCEnhancer.class);
|
||||
private static final String REDEFINED_ATTRIBUTE = PCEnhancer.class.getName() + "#redefined-type";
|
||||
|
||||
private static final AuxiliaryEnhancer[] _auxEnhancers;
|
||||
|
||||
|
@ -536,30 +538,32 @@ public class PCEnhancer {
|
|||
* @return <code>ENHANCE_*</code> constant
|
||||
*/
|
||||
public int run() {
|
||||
Class<?> type = _managedType.getType();
|
||||
try {
|
||||
// if enum, skip, no need of any meta
|
||||
if (type.isEnum())
|
||||
if ((managedType.getClassNode().access & Opcodes.ACC_ENUM) > 0) {
|
||||
return ENHANCE_NONE;
|
||||
}
|
||||
|
||||
// if managed interface, skip
|
||||
if (type.isInterface())
|
||||
if ((managedType.getClassNode().access & Opcodes.ACC_INTERFACE) > 0) {
|
||||
return ENHANCE_INTERFACE;
|
||||
}
|
||||
|
||||
// check if already enhanced
|
||||
// we cannot simply use instanceof or isAssignableFrom as we have a temp ClassLoader inbetween
|
||||
ClassLoader loader = AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(type));
|
||||
for (String iface : _managedType.getDeclaredInterfaceNames()) {
|
||||
if (iface.equals(PCTYPE.getName())) {
|
||||
ClassLoader loader = managedType.getClassLoader();
|
||||
for (String iface : managedType.getClassNode().interfaces) {
|
||||
final String pctypeInternalName = TYPE_PCTYPE.getInternalName();
|
||||
if (iface.equals(pctypeInternalName)) {
|
||||
if (_log.isTraceEnabled()) {
|
||||
_log.trace(_loc.get("pc-type", type, loader));
|
||||
_log.trace(_loc.get("pc-type", managedType.getClassNode().name, loader));
|
||||
}
|
||||
return ENHANCE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (_log.isTraceEnabled()) {
|
||||
_log.trace(_loc.get("enhance-start", type));
|
||||
_log.trace(_loc.get("enhance-start", managedType.getClassNode().name));
|
||||
}
|
||||
|
||||
|
||||
|
@ -597,15 +601,21 @@ public class PCEnhancer {
|
|||
}
|
||||
catch (Exception e) {
|
||||
throw new GeneralException(_loc.get("enhance-error",
|
||||
type.getName(), e.getMessage()), e);
|
||||
managedType.getClassNode().name, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureBCs() {
|
||||
if (!_bcsConfigured) {
|
||||
if (getRedefine()) {
|
||||
if (_managedType.getAttribute(REDEFINED_ATTRIBUTE) == null) {
|
||||
_managedType.addAttribute(REDEFINED_ATTRIBUTE);
|
||||
final boolean isRedefined = managedType.getClassNode().attrs != null &&
|
||||
managedType.getClassNode().attrs.stream().anyMatch(a -> a.isUnknown() && a.type.equals(RedefinedAttribute.ATTR_TYPE));
|
||||
|
||||
if (!isRedefined) {
|
||||
if (managedType.getClassNode().attrs == null) {
|
||||
managedType.getClassNode().attrs = new ArrayList<>();
|
||||
}
|
||||
managedType.getClassNode().attrs.add(new RedefinedAttribute());
|
||||
}
|
||||
else {
|
||||
_isAlreadyRedefined = true;
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.openjpa.enhance.asm;
|
||||
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.asm.ClassNodeTracker;
|
||||
|
||||
|
||||
/**
|
||||
* A special ClassLoader to handle classes currently under bytecode enhancement.
|
||||
* Inspired by the Serp BCClassLoader, but for ASM based enhancement.
|
||||
*
|
||||
* @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
|
||||
* @author: Abe White
|
||||
*/
|
||||
public class EnhancementClassLoader extends ClassLoader {
|
||||
|
||||
private final EnhancementProject project;
|
||||
|
||||
public EnhancementClassLoader(EnhancementProject project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public EnhancementClassLoader(ClassLoader parent, EnhancementProject project) {
|
||||
super(parent);
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public EnhancementProject getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
|
||||
protected Class findClass(String name) throws ClassNotFoundException {
|
||||
byte[] bytes;
|
||||
try {
|
||||
ClassNodeTracker type;
|
||||
if (!project.containsClass(name)) {
|
||||
type = createClass(name);
|
||||
}
|
||||
else {
|
||||
type = project.loadClass(name);
|
||||
}
|
||||
if (type == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
|
||||
bytes = AsmHelper.toByteArray(type);
|
||||
} catch (RuntimeException re) {
|
||||
throw new ClassNotFoundException(re.toString());
|
||||
}
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if unfound classes should be created on-the-fly.
|
||||
* Returns null by default.
|
||||
*/
|
||||
protected ClassNodeTracker createClass(String name) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.openjpa.enhance.asm;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.asm.ClassNodeTracker;
|
||||
import org.apache.xbean.asm9.tree.ClassNode;
|
||||
|
||||
|
||||
/**
|
||||
* Keep track of classes under enhancement.
|
||||
*
|
||||
* @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
|
||||
*/
|
||||
public class EnhancementProject {
|
||||
|
||||
private HashMap<String, ClassNodeTracker> classNodTrackers = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the project already contains the given class.
|
||||
*/
|
||||
public boolean containsClass(String type) {
|
||||
return classNodTrackers.containsKey(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a class with the given name.
|
||||
*
|
||||
* @see #loadClass(String,ClassLoader)
|
||||
*/
|
||||
public ClassNodeTracker loadClass(String name) {
|
||||
return loadClass(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the bytecode for the class with the given name.
|
||||
* If a {@link ClassNodeTracker} with the given name already exists in this project,
|
||||
* it will be returned. Otherwise, a new {@link ClassNodeTracker} will be created
|
||||
* with the given name and returned. If the name represents an existing
|
||||
* type, the returned instance will contain the parsed bytecode for
|
||||
* that type. If the name is of a primitive or array type, the returned
|
||||
* instance will act accordingly.
|
||||
*
|
||||
* @param name the name of the class, including package
|
||||
* @param loader the class loader to use to search for an existing
|
||||
* class with the given name; if null defaults to the
|
||||
* context loader of the current thread
|
||||
* @throws RuntimeException on parse error
|
||||
*/
|
||||
public ClassNodeTracker loadClass(String name, ClassLoader loader) {
|
||||
ClassNodeTracker cached = classNodTrackers.get(name);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// check for existing type
|
||||
if (loader == null) {
|
||||
loader = Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
final ClassNode classNode = AsmHelper.readClassNode(loader, name);
|
||||
ClassNodeTracker cnt = new ClassNodeTracker(classNode, loader);
|
||||
classNodTrackers.put(name, cnt);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import java.util.Arrays;
|
|||
import java.util.Optional;
|
||||
|
||||
import org.apache.openjpa.enhance.asm.BCClassWriter;
|
||||
import org.apache.xbean.asm9.Attribute;
|
||||
import org.apache.xbean.asm9.ClassReader;
|
||||
import org.apache.xbean.asm9.ClassWriter;
|
||||
import org.apache.xbean.asm9.Opcodes;
|
||||
|
@ -50,6 +51,9 @@ import serp.bytecode.Project;
|
|||
*/
|
||||
public final class AsmHelper {
|
||||
private static final char[] PRIMITIVE_DESCRIPTORS = {'V','Z','C','B','S','I','F','J','D'};
|
||||
private static final Attribute[] ATTRS = new Attribute[] {
|
||||
new RedefinedAttribute()
|
||||
};
|
||||
|
||||
private AsmHelper() {
|
||||
// utility class ct
|
||||
|
@ -67,7 +71,7 @@ public final class AsmHelper {
|
|||
try (InputStream in = clazz.getResourceAsStream(className + ".class")) {
|
||||
ClassReader cr = new ClassReader(in);
|
||||
ClassNode classNode = new ClassNode();
|
||||
cr.accept(classNode, 0);
|
||||
cr.accept(classNode, ATTRS, 0);
|
||||
|
||||
return classNode;
|
||||
}
|
||||
|
@ -92,7 +96,7 @@ public final class AsmHelper {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
ClassNode classNode = new ClassNode();
|
||||
cr.accept(classNode, 0);
|
||||
cr.accept(classNode, ATTRS, 0);
|
||||
|
||||
return classNode;
|
||||
}
|
||||
|
@ -104,7 +108,7 @@ public final class AsmHelper {
|
|||
public static ClassWriterTracker toClassWriter(BCClass bcClass) {
|
||||
ClassReader cr = new ClassReader(bcClass.toByteArray());
|
||||
ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, bcClass.getClassLoader());
|
||||
cr.accept(cw, 0); // 0 -> don't skip anything
|
||||
cr.accept(cw, ATTRS, 0); // 0 -> don't skip anything
|
||||
ClassWriterTracker cwt = new ClassWriterTracker(cw, bcClass.getClassLoader());
|
||||
cwt.setName(bcClass.getName());
|
||||
|
||||
|
@ -164,7 +168,7 @@ public final class AsmHelper {
|
|||
public static ClassNodeTracker toClassNode(BCClass bcClass) {
|
||||
ClassReader cr = new ClassReader(bcClass.toByteArray());
|
||||
ClassNode classNode = new ClassNode(Opcodes.ASM9);
|
||||
cr.accept(classNode, 0);
|
||||
cr.accept(classNode, ATTRS, 0);
|
||||
|
||||
if ((classNode.version & 0xffff) < 49) {
|
||||
classNode.version = 49;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.openjpa.util.asm;
|
||||
|
||||
import org.apache.xbean.asm9.Attribute;
|
||||
import org.apache.xbean.asm9.ByteVector;
|
||||
import org.apache.xbean.asm9.ClassReader;
|
||||
import org.apache.xbean.asm9.ClassWriter;
|
||||
import org.apache.xbean.asm9.Label;
|
||||
|
||||
/**
|
||||
* Custom Attribute to mark that this class already got redefined.
|
||||
*
|
||||
* @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
|
||||
*/
|
||||
public class RedefinedAttribute extends Attribute {
|
||||
public static final String ATTR_TYPE = "org/apache/openjpa/Redefined";
|
||||
public RedefinedAttribute() {
|
||||
super(ATTR_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) {
|
||||
return new RedefinedAttribute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {
|
||||
int idx = classWriter.newUTF8("Redefined");
|
||||
return new ByteVector().putShort(idx);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue