4141import org .objectweb .asm .TypePath ;
4242import org .objectweb .asm .commons .Method ;
4343
44+ import de .thetaphi .forbiddenapis .Checker .ViolationSeverity ;
45+ import de .thetaphi .forbiddenapis .Signatures .ViolationResult ;
46+
4447public final class ClassScanner extends ClassVisitor implements Constants {
4548 private final boolean forbidNonPortableRuntime ;
4649 final ClassMetadata metadata ;
@@ -63,14 +66,16 @@ public final class ClassScanner extends ClassVisitor implements Constants {
6366 // all groups that were disabled due to suppressing annotation
6467 final BitSet suppressedGroups = new BitSet ();
6568 boolean classSuppressed = false ;
69+ private final boolean failOnViolation ;
6670
67- public ClassScanner (ClassMetadata metadata , RelatedClassLookup lookup , Signatures forbiddenSignatures , final Pattern suppressAnnotations ) {
71+ public ClassScanner (ClassMetadata metadata , RelatedClassLookup lookup , Signatures forbiddenSignatures , final Pattern suppressAnnotations , boolean failOnViolation ) {
6872 super (Opcodes .ASM9 );
6973 this .metadata = metadata ;
7074 this .lookup = lookup ;
7175 this .forbiddenSignatures = forbiddenSignatures ;
7276 this .suppressAnnotations = suppressAnnotations ;
7377 this .forbidNonPortableRuntime = forbiddenSignatures .isNonPortableRuntimeForbidden ();
78+ this .failOnViolation = failOnViolation ;
7479 }
7580
7681 private void checkDone () {
@@ -87,14 +92,14 @@ public String getSourceFile() {
8792 return source ;
8893 }
8994
90- String checkClassUse (Type type , String what , boolean isAnnotation , String origInternalName ) {
95+ ViolationResult checkClassUse (Type type , String what , boolean isAnnotation , String origInternalName ) {
9196 while (type .getSort () == Type .ARRAY ) {
9297 type = type .getElementType (); // unwrap array
9398 }
9499 if (type .getSort () != Type .OBJECT ) {
95100 return null ; // we don't know this type, just pass!
96101 }
97- final String violation = forbiddenSignatures .checkType (type , what );
102+ final ViolationResult violation = forbiddenSignatures .checkType (type , what );
98103 if (violation != null ) {
99104 return violation ;
100105 }
@@ -103,10 +108,9 @@ String checkClassUse(Type type, String what, boolean isAnnotation, String origIn
103108 final String binaryClassName = type .getClassName ();
104109 final ClassMetadata c = lookup .lookupRelatedClass (type .getInternalName (), origInternalName );
105110 if (c != null && c .isNonPortableRuntime ) {
106- return String .format (Locale .ENGLISH ,
111+ return new ViolationResult ( String .format (Locale .ENGLISH ,
107112 "Forbidden %s use: %s [non-portable or internal runtime class]" ,
108- what , binaryClassName
109- );
113+ what , binaryClassName ), failOnViolation ? ViolationSeverity .ERROR : ViolationSeverity .WARNING );
110114 }
111115 } catch (RelatedClassLoadingException e ) {
112116 // only throw exception if it is not an annotation
@@ -115,20 +119,20 @@ String checkClassUse(Type type, String what, boolean isAnnotation, String origIn
115119 return null ;
116120 }
117121
118- String checkClassUse (String internalName , String what , String origInternalName ) {
122+ ViolationResult checkClassUse (String internalName , String what , String origInternalName ) {
119123 return checkClassUse (Type .getObjectType (internalName ), what , false , origInternalName );
120124 }
121125
122126 // TODO: @FunctionalInterface from Java 8 on
123127 static interface AncestorVisitor {
124- final String STOP = new String ("STOP" );
128+ final ViolationResult STOP = new ViolationResult ("STOP" , null );
125129
126- String visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime );
130+ ViolationResult visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime );
127131 }
128132
129- String visitAncestors (ClassMetadata cls , AncestorVisitor visitor , boolean visitSelf , boolean visitInterfacesFirst ) {
133+ ViolationResult visitAncestors (ClassMetadata cls , AncestorVisitor visitor , boolean visitSelf , boolean visitInterfacesFirst ) {
130134 if (visitSelf ) {
131- final String result = visitor .visit (cls , cls .className , cls .isInterface , cls .isRuntimeClass );
135+ final ViolationResult result = visitor .visit (cls , cls .className , cls .isInterface , cls .isRuntimeClass );
132136 if (result == AncestorVisitor .STOP ) {
133137 return null ;
134138 }
@@ -139,11 +143,11 @@ String visitAncestors(ClassMetadata cls, AncestorVisitor visitor, boolean visitS
139143 return visitAncestorsRecursive (cls , cls .className , visitor , cls .isRuntimeClass , visitInterfacesFirst );
140144 }
141145
142- private String visitSuperclassRecursive (ClassMetadata cls , String origName , AncestorVisitor visitor , boolean previousInRuntime , boolean visitInterfacesFirst ) {
146+ private ViolationResult visitSuperclassRecursive (ClassMetadata cls , String origName , AncestorVisitor visitor , boolean previousInRuntime , boolean visitInterfacesFirst ) {
143147 if (cls .superName != null ) {
144148 final ClassMetadata c = lookup .lookupRelatedClass (cls .superName , origName );
145149 if (c != null ) {
146- String result = visitor .visit (c , origName , false , previousInRuntime );
150+ ViolationResult result = visitor .visit (c , origName , false , previousInRuntime );
147151 if (result != AncestorVisitor .STOP ) {
148152 if (result != null ) {
149153 return result ;
@@ -158,12 +162,12 @@ private String visitSuperclassRecursive(ClassMetadata cls, String origName, Ance
158162 return null ;
159163 }
160164
161- private String visitInterfacesRecursive (ClassMetadata cls , String origName , AncestorVisitor visitor , boolean previousInRuntime , boolean visitInterfacesFirst ) {
165+ private ViolationResult visitInterfacesRecursive (ClassMetadata cls , String origName , AncestorVisitor visitor , boolean previousInRuntime , boolean visitInterfacesFirst ) {
162166 if (cls .interfaces != null ) {
163167 for (String intf : cls .interfaces ) {
164168 final ClassMetadata c = lookup .lookupRelatedClass (intf , origName );
165169 if (c == null ) continue ;
166- String result = visitor .visit (c , origName , true , previousInRuntime );
170+ ViolationResult result = visitor .visit (c , origName , true , previousInRuntime );
167171 if (result != AncestorVisitor .STOP ) {
168172 if (result != null ) {
169173 return result ;
@@ -178,8 +182,8 @@ private String visitInterfacesRecursive(ClassMetadata cls, String origName, Ance
178182 return null ;
179183 }
180184
181- private String visitAncestorsRecursive (ClassMetadata cls , String origName , AncestorVisitor visitor , boolean previousInRuntime , boolean visitInterfacesFirst ) {
182- String result ;
185+ private ViolationResult visitAncestorsRecursive (ClassMetadata cls , String origName , AncestorVisitor visitor , boolean previousInRuntime , boolean visitInterfacesFirst ) {
186+ ViolationResult result ;
183187 if (visitInterfacesFirst ) {
184188 result = visitInterfacesRecursive (cls , origName , visitor , previousInRuntime , visitInterfacesFirst );
185189 if (result != null ) {
@@ -202,17 +206,17 @@ private String visitAncestorsRecursive(ClassMetadata cls, String origName, Ances
202206 // TODO: convert to lambda method with method reference
203207 private final AncestorVisitor classRelationAncestorVisitor = new AncestorVisitor () {
204208 @ Override
205- public String visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime ) {
209+ public ViolationResult visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime ) {
206210 if (previousInRuntime && c .isNonPortableRuntime ) {
207211 return null ; // something inside the JVM is extending internal class/interface
208212 }
209213 return checkClassUse (c .className , isInterfaceOfAncestor ? "interface" : "class" , origName );
210214 }
211215 };
212216
213- String checkType (Type type ) {
217+ ViolationResult checkType (Type type ) {
214218 while (type != null ) {
215- String violation ;
219+ ViolationResult violation ;
216220 switch (type .getSort ()) {
217221 case Type .OBJECT :
218222 final String internalName = type .getInternalName ();
@@ -226,7 +230,7 @@ String checkType(Type type) {
226230 type = type .getElementType ();
227231 break ;
228232 case Type .METHOD :
229- final ArrayList <String > violations = new ArrayList <>();
233+ final ArrayList <ViolationResult > violations = new ArrayList <>();
230234 violation = checkType (type .getReturnType ());
231235 if (violation != null ) {
232236 violations .add (violation );
@@ -244,12 +248,17 @@ String checkType(Type type) {
244248 } else {
245249 final StringBuilder sb = new StringBuilder ();
246250 boolean nl = false ;
247- for (final String v : violations ) {
251+ ViolationSeverity severity = null ;
252+ for (final ViolationResult v : violations ) {
248253 if (nl ) sb .append (ForbiddenViolation .SEPARATOR );
249254 sb .append (v );
250255 nl = true ;
256+ // use the highest severity reported on this method
257+ if (severity == null || v .severity .ordinal () > severity .ordinal ()) {
258+ severity = v .severity ;
259+ }
251260 }
252- return sb .toString ();
261+ return new ViolationResult ( sb .toString (), severity );
253262 }
254263 default :
255264 return null ;
@@ -258,11 +267,11 @@ String checkType(Type type) {
258267 return null ;
259268 }
260269
261- String checkDescriptor (String desc ) {
270+ ViolationResult checkDescriptor (String desc ) {
262271 return checkType (Type .getType (desc ));
263272 }
264273
265- String checkAnnotationDescriptor (Type type , boolean visible ) {
274+ ViolationResult checkAnnotationDescriptor (Type type , boolean visible ) {
266275 // for annotations, we don't need to look into super-classes, interfaces,...
267276 return checkClassUse (type , "annotation" , true , type .getInternalName ());
268277 }
@@ -273,9 +282,9 @@ void maybeSuppressCurrentGroup(Type annotation) {
273282 }
274283 }
275284
276- private void reportClassViolation (String violation , String where ) {
285+ private void reportClassViolation (ViolationResult violation , String where ) {
277286 if (violation != null ) {
278- violations .add (new ForbiddenViolation (currentGroupId , violation , where , -1 ));
287+ violations .add (new ForbiddenViolation (currentGroupId , violation . message , where , -1 , violation . severity ));
279288 }
280289 }
281290
@@ -352,9 +361,9 @@ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, Str
352361 return null ;
353362 }
354363
355- private void reportFieldViolation (String violation , String where ) {
356- if (violation != null ) {
357- violations .add (new ForbiddenViolation (currentGroupId , violation , String .format (Locale .ENGLISH , "%s of '%s'" , where , name ), -1 ));
364+ private void reportFieldViolation (ViolationResult violationResult , String where ) {
365+ if (violationResult != null ) {
366+ violations .add (new ForbiddenViolation (currentGroupId , violationResult . message , String .format (Locale .ENGLISH , "%s of '%s'" , where , name ), -1 , violationResult . severity ));
358367 }
359368 }
360369 };
@@ -382,12 +391,12 @@ public MethodVisitor visitMethod(final int access, final String name, final Stri
382391 }
383392 }
384393
385- private String checkMethodAccess (String owner , final Method method , final boolean callIsVirtual ) {
394+ private ViolationResult checkMethodAccess (String owner , final Method method , final boolean callIsVirtual ) {
386395 if (CLASS_CONSTRUCTOR_METHOD_NAME .equals (method .getName ())) {
387396 // we don't check for violations on class constructors
388397 return null ;
389398 }
390- String violation = checkClassUse (owner , "class/interface" , owner );
399+ ViolationResult violation = checkClassUse (owner , "class/interface" , owner );
391400 if (violation != null ) {
392401 return violation ;
393402 }
@@ -405,7 +414,7 @@ private String checkMethodAccess(String owner, final Method method, final boolea
405414 }
406415 return visitAncestors (c , new AncestorVisitor () {
407416 @ Override
408- public String visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime ) {
417+ public ViolationResult visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime ) {
409418 final Method lookupMethod ;
410419 if (c .signaturePolymorphicMethods .contains (method .getName ())) {
411420 // convert the invoked descriptor to a signature polymorphic one for the lookup
@@ -417,11 +426,11 @@ public String visit(ClassMetadata c, String origName, boolean isInterfaceOfAnces
417426 return null ;
418427 }
419428 // is we have a virtual call, look into superclasses, otherwise stop:
420- final String notFoundRet = callIsVirtual ? null : AncestorVisitor .STOP ;
429+ final ViolationResult notFoundRet = callIsVirtual ? null : AncestorVisitor .STOP ;
421430 if (previousInRuntime && c .isNonPortableRuntime ) {
422431 return notFoundRet ; // something inside the JVM is extending internal class/interface
423432 }
424- String violation = forbiddenSignatures .checkMethod (c .className , lookupMethod );
433+ ViolationResult violation = forbiddenSignatures .checkMethod (c .className , lookupMethod );
425434 if (violation != null ) {
426435 return violation ;
427436 }
@@ -437,8 +446,8 @@ public String visit(ClassMetadata c, String origName, boolean isInterfaceOfAnces
437446 }, true , false /* JVM spec says: interfaces after superclasses */ );
438447 }
439448
440- private String checkFieldAccess (String owner , final String field ) {
441- String violation = checkClassUse (owner , "class/interface" , owner );
449+ private ViolationResult checkFieldAccess (String owner , final String field ) {
450+ ViolationResult violation = checkClassUse (owner , "class/interface" , owner );
442451 if (violation != null ) {
443452 return violation ;
444453 }
@@ -453,15 +462,15 @@ private String checkFieldAccess(String owner, final String field) {
453462 }
454463 return visitAncestors (c , new AncestorVisitor () {
455464 @ Override
456- public String visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime ) {
465+ public ViolationResult visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime ) {
457466 if (!c .fields .contains (field )) {
458467 return null ;
459468 }
460469 // we found the field: from now on we use STOP to exit, because fields are not virtual!
461470 if (previousInRuntime && c .isNonPortableRuntime ) {
462471 return STOP ; // something inside the JVM is extending internal class/interface
463472 }
464- String violation = forbiddenSignatures .checkField (c .className , field );
473+ ViolationResult violation = forbiddenSignatures .checkField (c .className , field );
465474 if (violation != null ) {
466475 return violation ;
467476 }
@@ -478,7 +487,7 @@ public String visit(ClassMetadata c, String origName, boolean isInterfaceOfAnces
478487 }, true , true /* JVM spec says: superclasses after interfaces */ );
479488 }
480489
481- private String checkHandle (Handle handle , boolean checkLambdaHandle ) {
490+ private ViolationResult checkHandle (Handle handle , boolean checkLambdaHandle ) {
482491 switch (handle .getTag ()) {
483492 case Opcodes .H_GETFIELD :
484493 case Opcodes .H_PUTFIELD :
@@ -503,7 +512,7 @@ private String checkHandle(Handle handle, boolean checkLambdaHandle) {
503512 return null ;
504513 }
505514
506- private String checkConstant (Object cst , boolean checkLambdaHandle ) {
515+ private ViolationResult checkConstant (Object cst , boolean checkLambdaHandle ) {
507516 if (cst instanceof Type ) {
508517 return checkType ((Type ) cst );
509518 } else if (cst instanceof Handle ) {
@@ -604,9 +613,9 @@ private String getHumanReadableMethodSignature() {
604613 return sb .toString ();
605614 }
606615
607- private void reportMethodViolation (String violation , String where ) {
616+ private void reportMethodViolation (ViolationResult violation , String where ) {
608617 if (violation != null ) {
609- violations .add (new ForbiddenViolation (currentGroupId , myself , violation , String .format (Locale .ENGLISH , "%s of '%s'" , where , getHumanReadableMethodSignature ()), lineNo ));
618+ violations .add (new ForbiddenViolation (currentGroupId , myself , violation . message , String .format (Locale .ENGLISH , "%s of '%s'" , where , getHumanReadableMethodSignature ()), lineNo , violation . severity ));
610619 }
611620 }
612621
@@ -642,9 +651,9 @@ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, Str
642651 return null ;
643652 }
644653
645- private void reportRecordComponentViolation (String violation , String where ) {
646- if (violation != null ) {
647- violations .add (new ForbiddenViolation (currentGroupId , violation , String .format (Locale .ENGLISH , "%s of '%s'" , where , name ), -1 ));
654+ private void reportRecordComponentViolation (ViolationResult violationResult , String where ) {
655+ if (violationResult != null ) {
656+ violations .add (new ForbiddenViolation (currentGroupId , violationResult . message , String .format (Locale .ENGLISH , "%s of '%s'" , where , name ), -1 , violationResult . severity ));
648657 }
649658 }
650659 };
0 commit comments