Skip to content

Commit 85ec17a

Browse files
authored
Fall back to Class.forName in case boot class is already defined (#41)
* Remove unnecessary DUP * Align non-boot and boot define-class approaches * Fall back to Class.forName in case boot class is already defined
1 parent 99d686d commit 85ec17a

File tree

1 file changed

+50
-9
lines changed

1 file changed

+50
-9
lines changed

class-inject/src/glue/java/datadog/instrument/glue/DefineClassGlueGenerator.java

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ final class DefineClassGlueGenerator {
8181
+ CLASS_CLASS
8282
+ ";";
8383

84+
private static final String CLASS_FORNAME_DESCRIPTOR =
85+
"(L" + STRING_CLASS + ";ZL" + CLASSLOADER_CLASS + ";)L" + CLASS_CLASS + ";";
86+
8487
private DefineClassGlueGenerator() {}
8588

8689
/**
@@ -166,6 +169,10 @@ private static byte[] generateBytecode(String unsafeNamespace) {
166169
final Label unlockClassLoaderAndThrow = new Label();
167170
final Label setupUnsafeDefiner = new Label();
168171
final Label hasNextBootClass = new Label();
172+
final Label defineBootClass = new Label();
173+
final Label storeBootClass = new Label();
174+
final Label loadBootClass = new Label();
175+
final Label rethrowOriginal = new Label();
169176
final Label returnDefinedClasses = new Label();
170177

171178
// common bytecode variables
@@ -175,20 +182,25 @@ private static byte[] generateBytecode(String unsafeNamespace) {
175182
final int mapEntrySetIterator = 4;
176183
final int protectionDomain = 5;
177184
final int classLoader = 6;
185+
final int className = 7;
178186

179187
// bytecode variables for non-boot classes
180-
final int classLoadingLock = 7;
181-
final int bytecodeMapEntry = 8;
182-
final int className = 9;
188+
final int classLoadingLock = 8;
189+
final int bytecodeMapEntry = 9;
183190

184191
// bytecode variables for boot classes
185-
final int unsafeInstance = 7;
192+
final int unsafeInstance = 8;
193+
final int originalError = 9;
186194

187195
mv.visitCode();
188196

189197
// make sure we unlock the class-loader if an exception occurs while defining a class
190198
mv.visitTryCatchBlock(classLoaderLocked, classLoaderUnlocked, unlockClassLoaderAndThrow, null);
191199

200+
// fall back and see if the boot class is already loaded if defining it fails
201+
mv.visitTryCatchBlock(defineBootClass, storeBootClass, loadBootClass, null);
202+
mv.visitTryCatchBlock(loadBootClass, rethrowOriginal, rethrowOriginal, null);
203+
192204
// -------- SHARED SETUP CODE --------
193205

194206
// store the defined classes in a list
@@ -278,11 +290,13 @@ private static byte[] generateBytecode(String unsafeNamespace) {
278290
false);
279291
mv.visitInsn(DUP);
280292
mv.visitJumpInsn(IFNONNULL, storeNonBootClass);
281-
282-
// define the class using the given class-name and bytecode
283293
mv.visitInsn(POP);
294+
295+
// not yet defined, prepare arguments to define it
284296
mv.visitVarInsn(ALOAD, classLoader);
285297
mv.visitVarInsn(ALOAD, className);
298+
299+
// extract the bytecode of the next class to define
286300
mv.visitVarInsn(ALOAD, bytecodeMapEntry);
287301
mv.visitMethodInsn(
288302
INVOKEINTERFACE, MAP_ENTRY_CLASS, "getValue", "()L" + OBJECT_CLASS + ";", true);
@@ -291,7 +305,10 @@ private static byte[] generateBytecode(String unsafeNamespace) {
291305
mv.visitInsn(ARRAYLENGTH);
292306
mv.visitInsn(ICONST_0);
293307
mv.visitInsn(SWAP);
308+
294309
mv.visitVarInsn(ALOAD, protectionDomain);
310+
311+
// define the class using the given class-name and bytecode
295312
mv.visitMethodInsn(
296313
INVOKEVIRTUAL, CLASSLOADER_CLASS, "defineClass", CLASSLOADER_DEFINECLASS_DESCRIPTOR, false);
297314

@@ -312,7 +329,6 @@ private static byte[] generateBytecode(String unsafeNamespace) {
312329

313330
// unlock the class-loader if something goes wrong
314331
mv.visitLabel(unlockClassLoaderAndThrow);
315-
mv.visitInsn(DUP);
316332
mv.visitVarInsn(ALOAD, classLoadingLock);
317333
mv.visitInsn(MONITOREXIT);
318334
mv.visitInsn(ATHROW);
@@ -330,28 +346,38 @@ private static byte[] generateBytecode(String unsafeNamespace) {
330346
mv.visitMethodInsn(INVOKEINTERFACE, ITERATOR_CLASS, "hasNext", "()Z", true);
331347
mv.visitJumpInsn(IFEQ, returnDefinedClasses);
332348

333-
// define the class using the given class-name and bytecode
334349
mv.visitVarInsn(ALOAD, unsafeInstance);
350+
351+
// extract the name of the next class to define
335352
mv.visitVarInsn(ALOAD, mapEntrySetIterator);
336353
mv.visitMethodInsn(INVOKEINTERFACE, ITERATOR_CLASS, "next", "()L" + OBJECT_CLASS + ";", true);
337354
mv.visitInsn(DUP);
338355
mv.visitMethodInsn(
339356
INVOKEINTERFACE, MAP_ENTRY_CLASS, "getKey", "()L" + OBJECT_CLASS + ";", true);
340357
mv.visitTypeInsn(CHECKCAST, STRING_CLASS);
358+
mv.visitInsn(DUP);
359+
mv.visitVarInsn(ASTORE, className);
341360
mv.visitInsn(SWAP);
361+
362+
// extract the bytecode of the next class to define
342363
mv.visitMethodInsn(
343364
INVOKEINTERFACE, MAP_ENTRY_CLASS, "getValue", "()L" + OBJECT_CLASS + ";", true);
344365
mv.visitTypeInsn(CHECKCAST, BYTE_ARRAY);
345366
mv.visitInsn(DUP);
346367
mv.visitInsn(ARRAYLENGTH);
347368
mv.visitInsn(ICONST_0);
348369
mv.visitInsn(SWAP);
370+
349371
mv.visitInsn(ACONST_NULL);
350372
mv.visitVarInsn(ALOAD, protectionDomain);
373+
374+
// define the boot class using the given class-name and bytecode
375+
mv.visitLabel(defineBootClass);
351376
mv.visitMethodInsn(
352377
INVOKEVIRTUAL, unsafeClass, "defineClass", UNSAFE_DEFINECLASS_DESCRIPTOR, false);
353378

354-
// store the class in the list
379+
// store the class in the list whether we defined it or it already existed
380+
mv.visitLabel(storeBootClass);
355381
mv.visitVarInsn(ALOAD, definedClasses);
356382
mv.visitInsn(SWAP);
357383
mv.visitMethodInsn(INVOKEVIRTUAL, ARRAYLIST_CLASS, "add", "(L" + OBJECT_CLASS + ";)Z", false);
@@ -360,6 +386,21 @@ private static byte[] generateBytecode(String unsafeNamespace) {
360386
// check again if we've defined all the given bytecode
361387
mv.visitJumpInsn(GOTO, hasNextBootClass);
362388

389+
// see if the boot class has already been defined
390+
mv.visitLabel(loadBootClass);
391+
mv.visitVarInsn(ASTORE, originalError);
392+
mv.visitVarInsn(ALOAD, className);
393+
mv.visitInsn(ICONST_0); // initialize=false
394+
mv.visitInsn(ACONST_NULL);
395+
mv.visitMethodInsn(INVOKESTATIC, CLASS_CLASS, "forName", CLASS_FORNAME_DESCRIPTOR, false);
396+
mv.visitJumpInsn(GOTO, storeBootClass);
397+
398+
// boot class is not defined, report original error
399+
mv.visitLabel(rethrowOriginal);
400+
mv.visitInsn(POP);
401+
mv.visitVarInsn(ALOAD, originalError);
402+
mv.visitInsn(ATHROW);
403+
363404
// -------- SHARED RETURN CODE --------
364405

365406
// return the defined classes

0 commit comments

Comments
 (0)