1 /*
2  * Copyright (c) 2001, 2019, 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 package com.sun.tools.javac.jvm;
27 
28 import java.util.*;
29 
30 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
31 import com.sun.tools.javac.tree.*;
32 import com.sun.tools.javac.util.*;
33 import com.sun.tools.javac.util.List;
34 import com.sun.tools.javac.tree.JCTree.*;
35 import com.sun.tools.javac.tree.EndPosTable;
36 import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
37 
38 /** This class contains the CharacterRangeTable for some method
39  *  and the hashtable for mapping trees or lists of trees to their
40  *  ending positions.
41  *
42  *  <p><b>This is NOT part of any supported API.
43  *  If you write code that depends on this, you do so at your own risk.
44  *  This code and its internal interfaces are subject to change or
45  *  deletion without notice.</b>
46  */
47 public class CRTable
48 implements CRTFlags {
49 
50     private final boolean crtDebug = false;
51 
52     /** The list of CRTable entries.
53      */
54     private ListBuffer<CRTEntry> entries = new ListBuffer<>();
55 
56     /** The hashtable for source positions.
57      */
58     private Map<Object,SourceRange> positions = new HashMap<>();
59 
60     /** The object for ending positions stored in the parser.
61      */
62     private EndPosTable endPosTable;
63 
64     /** The tree of the method this table is intended for.
65      *  We should traverse this tree to get source ranges.
66      */
67     JCTree.JCMethodDecl methodTree;
68 
69     /** Constructor
70      */
CRTable(JCTree.JCMethodDecl tree, EndPosTable endPosTable)71     public CRTable(JCTree.JCMethodDecl tree, EndPosTable endPosTable) {
72         this.methodTree = tree;
73         this.endPosTable = endPosTable;
74     }
75 
76     /** Create a new CRTEntry and add it to the entries.
77      *  @param tree     The tree or the list of trees for which
78      *                  we are storing the code pointers.
79      *  @param flags    The set of flags designating type of the entry.
80      *  @param startPc  The starting code position.
81      *  @param endPc    The ending code position.
82      */
put(Object tree, int flags, int startPc, int endPc)83     public void put(Object tree, int flags, int startPc, int endPc) {
84         entries.append(new CRTEntry(tree, flags, startPc, endPc));
85     }
86 
87     /** Compute source positions and write CRT to the databuf.
88      *  @param databuf  The buffer to write bytecodes to.
89      */
writeCRT(ByteBuffer databuf, Position.LineMap lineMap, Log log)90     public int writeCRT(ByteBuffer databuf, Position.LineMap lineMap, Log log) {
91 
92         int crtEntries = 0;
93 
94         // compute source positions for the method
95         new SourceComputer().csp(methodTree);
96 
97         for (List<CRTEntry> l = entries.toList(); l.nonEmpty(); l = l.tail) {
98 
99             CRTEntry entry = l.head;
100 
101             // eliminate entries that do not produce bytecodes:
102             // for example, empty blocks and statements
103             if (entry.startPc == entry.endPc)
104                 continue;
105 
106             SourceRange pos = positions.get(entry.tree);
107             Assert.checkNonNull(pos, "CRT: tree source positions are undefined");
108             if ((pos.startPos == Position.NOPOS) || (pos.endPos == Position.NOPOS))
109                 continue;
110 
111             if (crtDebug) {
112                 System.out.println("Tree: " + entry.tree + ", type:" + getTypes(entry.flags));
113                 System.out.print("Start: pos = " + pos.startPos + ", pc = " + entry.startPc);
114             }
115 
116             // encode startPos into line/column representation
117             int startPos = encodePosition(pos.startPos, lineMap, log);
118             if (startPos == Position.NOPOS)
119                 continue;
120 
121             if (crtDebug) {
122                 System.out.print("End:   pos = " + pos.endPos + ", pc = " + (entry.endPc - 1));
123             }
124 
125             // encode endPos into line/column representation
126             int endPos = encodePosition(pos.endPos, lineMap, log);
127             if (endPos == Position.NOPOS)
128                 continue;
129 
130             // write attribute
131             databuf.appendChar(entry.startPc);
132             // 'endPc - 1' because endPc actually points to start of the next command
133             databuf.appendChar(entry.endPc - 1);
134             databuf.appendInt(startPos);
135             databuf.appendInt(endPos);
136             databuf.appendChar(entry.flags);
137 
138             crtEntries++;
139         }
140 
141         return crtEntries;
142     }
143 
144     /** Return the number of the entries.
145      */
length()146     public int length() {
147         return entries.length();
148     }
149 
150     /** Return string describing flags enabled.
151      */
getTypes(int flags)152     private String getTypes(int flags) {
153         String types = "";
154         if ((flags & CRT_STATEMENT)       != 0) types += " CRT_STATEMENT";
155         if ((flags & CRT_BLOCK)           != 0) types += " CRT_BLOCK";
156         if ((flags & CRT_ASSIGNMENT)      != 0) types += " CRT_ASSIGNMENT";
157         if ((flags & CRT_FLOW_CONTROLLER) != 0) types += " CRT_FLOW_CONTROLLER";
158         if ((flags & CRT_FLOW_TARGET)     != 0) types += " CRT_FLOW_TARGET";
159         if ((flags & CRT_INVOKE)          != 0) types += " CRT_INVOKE";
160         if ((flags & CRT_CREATE)          != 0) types += " CRT_CREATE";
161         if ((flags & CRT_BRANCH_TRUE)     != 0) types += " CRT_BRANCH_TRUE";
162         if ((flags & CRT_BRANCH_FALSE)    != 0) types += " CRT_BRANCH_FALSE";
163         return types;
164     }
165 
166     /** Source file positions in CRT are integers in the format:
167      *  {@literal line-number << LINESHIFT + column-number }
168      */
encodePosition(int pos, Position.LineMap lineMap, Log log)169      private int encodePosition(int pos, Position.LineMap lineMap, Log log) {
170          int line = lineMap.getLineNumber(pos);
171          int col = lineMap.getColumnNumber(pos);
172          int new_pos = Position.encodePosition(line, col);
173          if (crtDebug) {
174              System.out.println(", line = " + line + ", column = " + col +
175                                 ", new_pos = " + new_pos);
176          }
177          if (new_pos == Position.NOPOS)
178              log.warning(pos, Warnings.PositionOverflow(line));
179 
180         return new_pos;
181      }
182 
183 /* ************************************************************************
184  * Traversal methods
185  *************************************************************************/
186 
187     /**
188      *  This class contains methods to compute source positions for trees.
189      *  Extends Tree.Visitor to traverse the abstract syntax tree.
190      */
191     class SourceComputer extends JCTree.Visitor {
192 
193         /** The result of the tree traversal methods.
194          */
195         SourceRange result;
196 
197         /** Visitor method: compute source positions for a single node.
198          */
csp(JCTree tree)199         public SourceRange csp(JCTree tree) {
200             if (tree == null) return null;
201             tree.accept(this);
202             if (result != null) {
203                 positions.put(tree, result);
204             }
205             return result;
206         }
207 
208         /** Visitor method: compute source positions for a list of nodes.
209          */
csp(List<? extends JCTree> trees)210         public SourceRange csp(List<? extends JCTree> trees) {
211             if ((trees == null) || !(trees.nonEmpty())) return null;
212             SourceRange list_sr = new SourceRange();
213             for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
214                 list_sr.mergeWith(csp(l.head));
215             }
216             positions.put(trees, list_sr);
217             return list_sr;
218         }
219 
220         /**  Visitor method: compute source positions for
221          *    a list of case blocks of switch statements.
222          */
cspCases(List<JCCase> trees)223         public SourceRange cspCases(List<JCCase> trees) {
224             if ((trees == null) || !(trees.nonEmpty())) return null;
225             SourceRange list_sr = new SourceRange();
226             for (List<JCCase> l = trees; l.nonEmpty(); l = l.tail) {
227                 list_sr.mergeWith(csp(l.head));
228             }
229             positions.put(trees, list_sr);
230             return list_sr;
231         }
232 
233         /**  Visitor method: compute source positions for
234          *   a list of catch clauses in try statements.
235          */
cspCatchers(List<JCCatch> trees)236         public SourceRange cspCatchers(List<JCCatch> trees) {
237             if ((trees == null) || !(trees.nonEmpty())) return null;
238             SourceRange list_sr = new SourceRange();
239             for (List<JCCatch> l = trees; l.nonEmpty(); l = l.tail) {
240                 list_sr.mergeWith(csp(l.head));
241             }
242             positions.put(trees, list_sr);
243             return list_sr;
244         }
245 
visitMethodDef(JCMethodDecl tree)246         public void visitMethodDef(JCMethodDecl tree) {
247             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
248             sr.mergeWith(csp(tree.body));
249             result = sr;
250         }
251 
visitVarDef(JCVariableDecl tree)252         public void visitVarDef(JCVariableDecl tree) {
253             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
254             csp(tree.vartype);
255             sr.mergeWith(csp(tree.init));
256             result = sr;
257         }
258 
visitSkip(JCSkip tree)259         public void visitSkip(JCSkip tree) {
260             // endPos is the same as startPos for the empty statement
261             SourceRange sr = new SourceRange(startPos(tree), startPos(tree));
262             result = sr;
263         }
264 
visitBlock(JCBlock tree)265         public void visitBlock(JCBlock tree) {
266             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
267             csp(tree.stats);    // doesn't compare because block's ending position is defined
268             result = sr;
269         }
270 
visitDoLoop(JCDoWhileLoop tree)271         public void visitDoLoop(JCDoWhileLoop tree) {
272             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
273             sr.mergeWith(csp(tree.body));
274             sr.mergeWith(csp(tree.cond));
275             result = sr;
276         }
277 
visitWhileLoop(JCWhileLoop tree)278         public void visitWhileLoop(JCWhileLoop tree) {
279             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
280             sr.mergeWith(csp(tree.cond));
281             sr.mergeWith(csp(tree.body));
282             result = sr;
283         }
284 
visitForLoop(JCForLoop tree)285         public void visitForLoop(JCForLoop tree) {
286             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
287             sr.mergeWith(csp(tree.init));
288             sr.mergeWith(csp(tree.cond));
289             sr.mergeWith(csp(tree.step));
290             sr.mergeWith(csp(tree.body));
291             result = sr;
292         }
293 
visitForeachLoop(JCEnhancedForLoop tree)294         public void visitForeachLoop(JCEnhancedForLoop tree) {
295             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
296             sr.mergeWith(csp(tree.var));
297             sr.mergeWith(csp(tree.expr));
298             sr.mergeWith(csp(tree.body));
299             result = sr;
300         }
301 
visitLabelled(JCLabeledStatement tree)302         public void visitLabelled(JCLabeledStatement tree) {
303             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
304             sr.mergeWith(csp(tree.body));
305             result = sr;
306         }
307 
visitSwitch(JCSwitch tree)308         public void visitSwitch(JCSwitch tree) {
309             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
310             sr.mergeWith(csp(tree.selector));
311             sr.mergeWith(cspCases(tree.cases));
312             result = sr;
313         }
314 
315         @Override
visitSwitchExpression(JCSwitchExpression tree)316         public void visitSwitchExpression(JCSwitchExpression tree) {
317             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
318             sr.mergeWith(csp(tree.selector));
319             sr.mergeWith(cspCases(tree.cases));
320             result = sr;
321         }
322 
visitCase(JCCase tree)323         public void visitCase(JCCase tree) {
324             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
325             sr.mergeWith(csp(tree.pats));
326             sr.mergeWith(csp(tree.stats));
327             result = sr;
328         }
329 
visitSynchronized(JCSynchronized tree)330         public void visitSynchronized(JCSynchronized tree) {
331             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
332             sr.mergeWith(csp(tree.lock));
333             sr.mergeWith(csp(tree.body));
334             result = sr;
335         }
336 
visitTry(JCTry tree)337         public void visitTry(JCTry tree) {
338             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
339             sr.mergeWith(csp(tree.resources));
340             sr.mergeWith(csp(tree.body));
341             sr.mergeWith(cspCatchers(tree.catchers));
342             sr.mergeWith(csp(tree.finalizer));
343             result = sr;
344         }
345 
visitCatch(JCCatch tree)346         public void visitCatch(JCCatch tree) {
347             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
348             sr.mergeWith(csp(tree.param));
349             sr.mergeWith(csp(tree.body));
350             result = sr;
351         }
352 
visitConditional(JCConditional tree)353         public void visitConditional(JCConditional tree) {
354             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
355             sr.mergeWith(csp(tree.cond));
356             sr.mergeWith(csp(tree.truepart));
357             sr.mergeWith(csp(tree.falsepart));
358             result = sr;
359         }
360 
visitIf(JCIf tree)361         public void visitIf(JCIf tree) {
362             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
363             sr.mergeWith(csp(tree.cond));
364             sr.mergeWith(csp(tree.thenpart));
365             sr.mergeWith(csp(tree.elsepart));
366             result = sr;
367         }
368 
visitExec(JCExpressionStatement tree)369         public void visitExec(JCExpressionStatement tree) {
370             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
371             sr.mergeWith(csp(tree.expr));
372             result = sr;
373         }
374 
visitBreak(JCBreak tree)375         public void visitBreak(JCBreak tree) {
376             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
377             result = sr;
378         }
379 
visitYield(JCYield tree)380         public void visitYield(JCYield tree) {
381             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
382             result = sr;
383         }
384 
visitContinue(JCContinue tree)385         public void visitContinue(JCContinue tree) {
386             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
387             result = sr;
388         }
389 
visitReturn(JCReturn tree)390         public void visitReturn(JCReturn tree) {
391             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
392             sr.mergeWith(csp(tree.expr));
393             result = sr;
394         }
395 
visitThrow(JCThrow tree)396         public void visitThrow(JCThrow tree) {
397             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
398             sr.mergeWith(csp(tree.expr));
399             result = sr;
400         }
401 
visitAssert(JCAssert tree)402         public void visitAssert(JCAssert tree) {
403             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
404             sr.mergeWith(csp(tree.cond));
405             sr.mergeWith(csp(tree.detail));
406             result = sr;
407         }
408 
visitApply(JCMethodInvocation tree)409         public void visitApply(JCMethodInvocation tree) {
410             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
411             sr.mergeWith(csp(tree.meth));
412             sr.mergeWith(csp(tree.args));
413             result = sr;
414         }
415 
visitNewClass(JCNewClass tree)416         public void visitNewClass(JCNewClass tree) {
417             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
418             sr.mergeWith(csp(tree.encl));
419             sr.mergeWith(csp(tree.clazz));
420             sr.mergeWith(csp(tree.args));
421             sr.mergeWith(csp(tree.def));
422             result = sr;
423         }
424 
visitNewArray(JCNewArray tree)425         public void visitNewArray(JCNewArray tree) {
426             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
427             sr.mergeWith(csp(tree.elemtype));
428             sr.mergeWith(csp(tree.dims));
429             sr.mergeWith(csp(tree.elems));
430             result = sr;
431         }
432 
visitParens(JCParens tree)433         public void visitParens(JCParens tree) {
434             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
435             sr.mergeWith(csp(tree.expr));
436             result = sr;
437         }
438 
visitAssign(JCAssign tree)439         public void visitAssign(JCAssign tree) {
440             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
441             sr.mergeWith(csp(tree.lhs));
442             sr.mergeWith(csp(tree.rhs));
443             result = sr;
444         }
445 
visitAssignop(JCAssignOp tree)446         public void visitAssignop(JCAssignOp tree) {
447             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
448             sr.mergeWith(csp(tree.lhs));
449             sr.mergeWith(csp(tree.rhs));
450             result = sr;
451         }
452 
visitUnary(JCUnary tree)453         public void visitUnary(JCUnary tree) {
454             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
455             sr.mergeWith(csp(tree.arg));
456             result = sr;
457         }
458 
visitBinary(JCBinary tree)459         public void visitBinary(JCBinary tree) {
460             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
461             sr.mergeWith(csp(tree.lhs));
462             sr.mergeWith(csp(tree.rhs));
463             result = sr;
464         }
465 
visitTypeCast(JCTypeCast tree)466         public void visitTypeCast(JCTypeCast tree) {
467             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
468             sr.mergeWith(csp(tree.clazz));
469             sr.mergeWith(csp(tree.expr));
470             result = sr;
471         }
472 
visitTypeTest(JCInstanceOf tree)473         public void visitTypeTest(JCInstanceOf tree) {
474             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
475             sr.mergeWith(csp(tree.expr));
476             sr.mergeWith(csp(tree.clazz));
477             result = sr;
478         }
479 
visitIndexed(JCArrayAccess tree)480         public void visitIndexed(JCArrayAccess tree) {
481             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
482             sr.mergeWith(csp(tree.indexed));
483             sr.mergeWith(csp(tree.index));
484             result = sr;
485         }
486 
visitSelect(JCFieldAccess tree)487         public void visitSelect(JCFieldAccess tree) {
488             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
489             sr.mergeWith(csp(tree.selected));
490             result = sr;
491         }
492 
visitIdent(JCIdent tree)493         public void visitIdent(JCIdent tree) {
494             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
495             result = sr;
496         }
497 
visitLiteral(JCLiteral tree)498         public void visitLiteral(JCLiteral tree) {
499             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
500             result = sr;
501         }
502 
visitTypeIdent(JCPrimitiveTypeTree tree)503         public void visitTypeIdent(JCPrimitiveTypeTree tree) {
504             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
505             result = sr;
506         }
507 
visitTypeArray(JCArrayTypeTree tree)508         public void visitTypeArray(JCArrayTypeTree tree) {
509             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
510             sr.mergeWith(csp(tree.elemtype));
511             result = sr;
512         }
513 
visitTypeApply(JCTypeApply tree)514         public void visitTypeApply(JCTypeApply tree) {
515             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
516             sr.mergeWith(csp(tree.clazz));
517             sr.mergeWith(csp(tree.arguments));
518             result = sr;
519         }
520 
521         @Override
visitLetExpr(LetExpr tree)522         public void visitLetExpr(LetExpr tree) {
523             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
524             sr.mergeWith(csp(tree.defs));
525             sr.mergeWith(csp(tree.expr));
526             result = sr;
527         }
528 
visitTypeParameter(JCTypeParameter tree)529         public void visitTypeParameter(JCTypeParameter tree) {
530             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
531             sr.mergeWith(csp(tree.bounds));
532             result = sr;
533         }
534 
535         @Override
visitTypeUnion(JCTypeUnion tree)536         public void visitTypeUnion(JCTypeUnion tree) {
537             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
538             sr.mergeWith(csp(tree.alternatives));
539             result = sr;
540         }
541 
visitWildcard(JCWildcard tree)542         public void visitWildcard(JCWildcard tree) {
543             result = null;
544         }
545 
visitErroneous(JCErroneous tree)546         public void visitErroneous(JCErroneous tree) {
547             result = null;
548         }
549 
visitTree(JCTree tree)550         public void visitTree(JCTree tree) {
551             Assert.error();
552         }
553 
554         /** The start position of given tree.
555          */
startPos(JCTree tree)556         public int startPos(JCTree tree) {
557             if (tree == null) return Position.NOPOS;
558             return TreeInfo.getStartPos(tree);
559         }
560 
561         /** The end position of given tree, if it has
562          *  defined endpos, NOPOS otherwise.
563          */
endPos(JCTree tree)564         public int endPos(JCTree tree) {
565             if (tree == null) return Position.NOPOS;
566             return TreeInfo.getEndPos(tree, endPosTable);
567         }
568     }
569 
570     /** This class contains a CharacterRangeTableEntry.
571      */
572     static class CRTEntry {
573 
574         /** A tree or a list of trees to obtain source positions.
575          */
576         Object tree;
577 
578         /** The flags described in the CharacterRangeTable spec.
579          */
580         int flags;
581 
582         /** The starting code position of this entry.
583          */
584         int startPc;
585 
586         /** The ending code position of this entry.
587          */
588         int endPc;
589 
590         /** Constructor */
CRTEntry(Object tree, int flags, int startPc, int endPc)591         CRTEntry(Object tree, int flags, int startPc, int endPc) {
592             this.tree = tree;
593             this.flags = flags;
594             this.startPc = startPc;
595             this.endPc = endPc;
596         }
597     }
598 
599 
600     /** This class contains source positions
601      *  for some tree or list of trees.
602      */
603     static class SourceRange {
604 
605         /** The starting source position.
606          */
607         int startPos;
608 
609         /** The ending source position.
610          */
611         int endPos;
612 
613         /** Constructor */
SourceRange()614         SourceRange() {
615             startPos = Position.NOPOS;
616             endPos = Position.NOPOS;
617         }
618 
619         /** Constructor */
SourceRange(int startPos, int endPos)620         SourceRange(int startPos, int endPos) {
621             this.startPos = startPos;
622             this.endPos = endPos;
623         }
624 
625         /** Compare the starting and the ending positions
626          *  of the source range and combines them assigning
627          *  the widest range to this.
628          */
mergeWith(SourceRange sr)629         SourceRange mergeWith(SourceRange sr) {
630             if (sr == null) return this;
631             if (startPos == Position.NOPOS)
632                 startPos = sr.startPos;
633             else if (sr.startPos != Position.NOPOS)
634                 startPos = (startPos < sr.startPos ? startPos : sr.startPos);
635             if (endPos == Position.NOPOS)
636                 endPos = sr.endPos;
637             else if (sr.endPos != Position.NOPOS)
638                 endPos = (endPos > sr.endPos ? endPos : sr.endPos);
639             return this;
640         }
641     }
642 
643 }
644