1/*
2 * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.instrument;
27
28import java.lang.instrument.Instrumentation;
29import java.lang.instrument.ClassFileTransformer;
30import java.security.ProtectionDomain;
31
32/*
33 * Copyright 2003 Wily Technology, Inc.
34 */
35
36/**
37 * Support class for the InstrumentationImpl. Manages the list of registered transformers.
38 * Keeps everything in the right order, deals with sync of the list,
39 * and actually does the calling of the transformers.
40 */
41public class TransformerManager
42{
43    private class TransformerInfo {
44        final ClassFileTransformer  mTransformer;
45        String                      mPrefix;
46
47        TransformerInfo(ClassFileTransformer transformer) {
48            mTransformer = transformer;
49            mPrefix = null;
50        }
51
52        ClassFileTransformer transformer() {
53            return  mTransformer;
54        }
55
56        String getPrefix() {
57            return mPrefix;
58        }
59
60        void setPrefix(String prefix) {
61            mPrefix = prefix;
62        }
63    }
64
65    /**
66     * a given instance of this list is treated as immutable to simplify sync;
67     * we pay copying overhead whenever the list is changed rather than every time
68     * the list is referenced.
69     * The array is kept in the order the transformers are added via addTransformer
70     * (first added is 0, last added is length-1)
71     * Use an array, not a List or other Collection. This keeps the set of classes
72     * used by this code to a minimum. We want as few dependencies as possible in this
73     * code, since it is used inside the class definition system. Any class referenced here
74     * cannot be transformed by Java code.
75     */
76    private TransformerInfo[]  mTransformerList;
77
78    /***
79     * Is this TransformerManager for transformers capable of retransformation?
80     */
81    private boolean            mIsRetransformable;
82
83    TransformerManager(boolean isRetransformable) {
84        mTransformerList    = new TransformerInfo[0];
85        mIsRetransformable  = isRetransformable;
86    }
87
88    boolean isRetransformable() {
89        return mIsRetransformable;
90    }
91
92    public synchronized void
93    addTransformer( ClassFileTransformer    transformer) {
94        TransformerInfo[] oldList = mTransformerList;
95        TransformerInfo[] newList = new TransformerInfo[oldList.length + 1];
96        System.arraycopy(   oldList,
97                            0,
98                            newList,
99                            0,
100                            oldList.length);
101        newList[oldList.length] = new TransformerInfo(transformer);
102        mTransformerList = newList;
103    }
104
105    public synchronized boolean
106    removeTransformer(ClassFileTransformer  transformer) {
107        boolean                 found           = false;
108        TransformerInfo[]       oldList         = mTransformerList;
109        int                     oldLength       = oldList.length;
110        int                     newLength       = oldLength - 1;
111
112        // look for it in the list, starting at the last added, and remember
113        // where it was if we found it
114        int matchingIndex   = 0;
115        for ( int x = oldLength - 1; x >= 0; x-- ) {
116            if ( oldList[x].transformer() == transformer ) {
117                found           = true;
118                matchingIndex   = x;
119                break;
120            }
121        }
122
123        // make a copy of the array without the matching element
124        if ( found ) {
125            TransformerInfo[]  newList = new TransformerInfo[newLength];
126
127            // copy up to but not including the match
128            if ( matchingIndex > 0 ) {
129                System.arraycopy(   oldList,
130                                    0,
131                                    newList,
132                                    0,
133                                    matchingIndex);
134            }
135
136            // if there is anything after the match, copy it as well
137            if ( matchingIndex < (newLength) ) {
138                System.arraycopy(   oldList,
139                                    matchingIndex + 1,
140                                    newList,
141                                    matchingIndex,
142                                    (newLength) - matchingIndex);
143            }
144            mTransformerList = newList;
145        }
146        return found;
147    }
148
149    synchronized boolean
150    includesTransformer(ClassFileTransformer transformer) {
151        for (TransformerInfo info : mTransformerList) {
152            if ( info.transformer() == transformer ) {
153                return true;
154            }
155        }
156        return false;
157    }
158
159    // This function doesn't actually snapshot anything, but should be
160    // used to set a local variable, which will snapshot the transformer
161    // list because of the copying semantics of mTransformerList (see
162    // the comment for mTransformerList).
163    private TransformerInfo[]
164    getSnapshotTransformerList() {
165        return mTransformerList;
166    }
167
168    public byte[]
169    transform(  Module              module,
170                ClassLoader         loader,
171                String              classname,
172                Class<?>            classBeingRedefined,
173                ProtectionDomain    protectionDomain,
174                byte[]              classfileBuffer) {
175        boolean someoneTouchedTheBytecode = false;
176
177        TransformerInfo[]  transformerList = getSnapshotTransformerList();
178
179        byte[]  bufferToUse = classfileBuffer;
180
181        // order matters, gotta run 'em in the order they were added
182        for ( int x = 0; x < transformerList.length; x++ ) {
183            TransformerInfo         transformerInfo = transformerList[x];
184            ClassFileTransformer    transformer = transformerInfo.transformer();
185            byte[]                  transformedBytes = null;
186
187            try {
188                transformedBytes = transformer.transform(   module,
189                                                            loader,
190                                                            classname,
191                                                            classBeingRedefined,
192                                                            protectionDomain,
193                                                            bufferToUse);
194            }
195            catch (Throwable t) {
196                // don't let any one transformer mess it up for the others.
197                // This is where we need to put some logging. What should go here? FIXME
198            }
199
200            if ( transformedBytes != null ) {
201                someoneTouchedTheBytecode = true;
202                bufferToUse = transformedBytes;
203            }
204        }
205
206        // if someone modified it, return the modified buffer.
207        // otherwise return null to mean "no transforms occurred"
208        byte [] result;
209        if ( someoneTouchedTheBytecode ) {
210            result = bufferToUse;
211        }
212        else {
213            result = null;
214        }
215
216        return result;
217    }
218
219
220    int
221    getTransformerCount() {
222        TransformerInfo[]  transformerList = getSnapshotTransformerList();
223        return transformerList.length;
224    }
225
226    boolean
227    setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
228        TransformerInfo[]  transformerList = getSnapshotTransformerList();
229
230        for ( int x = 0; x < transformerList.length; x++ ) {
231            TransformerInfo         transformerInfo = transformerList[x];
232            ClassFileTransformer    aTransformer = transformerInfo.transformer();
233
234            if ( aTransformer == transformer ) {
235                transformerInfo.setPrefix(prefix);
236                return true;
237            }
238        }
239        return false;
240    }
241
242
243    String[]
244    getNativeMethodPrefixes() {
245        TransformerInfo[]  transformerList = getSnapshotTransformerList();
246        String[] prefixes                  = new String[transformerList.length];
247
248        for ( int x = 0; x < transformerList.length; x++ ) {
249            TransformerInfo         transformerInfo = transformerList[x];
250            prefixes[x] = transformerInfo.getPrefix();
251        }
252        return prefixes;
253    }
254}
255