URLStreamHandlerRuntime.java revision 351b20ee84db15e054b01c3a5e7b4234cecad890
1/*******************************************************************************
2 * Copyright (c) 2009, 2014 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 *******************************************************************************/
12package org.jacoco.core.runtime;
13
14import java.io.IOException;
15import java.lang.reflect.Field;
16import java.net.URL;
17import java.net.URLConnection;
18import java.net.URLStreamHandler;
19import java.util.Map;
20
21import org.jacoco.core.internal.instr.InstrSupport;
22import org.objectweb.asm.MethodVisitor;
23import 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 */
30public 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