Comments (5)
Method calls and method references (and also lambda expressions) are very different in the byte code (invokevirtual
/invokestatic
vs. invokedynamic
)ยน, and correspondingly in ArchUnit's domain model (JavaMethodCall
vs. JavaMethodReference
; see also #1221).
These different domain objects need to be addressed accordingly. There is a fluent API for both kinds of rules:
-
A rule using
callMethodWhere
@ArchTest ArchRule noMethodCall = noClasses().should().callMethodWhere( target(owner(assignableTo(Target.class)).and(nameMatching("method"))) );
explicitly forbids method calls (including those from lambda expressions).
-
If you additionally want to forbid method references, you can use
accessTargetWhere
:@ArchTest ArchRule noMethodAccess = noClasses().should().accessTargetWhere( // WRONG: owner(assignableTo(Target.class)).and(nameMatching("method")) // see #1260 target(owner(assignableTo(Target.class))).and(nameMatching("method")) // Thanks to ben-Draeger for the fix! #issuecomment-1984051341 );
ยน Example:
The following java code:
class Target {
static void method(int i) {
System.out.println("method called!");
}
void methodCall() {
Target.method(1);
}
void methodCall() {
Target.method(1);
}
void methodReference() {
stream1().forEach(Target::method);
}
void lambda() {
stream1().forEach(i -> Target.method(i));
}
static Stream<Integer> stream1() {
return Stream.of(1);
}
}
may be compiled to the following byte code:
class Target
{
Target();
descriptor: ()V
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
static void method(int);
descriptor: (I)V
flags: ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String method called!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
void methodCall();
descriptor: ()V
flags:
Code:
stack=1, locals=1, args_size=1
0: iconst_1
1: invokestatic #5 // Method method:(I)V
4: return
LineNumberTable:
line 10: 0
line 11: 4
void methodReference();
descriptor: ()V
flags:
Code:
stack=2, locals=1, args_size=1
0: invokestatic #6 // Method stream:()Ljava/util/stream/Stream;
3: invokedynamic #7, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
8: invokeinterface #8, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
13: return
LineNumberTable:
line 14: 0
line 15: 13
void lambda();
descriptor: ()V
flags:
Code:
stack=2, locals=1, args_size=1
0: invokestatic #6 // Method stream:()Ljava/util/stream/Stream;
3: invokedynamic #9, 0 // InvokeDynamic #1:accept:()Ljava/util/function/Consumer;
8: invokeinterface #8, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
13: return
LineNumberTable:
line 18: 0
line 19: 13
static java.util.stream.Stream<java.lang.Integer> stream();
descriptor: ()Ljava/util/stream/Stream;
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_1
1: invokestatic #10 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: invokestatic #11 // InterfaceMethod java/util/stream/Stream.of:(Ljava/lang/Object;)Ljava/util/stream/Stream;
7: areturn
LineNumberTable:
line 22: 0
Signature: #27 // ()Ljava/util/stream/Stream<Ljava/lang/Integer;>;
private static void lambda$lambda$0(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #12 // Method java/lang/Integer.intValue:()I
4: invokestatic #5 // Method method:(I)V
7: return
LineNumberTable:
line 18: 0
}
BootstrapMethods:
0: #41 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#42 (Ljava/lang/Object;)V
#43 invokestatic Target.method:(I)V
#44 (Ljava/lang/Integer;)V
1: #41 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#42 (Ljava/lang/Object;)V
#48 invokestatic Target.lambda$lambda$0:(Ljava/lang/Integer;)V
#44 (Ljava/lang/Integer;)V
from archunit.
Thanks for the clarification, accessTargetWhere() indeed works in both cases.
I will adapt our rules and that should fix the problem in our case.
However, please make sure that this is mentioned in the Documentation
to enable users to choose the API that is appropriate for their usecase.
In [1], both callMethodWhere() and accessTargetWhere() do not provide any JavaDoc.
Also in [2], callMethodWhere() is used 2 times without mentioning this restriction and
accessTargetWhere() is not mentioned at all.
References
[1] https://javadoc.io/doc/com.tngtech.archunit/archunit/latest/com/tngtech/archunit/lang/conditions/ArchConditions.html#callMethodWhere(com.tngtech.archunit.base.DescribedPredicate)
[2] https://www.archunit.org/userguide/html/000_Index.html
from archunit.
You're clearly right that the documentation can be improved. ๐
We also welcome pull requests to this open source project.
from archunit.
A note for the people that come after us:
when using accessTargetWhere(), it is better to do it this way:
@ArchTest
ArchRule noMethodAccess = noClasses().should().accessTargetWhere(
target(owner(assignableTo(Target.class))).and(nameMatching("method"))
);
Otherwise, you might run into rather subtle issues.
from archunit.
Aaah... ๐คฏ I'm sorry for that!
from archunit.
Related Issues (20)
- question about calling line numbers HOT 3
- Usability: Java confuses the static and non-static versions of DescribedPredicate.and() when using the API the wrong way. HOT 3
- Unable to find the JavaAccess call HOT 8
- Feature suggestion: Freeze files and stored.rules validation / sanity check
- How To : validate return type is not `void` HOT 2
- How To : Is there way to filter classes when using `importJar()` HOT 1
- DEPRECATED_API_SHOULD_NOT_BE_USED does not consider constants nor final fields HOT 2
- ASM is not up-to-date
- Jakarta Inject package name incorrect in BE_ANNOTATED_WITH_AN_INJECTION_ANNOTATION rule HOT 1
- How can I find out which gradle module a class belongs to HOT 7
- Problem with maven multi module project HOT 2
- False positive results when I use ArchRuleDefinition.noClasses() HOT 7
- Check access classes only works for some cases HOT 1
- ArchUnit do not correctly check Annotation on enum Constants HOT 2
- ArchUnit thinks "Switch with arrows" produces non-final fields HOT 1
- Feature suggestion: Expose synchronized blocks in core API
- Performance: archunit-junit5 may scan complete JDK for `@ArchTest`
- SimpleName-Check failing because of trailing file extension (Kt) HOT 6
- ArchUnits - get name of lambda method HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from archunit.