blob: afce7246582eb16f7104b47e14af648881f3dc34 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc R. Hoffmann - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.runtime;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.Map;
import org.jacoco.core.internal.instr.InstrSupport;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* This {@link IRuntime} implementation registers a special
* {@link URLStreamHandler} to process coverage data. The handler is not
* actually used for opening a URL, but to get access to the runtime object.
*/
public class URLStreamHandlerRuntime extends AbstractRuntime {
private static final String PROTOCOLPREFIX = "jacoco-";
private final String protocol;
private Map<String, URLStreamHandler> handlers;
/**
* Creates a new runtime.
*/
public URLStreamHandlerRuntime() {
super();
protocol = PROTOCOLPREFIX + Integer.toHexString(hashCode());
}
@Override
public void startup(final RuntimeData data) throws Exception {
super.startup(data);
handlers = getHandlersReference();
handlers.put(protocol, handler);
}
private Map<String, URLStreamHandler> getHandlersReference()
throws Exception {
final Field field = URL.class.getDeclaredField("handlers");
field.setAccessible(true);
@SuppressWarnings("unchecked")
final Map<String, URLStreamHandler> map = (Map<String, URLStreamHandler>) field
.get(null);
return map;
}
public void shutdown() {
handlers.remove(protocol);
}
public int generateDataAccessor(final long classid, final String classname,
final int probecount, final MethodVisitor mv) {
// The data accessor performs the following steps:
//
// final URL url = new URL(protocol, null, "");
// final URLConnection connection = url.openConnection();
// final Object[] args = new Object[3];
// args[0] = Long.valueOf(classid);
// args[1] = classname;
// args[2] = Integer.valueOf(probecount);
// connection.equals(args);
// final byte[] probedata = (byte[]) args[0];
RuntimeData.generateArgumentArray(classid, classname, probecount, mv);
mv.visitInsn(Opcodes.DUP);
// Stack[1]: [Ljava/lang/Object;
// Stack[0]: [Ljava/lang/Object;
mv.visitTypeInsn(Opcodes.NEW, "java/net/URL");
mv.visitInsn(Opcodes.DUP);
mv.visitLdcInsn(protocol);
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitLdcInsn("");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/net/URL", "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false);
// Stack[2]: [Ljava/net/URL;
// Stack[1]: [Ljava/lang/Object;
// Stack[0]: [Ljava/lang/Object;
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/URL",
"openConnection", "()Ljava/net/URLConnection;", false);
// Stack[2]: [Ljava/net/URLConnection;
// Stack[1]: [Ljava/lang/Object;
// Stack[0]: [Ljava/lang/Object;
mv.visitInsn(Opcodes.SWAP);
// Stack[2]: [Ljava/lang/Object;
// Stack[1]: [Ljava/net/URLConnection;
// Stack[0]: [Ljava/lang/Object;
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals",
"(Ljava/lang/Object;)Z", false);
// Stack[1]: Z;
// Stack[0]: [Ljava/lang/Object;
mv.visitInsn(Opcodes.POP);
// Stack[0]: [Ljava/lang/Object;
mv.visitInsn(Opcodes.ICONST_0);
mv.visitInsn(Opcodes.AALOAD);
mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC);
return 7;
}
private final URLStreamHandler handler = new URLStreamHandler() {
@Override
protected URLConnection openConnection(final URL u) throws IOException {
return connection;
}
};
private final URLConnection connection = new URLConnection(null) {
@Override
public void connect() throws IOException {
throw new AssertionError();
}
@Override
public boolean equals(final Object obj) {
return data.equals(obj);
}
};
}