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 * Marc R. Hoffmann - initial API and implementation 10 * 11 *******************************************************************************/ 12 package org.jacoco.core.runtime; 13 14 import java.io.IOException; 15 import java.lang.reflect.Field; 16 import java.net.URL; 17 import java.net.URLConnection; 18 import java.net.URLStreamHandler; 19 import java.util.Map; 20 21 import org.jacoco.core.internal.instr.InstrSupport; 22 import org.objectweb.asm.MethodVisitor; 23 import org.objectweb.asm.Opcodes; 24 25 /** 26 * This {@link IRuntime} implementation registers a special 27 * {@link URLStreamHandler} to process coverage data. The handler is not 28 * actually used for opening a URL, but to get access to the runtime object. 29 */ 30 public class URLStreamHandlerRuntime extends AbstractRuntime { 31 32 private static final String PROTOCOLPREFIX = "jacoco-"; 33 34 private final String protocol; 35 36 private Map<String, URLStreamHandler> handlers; 37 38 /** 39 * Creates a new runtime. 40 */ 41 public URLStreamHandlerRuntime() { 42 super(); 43 protocol = PROTOCOLPREFIX + Integer.toHexString(hashCode()); 44 } 45 46 @Override 47 public void startup(final RuntimeData data) throws Exception { 48 super.startup(data); 49 handlers = getHandlersReference(); 50 handlers.put(protocol, handler); 51 } 52 53 private Map<String, URLStreamHandler> getHandlersReference() 54 throws Exception { 55 final Field field = URL.class.getDeclaredField("handlers"); 56 field.setAccessible(true); 57 @SuppressWarnings("unchecked") 58 final Map<String, URLStreamHandler> map = (Map<String, URLStreamHandler>) field 59 .get(null); 60 return map; 61 } 62 63 public void shutdown() { 64 handlers.remove(protocol); 65 } 66 67 public int generateDataAccessor(final long classid, final String classname, 68 final int probecount, final MethodVisitor mv) { 69 70 // The data accessor performs the following steps: 71 // 72 // final URL url = new URL(protocol, null, ""); 73 // final URLConnection connection = url.openConnection(); 74 // final Object[] args = new Object[3]; 75 // args[0] = Long.valueOf(classid); 76 // args[1] = classname; 77 // args[2] = Integer.valueOf(probecount); 78 // connection.equals(args); 79 // final byte[] probedata = (byte[]) args[0]; 80 81 RuntimeData.generateArgumentArray(classid, classname, probecount, mv); 82 mv.visitInsn(Opcodes.DUP); 83 84 // Stack[1]: [Ljava/lang/Object; 85 // Stack[0]: [Ljava/lang/Object; 86 87 mv.visitTypeInsn(Opcodes.NEW, "java/net/URL"); 88 mv.visitInsn(Opcodes.DUP); 89 mv.visitLdcInsn(protocol); 90 mv.visitInsn(Opcodes.ACONST_NULL); 91 mv.visitLdcInsn(""); 92 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/net/URL", "<init>", 93 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", 94 false); 95 96 // Stack[2]: [Ljava/net/URL; 97 // Stack[1]: [Ljava/lang/Object; 98 // Stack[0]: [Ljava/lang/Object; 99 100 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/URL", 101 "openConnection", "()Ljava/net/URLConnection;", false); 102 103 // Stack[2]: [Ljava/net/URLConnection; 104 // Stack[1]: [Ljava/lang/Object; 105 // Stack[0]: [Ljava/lang/Object; 106 107 mv.visitInsn(Opcodes.SWAP); 108 109 // Stack[2]: [Ljava/lang/Object; 110 // Stack[1]: [Ljava/net/URLConnection; 111 // Stack[0]: [Ljava/lang/Object; 112 113 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals", 114 "(Ljava/lang/Object;)Z", false); 115 116 // Stack[1]: Z; 117 // Stack[0]: [Ljava/lang/Object; 118 119 mv.visitInsn(Opcodes.POP); 120 121 // Stack[0]: [Ljava/lang/Object; 122 123 mv.visitInsn(Opcodes.ICONST_0); 124 mv.visitInsn(Opcodes.AALOAD); 125 mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC); 126 127 return 7; 128 } 129 130 private final URLStreamHandler handler = new URLStreamHandler() { 131 @Override 132 protected URLConnection openConnection(final URL u) throws IOException { 133 return connection; 134 } 135 }; 136 137 private final URLConnection connection = new URLConnection(null) { 138 @Override 139 public void connect() throws IOException { 140 throw new AssertionError(); 141 } 142 143 @Override 144 public boolean equals(final Object obj) { 145 return data.equals(obj); 146 } 147 }; 148 149 } 150