use of com.newrelic.weave.utils.SynchronizedClassNode in project newrelic-java-agent by newrelic.
the class PreparedExtension method generateExtensionClass.
/**
* Generate the {@link ClassNode} for the extension class.
*
* @return the {@link ClassNode} for the extension class
*/
public ClassNode generateExtensionClass() {
// create the extension node
ClassNode extension;
{
extension = new SynchronizedClassNode();
Map<String, String> oldToNew = new HashMap<>(1);
// first set the class name to the generated name
oldToNew.put(extensionTemplate.name, extensionClassName);
// supertype is just a marker. Remove for generated class
oldToNew.put(extensionTemplate.superName, "java/lang/Object");
ClassVisitor visitor = ReferenceUtils.getRenamingVisitor(oldToNew, extension);
// update and methods or fields that collide with the @NewField names
for (String newFieldName : match.getNewFields()) {
visitor = updateCollidingFieldNames(visitor, newFieldName);
}
// copy extensionNode (with renames) into extension
extensionTemplate.accept(visitor);
}
extension.visit(WeaveUtils.RUNTIME_MAX_SUPPORTED_CLASS_VERSION, ACC_PUBLIC + ACC_SUPER, extensionClassName, null, "java/lang/Object", extensionTemplate.interfaces.toArray(new String[extensionTemplate.interfaces.size()]));
FieldVisitor fv;
ClassNode weaveClass = match.getWeave();
// add new fields
for (String newFieldName : match.getNewFields()) {
FieldNode newField = WeaveUtils.findRequiredMatch(weaveClass.fields, newFieldName);
// make the fields public since they'll be accessed from a different object
// clear private, protected and final flags
int access = newField.access;
access |= Opcodes.ACC_PUBLIC;
access &= ~(Opcodes.ACC_PRIVATE + Opcodes.ACC_PROTECTED + Opcodes.ACC_FINAL);
fv = extension.visitField(access, newField.name, newField.desc, newField.signature, newField.value);
fv.visitEnd();
}
if (match.getExtensionClassInit() != null) {
// static init
MethodNode clinitMethodNode = WeaveUtils.getMethodNode(extension, "<clinit>", "()V");
if (null == clinitMethodNode) {
// no clinit, let's make an empty one
extension.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
clinitMethodNode = WeaveUtils.getMethodNode(extension, "<clinit>", "()V");
clinitMethodNode.visitCode();
clinitMethodNode.visitInsn(RETURN);
// these get recomputed later
clinitMethodNode.visitMaxs(0, 0);
clinitMethodNode.visitEnd();
}
// initialize new fields from weaver class initializer
Set<MethodNode> toInline = new HashSet<>();
MethodNode classInit = rewriteNewFieldCalls(match.getExtensionClassInit());
for (Method newMethod : match.getNewMethods()) {
MethodNode newMethodNode = WeaveUtils.findMatch(match.getWeave().methods, newMethod);
if (newMethodNode != null) {
toInline.add(newMethodNode);
}
}
classInit = MethodProcessors.inlineMethods(weaveClass.name, toInline, extensionClassName, classInit);
AbstractInsnNode retInsn = clinitMethodNode.instructions.getLast();
while (retInsn != null && retInsn.getOpcode() != Opcodes.RETURN) {
retInsn = retInsn.getPrevious();
}
if (null != retInsn) {
clinitMethodNode.instructions.remove(retInsn);
}
clinitMethodNode.instructions.add(classInit.instructions);
clinitMethodNode.instructions.resetLabels();
}
extension.visitEnd();
extension.visitMethod(ACC_PUBLIC, RESET_CHECK_NAME, RESET_CHECK_DESC, null, null);
MethodNode resetMethod = WeaveUtils.getMethodNode(extension, RESET_CHECK_NAME, RESET_CHECK_DESC);
final Label FALSE = new Label();
for (String newFieldName : match.getNewFields()) {
FieldNode newFieldNode = WeaveUtils.findMatch(match.getWeave().fields, newFieldName);
if ((newFieldNode.access & Opcodes.ACC_STATIC) == 0) {
writeFieldResetCheck(resetMethod, newFieldNode, FALSE);
}
}
resetMethod.visitInsn(Opcodes.ICONST_1);
resetMethod.visitInsn(IRETURN);
resetMethod.visitLabel(FALSE);
resetMethod.visitInsn(Opcodes.ICONST_0);
resetMethod.visitInsn(IRETURN);
resetMethod.visitMaxs(0, 0);
resetMethod.visitEnd();
return extension;
}
use of com.newrelic.weave.utils.SynchronizedClassNode in project newrelic-java-agent by newrelic.
the class PreparedMatch method prepareNewInnerClass.
/**
* Prepares a <i>new</i> inner class (i.e. child of the weave class for this match) so it can be loaded into a
* classloader. New inner classes can access new fields (but NOT new methods) in their parent, and anonymous inner
* classes need to have thier names rewritten so they don't conflict with those in the original or target. This
* rewrites new inner classes so all of this works.
*
* <p>
* New inner classes should be validated with {@link ClassMatch} before calling this method.
* </p>
*
* <p>
* This method is not to be used with <i>matched</i> inner classes; they have more restrictions and will be modified
* through the standard weave process.
* </p>
*
* @param newInnerClassNode new inner class
* @return processed inner class that can be loaded into a classloader
* @see ClassMatch#validateNewInnerClass(ClassNode)
*/
public ClassNode prepareNewInnerClass(final ClassNode newInnerClassNode) {
// rewrite new fields
if (null != extension && null != newInnerClassNode.methods) {
for (MethodNode method : newInnerClassNode.methods) {
extension.rewriteNewFieldCalls(method);
}
}
ClassNode anonClass = new SynchronizedClassNode(WeaveUtils.ASM_API_LEVEL);
// remaps anonymous inner class names and maps weave -> original
Map<String, String> typeMap = new HashMap<>(anonymousInnerClassTypeMap);
typeMap.put(weaveName, originalName);
ClassVisitor remapper = new ClassRemapper(anonClass, new SimpleRemapper(typeMap));
// inlines new methods and rewrites new fields to use the extension class
ClassVisitor rewriteNewVisitor = new ClassVisitor(WeaveUtils.ASM_API_LEVEL, remapper) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, makePublic(access), name, signature, superName, interfaces);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
return super.visitField(makePublic(access), name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return super.visitMethod(makePublic(access), name, desc, signature, exceptions);
}
};
// note the delegation order - we will rewrite new methods/fields BEFORE remapping types
newInnerClassNode.accept(rewriteNewVisitor);
return anonClass;
}
use of com.newrelic.weave.utils.SynchronizedClassNode in project newrelic-java-agent by newrelic.
the class PackageValidationResult method computeUtilityClassBytes.
/**
* Compute and return a map of (name -> bytes) for all utility classes from this validation result.
*
* @see NewClassAppender
*/
public Map<String, byte[]> computeUtilityClassBytes(ClassCache cache) {
Map<String, byte[]> utilClassBytes = new HashMap<>(utilClasses.size());
for (Map.Entry<String, ClassNode> entry : utilClasses.entrySet()) {
// Run post-processors on our utility classes to ensure that we capture API supportability metrics properly
ClassNode result = new SynchronizedClassNode(WeaveUtils.ASM_API_LEVEL);
ClassVisitor postprocessChain = weavePackage.getConfig().getPostprocessor().postprocess(entry.getValue().name, result, Collections.<String>emptySet(), weavePackage, true);
entry.getValue().accept(postprocessChain);
utilClassBytes.put(entry.getKey(), WeaveUtils.convertToClassBytes(result, cache));
}
return utilClassBytes;
}
use of com.newrelic.weave.utils.SynchronizedClassNode in project newrelic-java-agent by newrelic.
the class PackageValidationResult method weave.
/**
* Weave the target class and return a {@link PackageWeaveResult}.
*/
public PackageWeaveResult weave(String className, String[] superNames, String[] interfaceNames, ClassNode targetNode, ClassCache cache, Map<Method, Collection<String>> skipMethods) {
ClassNode composite = targetNode;
final Map<String, List<Method>> weavedMethods = new HashMap<>();
// Locks during weaving are due to the non-thread safe nature of {@link ClassNode}.
// first apply exact matches
{
PreparedMatch exactMatch = exactMatches.get(className);
if (null != exactMatch) {
ClassWeave classWeave;
classWeave = ClassWeave.weave(exactMatch, composite, weavePackage, skipMethods);
composite = classWeave.getComposite();
final String key = exactMatch.getOriginalName();
if (weavedMethods.containsKey(key)) {
weavedMethods.get(key).addAll(classWeave.getWeavedMethods());
} else {
weavedMethods.put(key, classWeave.getWeavedMethods());
}
}
}
{
// matcher for abstract class
PreparedMatch exactMatch = baseMatches.get(className);
if (null != exactMatch) {
ClassWeave classWeave;
classWeave = ClassWeave.weave(exactMatch, composite, weavePackage, skipMethods);
composite = classWeave.getComposite();
final String key = exactMatch.getOriginalName();
if (weavedMethods.containsKey(key)) {
weavedMethods.get(key).addAll(classWeave.getWeavedMethods());
} else {
weavedMethods.put(key, classWeave.getWeavedMethods());
}
}
}
// then apply super classes
for (int i = 0; i < superNames.length; ++i) {
PreparedMatch baseMatch = baseMatches.get(superNames[i]);
if (null != baseMatch) {
ClassWeave classWeave;
classWeave = ClassWeave.weave(baseMatch, composite, weavePackage, skipMethods);
composite = classWeave.getComposite();
final String key = baseMatch.getOriginalName();
if (weavedMethods.containsKey(key)) {
weavedMethods.get(key).addAll(classWeave.getWeavedMethods());
} else {
weavedMethods.put(key, classWeave.getWeavedMethods());
}
}
}
// then apply interfaces
for (int i = 0; i < interfaceNames.length; ++i) {
PreparedMatch baseMatch = baseMatches.get(interfaceNames[i]);
if (null != baseMatch) {
ClassWeave classWeave;
classWeave = ClassWeave.weave(baseMatch, composite, weavePackage, skipMethods);
composite = classWeave.getComposite();
final String key = baseMatch.getOriginalName();
if (weavedMethods.containsKey(key)) {
weavedMethods.get(key).addAll(classWeave.getWeavedMethods());
} else {
weavedMethods.put(key, classWeave.getWeavedMethods());
}
}
}
final Map<String, byte[]> annotationProxyClasses = new HashMap<>();
// class annotation matches
if (!allAnnotationClasses.isEmpty()) {
Set<String> targetAnnotationsClasses = getAnnotationClasses(targetNode);
Set<String> targetInterfacesAnnotationClasses = Collections.emptySet();
if (!baseAnnotationClasses.isEmpty()) {
targetInterfacesAnnotationClasses = getAllInterfaceAnnotationClasses(targetNode, cache);
}
// check for exact annotation matches
for (Map.Entry<String, ClassNode> entry : allAnnotationClasses.entrySet()) {
if (targetAnnotationsClasses.contains(entry.getKey())) {
composite = getAnnotationMatchComposite(targetNode, entry.getValue(), composite, weavedMethods, cache, annotationProxyClasses, skipMethods);
}
}
// check for base annotation matches
for (Map.Entry<String, ClassNode> entry : baseAnnotationClasses.entrySet()) {
if (targetInterfacesAnnotationClasses.contains(entry.getKey())) {
composite = getAnnotationMatchComposite(targetNode, entry.getValue(), composite, weavedMethods, cache, annotationProxyClasses, skipMethods);
}
}
}
// method annotation matches
if (!allMethodAnnotationClasses.isEmpty()) {
Set<String> targetMethodAnnotationsClasses = getMethodAnnotationClasses(targetNode);
for (Map.Entry<String, ClassNode> entry : allMethodAnnotationClasses.entrySet()) {
if (targetMethodAnnotationsClasses.contains(entry.getKey())) {
composite = getAnnotationMatchComposite(targetNode, entry.getValue(), composite, weavedMethods, cache, annotationProxyClasses, skipMethods);
// due to having multiple matching annotations that have the same underlying weave code.
break;
}
}
}
if (weavePackage != null) {
// Run any postprocessors that were passed in. It's important that we do this before the inliner runs below
ClassNode result = new SynchronizedClassNode(WeaveUtils.ASM_API_LEVEL);
ClassVisitor postprocessChain = weavePackage.getConfig().getPostprocessor().postprocess(className, result, Collections.<String>emptySet(), weavePackage, false);
composite.accept(postprocessChain);
composite = result;
}
return new PackageWeaveResult(this, className, composite, weavedMethods, annotationProxyClasses);
}
use of com.newrelic.weave.utils.SynchronizedClassNode in project newrelic-java-agent by newrelic.
the class WeavePackage method preprocess.
/**
* Run the pre-processor on a specific class in the package. Part of package initialization.
*
* @param input class to process
* @return processed class
*/
private ClassNode preprocess(ClassNode input) {
ClassNode result = new SynchronizedClassNode(WeaveUtils.ASM_API_LEVEL);
// user configured preprocessors go first
ClassVisitor preprocessChain = config.getPreprocessor().preprocess(result, utilClasses.keySet(), this);
preprocessChain = MethodProcessors.fixInvocationInstructions(preprocessChain, weaveMatches);
preprocessChain = MethodProcessors.replaceGetImplementationTitle(preprocessChain, this.getName());
if (renames.size() > 0) {
preprocessChain = ReferenceUtils.getRenamingVisitor(renames, preprocessChain);
}
input.accept(preprocessChain);
return result;
}
Aggregations