1/*
2 * Copyright (c) 2001, 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
26#include <setjmp.h>
27
28#include "util.h"
29#include "SDE.h"
30
31#ifdef __APPLE__
32/* use setjmp/longjmp versions that do not save/restore the signal mask */
33#define setjmp _setjmp
34#define longjmp _longjmp
35#endif
36
37/**
38 * This SourceDebugExtension code does not
39 * allow concurrent translation - due to caching method.
40 * A separate thread setting the default stratum ID
41 * is, however, fine.
42 */
43
44#define INIT_SIZE_FILE 10
45#define INIT_SIZE_LINE 100
46#define INIT_SIZE_STRATUM 3
47
48#define BASE_STRATUM_NAME "Java"
49
50#define null NULL
51#define String char *
52#define private static
53
54typedef struct {
55  int fileId;
56  String sourceName;
57  String sourcePath; // do not read - use accessor
58  int isConverted;
59} FileTableRecord;
60
61typedef struct {
62    int jplsStart;
63    int jplsEnd;
64    int jplsLineInc;
65    int njplsStart;
66    int njplsEnd;
67    int fileId;
68} LineTableRecord;
69
70typedef struct {
71    String id;
72    int fileIndex;
73    int lineIndex;
74} StratumTableRecord;
75
76/* back-end wide value for default stratum */
77private String globalDefaultStratumId = null;
78
79/* reference type default */
80private String defaultStratumId = null;
81
82private jclass cachedClass = NULL;
83
84private FileTableRecord* fileTable;
85private LineTableRecord* lineTable;
86private StratumTableRecord* stratumTable;
87
88private int fileTableSize;
89private int lineTableSize;
90private int stratumTableSize;
91
92private int fileIndex;
93private int lineIndex;
94private int stratumIndex = 0;
95private int currentFileId;
96
97private int defaultStratumIndex;
98private int baseStratumIndex;
99private char* sdePos;
100
101private char* jplsFilename = null;
102private char* NullString = null;
103
104/* mangled in parse, cannot be parsed.  Must be kept. */
105private String sourceDebugExtension;
106
107private jboolean sourceMapIsValid;
108
109private jmp_buf jmp_buf_env;
110
111private int stratumTableIndex(String stratumId);
112private int stiLineTableIndex(int sti, int jplsLine);
113private int stiLineNumber(int sti, int lti, int jplsLine);
114private void decode(void);
115private void ignoreWhite(void);
116private jboolean isValid(void);
117
118    private void
119    loadDebugInfo(JNIEnv *env, jclass clazz) {
120
121        if (!isSameObject(env, clazz, cachedClass)) {
122            /* Not the same - swap out the info */
123
124            /* Delete existing info */
125            if ( cachedClass != null ) {
126                tossGlobalRef(env, &cachedClass);
127                cachedClass = null;
128            }
129            if ( sourceDebugExtension!=null ) {
130                jvmtiDeallocate(sourceDebugExtension);
131            }
132            sourceDebugExtension = null;
133
134            /* Init info */
135            lineTable = null;
136            fileTable = null;
137            stratumTable = null;
138            lineTableSize = 0;
139            fileTableSize = 0;
140            stratumTableSize = 0;
141            fileIndex = 0;
142            lineIndex = 0;
143            stratumIndex = 0;
144            currentFileId = 0;
145            defaultStratumId = null;
146            defaultStratumIndex = -1;
147            baseStratumIndex = -2; /* so as not to match -1 above */
148            sourceMapIsValid = JNI_FALSE;
149
150            if (getSourceDebugExtension(clazz, &sourceDebugExtension) ==
151                JVMTI_ERROR_NONE) {
152                sdePos = sourceDebugExtension;
153                if (setjmp(jmp_buf_env) == 0) {
154                    /* this is the initial (non-error) case, do parse */
155                    decode();
156                }
157            }
158
159            cachedClass = null;
160            saveGlobalRef(env, clazz, &cachedClass);
161        }
162    }
163
164    /* Return 1 if match, 0 if no match */
165    private int
166    patternMatch(char *classname, const char *pattern) {
167        int pattLen;
168        int compLen;
169        char *start;
170        int offset;
171
172        if (pattern == NULL || classname == NULL) {
173            return 0;
174        }
175        pattLen = (int)strlen(pattern);
176
177        if ((pattern[0] != '*') && (pattern[pattLen-1] != '*')) {
178            return strcmp(pattern, classname) == 0;
179        }
180
181        compLen = pattLen - 1;
182        offset = (int)strlen(classname) - compLen;
183        if (offset < 0) {
184            return 0;
185        }
186        if (pattern[0] == '*') {
187            pattern++;
188            start = classname + offset;
189        }  else {
190            start = classname;
191        }
192        return strncmp(pattern, start, compLen) == 0;
193    }
194
195    /**
196     * Return 1 if p1 is a SourceName for stratum sti,
197     * else, return 0.
198     */
199    private int
200    searchOneSourceName(int sti, char *p1) {
201        int fileIndexStart = stratumTable[sti].fileIndex;
202        /* one past end */
203        int fileIndexEnd = stratumTable[sti+1].fileIndex;
204        int ii;
205        for (ii = fileIndexStart; ii < fileIndexEnd; ++ii) {
206            if (patternMatch(fileTable[ii].sourceName, p1)) {
207              return 1;
208            }
209        }
210        return 0;
211    }
212
213    /**
214     * Return 1 if p1 is a SourceName for any stratum
215     * else, return 0.
216     */
217    int searchAllSourceNames(JNIEnv *env,
218                             jclass clazz,
219                             char *p1) {
220        int ii;
221        loadDebugInfo(env, clazz);
222        if (!isValid()) {
223          return 0; /* no SDE or not SourceMap */
224        }
225
226        for (ii = 0; ii < stratumIndex - 1; ++ii) {
227            if (searchOneSourceName(ii, p1) == 1) {
228                return 1;
229            }
230        }
231        return 0;
232    }
233
234    /**
235     * Convert a line number table, as returned by the JVMTI
236     * function GetLineNumberTable, to one for another stratum.
237     * Conversion is by overwrite.
238     * Actual line numbers are not returned - just a unique
239     * number (file ID in top 16 bits, line number in
240     * bottom 16 bits) - this is all stepping needs.
241     */
242    void
243    convertLineNumberTable(JNIEnv *env, jclass clazz,
244                           jint *entryCountPtr,
245                           jvmtiLineNumberEntry **tablePtr) {
246        jvmtiLineNumberEntry *fromEntry = *tablePtr;
247        jvmtiLineNumberEntry *toEntry = *tablePtr;
248        int cnt = *entryCountPtr;
249        int lastLn = 0;
250        int sti;
251
252        if (cnt < 0) {
253            return;
254        }
255        loadDebugInfo(env, clazz);
256        if (!isValid()) {
257            return; /* no SDE or not SourceMap - return unchanged */
258        }
259        sti = stratumTableIndex(globalDefaultStratumId);
260        if (sti == baseStratumIndex || sti < 0) {
261            return; /* Java stratum - return unchanged */
262        }
263        LOG_MISC(("SDE is re-ordering the line table"));
264        for (; cnt-- > 0; ++fromEntry) {
265            int jplsLine = fromEntry->line_number;
266            int lti = stiLineTableIndex(sti, jplsLine);
267            if (lti >= 0) {
268                int fileId = lineTable[lti].fileId;
269                int ln = stiLineNumber(sti, lti, jplsLine);
270                ln += (fileId << 16); /* create line hash */
271                if (ln != lastLn) {
272                    lastLn = ln;
273                    toEntry->start_location = fromEntry->start_location;
274                    toEntry->line_number = ln;
275                    ++toEntry;
276                }
277            }
278        }
279        /*LINTED*/
280        *entryCountPtr = (int)(toEntry - *tablePtr);
281    }
282
283    /**
284     * Set back-end wide default stratum ID .
285     */
286    void
287    setGlobalStratumId(char *id) {
288        globalDefaultStratumId = id;
289    }
290
291
292    private void syntax(String msg) {
293        char buf[200];
294        (void)snprintf(buf, sizeof(buf),
295                "bad SourceDebugExtension syntax - position %d - %s\n",
296                /*LINTED*/
297                (int)(sdePos-sourceDebugExtension),
298                msg);
299        JDI_ASSERT_FAILED(buf);
300
301        longjmp(jmp_buf_env, 1);  /* abort parse */
302    }
303
304    private char sdePeek(void) {
305        if (*sdePos == 0) {
306            syntax("unexpected EOF");
307        }
308        return *sdePos;
309    }
310
311    private char sdeRead(void) {
312        if (*sdePos == 0) {
313            syntax("unexpected EOF");
314        }
315        return *sdePos++;
316    }
317
318    private void sdeAdvance(void) {
319        sdePos++;
320    }
321
322    private void assureLineTableSize(void) {
323        if (lineIndex >= lineTableSize) {
324            size_t allocSize;
325            LineTableRecord* new_lineTable;
326            int new_lineTableSize;
327
328            new_lineTableSize = lineTableSize == 0?
329                                  INIT_SIZE_LINE :
330                                  lineTableSize * 2;
331            allocSize = new_lineTableSize * (int)sizeof(LineTableRecord);
332            new_lineTable = jvmtiAllocate((jint)allocSize);
333            if ( new_lineTable == NULL ) {
334                EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "SDE line table");
335            }
336            if ( lineTable!=NULL ) {
337                (void)memcpy(new_lineTable, lineTable,
338                        lineTableSize * (int)sizeof(LineTableRecord));
339                jvmtiDeallocate(lineTable);
340            }
341            lineTable     = new_lineTable;
342            lineTableSize = new_lineTableSize;
343        }
344    }
345
346    private void assureFileTableSize(void) {
347        if (fileIndex >= fileTableSize) {
348            size_t allocSize;
349            FileTableRecord* new_fileTable;
350            int new_fileTableSize;
351
352            new_fileTableSize = fileTableSize == 0?
353                                  INIT_SIZE_FILE :
354                                  fileTableSize * 2;
355            allocSize = new_fileTableSize * (int)sizeof(FileTableRecord);
356            new_fileTable = jvmtiAllocate((jint)allocSize);
357            if ( new_fileTable == NULL ) {
358                EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "SDE file table");
359            }
360            if ( fileTable!=NULL ) {
361                (void)memcpy(new_fileTable, fileTable,
362                        fileTableSize * (int)sizeof(FileTableRecord));
363                jvmtiDeallocate(fileTable);
364            }
365            fileTable     = new_fileTable;
366            fileTableSize = new_fileTableSize;
367        }
368    }
369
370    private void assureStratumTableSize(void) {
371        if (stratumIndex >= stratumTableSize) {
372            size_t allocSize;
373            StratumTableRecord* new_stratumTable;
374            int new_stratumTableSize;
375
376            new_stratumTableSize = stratumTableSize == 0?
377                                  INIT_SIZE_STRATUM :
378                                  stratumTableSize * 2;
379            allocSize = new_stratumTableSize * (int)sizeof(StratumTableRecord);
380            new_stratumTable = jvmtiAllocate((jint)allocSize);
381            if ( new_stratumTable == NULL ) {
382                EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "SDE stratum table");
383            }
384            if ( stratumTable!=NULL ) {
385                (void)memcpy(new_stratumTable, stratumTable,
386                        stratumTableSize * (int)sizeof(StratumTableRecord));
387                jvmtiDeallocate(stratumTable);
388            }
389            stratumTable     = new_stratumTable;
390            stratumTableSize = new_stratumTableSize;
391        }
392    }
393
394    private String readLine(void) {
395        char *initialPos;
396        char ch;
397
398        ignoreWhite();
399        initialPos = sdePos;
400        while (((ch = *sdePos) != '\n') && (ch != '\r')) {
401            if (ch == 0) {
402                syntax("unexpected EOF");
403            }
404            ++sdePos;
405        }
406        *sdePos++ = 0; /* null terminate string - mangles SDE */
407
408        /* check for CR LF */
409        if ((ch == '\r') && (*sdePos == '\n')) {
410            ++sdePos;
411        }
412        ignoreWhite(); /* leading white */
413        return initialPos;
414    }
415
416    private int defaultStratumTableIndex(void) {
417        if ((defaultStratumIndex == -1) && (defaultStratumId != null)) {
418            defaultStratumIndex =
419                stratumTableIndex(defaultStratumId);
420        }
421        return defaultStratumIndex;
422    }
423
424    private int stratumTableIndex(String stratumId) {
425        int i;
426
427        if (stratumId == null) {
428            return defaultStratumTableIndex();
429        }
430        for (i = 0; i < (stratumIndex-1); ++i) {
431            if (strcmp(stratumTable[i].id, stratumId) == 0) {
432                return i;
433            }
434        }
435        return defaultStratumTableIndex();
436    }
437
438
439/*****************************
440 * below functions/methods are written to compile under either Java or C
441 *
442 * Needed support functions:
443 *   sdePeek()
444 *   sdeRead()
445 *   sdeAdvance()
446 *   readLine()
447 *   assureLineTableSize()
448 *   assureFileTableSize()
449 *   assureStratumTableSize()
450 *   syntax(String)
451 *
452 *   stratumTableIndex(String)
453 *
454 * Needed support variables:
455 *   lineTable
456 *   lineIndex
457 *   fileTable
458 *   fileIndex
459 *   currentFileId
460 *
461 * Needed types:
462 *   String
463 *
464 * Needed constants:
465 *   NullString
466 */
467
468    private void ignoreWhite(void) {
469        char ch;
470
471        while (((ch = sdePeek()) == ' ') || (ch == '\t')) {
472            sdeAdvance();
473        }
474    }
475
476    private void ignoreLine(void) {
477        char ch;
478
479        do {
480           ch = sdeRead();
481        } while ((ch != '\n') && (ch != '\r'));
482
483        /* check for CR LF */
484        if ((ch == '\r') && (sdePeek() == '\n')) {
485            sdeAdvance();
486        }
487        ignoreWhite(); /* leading white */
488    }
489
490    private int readNumber(void) {
491        int value = 0;
492        char ch;
493
494        ignoreWhite();
495        while (((ch = sdePeek()) >= '0') && (ch <= '9')) {
496            sdeAdvance();
497            value = (value * 10) + ch - '0';
498        }
499        ignoreWhite();
500        return value;
501    }
502
503    private void storeFile(int fileId, String sourceName, String sourcePath) {
504        assureFileTableSize();
505        fileTable[fileIndex].fileId = fileId;
506        fileTable[fileIndex].sourceName = sourceName;
507        fileTable[fileIndex].sourcePath = sourcePath;
508        ++fileIndex;
509    }
510
511    private void fileLine(void) {
512        int hasAbsolute = 0; /* acts as boolean */
513        int fileId;
514        String sourceName;
515        String sourcePath = null;
516
517        /* is there an absolute filename? */
518        if (sdePeek() == '+') {
519            sdeAdvance();
520            hasAbsolute = 1;
521        }
522        fileId = readNumber();
523        sourceName = readLine();
524        if (hasAbsolute == 1) {
525            sourcePath = readLine();
526        }
527        storeFile(fileId, sourceName, sourcePath);
528    }
529
530    private void storeLine(int jplsStart, int jplsEnd, int jplsLineInc,
531                  int njplsStart, int njplsEnd, int fileId) {
532        assureLineTableSize();
533        lineTable[lineIndex].jplsStart = jplsStart;
534        lineTable[lineIndex].jplsEnd = jplsEnd;
535        lineTable[lineIndex].jplsLineInc = jplsLineInc;
536        lineTable[lineIndex].njplsStart = njplsStart;
537        lineTable[lineIndex].njplsEnd = njplsEnd;
538        lineTable[lineIndex].fileId = fileId;
539        ++lineIndex;
540    }
541
542    /**
543     * Parse line translation info.  Syntax is
544     *     <NJ-start-line> [ # <file-id> ] [ , <line-count> ] :
545     *                 <J-start-line> [ , <line-increment> ] CR
546     */
547    private void lineLine(void) {
548        int lineCount = 1;
549        int lineIncrement = 1;
550        int njplsStart;
551        int jplsStart;
552
553        njplsStart = readNumber();
554
555        /* is there a fileID? */
556        if (sdePeek() == '#') {
557            sdeAdvance();
558            currentFileId = readNumber();
559        }
560
561        /* is there a line count? */
562        if (sdePeek() == ',') {
563            sdeAdvance();
564            lineCount = readNumber();
565        }
566
567        if (sdeRead() != ':') {
568            syntax("expected ':'");
569        }
570        jplsStart = readNumber();
571        if (sdePeek() == ',') {
572            sdeAdvance();
573            lineIncrement = readNumber();
574        }
575        ignoreLine(); /* flush the rest */
576
577        storeLine(jplsStart,
578                  jplsStart + (lineCount * lineIncrement) -1,
579                  lineIncrement,
580                  njplsStart,
581                  njplsStart + lineCount -1,
582                  currentFileId);
583    }
584
585    /**
586     * Until the next stratum section, everything after this
587     * is in stratumId - so, store the current indicies.
588     */
589    private void storeStratum(String stratumId) {
590        /* remove redundant strata */
591        if (stratumIndex > 0) {
592            if ((stratumTable[stratumIndex-1].fileIndex
593                                            == fileIndex) &&
594                (stratumTable[stratumIndex-1].lineIndex
595                                            == lineIndex)) {
596                /* nothing changed overwrite it */
597                --stratumIndex;
598            }
599        }
600        /* store the results */
601        assureStratumTableSize();
602        stratumTable[stratumIndex].id = stratumId;
603        stratumTable[stratumIndex].fileIndex = fileIndex;
604        stratumTable[stratumIndex].lineIndex = lineIndex;
605        ++stratumIndex;
606        currentFileId = 0;
607    }
608
609    /**
610     * The beginning of a stratum's info
611     */
612    private void stratumSection(void) {
613        storeStratum(readLine());
614    }
615
616    private void fileSection(void) {
617        ignoreLine();
618        while (sdePeek() != '*') {
619            fileLine();
620        }
621    }
622
623    private void lineSection(void) {
624        ignoreLine();
625        while (sdePeek() != '*') {
626            lineLine();
627        }
628    }
629
630    /**
631     * Ignore a section we don't know about.
632     */
633    private void ignoreSection(void) {
634        ignoreLine();
635        while (sdePeek() != '*') {
636            ignoreLine();
637        }
638    }
639
640    /**
641     * A base "Java" stratum is always available, though
642     * it is not in the SourceDebugExtension.
643     * Create the base stratum.
644     */
645    private void createJavaStratum(void) {
646        baseStratumIndex = stratumIndex;
647        storeStratum(BASE_STRATUM_NAME);
648        storeFile(1, jplsFilename, NullString);
649        /* JPL line numbers cannot exceed 65535 */
650        storeLine(1, 65536, 1, 1, 65536, 1);
651        storeStratum("Aux"); /* in case they don't declare */
652    }
653
654    /**
655     * Decode a SourceDebugExtension which is in SourceMap format.
656     * This is the entry point into the recursive descent parser.
657     */
658    private void decode(void) {
659        /* check for "SMAP" - allow EOF if not ours */
660        if (strlen(sourceDebugExtension) <= 4 ||
661            (sdeRead() != 'S') ||
662            (sdeRead() != 'M') ||
663            (sdeRead() != 'A') ||
664            (sdeRead() != 'P')) {
665            return; /* not our info */
666        }
667        ignoreLine(); /* flush the rest */
668        jplsFilename = readLine();
669        defaultStratumId = readLine();
670        createJavaStratum();
671        while (1) {
672            if (sdeRead() != '*') {
673                syntax("expected '*'");
674            }
675            switch (sdeRead()) {
676                case 'S':
677                    stratumSection();
678                    break;
679                case 'F':
680                    fileSection();
681                    break;
682                case 'L':
683                    lineSection();
684                    break;
685                case 'E':
686                    /* set end points */
687                    storeStratum("*terminator*");
688                    sourceMapIsValid = JNI_TRUE;
689                    return;
690                default:
691                    ignoreSection();
692            }
693        }
694    }
695
696    /***************** query functions ***********************/
697
698    private int stiLineTableIndex(int sti, int jplsLine) {
699        int i;
700        int lineIndexStart;
701        int lineIndexEnd;
702
703        lineIndexStart = stratumTable[sti].lineIndex;
704        /* one past end */
705        lineIndexEnd = stratumTable[sti+1].lineIndex;
706        for (i = lineIndexStart; i < lineIndexEnd; ++i) {
707            if ((jplsLine >= lineTable[i].jplsStart) &&
708                            (jplsLine <= lineTable[i].jplsEnd)) {
709                return i;
710            }
711        }
712        return -1;
713    }
714
715    private int stiLineNumber(int sti, int lti, int jplsLine) {
716        return lineTable[lti].njplsStart +
717                (((jplsLine - lineTable[lti].jplsStart) /
718                                   lineTable[lti].jplsLineInc));
719    }
720
721    private int fileTableIndex(int sti, int fileId) {
722        int i;
723        int fileIndexStart = stratumTable[sti].fileIndex;
724        /* one past end */
725        int fileIndexEnd = stratumTable[sti+1].fileIndex;
726        for (i = fileIndexStart; i < fileIndexEnd; ++i) {
727            if (fileTable[i].fileId == fileId) {
728                return i;
729            }
730        }
731        return -1;
732    }
733
734    private int stiFileTableIndex(int sti, int lti) {
735        return fileTableIndex(sti, lineTable[lti].fileId);
736    }
737
738    private jboolean isValid(void) {
739        return sourceMapIsValid;
740    }
741