| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: InstrClassLoadHook.java,v 1.1.1.1 2004/05/09 16:57:44 vlad_r Exp $ |
| */ |
| package com.vladium.emma.rt; |
| |
| import java.io.IOException; |
| |
| import com.vladium.jcd.cls.ClassDef; |
| import com.vladium.jcd.compiler.ClassWriter; |
| import com.vladium.jcd.parser.ClassDefParser; |
| import com.vladium.util.ByteArrayOStream; |
| import com.vladium.util.Descriptors; |
| import com.vladium.util.asserts.$assert; |
| import com.vladium.emma.filter.IInclExclFilter; |
| import com.vladium.emma.instr.InstrVisitor; |
| import com.vladium.emma.data.CoverageOptions; |
| import com.vladium.emma.data.IMetaData; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * MT-safety ensured by the containing loader |
| * |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| final class InstrClassLoadHook implements IClassLoadHook |
| { |
| // public: ................................................................ |
| |
| /** |
| * @param filter [can be null] |
| */ |
| public InstrClassLoadHook (final IInclExclFilter filter, final IMetaData mdata) |
| { |
| if (mdata == null) throw new IllegalArgumentException ("null input: mdata"); |
| |
| m_filter = filter; // can be null |
| m_metadata = mdata; |
| |
| // important to use the same options as the metadata may have been populated earlier: |
| final CoverageOptions options = mdata.getOptions (); |
| m_classDefProcessor = new InstrVisitor (options); |
| |
| m_instrResult = new InstrVisitor.InstrResult (); |
| } |
| |
| |
| public boolean processClassDef (final String className, |
| final byte [] bytes, final int length, |
| ByteArrayOStream out) |
| throws IOException |
| { |
| if ($assert.ENABLED) |
| { |
| $assert.ASSERT (className != null, "className is null"); |
| $assert.ASSERT (bytes != null, "bytes is null"); |
| $assert.ASSERT (bytes != null, "out is null"); |
| } |
| |
| final IInclExclFilter filter = m_filter; |
| if ((filter == null) || filter.included (className)) |
| { |
| final ClassDef clsDef = ClassDefParser.parseClass (bytes, length); |
| final String classVMName = Descriptors.javaNameToVMName (className); |
| |
| final Object lock = m_metadata.lock (); |
| |
| final boolean metadataExists; |
| synchronized (lock) |
| { |
| metadataExists = m_metadata.hasDescriptor (classVMName); |
| } |
| |
| // since this is the first [and only] time the parent loader is |
| // loading the class in question, if metadata for 'className' exists |
| // it means it was created during the app runner's classpath scan -- |
| // do not overwrite it (the class def should be the same) |
| |
| // [this picture breaks down if the design changes so that the same |
| // metadata instance could be associated with more than one app loader] |
| |
| m_classDefProcessor.process (clsDef, false, true, ! metadataExists, m_instrResult); |
| |
| boolean useOurs = m_instrResult.m_instrumented; |
| |
| if (m_instrResult.m_descriptor != null) // null means either the metadata existed already or the class is an interface |
| { |
| // try to update metadata [this supports the "no initial full cp |
| // scan mode" in the app runner and also ensures that we pick up |
| // any dynamically generated classes to support (hacky) apps that |
| // do dynamic source generation/compilation]: |
| |
| synchronized (lock) |
| { |
| // do not force overwrites of existing descriptors to support |
| // correct handling of race conditions: if another thread |
| // updates the metadata first, discard our version of the class def |
| |
| // [actually, this guard is redundant here because |
| // right now the hook can only have a single classloader parent |
| // and the parent's loadClass() is a critical section] |
| |
| if (! m_metadata.add (m_instrResult.m_descriptor, false)) |
| useOurs = false; |
| } |
| } |
| |
| if (useOurs) |
| { |
| ClassWriter.writeClassTable (clsDef, out); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| private final IInclExclFilter m_filter; // can be null [equivalent to no filtering] |
| private final IMetaData m_metadata; // never null |
| private final InstrVisitor m_classDefProcessor; // never null |
| private final InstrVisitor.InstrResult m_instrResult; |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |