1/*
2 * Copyright (c) 1998, 2011, 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
26/*
27 * This source code is provided to illustrate the usage of a given feature
28 * or technique and has been deliberately simplified. Additional steps
29 * required for a production-quality application, such as security checks,
30 * input validation and proper error handling, might not be present in
31 * this sample code.
32 */
33
34
35package com.sun.tools.example.debug.tty;
36
37import com.sun.jdi.*;
38import com.sun.jdi.request.StepRequest;
39import com.sun.jdi.request.MethodEntryRequest;
40import com.sun.jdi.request.MethodExitRequest;
41import java.util.*;
42import java.io.*;
43
44
45class Env {
46
47    static EventRequestSpecList specList = new EventRequestSpecList();
48
49    private static VMConnection connection;
50
51    private static SourceMapper sourceMapper = new SourceMapper("");
52    private static List<String> excludes;
53
54    private static final int SOURCE_CACHE_SIZE = 5;
55    private static List<SourceCode> sourceCache = new LinkedList<SourceCode>();
56
57    private static HashMap<String, Value> savedValues = new HashMap<String, Value>();
58    private static Method atExitMethod;
59
60    static void init(String connectSpec, boolean openNow, int flags) {
61        connection = new VMConnection(connectSpec, flags);
62        if (!connection.isLaunch() || openNow) {
63            connection.open();
64        }
65    }
66
67    static VMConnection connection() {
68        return connection;
69    }
70
71    static VirtualMachine vm() {
72        return connection.vm();
73    }
74
75    static void shutdown() {
76        shutdown(null);
77    }
78
79    static void shutdown(String message) {
80        if (connection != null) {
81            try {
82                connection.disposeVM();
83            } catch (VMDisconnectedException e) {
84                // Shutting down after the VM has gone away. This is
85                // not an error, and we just ignore it.
86            }
87        }
88        if (message != null) {
89            MessageOutput.lnprint(message);
90            MessageOutput.println();
91        }
92        System.exit(0);
93    }
94
95    static void setSourcePath(String srcPath) {
96        sourceMapper = new SourceMapper(srcPath);
97        sourceCache.clear();
98    }
99
100    static void setSourcePath(List<String> srcList) {
101        sourceMapper = new SourceMapper(srcList);
102        sourceCache.clear();
103    }
104
105    static String getSourcePath() {
106        return sourceMapper.getSourcePath();
107    }
108
109    static private List<String> excludes() {
110        if (excludes == null) {
111            setExcludes("java.*, javax.*, sun.*, com.sun.*");
112        }
113        return excludes;
114    }
115
116    static String excludesString() {
117        StringBuilder sb = new StringBuilder();
118        for (String pattern : excludes()) {
119            sb.append(pattern);
120            sb.append(",");
121        }
122        return sb.toString();
123    }
124
125    static void addExcludes(StepRequest request) {
126        for (String pattern : excludes()) {
127            request.addClassExclusionFilter(pattern);
128        }
129    }
130
131    static void addExcludes(MethodEntryRequest request) {
132        for (String pattern : excludes()) {
133            request.addClassExclusionFilter(pattern);
134        }
135    }
136
137    static void addExcludes(MethodExitRequest request) {
138        for (String pattern : excludes()) {
139            request.addClassExclusionFilter(pattern);
140        }
141    }
142
143    static void setExcludes(String excludeString) {
144        StringTokenizer t = new StringTokenizer(excludeString, " ,;");
145        List<String> list = new ArrayList<String>();
146        while (t.hasMoreTokens()) {
147            list.add(t.nextToken());
148        }
149        excludes = list;
150    }
151
152    static Method atExitMethod() {
153        return atExitMethod;
154    }
155
156    static void setAtExitMethod(Method mmm) {
157        atExitMethod = mmm;
158    }
159
160    /**
161     * Return a Reader cooresponding to the source of this location.
162     * Return null if not available.
163     * Note: returned reader must be closed.
164     */
165    static BufferedReader sourceReader(Location location) {
166        return sourceMapper.sourceReader(location);
167    }
168
169    static synchronized String sourceLine(Location location, int lineNumber)
170                                          throws IOException {
171        if (lineNumber == -1) {
172            throw new IllegalArgumentException();
173        }
174
175        try {
176            String fileName = location.sourceName();
177
178            Iterator<SourceCode> iter = sourceCache.iterator();
179            SourceCode code = null;
180            while (iter.hasNext()) {
181                SourceCode candidate = iter.next();
182                if (candidate.fileName().equals(fileName)) {
183                    code = candidate;
184                    iter.remove();
185                    break;
186                }
187            }
188            if (code == null) {
189                BufferedReader reader = sourceReader(location);
190                if (reader == null) {
191                    throw new FileNotFoundException(fileName);
192                }
193                code = new SourceCode(fileName, reader);
194                if (sourceCache.size() == SOURCE_CACHE_SIZE) {
195                    sourceCache.remove(sourceCache.size() - 1);
196                }
197            }
198            sourceCache.add(0, code);
199            return code.sourceLine(lineNumber);
200        } catch (AbsentInformationException e) {
201            throw new IllegalArgumentException();
202        }
203    }
204
205    /** Return a description of an object. */
206    static String description(ObjectReference ref) {
207        ReferenceType clazz = ref.referenceType();
208        long id = ref.uniqueID();
209        if (clazz == null) {
210            return toHex(id);
211        } else {
212            return MessageOutput.format("object description and hex id",
213                                        new Object [] {clazz.name(),
214                                                       toHex(id)});
215        }
216    }
217
218    /** Convert a long to a hexadecimal string. */
219    static String toHex(long n) {
220        char s1[] = new char[16];
221        char s2[] = new char[18];
222
223        /* Store digits in reverse order. */
224        int i = 0;
225        do {
226            long d = n & 0xf;
227            s1[i++] = (char)((d < 10) ? ('0' + d) : ('a' + d - 10));
228        } while ((n >>>= 4) > 0);
229
230        /* Now reverse the array. */
231        s2[0] = '0';
232        s2[1] = 'x';
233        int j = 2;
234        while (--i >= 0) {
235            s2[j++] = s1[i];
236        }
237        return new String(s2, 0, j);
238    }
239
240    /** Convert hexadecimal strings to longs. */
241    static long fromHex(String hexStr) {
242        String str = hexStr.startsWith("0x") ?
243            hexStr.substring(2).toLowerCase() : hexStr.toLowerCase();
244        if (hexStr.length() == 0) {
245            throw new NumberFormatException();
246        }
247
248        long ret = 0;
249        for (int i = 0; i < str.length(); i++) {
250            int c = str.charAt(i);
251            if (c >= '0' && c <= '9') {
252                ret = (ret * 16) + (c - '0');
253            } else if (c >= 'a' && c <= 'f') {
254                ret = (ret * 16) + (c - 'a' + 10);
255            } else {
256                throw new NumberFormatException();
257            }
258        }
259        return ret;
260    }
261
262    static ReferenceType getReferenceTypeFromToken(String idToken) {
263        ReferenceType cls = null;
264        if (Character.isDigit(idToken.charAt(0))) {
265            cls = null;
266        } else if (idToken.startsWith("*.")) {
267        // This notation saves typing by letting the user omit leading
268        // package names. The first
269        // loaded class whose name matches this limited regular
270        // expression is selected.
271        idToken = idToken.substring(1);
272        for (ReferenceType type : Env.vm().allClasses()) {
273            if (type.name().endsWith(idToken)) {
274                cls = type;
275                break;
276            }
277        }
278    } else {
279            // It's a class name
280            List<ReferenceType> classes = Env.vm().classesByName(idToken);
281            if (classes.size() > 0) {
282                // TO DO: handle multiples
283                cls = classes.get(0);
284            }
285        }
286        return cls;
287    }
288
289    static Set<String> getSaveKeys() {
290        return savedValues.keySet();
291    }
292
293    static Value getSavedValue(String key) {
294        return savedValues.get(key);
295    }
296
297    static void setSavedValue(String key, Value value) {
298        savedValues.put(key, value);
299    }
300
301    static class SourceCode {
302        private String fileName;
303        private List<String> sourceLines = new ArrayList<String>();
304
305        SourceCode(String fileName, BufferedReader reader)  throws IOException {
306            this.fileName = fileName;
307            try {
308                String line = reader.readLine();
309                while (line != null) {
310                    sourceLines.add(line);
311                    line = reader.readLine();
312                }
313            } finally {
314                reader.close();
315            }
316        }
317
318        String fileName() {
319            return fileName;
320        }
321
322        String sourceLine(int number) {
323            int index = number - 1; // list is 0-indexed
324            if (index >= sourceLines.size()) {
325                return null;
326            } else {
327                return sourceLines.get(index);
328            }
329        }
330    }
331}
332