com.newrelic.weave.weavepackage.language.scala.ScalaAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of newrelic-weaver Show documentation
Show all versions of newrelic-weaver Show documentation
The Weaver of the Java agent.
/*
*
* * Copyright 2020 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.newrelic.weave.weavepackage.language.scala;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.scala.ScalaMatchType;
import com.newrelic.api.agent.weaver.scala.ScalaWeave;
import com.newrelic.weave.utils.ClassCache;
import com.newrelic.weave.utils.ClassLoaderFinder;
import com.newrelic.weave.utils.WeaveClassInfo;
import com.newrelic.weave.utils.WeaveUtils;
import com.newrelic.weave.violation.WeaveViolation;
import com.newrelic.weave.weavepackage.language.LanguageAdapter;
import com.newrelic.weave.weavepackage.language.LanguageAdapterResult;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ScalaAdapter implements LanguageAdapter {
private static final String WEAVE_API_DESC = Type.getType(Weave.class).getDescriptor();
private static final String SCALA_WEAVE_API_DESC = Type.getType(ScalaWeave.class).getDescriptor();
private static final String MATCH_TYPE_DESC = Type.getType(MatchType.class).getDescriptor();
private static final String SCALA_MATCH_TYPE_DESC = Type.getType(ScalaMatchType.class).getDescriptor();
@Override
public LanguageAdapterResult adapt(List input) {
List violations = new ArrayList<>();
// internalName -> classNode
Map scalaNodes = new HashMap<>(input.size());
List outputNodes = new ArrayList<>(input.size());
for(byte[] bytes : input) {
ClassNode node = WeaveUtils.convertToClassNode(bytes);
if(isScalaNode(node)) {
scalaNodes.put(node.name, node);
} else{
// don't alter non-scala sources
outputNodes.add(node);
}
}
if (scalaNodes.size() == 0) {
return new ScalaAdapterResult(input, violations);
}
// scala-weaver-api -> weaver-api
apiConversions(scalaNodes);
// check for violations on scala nodes
violations.addAll(validateScalaNodes(scalaNodes));
outputNodes.addAll(scalaNodes.values());
return new ScalaAdapterResult(convertToBytes(outputNodes), violations);
}
private List convertToBytes(List nodes) {
ClassCache packageCache = new ClassCache(new ClassLoaderFinder(Thread.currentThread().getContextClassLoader()));
List converted = new ArrayList<>(nodes.size());
for(ClassNode node : nodes) {
converted.add(WeaveUtils.convertToClassBytes(node, packageCache));
}
return converted;
}
private boolean isScalaNode(ClassNode node) {
if(node.sourceFile != null && node.sourceFile.endsWith(".scala")) {
return true;
}
return false;
}
private void apiConversions(Map input) {
for(ClassNode node : input.values()) {
if(null != node.visibleAnnotations) {
for(AnnotationNode annotation : node.visibleAnnotations) {
if (SCALA_WEAVE_API_DESC.equals(annotation.desc)) {
annotation.desc = WEAVE_API_DESC;
int originalNameIndex = -1;
int matchTypeIndex = -1;
for (int i = 0; i < annotation.values.size(); ++i) {
Object key = annotation.values.get(i);
if (key instanceof String) {
if ("type".equals(key)) {
matchTypeIndex = i;
} else if ("originalName".equals(key)) {
originalNameIndex = i;
}
}
}
if (matchTypeIndex != -1) {
String[] matchVal = (String[])annotation.values.get(matchTypeIndex+1);
String scalaMatchType = matchVal[1];
matchVal[0] = MATCH_TYPE_DESC;
matchVal[1] = convertToJavaMatchType(matchVal[1]);
if ("Object".equals(scalaMatchType)) {
if (originalNameIndex != -1) {
annotation.values.set(originalNameIndex+1, annotation.values.get(originalNameIndex+1)+"$");
} else {
annotation.values.add("originalName");
annotation.values.add(WeaveUtils.getClassBinaryName(node.name)+"$");
}
}
}
}
}
}
}
}
/**
* Scan Scala ClassNodes for any violations.
*
* @param input The nodes to check
* @return A list of all violations encountered
*/
private List validateScalaNodes(Map input) {
// check that the user only annotated scala classes with @Weave or @ScalaWeave
List violations = new ArrayList<>();
for (String name : input.keySet()) {
ClassNode node = input.get(name);
boolean isWeaveClass = isWeaveClass(node);
if (isWeaveClass && isInterface(node)) {
violations.add(new ScalaWeaveViolation(name, ScalaWeaveViolationType.CLASS_WEAVE_IS_TRAIT));
}
if (isWeaveClass && name.endsWith("$")) {
// check for object
String implName = name.substring(0, name.length() - 1);
if (input.containsKey(implName)) {
violations.add(new ScalaWeaveViolation(name, ScalaWeaveViolationType.CLASS_WEAVE_IS_OBJECT));
}
}
}
return violations;
}
private boolean isInterface(ClassNode node) {
return (node.access & Opcodes.ACC_INTERFACE) != 0;
}
private boolean isWeaveClass(ClassNode node) {
if (null != node && null != node.visibleAnnotations) {
for (AnnotationNode annotation : node.visibleAnnotations) {
if (SCALA_WEAVE_API_DESC.equals(annotation.desc) || WeaveClassInfo.WEAVE_DESC.equals(annotation.desc)) {
return true;
}
}
}
return false;
}
private String convertToJavaMatchType(String scalaMatch) {
if (ScalaMatchType.Trait.name().equals(scalaMatch)) {
return MatchType.Interface.name();
} else {
return MatchType.ExactClass.name();
}
}
private static class ScalaAdapterResult implements LanguageAdapterResult {
private final List adaptedBytes;
private final List violations;
public ScalaAdapterResult(List adaptedBytes, List violations) {
this.adaptedBytes = adaptedBytes;
this.violations = violations;
}
@Override
public List getAdaptedBytes() {
return adaptedBytes;
}
@Override
public List getViolations() {
return violations;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy