org.glassfish.pfl.tf.spi.EnhancedClassDataASMImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webservices-rt Show documentation
Show all versions of webservices-rt Show documentation
This module contains the Metro runtime code.
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.glassfish.pfl.tf.spi;
import org.glassfish.pfl.tf.spi.annotation.TFEnhanced;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.glassfish.pfl.objectweb.asm.Opcodes;
import org.glassfish.pfl.objectweb.asm.Type;
import org.glassfish.pfl.objectweb.asm.tree.AnnotationNode;
import org.glassfish.pfl.objectweb.asm.tree.ClassNode;
import org.glassfish.pfl.objectweb.asm.tree.MethodNode;
public class EnhancedClassDataASMImpl extends EnhancedClassDataBase {
private final ClassNode currentClass ;
private static final String TFENHANCED_ANNO_NAME =
Type.getInternalName( TFEnhanced.class ) ;
// Get Set for MM annotations present on class
private void processClassAnnotations() {
final List classAnnotations =
currentClass.visibleAnnotations ;
if (classAnnotations != null) {
for (AnnotationNode an : classAnnotations) {
final String aname = Type.getType( an.desc ).getInternalName() ;
if (annotationNames.contains( aname )) {
annoNamesForClass.add( aname ) ;
}
}
List acnames = new ArrayList( annoNamesForClass ) ;
Collections.sort( acnames ) ;
int ctr=0 ;
for (String aname : acnames ) {
annoToHolderName.put( aname, "__$mm$__" + ctr ) ;
ctr++ ;
}
if (util.getDebug()) {
util.msg( "Enhancing class " + currentClass.name ) ;
util.msg( "\tannoNamesForClass = " + annoNamesForClass ) ;
util.msg( "\tannoToHolderName = " + annoToHolderName ) ;
}
}
}
private Object getAttribute( AnnotationNode an, String name ) {
if (an.values != null) {
Iterator iter = an.values.iterator() ;
while (iter.hasNext()) {
Object key = iter.next() ;
Object value = iter.next() ;
if (!(key instanceof String)) {
return null ;
}
if (key.equals(name)) {
return value ;
}
}
}
return null ;
}
// Scan methods:
// - Build List to map names of MM annotated methods to ints
// validate: such methods must have exactly 1 MM annotation that
// is in annoNamesForClass.
// - Build Set of all InfoMethod annotated methods.
// validate: such methods must be private, return void, and have
// an empty body. May NOT have MM annotation.
private void scanMethods() {
final List methods = currentClass.methods ;
Map mmnToDescriptions =
new HashMap() ;
Map mmnToTPT =
new HashMap() ;
Map mmnToTPN =
new HashMap() ;
Map mmnToAnnotationName =
new HashMap() ;
for (MethodNode mn : methods) {
final String mname = mn.name ;
final String mdesc = util.getFullMethodDescriptor( mn ) ;
String monitoredMethodMMAnno = null ;
String shortClassName = className ;
int index = shortClassName.lastIndexOf( '/' ) ;
if (index >= 0) {
shortClassName = className.substring( index + 1 ) ;
}
String description = "Timer for method " + mname
+ " in class " + shortClassName ; // default
TimingPointType tpt = TimingPointType.BOTH ; // default for non-InfoMethod
String tpName = mname ; // default for non-InfoMethod
boolean hasMethodInfoAnno = false ;
final List annotations = mn.visibleAnnotations ;
if (annotations != null) {
for (AnnotationNode an : annotations) {
final String aname =
Type.getType( an.desc ).getInternalName() ;
if (aname.equals( DESCRIPTION_NAME )) {
Object value = getAttribute( an, "value" ) ;
if (value != null && value instanceof String) {
description = (String)value ;
}
} else if (aname.equals( INFO_METHOD_NAME)) {
// set the correct default for InfoMethod!
tpt = TimingPointType.NONE ;
// Check for private method!
if (!util.hasAccess( mn.access,
Opcodes.ACC_PRIVATE )) {
util.error( "Method " + mdesc
+ " for Class " + currentClass.name
+ " is a non-private @InfoMethod,"
+ " which is not allowed" ) ;
}
hasMethodInfoAnno = true ;
Object value = getAttribute( an, "tpType" ) ;
if (value != null && value instanceof String[]) {
String[] enumData = (String[])value ;
if (enumData.length == 2) {
// [0] is desc, [1] is value
tpt = TimingPointType.valueOf( enumData[1] ) ;
}
}
Object value2 = getAttribute( an, "tpName") ;
String tpn = "" ;
if ((value2 != null ) && value2 instanceof String) {
tpn = (String)value2 ;
}
if (tpt != TimingPointType.NONE) {
if (tpn.length() == 0) {
util.error( "Method " + mdesc
+ " for Class " + currentClass.name
+ " is an info method with timing point type "
+ tpt + " but no tpName was specified" ) ;
} else {
tpName = tpn ;
}
}
} else if (annoNamesForClass.contains( aname)) {
if (monitoredMethodMMAnno == null) {
monitoredMethodMMAnno = aname ;
} else {
util.error( "Method " + mdesc
+ " for Class " + currentClass.name
+ "has multiple MM annotations" ) ;
}
} else if (annotationNames.contains( aname )) {
util.error( "Method " + mdesc
+ " for Class " + currentClass.name
+ " has an MM annotation which "
+ "is not on its class" ) ;
}
}
if (hasMethodInfoAnno && monitoredMethodMMAnno != null) {
util.error( "Method " + mdesc
+ " for Class " + currentClass.name
+ " has both @InfoMethod annotation and"
+ " a MM annotation" ) ;
}
// This check is not really essential, but it simplifies
// passing information to later phases for code generation
// if we can assume that all @InfoMethod annotated methods
// are non-static. (Simply because we only need to look for
// INVOKESPECIAL).
final boolean isStatic = util.hasAccess( mn.access,
Opcodes.ACC_STATIC ) ;
if (hasMethodInfoAnno && isStatic) {
util.error( "Method " + mdesc
+ " for Class " + currentClass.name
+ " is a static method, but must not be" ) ;
}
// TF Annotations are not permitted on constructors
if (mname.equals( "" )) {
if (hasMethodInfoAnno) {
util.error( "Constructors must not have an "
+ "@InfoMethod annotations") ;
} else if (monitoredMethodMMAnno != null) {
util.error( "Constructors must not have an "
+ "MM annotation") ;
}
}
// This will be a null value for InfoMethods, which is what
// we want.
mmnToAnnotationName.put(mname, monitoredMethodMMAnno ) ;
// We could have a method at this point that is annotated with
// something OTHER than tracing annotations. Do not add
// such methods to the ECD.
if (hasMethodInfoAnno || (monitoredMethodMMAnno != null)) {
// Both infoMethods and MM annotated methods go into
// methodNames
methodNames.add( mname ) ;
mmnToDescriptions.put( mname, description ) ;
mmnToTPT.put( mname, tpt ) ;
mmnToTPN.put( mname, tpName ) ;
if (hasMethodInfoAnno) {
infoMethodDescs.add( mdesc ) ;
} else {
mmMethodDescs.add( mdesc ) ;
methodToAnno.put( mdesc, monitoredMethodMMAnno ) ;
}
}
}
}
Collections.sort( methodNames ) ;
for (String str : methodNames ) {
methodDescriptions.add( mmnToDescriptions.get( str ) ) ;
methodTPTs.add( mmnToTPT.get( str ) ) ;
methodAnnoList.add( mmnToAnnotationName.get( str ) ) ;
methodTPNames.add( mmnToTPN.get( str ) ) ;
}
if (util.getDebug()) {
util.msg( "\tinfoMethodDescs = " + infoMethodDescs ) ;
util.msg( "\tmmMethodDescs = " + mmMethodDescs ) ;
util.msg( "\tmethodNames = " + methodNames ) ;
util.msg( "\tmethodToAnno = " + methodToAnno ) ;
util.msg( "\tmethodDescriptions = " + methodDescriptions ) ;
util.msg( "\tmethodTPTs = " + methodTPTs ) ;
util.msg( "\tmethodTPNs = " + methodTPNames ) ;
}
}
public EnhancedClassDataASMImpl( Util util, Set mmAnnotations,
ClassNode cn ) {
super( util, mmAnnotations ) ;
currentClass = cn ;
// Compute data here: only look at data available to
// java reflection, so that a runtime version of
// EnhancedClassData using reflection can be created.
className = cn.name ;
processClassAnnotations() ;
scanMethods();
}
}