1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: InstrClassLoadHook.java,v 1.1.1.1 2004/05/09 16:57:44 vlad_r Exp $
8 */
9package com.vladium.emma.rt;
10
11import java.io.IOException;
12
13import com.vladium.jcd.cls.ClassDef;
14import com.vladium.jcd.compiler.ClassWriter;
15import com.vladium.jcd.parser.ClassDefParser;
16import com.vladium.util.ByteArrayOStream;
17import com.vladium.util.Descriptors;
18import com.vladium.util.asserts.$assert;
19import com.vladium.emma.filter.IInclExclFilter;
20import com.vladium.emma.instr.InstrVisitor;
21import com.vladium.emma.data.CoverageOptions;
22import com.vladium.emma.data.IMetaData;
23
24// ----------------------------------------------------------------------------
25/**
26 * MT-safety ensured by the containing loader
27 *
28 * @author Vlad Roubtsov, (C) 2003
29 */
30public
31final class InstrClassLoadHook implements IClassLoadHook
32{
33    // public: ................................................................
34
35    /**
36     * @param filter [can be null]
37     */
38    public InstrClassLoadHook (final IInclExclFilter filter, final IMetaData mdata)
39    {
40        if (mdata == null) throw new IllegalArgumentException ("null input: mdata");
41
42        m_filter = filter; // can be null
43        m_metadata = mdata;
44
45        // important to use the same options as the metadata may have been populated earlier:
46        final CoverageOptions options = mdata.getOptions ();
47        m_classDefProcessor = new InstrVisitor (options);
48
49        m_instrResult = new InstrVisitor.InstrResult ();
50    }
51
52
53    public boolean processClassDef (final String className,
54                                    final byte [] bytes, final int length,
55                                    ByteArrayOStream out)
56        throws IOException
57    {
58        if ($assert.ENABLED)
59        {
60            $assert.ASSERT (className != null, "className is null");
61            $assert.ASSERT (bytes != null, "bytes is null");
62            $assert.ASSERT (bytes != null, "out is null");
63        }
64
65        final IInclExclFilter filter = m_filter;
66        if ((filter == null) || filter.included (className))
67        {
68            final ClassDef clsDef = ClassDefParser.parseClass (bytes, length);
69            final String classVMName = Descriptors.javaNameToVMName (className);
70
71            final Object lock = m_metadata.lock ();
72
73            final boolean metadataExists;
74            synchronized (lock)
75            {
76                metadataExists = m_metadata.hasDescriptor (classVMName);
77            }
78
79            // since this is the first [and only] time the parent loader is
80            // loading the class in question, if metadata for 'className' exists
81            // it means it was created during the app runner's classpath scan --
82            // do not overwrite it (the class def should be the same)
83
84            // [this picture breaks down if the design changes so that the same
85            // metadata instance could be associated with more than one app loader]
86
87            m_classDefProcessor.process (clsDef, false, true, ! metadataExists, m_instrResult);
88
89            boolean useOurs = m_instrResult.m_instrumented;
90
91            if (m_instrResult.m_descriptor != null) // null means either the metadata existed already or the class is an interface
92            {
93                // try to update metadata [this supports the "no initial full cp
94                // scan mode" in the app runner and also ensures that we pick up
95                // any dynamically generated classes to support (hacky) apps that
96                // do dynamic source generation/compilation]:
97
98                synchronized (lock)
99                {
100                    // do not force overwrites of existing descriptors to support
101                    // correct handling of race conditions: if another thread
102                    // updates the metadata first, discard our version of the class def
103
104                    // [actually, this guard is redundant here because
105                    // right now the hook can only have a single classloader parent
106                    // and the parent's loadClass() is a critical section]
107
108                    if (! m_metadata.add (m_instrResult.m_descriptor, false))
109                         useOurs = false;
110                }
111            }
112
113            if (useOurs)
114            {
115                ClassWriter.writeClassTable (clsDef, out);
116                return true;
117            }
118        }
119
120        return false;
121    }
122
123    // protected: .............................................................
124
125    // package: ...............................................................
126
127    // private: ...............................................................
128
129
130    private final IInclExclFilter m_filter; // can be null [equivalent to no filtering]
131    private final IMetaData m_metadata; // never null
132    private final InstrVisitor m_classDefProcessor; // never null
133    private final InstrVisitor.InstrResult m_instrResult;
134
135} // end of class
136// ----------------------------------------------------------------------------