Home | History | Annotate | Download | only in runtime
      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