3333import net .bytebuddy .matcher .ElementMatchers ;
3434import net .bytebuddy .utility .JavaModule ;
3535
36+
3637/**
3738 * An ASM based implementation of {@link TaskDescriptor} to provide description for generated Lambda class.
3839 * 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
7172
7273 static {
7374
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 ;
8788 Class <?> unsafe = null ;
8889 try {
8990 unsafe = Class .forName ("jdk.internal.misc.Unsafe" );
9091 isJdkUnsafe = true ;
9192 } catch (ClassNotFoundException e ) {
9293 }
9394
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
135134 inst .addTransformer (new Analyzer ());
136- }
137- } catch (Exception e ) {
138- e .printStackTrace ();
139- }
135+ }
136+ } catch (Exception e ) {
137+ e .printStackTrace ();
138+ }
140139 }
141140
142141 @ Override
143142 public String getDescription (String className ) {
144143 Optional <String > lambdaClassDescription = getLambdaClassDescription (className );
145- if (lambdaClassDescription .isPresent ()) {
146- return lambdaClassDescription .get ();
147- } else {
148- return className ;
149- }
144+ return lambdaClassDescription .orElse (className );
150145 }
151146
152147 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+
153162 CountDownLatch latch = _latchRef .get ();
154163 if (latch != null ) {
155164 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.
160167 latch .await (1 , TimeUnit .MINUTES );
161168 } 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 ());
164171 e .printStackTrace ();
165172 Thread .currentThread ().interrupt ();
166173 }
167174 }
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- }
178175
179- return Optional .empty ();
176+ // Try again
177+ return Optional .ofNullable (_names .get (name )).filter (s -> !s .isEmpty ());
180178 }
181179
182180 private static void add (String lambdaClassName , String description ) {
@@ -187,8 +185,7 @@ public static class Analyzer implements ClassFileTransformer {
187185
188186 @ Override
189187 public byte [] transform (ClassLoader loader , String className , Class <?> classBeingRedefined ,
190- ProtectionDomain protectionDomain , byte [] classfileBuffer )
191- throws IllegalClassFormatException {
188+ ProtectionDomain protectionDomain , byte [] classfileBuffer ) throws IllegalClassFormatException {
192189 if (className == null && loader != null ) {
193190 analyze (classfileBuffer , loader );
194191 }
@@ -208,23 +205,20 @@ public static void analyze(byte[] byteCode, ClassLoader loader) {
208205 }
209206 }
210207 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 ();
228222 }
229223 });
230224 }
0 commit comments