SystemPropertiesRuntime.java

    1/*******************************************************************************
    2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
    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 * $Id: $
   12 *******************************************************************************/
   13package org.jacoco.core.runtime;
   14
   15import java.util.Collection;
   16import java.util.Map;
   17import java.util.Set;
   18
   19import org.jacoco.core.instr.GeneratorConstants;
   20import org.objectweb.asm.Opcodes;
   21import org.objectweb.asm.commons.GeneratorAdapter;
   22
   23/**
   24 * This {@link IRuntime} implementation makes the execution data available
   25 * through a special entry of the type {@link Map} in the
   26 * {@link System#getProperties()} hash table. The advantage is, that the
   27 * instrumented classes do not get dependencies to other classes than the JRE
   28 * library itself.
   29 * 
   30 * This runtime may cause problems in environments with security restrictions,
   31 * in applications that replace the system properties or in applications that
   32 * fail if non-String values are placed in the system properties.
   33 * 
   34 * @author Marc R. Hoffmann
   35 * @version $Revision: $
   36 */
   37public class SystemPropertiesRuntime extends AbstractRuntime {
   38
   39    private static final String KEYPREFIX = "jacoco-";
   40
   41    private final String key;
   42
   43    private final Map<Long, boolean[]> dataAccess = new Map<Long, boolean[]>() {
   44
   45        public boolean[] get(final Object key) {
   46            final Long id = (Long) key;
   47            synchronized (store) {
   48                final boolean[] data = store.getData(id);
   49                if (data == null) {
   50                    throw new IllegalStateException(String.format(
   51                            "Unknown class id %x.", id));
   52                }
   53                return data;
   54            }
   55        }
   56
   57        public void clear() {
   58            throw new UnsupportedOperationException();
   59        }
   60
   61        public boolean containsKey(final Object key) {
   62            throw new UnsupportedOperationException();
   63        }
   64
   65        public boolean containsValue(final Object value) {
   66            throw new UnsupportedOperationException();
   67        }
   68
   69        public Set<Entry<Long, boolean[]>> entrySet() {
   70            throw new UnsupportedOperationException();
   71        }
   72
   73        public boolean isEmpty() {
   74            throw new UnsupportedOperationException();
   75        }
   76
   77        public Set<Long> keySet() {
   78            throw new UnsupportedOperationException();
   79        }
   80
   81        public boolean[] put(final Long key, final boolean[] value) {
   82            throw new UnsupportedOperationException();
   83        }
   84
   85        public void putAll(final Map<? extends Long, ? extends boolean[]> t) {
   86            throw new UnsupportedOperationException();
   87        }
   88
   89        public boolean[] remove(final Object key) {
   90            throw new UnsupportedOperationException();
   91        }
   92
   93        public Collection<boolean[]> values() {
   94            throw new UnsupportedOperationException();
   95        }
   96
   97        public int size() {
   98            throw new UnsupportedOperationException();
   99        }
  100    };
  101
  102    /**
  103     * Creates a new runtime.
  104     */
  105    public SystemPropertiesRuntime() {
  106        this.key = KEYPREFIX + hashCode();
  107    }
  108
  109    public int generateDataAccessor(final long classid,
  110            final GeneratorAdapter gen) {
  111
  112        gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System",
  113                "getProperties", "()Ljava/util/Properties;");
  114
  115        // Stack[0]: Ljava/util/Properties;
  116
  117        gen.push(key);
  118
  119        // Stack[1]: Ljava/lang/String;
  120        // Stack[0]: Ljava/util/Properties;
  121
  122        gen.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/Properties",
  123                "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
  124
  125        // Stack[0]: Ljava/lang/Object;
  126
  127        gen.visitTypeInsn(Opcodes.CHECKCAST, "java/util/Map");
  128
  129        // Stack[0]: Ljava/util/Map;
  130
  131        gen.push(classid);
  132
  133        // Stack[2]: J
  134        // Stack[1]: .
  135        // Stack[0]: Ljava/util/Map;
  136
  137        gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf",
  138                "(J)Ljava/lang/Long;");
  139
  140        // Stack[1]: Ljava/lang/Long;
  141        // Stack[0]: Ljava/util/Map;
  142
  143        gen.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "get",
  144                "(Ljava/lang/Object;)Ljava/lang/Object;");
  145
  146        // Stack[0]: Ljava/lang/Object;
  147
  148        gen.checkCast(GeneratorConstants.PROBEDATA_TYPE);
  149
  150        // Stack[0]: [Z
  151
  152        return 3; // Maximum local stack size is 3
  153    }
  154
  155    public void startup() {
  156        System.getProperties().put(key, dataAccess);
  157    }
  158
  159    public void shutdown() {
  160        System.getProperties().remove(key);
  161    }
  162
  163}