41
41
import org .objectweb .asm .TypePath ;
42
42
import org .objectweb .asm .commons .Method ;
43
43
44
+ import de .thetaphi .forbiddenapis .Checker .ViolationSeverity ;
45
+ import de .thetaphi .forbiddenapis .Signatures .ViolationResult ;
46
+
44
47
public final class ClassScanner extends ClassVisitor implements Constants {
45
48
private final boolean forbidNonPortableRuntime ;
46
49
final ClassMetadata metadata ;
@@ -63,14 +66,16 @@ public final class ClassScanner extends ClassVisitor implements Constants {
63
66
// all groups that were disabled due to suppressing annotation
64
67
final BitSet suppressedGroups = new BitSet ();
65
68
boolean classSuppressed = false ;
69
+ private final boolean failOnViolation ;
66
70
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 ) {
68
72
super (Opcodes .ASM9 );
69
73
this .metadata = metadata ;
70
74
this .lookup = lookup ;
71
75
this .forbiddenSignatures = forbiddenSignatures ;
72
76
this .suppressAnnotations = suppressAnnotations ;
73
77
this .forbidNonPortableRuntime = forbiddenSignatures .isNonPortableRuntimeForbidden ();
78
+ this .failOnViolation = failOnViolation ;
74
79
}
75
80
76
81
private void checkDone () {
@@ -87,14 +92,14 @@ public String getSourceFile() {
87
92
return source ;
88
93
}
89
94
90
- String checkClassUse (Type type , String what , boolean isAnnotation , String origInternalName ) {
95
+ ViolationResult checkClassUse (Type type , String what , boolean isAnnotation , String origInternalName ) {
91
96
while (type .getSort () == Type .ARRAY ) {
92
97
type = type .getElementType (); // unwrap array
93
98
}
94
99
if (type .getSort () != Type .OBJECT ) {
95
100
return null ; // we don't know this type, just pass!
96
101
}
97
- final String violation = forbiddenSignatures .checkType (type , what );
102
+ final ViolationResult violation = forbiddenSignatures .checkType (type , what );
98
103
if (violation != null ) {
99
104
return violation ;
100
105
}
@@ -103,10 +108,9 @@ String checkClassUse(Type type, String what, boolean isAnnotation, String origIn
103
108
final String binaryClassName = type .getClassName ();
104
109
final ClassMetadata c = lookup .lookupRelatedClass (type .getInternalName (), origInternalName );
105
110
if (c != null && c .isNonPortableRuntime ) {
106
- return String .format (Locale .ENGLISH ,
111
+ return new ViolationResult ( String .format (Locale .ENGLISH ,
107
112
"Forbidden %s use: %s [non-portable or internal runtime class]" ,
108
- what , binaryClassName
109
- );
113
+ what , binaryClassName ), failOnViolation ? ViolationSeverity .ERROR : ViolationSeverity .WARNING );
110
114
}
111
115
} catch (RelatedClassLoadingException e ) {
112
116
// only throw exception if it is not an annotation
@@ -115,20 +119,20 @@ String checkClassUse(Type type, String what, boolean isAnnotation, String origIn
115
119
return null ;
116
120
}
117
121
118
- String checkClassUse (String internalName , String what , String origInternalName ) {
122
+ ViolationResult checkClassUse (String internalName , String what , String origInternalName ) {
119
123
return checkClassUse (Type .getObjectType (internalName ), what , false , origInternalName );
120
124
}
121
125
122
126
// TODO: @FunctionalInterface from Java 8 on
123
127
static interface AncestorVisitor {
124
- final String STOP = new String ("STOP" );
128
+ final ViolationResult STOP = new ViolationResult ("STOP" , null );
125
129
126
- String visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime );
130
+ ViolationResult visit (ClassMetadata c , String origName , boolean isInterfaceOfAncestor , boolean previousInRuntime );
127
131
}
128
132
129
- String visitAncestors (ClassMetadata cls , AncestorVisitor visitor , boolean visitSelf , boolean visitInterfacesFirst ) {
133
+ ViolationResult visitAncestors (ClassMetadata cls , AncestorVisitor visitor , boolean visitSelf , boolean visitInterfacesFirst ) {
130
134
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 );
132
136
if (result == AncestorVisitor .STOP ) {
133
137
return null ;
134
138
}
@@ -139,11 +143,11 @@ String visitAncestors(ClassMetadata cls, AncestorVisitor visitor, boolean visitS
139
143
return visitAncestorsRecursive (cls , cls .className , visitor , cls .isRuntimeClass , visitInterfacesFirst );
140
144
}
141
145
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 ) {
143
147
if (cls .superName != null ) {
144
148
final ClassMetadata c = lookup .lookupRelatedClass (cls .superName , origName );
145
149
if (c != null ) {
146
- String result = visitor .visit (c , origName , false , previousInRuntime );
150
+ ViolationResult result = visitor .visit (c , origName , false , previousInRuntime );
147
151
if (result != AncestorVisitor .STOP ) {
148
152
if (result != null ) {
149
153
return result ;
@@ -158,12 +162,12 @@ private String visitSuperclassRecursive(ClassMetadata cls, String origName, Ance
158
162
return null ;
159
163
}
160
164
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 ) {
162
166
if (cls .interfaces != null ) {
163
167
for (String intf : cls .interfaces ) {
164
168
final ClassMetadata c = lookup .lookupRelatedClass (intf , origName );
165
169
if (c == null ) continue ;
166
- String result = visitor .visit (c , origName , true , previousInRuntime );
170
+ ViolationResult result = visitor .visit (c , origName , true , previousInRuntime );
167
171
if (result != AncestorVisitor .STOP ) {
168
172
if (result != null ) {
169
173
return result ;
@@ -178,8 +182,8 @@ private String visitInterfacesRecursive(ClassMetadata cls, String origName, Ance
178
182
return null ;
179
183
}
180
184
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 ;
183
187
if (visitInterfacesFirst ) {
184
188
result = visitInterfacesRecursive (cls , origName , visitor , previousInRuntime , visitInterfacesFirst );
185
189
if (result != null ) {
@@ -202,17 +206,17 @@ private String visitAncestorsRecursive(ClassMetadata cls, String origName, Ances
202
206
// TODO: convert to lambda method with method reference
203
207
private final AncestorVisitor classRelationAncestorVisitor = new AncestorVisitor () {
204
208
@ 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 ) {
206
210
if (previousInRuntime && c .isNonPortableRuntime ) {
207
211
return null ; // something inside the JVM is extending internal class/interface
208
212
}
209
213
return checkClassUse (c .className , isInterfaceOfAncestor ? "interface" : "class" , origName );
210
214
}
211
215
};
212
216
213
- String checkType (Type type ) {
217
+ ViolationResult checkType (Type type ) {
214
218
while (type != null ) {
215
- String violation ;
219
+ ViolationResult violation ;
216
220
switch (type .getSort ()) {
217
221
case Type .OBJECT :
218
222
final String internalName = type .getInternalName ();
@@ -226,7 +230,7 @@ String checkType(Type type) {
226
230
type = type .getElementType ();
227
231
break ;
228
232
case Type .METHOD :
229
- final ArrayList <String > violations = new ArrayList <>();
233
+ final ArrayList <ViolationResult > violations = new ArrayList <>();
230
234
violation = checkType (type .getReturnType ());
231
235
if (violation != null ) {
232
236
violations .add (violation );
@@ -244,12 +248,17 @@ String checkType(Type type) {
244
248
} else {
245
249
final StringBuilder sb = new StringBuilder ();
246
250
boolean nl = false ;
247
- for (final String v : violations ) {
251
+ ViolationSeverity severity = null ;
252
+ for (final ViolationResult v : violations ) {
248
253
if (nl ) sb .append (ForbiddenViolation .SEPARATOR );
249
254
sb .append (v );
250
255
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
+ }
251
260
}
252
- return sb .toString ();
261
+ return new ViolationResult ( sb .toString (), severity );
253
262
}
254
263
default :
255
264
return null ;
@@ -258,11 +267,11 @@ String checkType(Type type) {
258
267
return null ;
259
268
}
260
269
261
- String checkDescriptor (String desc ) {
270
+ ViolationResult checkDescriptor (String desc ) {
262
271
return checkType (Type .getType (desc ));
263
272
}
264
273
265
- String checkAnnotationDescriptor (Type type , boolean visible ) {
274
+ ViolationResult checkAnnotationDescriptor (Type type , boolean visible ) {
266
275
// for annotations, we don't need to look into super-classes, interfaces,...
267
276
return checkClassUse (type , "annotation" , true , type .getInternalName ());
268
277
}
@@ -273,9 +282,9 @@ void maybeSuppressCurrentGroup(Type annotation) {
273
282
}
274
283
}
275
284
276
- private void reportClassViolation (String violation , String where ) {
285
+ private void reportClassViolation (ViolationResult violation , String where ) {
277
286
if (violation != null ) {
278
- violations .add (new ForbiddenViolation (currentGroupId , violation , where , -1 ));
287
+ violations .add (new ForbiddenViolation (currentGroupId , violation . message , where , -1 , violation . severity ));
279
288
}
280
289
}
281
290
@@ -352,9 +361,9 @@ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, Str
352
361
return null ;
353
362
}
354
363
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 ));
358
367
}
359
368
}
360
369
};
@@ -382,12 +391,12 @@ public MethodVisitor visitMethod(final int access, final String name, final Stri
382
391
}
383
392
}
384
393
385
- private String checkMethodAccess (String owner , final Method method , final boolean callIsVirtual ) {
394
+ private ViolationResult checkMethodAccess (String owner , final Method method , final boolean callIsVirtual ) {
386
395
if (CLASS_CONSTRUCTOR_METHOD_NAME .equals (method .getName ())) {
387
396
// we don't check for violations on class constructors
388
397
return null ;
389
398
}
390
- String violation = checkClassUse (owner , "class/interface" , owner );
399
+ ViolationResult violation = checkClassUse (owner , "class/interface" , owner );
391
400
if (violation != null ) {
392
401
return violation ;
393
402
}
@@ -405,7 +414,7 @@ private String checkMethodAccess(String owner, final Method method, final boolea
405
414
}
406
415
return visitAncestors (c , new AncestorVisitor () {
407
416
@ 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 ) {
409
418
final Method lookupMethod ;
410
419
if (c .signaturePolymorphicMethods .contains (method .getName ())) {
411
420
// 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
417
426
return null ;
418
427
}
419
428
// 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 ;
421
430
if (previousInRuntime && c .isNonPortableRuntime ) {
422
431
return notFoundRet ; // something inside the JVM is extending internal class/interface
423
432
}
424
- String violation = forbiddenSignatures .checkMethod (c .className , lookupMethod );
433
+ ViolationResult violation = forbiddenSignatures .checkMethod (c .className , lookupMethod );
425
434
if (violation != null ) {
426
435
return violation ;
427
436
}
@@ -437,8 +446,8 @@ public String visit(ClassMetadata c, String origName, boolean isInterfaceOfAnces
437
446
}, true , false /* JVM spec says: interfaces after superclasses */ );
438
447
}
439
448
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 );
442
451
if (violation != null ) {
443
452
return violation ;
444
453
}
@@ -453,15 +462,15 @@ private String checkFieldAccess(String owner, final String field) {
453
462
}
454
463
return visitAncestors (c , new AncestorVisitor () {
455
464
@ 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 ) {
457
466
if (!c .fields .contains (field )) {
458
467
return null ;
459
468
}
460
469
// we found the field: from now on we use STOP to exit, because fields are not virtual!
461
470
if (previousInRuntime && c .isNonPortableRuntime ) {
462
471
return STOP ; // something inside the JVM is extending internal class/interface
463
472
}
464
- String violation = forbiddenSignatures .checkField (c .className , field );
473
+ ViolationResult violation = forbiddenSignatures .checkField (c .className , field );
465
474
if (violation != null ) {
466
475
return violation ;
467
476
}
@@ -478,7 +487,7 @@ public String visit(ClassMetadata c, String origName, boolean isInterfaceOfAnces
478
487
}, true , true /* JVM spec says: superclasses after interfaces */ );
479
488
}
480
489
481
- private String checkHandle (Handle handle , boolean checkLambdaHandle ) {
490
+ private ViolationResult checkHandle (Handle handle , boolean checkLambdaHandle ) {
482
491
switch (handle .getTag ()) {
483
492
case Opcodes .H_GETFIELD :
484
493
case Opcodes .H_PUTFIELD :
@@ -503,7 +512,7 @@ private String checkHandle(Handle handle, boolean checkLambdaHandle) {
503
512
return null ;
504
513
}
505
514
506
- private String checkConstant (Object cst , boolean checkLambdaHandle ) {
515
+ private ViolationResult checkConstant (Object cst , boolean checkLambdaHandle ) {
507
516
if (cst instanceof Type ) {
508
517
return checkType ((Type ) cst );
509
518
} else if (cst instanceof Handle ) {
@@ -604,9 +613,9 @@ private String getHumanReadableMethodSignature() {
604
613
return sb .toString ();
605
614
}
606
615
607
- private void reportMethodViolation (String violation , String where ) {
616
+ private void reportMethodViolation (ViolationResult violation , String where ) {
608
617
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 ));
610
619
}
611
620
}
612
621
@@ -642,9 +651,9 @@ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, Str
642
651
return null ;
643
652
}
644
653
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 ));
648
657
}
649
658
}
650
659
};
0 commit comments