33
33
import net .bytebuddy .matcher .ElementMatchers ;
34
34
import net .bytebuddy .utility .JavaModule ;
35
35
36
+
36
37
/**
37
38
* An ASM based implementation of {@link TaskDescriptor} to provide description for generated Lambda class.
38
39
* Description of Lambda expression includes source code location of lambda, function call or method reference
@@ -71,112 +72,109 @@ static void onExit(@Advice.Argument(0) Class<?> hostClass, @Advice.Argument(1) b
71
72
72
73
static {
73
74
74
- try {
75
- Instrumentation inst = ByteBuddyAgent .install ();
76
-
77
- /*
78
- * If we can get the instance of jdk.internal.misc.Unsafe then we will
79
- * attempt to instrument Unsafe.defineAnonymousClass(...) to capture classes
80
- * generated for lambdas.
81
- * This approach does not work for Oracle Java 8 because
82
- * sun.misc.Unsafe.defineAnonymousClass(...) is a native method and we can
83
- * at most replace it but there is no reasonably easy way to replace it and
84
- * still invoke the original method.
85
- */
86
- boolean isJdkUnsafe = false ;
75
+ try {
76
+ Instrumentation inst = ByteBuddyAgent .install ();
77
+
78
+ /*
79
+ * If we can get the instance of jdk.internal.misc.Unsafe then we will
80
+ * attempt to instrument Unsafe.defineAnonymousClass(...) to capture classes
81
+ * generated for lambdas.
82
+ * This approach does not work for Oracle Java 8 because
83
+ * sun.misc.Unsafe.defineAnonymousClass(...) is a native method and we can
84
+ * at most replace it but there is no reasonably easy way to replace it and
85
+ * still invoke the original method.
86
+ */
87
+ boolean isJdkUnsafe = false ;
87
88
Class <?> unsafe = null ;
88
89
try {
89
90
unsafe = Class .forName ("jdk.internal.misc.Unsafe" );
90
91
isJdkUnsafe = true ;
91
92
} catch (ClassNotFoundException e ) {
92
93
}
93
94
94
- if (isJdkUnsafe ) {
95
- // Code path that supports OpenJDK Java 11 and up
96
-
97
- /*
98
- * Inject AnalyzerAdvice to boot ClassLoader.
99
- * It has to be reachable from jdk.internal.misc.Unsafe.
100
- */
101
- ClassInjector .UsingUnsafe .ofBootLoader ()
102
- .inject (Collections .singletonMap (
103
- new TypeDescription .ForLoadedType (AnalyzerAdvice .class ),
104
- ClassFileLocator .ForClassLoader .read (AnalyzerAdvice .class )
105
- )
106
- );
107
-
108
- /*
109
- * Inject the analyze(byte[] byteCode, ClassLoader loader) method from this ClassLoader
110
- * to the AnalyzerAdvice class from boot ClassLoader.
111
- */
112
- Class <?> injectedInt = ClassLoader .getSystemClassLoader ().getParent ().loadClass (AnalyzerAdvice .class .getName ());
113
- injectedInt .getField ("_method" ).set (null , Analyzer .class .getDeclaredMethod ("analyze" , byte [].class , ClassLoader .class ));
114
-
115
- JavaModule module = JavaModule .ofType (injectedInt );
116
-
117
- new AgentBuilder .Default ()
118
- .disableClassFormatChanges ()
119
- .ignore (noneOf (unsafe ))
120
- .with (AgentBuilder .InitializationStrategy .NoOp .INSTANCE )
121
- .with (AgentBuilder .RedefinitionStrategy .REDEFINITION )
122
- .with (AgentBuilder .TypeStrategy .Default .REDEFINE )
123
- .with (AgentBuilder .InjectionStrategy .UsingUnsafe .INSTANCE )
124
- .assureReadEdgeTo (inst , module )
125
- .type (is (unsafe ))
126
- .transform (new AgentBuilder .Transformer () {
127
- @ Override
128
- public Builder <?> transform (Builder <?> builder , TypeDescription typeDescription ,
129
- ClassLoader classLoader , JavaModule module ) {
130
- return builder .visit (Advice .to (AnalyzerAdvice .class ).on (ElementMatchers .named ("defineAnonymousClass" )));
131
- }
132
- }).installOnByteBuddyAgent ();
133
- } else {
134
- // Code path that supports Oracle Java 8 and 9
95
+ if (isJdkUnsafe ) {
96
+ // Code path that supports OpenJDK Java 11 and up
97
+
98
+ /*
99
+ * Inject AnalyzerAdvice to boot ClassLoader.
100
+ * It has to be reachable from jdk.internal.misc.Unsafe.
101
+ */
102
+ ClassInjector .UsingUnsafe .ofBootLoader ()
103
+ .inject (Collections .singletonMap (new TypeDescription .ForLoadedType (AnalyzerAdvice .class ),
104
+ ClassFileLocator .ForClassLoader .read (AnalyzerAdvice .class )));
105
+
106
+ /*
107
+ * Inject the analyze(byte[] byteCode, ClassLoader loader) method from this ClassLoader
108
+ * to the AnalyzerAdvice class from boot ClassLoader.
109
+ */
110
+ Class <?> injectedInt = ClassLoader .getSystemClassLoader ().getParent ().loadClass (AnalyzerAdvice .class .getName ());
111
+ injectedInt .getField ("_method" )
112
+ .set (null , Analyzer .class .getDeclaredMethod ("analyze" , byte [].class , ClassLoader .class ));
113
+
114
+ JavaModule module = JavaModule .ofType (injectedInt );
115
+
116
+ new AgentBuilder .Default ().disableClassFormatChanges ()
117
+ .ignore (noneOf (unsafe ))
118
+ .with (AgentBuilder .InitializationStrategy .NoOp .INSTANCE )
119
+ .with (AgentBuilder .RedefinitionStrategy .REDEFINITION )
120
+ .with (AgentBuilder .TypeStrategy .Default .REDEFINE )
121
+ .with (AgentBuilder .InjectionStrategy .UsingUnsafe .INSTANCE )
122
+ .assureReadEdgeTo (inst , module )
123
+ .type (is (unsafe ))
124
+ .transform (new AgentBuilder .Transformer () {
125
+ @ Override
126
+ public Builder <?> transform (Builder <?> builder , TypeDescription typeDescription , ClassLoader classLoader ,
127
+ JavaModule module ) {
128
+ return builder .visit (Advice .to (AnalyzerAdvice .class ).on (ElementMatchers .named ("defineAnonymousClass" )));
129
+ }
130
+ })
131
+ .installOnByteBuddyAgent ();
132
+ } else {
133
+ // Code path that supports Oracle Java 8 and 9
135
134
inst .addTransformer (new Analyzer ());
136
- }
137
- } catch (Exception e ) {
138
- e .printStackTrace ();
139
- }
135
+ }
136
+ } catch (Exception e ) {
137
+ e .printStackTrace ();
138
+ }
140
139
}
141
140
142
141
@ Override
143
142
public String getDescription (String className ) {
144
143
Optional <String > lambdaClassDescription = getLambdaClassDescription (className );
145
- if (lambdaClassDescription .isPresent ()) {
146
- return lambdaClassDescription .get ();
147
- } else {
148
- return className ;
149
- }
144
+ return lambdaClassDescription .orElse (className );
150
145
}
151
146
152
147
Optional <String > getLambdaClassDescription (String className ) {
148
+ int slashIndex = className .lastIndexOf ('/' );
149
+ // If we can't find the slash, we can't find the name of the lambda.
150
+ if (slashIndex <= 0 ) {
151
+ return Optional .empty ();
152
+ }
153
+ String name = className .substring (0 , slashIndex );
154
+ String description = _names .get (name );
155
+
156
+ // If we have already analyzed the class, we don't need to await
157
+ // analysis on other lambdas.
158
+ if (description != null ) {
159
+ return Optional .of (description ).filter (s -> !s .isEmpty ());
160
+ }
161
+
153
162
CountDownLatch latch = _latchRef .get ();
154
163
if (latch != null ) {
155
164
try {
156
- /*
157
- * We wait up to one minute - an arbitrary, sufficiently large amount of time.
158
- * The wait period must be bounded to avoid locking out JVM.
159
- */
165
+ // We wait up to one minute - an arbitrary, sufficiently large amount of time.
166
+ // The wait period must be bounded to avoid locking out JVM.
160
167
latch .await (1 , TimeUnit .MINUTES );
161
168
} catch (InterruptedException e ) {
162
- System .out .println ("ERROR: ParSeq Latch timed out suggesting serious issue in ASMBasedTaskDescriptor. "
163
- + "Current number of class being analyzed: " + String . valueOf ( _count .get () ));
169
+ System .err .println ("ERROR: ParSeq Latch timed out suggesting serious issue in ASMBasedTaskDescriptor. "
170
+ + "Current number of class being analyzed: " + _count .get ());
164
171
e .printStackTrace ();
165
172
Thread .currentThread ().interrupt ();
166
173
}
167
174
}
168
- int slashIndex = className .lastIndexOf ('/' );
169
- if (slashIndex > 0 ) {
170
- String name = className .substring (0 , slashIndex );
171
- String desc = _names .get (name );
172
- if (desc == null || desc .isEmpty ()) {
173
- return Optional .empty ();
174
- } else {
175
- return Optional .of (desc );
176
- }
177
- }
178
175
179
- return Optional .empty ();
176
+ // Try again
177
+ return Optional .ofNullable (_names .get (name )).filter (s -> !s .isEmpty ());
180
178
}
181
179
182
180
private static void add (String lambdaClassName , String description ) {
@@ -187,8 +185,7 @@ public static class Analyzer implements ClassFileTransformer {
187
185
188
186
@ Override
189
187
public byte [] transform (ClassLoader loader , String className , Class <?> classBeingRedefined ,
190
- ProtectionDomain protectionDomain , byte [] classfileBuffer )
191
- throws IllegalClassFormatException {
188
+ ProtectionDomain protectionDomain , byte [] classfileBuffer ) throws IllegalClassFormatException {
192
189
if (className == null && loader != null ) {
193
190
analyze (classfileBuffer , loader );
194
191
}
@@ -208,23 +205,20 @@ public static void analyze(byte[] byteCode, ClassLoader loader) {
208
205
}
209
206
}
210
207
final Exception e = new Exception ();
211
- ForkJoinPool .commonPool ().execute (new Runnable () {
212
- @ Override
213
- public void run () {
214
- try {
215
- doAnalyze (byteCode , loader , e );
216
- } catch (Throwable t ) {
217
- /*
218
- * We need to catch everything because other
219
- * threads may be blocked on CountDownLatch.
220
- */
221
- System .out .println ("WARNING: Parseq cannot doAnalyze" );
222
- t .printStackTrace ();
223
- }
224
- if (_count .decrementAndGet () == 0 ) {
225
- CountDownLatch latch = _latchRef .getAndSet (null );
226
- latch .countDown ();
227
- }
208
+ ForkJoinPool .commonPool ().execute (() -> {
209
+ try {
210
+ doAnalyze (byteCode , loader , e );
211
+ } catch (Throwable t ) {
212
+ /*
213
+ * We need to catch everything because other
214
+ * threads may be blocked on CountDownLatch.
215
+ */
216
+ System .out .println ("WARNING: Parseq cannot doAnalyze" );
217
+ t .printStackTrace ();
218
+ }
219
+ if (_count .decrementAndGet () == 0 ) {
220
+ CountDownLatch latch = _latchRef .getAndSet (null );
221
+ latch .countDown ();
228
222
}
229
223
});
230
224
}
0 commit comments