org.jacoco.core.runtime.InjectedClassRuntime.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.runtime.InjectedClassRuntime.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Evgeny Mandrikov - initial API and implementation
 *
 *******************************************************************************/
package org.jacoco.core.runtime;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * {@link IRuntime} which defines a new class using
 * {@code java.lang.invoke.MethodHandles.Lookup.defineClass} introduced in Java
 * 9. Module where class will be defined must be opened to at least module of
 * this class.
 */
public class InjectedClassRuntime extends AbstractRuntime {

    private static final String FIELD_NAME = "data";

    private static final String FIELD_TYPE = "Ljava/lang/Object;";

    private final Class<?> locator;

    private final String injectedClassName;

    /**
     * Creates a new runtime which will define a class to the same class loader
     * and in the same package and protection domain as given class.
     *
     * @param locator
     *            class to identify the target class loader and package
     * @param simpleClassName
     *            simple name of the class to be defined
     */
    public InjectedClassRuntime(final Class<?> locator, final String simpleClassName) {
        this.locator = locator;
        this.injectedClassName = locator.getPackage().getName().replace('.', '/') + '/' + simpleClassName;
    }

    @Override
    public void startup(final RuntimeData data) throws Exception {
        super.startup(data);
        Lookup //
                .privateLookupIn(locator, Lookup.lookup()) //
                .defineClass(createClass(injectedClassName)) //
                .getField(FIELD_NAME) //
                .set(null, data);
    }

    public void shutdown() {
        // nothing to do
    }

    public int generateDataAccessor(final long classid, final String classname, final int probecount,
            final MethodVisitor mv) {
        mv.visitFieldInsn(Opcodes.GETSTATIC, injectedClassName, FIELD_NAME, FIELD_TYPE);

        RuntimeData.generateAccessCall(classid, classname, probecount, mv);

        return 6;
    }

    private static byte[] createClass(final String name) {
        final ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V9, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC, name.replace('.', '/'), null,
                "java/lang/Object", null);
        cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, FIELD_NAME, FIELD_TYPE, null, null);
        cw.visitEnd();
        return cw.toByteArray();
    }

    /**
     * Provides access to classes {@code java.lang.invoke.MethodHandles} and
     * {@code java.lang.invoke.MethodHandles.Lookup} introduced in Java 8.
     */
    private static class Lookup {

        private final Object instance;

        private Lookup(final Object instance) {
            this.instance = instance;
        }

        /**
         * @return a lookup object for the caller of this method
         */
        static Lookup lookup() throws Exception {
            return new Lookup(Class //
                    .forName("java.lang.invoke.MethodHandles") //
                    .getMethod("lookup") //
                    .invoke(null));
        }

        /**
         * See corresponding method introduced in Java 9.
         *
         * @param targetClass
         *            the target class
         * @param lookup
         *            the caller lookup object
         * @return a lookup object for the target class, with private access
         */
        static Lookup privateLookupIn(final Class<?> targetClass, final Lookup lookup) throws Exception {
            return new Lookup(Class //
                    .forName("java.lang.invoke.MethodHandles") //
                    .getMethod("privateLookupIn", Class.class,
                            Class.forName("java.lang.invoke.MethodHandles$Lookup")) //
                    .invoke(null, targetClass, lookup.instance));
        }

        /**
         * See corresponding method introduced in Java 9.
         *
         * @param bytes
         *            the class bytes
         * @return class
         */
        Class<?> defineClass(final byte[] bytes) throws Exception {
            return (Class<?>) Class //
                    .forName("java.lang.invoke.MethodHandles$Lookup").getMethod("defineClass", byte[].class)
                    .invoke(this.instance, new Object[] { bytes });
        }

    }

}