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