1 /******************************************************************************* 2 * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * Evgeny Mandrikov - initial API and implementation 10 * 11 *******************************************************************************/ 12 package org.jacoco.core.test.validation; 13 14 import static org.junit.Assert.assertEquals; 15 16 import java.lang.invoke.CallSite; 17 import java.lang.invoke.ConstantCallSite; 18 import java.lang.invoke.MethodHandles; 19 import java.lang.invoke.MethodType; 20 import java.lang.reflect.InvocationTargetException; 21 22 import org.jacoco.core.instr.Instrumenter; 23 import org.jacoco.core.runtime.IRuntime; 24 import org.jacoco.core.runtime.RuntimeData; 25 import org.jacoco.core.runtime.SystemPropertiesRuntime; 26 import org.jacoco.core.test.TargetLoader; 27 import org.junit.After; 28 import org.junit.Before; 29 import org.junit.Test; 30 import org.objectweb.asm.ClassWriter; 31 import org.objectweb.asm.Handle; 32 import org.objectweb.asm.Label; 33 import org.objectweb.asm.MethodVisitor; 34 import org.objectweb.asm.Opcodes; 35 36 /** 37 * Test of ASM bug 38 * <a href="https://gitlab.ow2.org/asm/asm/issues/317748">#317748</a> that 39 * caused 40 * {@code java.lang.ClassFormatError: Short length on BootstrapMethods in class file} 41 * during instrumentation. 42 */ 43 public class BootstrapMethodReferenceTest { 44 45 private final IRuntime runtime = new SystemPropertiesRuntime(); 46 private final Instrumenter instrumenter = new Instrumenter(runtime); 47 48 @Before 49 public void setup() throws Exception { 50 runtime.startup(new RuntimeData()); 51 } 52 53 @After 54 public void teardown() { 55 runtime.shutdown(); 56 } 57 58 @Test 59 public void test() throws Exception { 60 final String className = "Example"; 61 62 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 63 cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, className, null, 64 "java/lang/Object", null); 65 66 final MethodVisitor mv = cw.visitMethod( 67 Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "run", "()I", null, 68 null); 69 mv.visitCode(); 70 addCauseOfResizeInstructions(mv); 71 final MethodType methodType = MethodType.methodType(CallSite.class, 72 MethodHandles.Lookup.class, String.class, MethodType.class); 73 final Handle handle = new Handle(Opcodes.H_INVOKESTATIC, 74 this.getClass().getCanonicalName().replace('.', '/'), 75 "bootstrap", methodType.toMethodDescriptorString(), false); 76 mv.visitInvokeDynamicInsn("invoke", "()I", handle); 77 mv.visitInsn(Opcodes.IRETURN); 78 mv.visitMaxs(1, 0); 79 mv.visitEnd(); 80 81 cw.visitEnd(); 82 83 final byte[] original = cw.toByteArray(); 84 assertEquals(42, run(className, original)); 85 86 final byte[] instrumented = instrumenter.instrument(original, 87 className); 88 assertEquals(42, run(className, instrumented)); 89 } 90 91 private static int run(final String className, final byte[] bytes) 92 throws ClassNotFoundException, NoSuchMethodException, 93 InvocationTargetException, IllegalAccessException { 94 return (Integer) new TargetLoader().add(className, bytes) 95 .getMethod("run").invoke(null); 96 } 97 98 /** 99 * Adds code that triggers usage of 100 * {@link org.objectweb.asm.MethodWriter#INSERTED_FRAMES} during 101 * instrumentation. 102 */ 103 private static void addCauseOfResizeInstructions(final MethodVisitor mv) { 104 mv.visitInsn(Opcodes.ICONST_0); 105 mv.visitInsn(Opcodes.ICONST_1); 106 final Label target = new Label(); 107 mv.visitJumpInsn(Opcodes.IFLE, target); 108 for (int i = 0; i < Short.MAX_VALUE; i++) { 109 mv.visitInsn(Opcodes.NOP); 110 } 111 mv.visitLabel(target); 112 } 113 114 @SuppressWarnings("unused") 115 public static CallSite bootstrap(final MethodHandles.Lookup caller, 116 final String name, final MethodType type) throws Exception { 117 return new ConstantCallSite(caller.findStatic(BootstrapMethodReferenceTest.class, 118 "callTarget", MethodType.methodType(int.class))); 119 } 120 121 @SuppressWarnings("unused") 122 public static int callTarget() { 123 return 42; 124 } 125 126 } 127