• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later,
9  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */
16 
17 package javassist.bytecode;
18 
19 import java.util.Map;
20 
21 import javassist.ClassPool;
22 import javassist.CtClass;
23 import javassist.CtPrimitiveType;
24 import javassist.NotFoundException;
25 
26 /**
27  * A support class for dealing with descriptors.
28  *
29  * <p>See chapter 4.3 in "The Java Virtual Machine Specification (2nd ed.)"
30  */
31 public class Descriptor {
32     /**
33      * Converts a class name into the internal representation used in
34      * the JVM.
35      *
36      * <p>Note that <code>toJvmName(toJvmName(s))</code> is equivalent
37      * to <code>toJvmName(s)</code>.
38      */
toJvmName(String classname)39     public static String toJvmName(String classname) {
40         return classname.replace('.', '/');
41     }
42 
43     /**
44      * Converts a class name from the internal representation used in
45      * the JVM to the normal one used in Java.
46      * This method does not deal with an array type name such as
47      * "[Ljava/lang/Object;" and "[I;".  For such names, use
48      * <code>toClassName()</code>.
49      *
50      * @see #toClassName(String)
51      */
toJavaName(String classname)52     public static String toJavaName(String classname) {
53         return classname.replace('/', '.');
54     }
55 
56     /**
57      * Returns the internal representation of the class name in the
58      * JVM.
59      */
toJvmName(CtClass clazz)60     public static String toJvmName(CtClass clazz) {
61         if (clazz.isArray())
62             return of(clazz);
63         return toJvmName(clazz.getName());
64     }
65 
66     /**
67      * Converts to a Java class name from a descriptor.
68      *
69      * @param descriptor        type descriptor.
70      */
toClassName(String descriptor)71     public static String toClassName(String descriptor) {
72         int arrayDim = 0;
73         int i = 0;
74         char c = descriptor.charAt(0);
75         while (c == '[') {
76             ++arrayDim;
77             c = descriptor.charAt(++i);
78         }
79 
80         String name;
81         if (c == 'L') {
82             int i2 = descriptor.indexOf(';', i++);
83             name = descriptor.substring(i, i2).replace('/', '.');
84             i = i2;
85         }
86         else if (c == 'V')
87             name =  "void";
88         else if (c == 'I')
89             name = "int";
90         else if (c == 'B')
91             name = "byte";
92         else if (c == 'J')
93             name = "long";
94         else if (c == 'D')
95             name = "double";
96         else if (c == 'F')
97             name = "float";
98         else if (c == 'C')
99             name = "char";
100         else if (c == 'S')
101             name = "short";
102         else if (c == 'Z')
103             name = "boolean";
104         else
105             throw new RuntimeException("bad descriptor: " + descriptor);
106 
107         if (i + 1 != descriptor.length())
108             throw new RuntimeException("multiple descriptors?: " + descriptor);
109 
110         if (arrayDim == 0)
111             return name;
112         StringBuffer sbuf = new StringBuffer(name);
113         do {
114             sbuf.append("[]");
115         } while (--arrayDim > 0);
116 
117         return sbuf.toString();
118     }
119 
120     /**
121      * Converts to a descriptor from a Java class name
122      */
of(String classname)123     public static String of(String classname) {
124         if (classname.equals("void"))
125             return "V";
126         else if (classname.equals("int"))
127             return "I";
128         else if (classname.equals("byte"))
129             return "B";
130         else if (classname.equals("long"))
131             return "J";
132         else if (classname.equals("double"))
133             return "D";
134         else if (classname.equals("float"))
135             return "F";
136         else if (classname.equals("char"))
137             return "C";
138         else if (classname.equals("short"))
139             return "S";
140         else if (classname.equals("boolean"))
141             return "Z";
142         else
143             return "L" + toJvmName(classname) + ";";
144     }
145 
146     /**
147      * Substitutes a class name
148      * in the given descriptor string.
149      *
150      * @param desc    descriptor string
151      * @param oldname replaced JVM class name
152      * @param newname substituted JVM class name
153      *
154      * @see Descriptor#toJvmName(String)
155      */
rename(String desc, String oldname, String newname)156     public static String rename(String desc, String oldname, String newname) {
157         if (desc.indexOf(oldname) < 0)
158             return desc;
159 
160         StringBuffer newdesc = new StringBuffer();
161         int head = 0;
162         int i = 0;
163         for (;;) {
164             int j = desc.indexOf('L', i);
165             if (j < 0)
166                 break;
167             else if (desc.startsWith(oldname, j + 1)
168                      && desc.charAt(j + oldname.length() + 1) == ';') {
169                 newdesc.append(desc.substring(head, j));
170                 newdesc.append('L');
171                 newdesc.append(newname);
172                 newdesc.append(';');
173                 head = i = j + oldname.length() + 2;
174             }
175             else {
176                 i = desc.indexOf(';', j) + 1;
177                 if (i < 1)
178                     break; // ';' was not found.
179             }
180         }
181 
182         if (head == 0)
183             return desc;
184         int len = desc.length();
185         if (head < len)
186             newdesc.append(desc.substring(head, len));
187 
188         return newdesc.toString();
189     }
190 
191     /**
192      * Substitutes class names in the given descriptor string
193      * according to the given <code>map</code>.
194      *
195      * @param map a map between replaced and substituted
196      *            JVM class names.
197      * @see Descriptor#toJvmName(String)
198      */
rename(String desc, Map<String,String> map)199     public static String rename(String desc, Map<String,String> map) {
200         if (map == null)
201             return desc;
202 
203         StringBuffer newdesc = new StringBuffer();
204         int head = 0;
205         int i = 0;
206         for (;;) {
207             int j = desc.indexOf('L', i);
208             if (j < 0)
209                 break;
210 
211             int k = desc.indexOf(';', j);
212             if (k < 0)
213                 break;
214 
215             i = k + 1;
216             String name = desc.substring(j + 1, k);
217             String name2 = map.get(name);
218             if (name2 != null) {
219                 newdesc.append(desc.substring(head, j));
220                 newdesc.append('L');
221                 newdesc.append(name2);
222                 newdesc.append(';');
223                 head = i;
224             }
225         }
226 
227         if (head == 0)
228             return desc;
229         int len = desc.length();
230         if (head < len)
231             newdesc.append(desc.substring(head, len));
232 
233         return newdesc.toString();
234     }
235 
236     /**
237      * Returns the descriptor representing the given type.
238      */
of(CtClass type)239     public static String of(CtClass type) {
240         StringBuffer sbuf = new StringBuffer();
241         toDescriptor(sbuf, type);
242         return sbuf.toString();
243     }
244 
toDescriptor(StringBuffer desc, CtClass type)245     private static void toDescriptor(StringBuffer desc, CtClass type) {
246         if (type.isArray()) {
247             desc.append('[');
248             try {
249                 toDescriptor(desc, type.getComponentType());
250             }
251             catch (NotFoundException e) {
252                 desc.append('L');
253                 String name = type.getName();
254                 desc.append(toJvmName(name.substring(0, name.length() - 2)));
255                 desc.append(';');
256             }
257         }
258         else if (type.isPrimitive()) {
259             CtPrimitiveType pt = (CtPrimitiveType)type;
260             desc.append(pt.getDescriptor());
261         }
262         else { // class type
263             desc.append('L');
264             desc.append(type.getName().replace('.', '/'));
265             desc.append(';');
266         }
267     }
268 
269     /**
270      * Returns the descriptor representing a constructor receiving
271      * the given parameter types.
272      *
273      * @param paramTypes parameter types
274      */
ofConstructor(CtClass[] paramTypes)275     public static String ofConstructor(CtClass[] paramTypes) {
276         return ofMethod(CtClass.voidType, paramTypes);
277     }
278 
279     /**
280      * Returns the descriptor representing a method that receives
281      * the given parameter types and returns the given type.
282      *
283      * @param returnType return type
284      * @param paramTypes parameter types
285      */
ofMethod(CtClass returnType, CtClass[] paramTypes)286     public static String ofMethod(CtClass returnType, CtClass[] paramTypes) {
287         StringBuffer desc = new StringBuffer();
288         desc.append('(');
289         if (paramTypes != null) {
290             int n = paramTypes.length;
291             for (int i = 0; i < n; ++i)
292                 toDescriptor(desc, paramTypes[i]);
293         }
294 
295         desc.append(')');
296         if (returnType != null)
297             toDescriptor(desc, returnType);
298 
299         return desc.toString();
300     }
301 
302     /**
303      * Returns the descriptor representing a list of parameter types.
304      * For example, if the given parameter types are two <code>int</code>,
305      * then this method returns <code>"(II)"</code>.
306      *
307      * @param paramTypes parameter types
308      */
ofParameters(CtClass[] paramTypes)309     public static String ofParameters(CtClass[] paramTypes) {
310         return ofMethod(null, paramTypes);
311     }
312 
313     /**
314      * Appends a parameter type to the parameter list represented
315      * by the given descriptor.
316      *
317      * <p><code>classname</code> must not be an array type.
318      *
319      * @param classname parameter type (not primitive type)
320      * @param desc      descriptor
321      */
appendParameter(String classname, String desc)322     public static String appendParameter(String classname, String desc) {
323         int i = desc.indexOf(')');
324         if (i < 0)
325             return desc;
326         StringBuffer newdesc = new StringBuffer();
327         newdesc.append(desc.substring(0, i));
328         newdesc.append('L');
329         newdesc.append(classname.replace('.', '/'));
330         newdesc.append(';');
331         newdesc.append(desc.substring(i));
332         return newdesc.toString();
333     }
334 
335     /**
336      * Inserts a parameter type at the beginning of the parameter
337      * list represented
338      * by the given descriptor.
339      *
340      * <p><code>classname</code> must not be an array type.
341      *
342      * @param classname parameter type (not primitive type)
343      * @param desc      descriptor
344      */
insertParameter(String classname, String desc)345     public static String insertParameter(String classname, String desc) {
346         if (desc.charAt(0) != '(')
347             return desc;
348         return "(L" + classname.replace('.', '/') + ';'
349                + desc.substring(1);
350     }
351 
352     /**
353      * Appends a parameter type to the parameter list represented
354      * by the given descriptor.  The appended parameter becomes
355      * the last parameter.
356      *
357      * @param type      the type of the appended parameter.
358      * @param descriptor      the original descriptor.
359      */
appendParameter(CtClass type, String descriptor)360     public static String appendParameter(CtClass type, String descriptor) {
361         int i = descriptor.indexOf(')');
362         if (i < 0)
363             return descriptor;
364         StringBuffer newdesc = new StringBuffer();
365         newdesc.append(descriptor.substring(0, i));
366         toDescriptor(newdesc, type);
367         newdesc.append(descriptor.substring(i));
368         return newdesc.toString();
369     }
370 
371     /**
372      * Inserts a parameter type at the beginning of the parameter
373      * list represented
374      * by the given descriptor.
375      *
376      * @param type              the type of the inserted parameter.
377      * @param descriptor        the descriptor of the method.
378      */
insertParameter(CtClass type, String descriptor)379     public static String insertParameter(CtClass type,
380                                          String descriptor) {
381         if (descriptor.charAt(0) != '(')
382             return descriptor;
383         return "(" + of(type) + descriptor.substring(1);
384     }
385 
386     /**
387      * Changes the return type included in the given descriptor.
388      *
389      * <p><code>classname</code> must not be an array type.
390      *
391      * @param classname return type
392      * @param desc      descriptor
393      */
changeReturnType(String classname, String desc)394     public static String changeReturnType(String classname, String desc) {
395         int i = desc.indexOf(')');
396         if (i < 0)
397             return desc;
398         StringBuffer newdesc = new StringBuffer();
399         newdesc.append(desc.substring(0, i + 1));
400         newdesc.append('L');
401         newdesc.append(classname.replace('.', '/'));
402         newdesc.append(';');
403         return newdesc.toString();
404     }
405 
406     /**
407      * Returns the <code>CtClass</code> objects representing the parameter
408      * types specified by the given descriptor.
409      *
410      * @param desc descriptor
411      * @param cp   the class pool used for obtaining
412      *             a <code>CtClass</code> object.
413      */
getParameterTypes(String desc, ClassPool cp)414     public static CtClass[] getParameterTypes(String desc, ClassPool cp)
415         throws NotFoundException
416     {
417         if (desc.charAt(0) != '(')
418             return null;
419         int num = numOfParameters(desc);
420         CtClass[] args = new CtClass[num];
421         int n = 0;
422         int i = 1;
423         do {
424             i = toCtClass(cp, desc, i, args, n++);
425         } while (i > 0);
426         return args;
427     }
428 
429     /**
430      * Returns true if the list of the parameter types of desc1 is equal to
431      * that of desc2.
432      * For example, "(II)V" and "(II)I" are equal.
433      */
eqParamTypes(String desc1, String desc2)434     public static boolean eqParamTypes(String desc1, String desc2) {
435         if (desc1.charAt(0) != '(')
436             return false;
437 
438         for (int i = 0; true; ++i) {
439             char c = desc1.charAt(i);
440             if (c != desc2.charAt(i))
441                 return false;
442 
443             if (c == ')')
444                 return true;
445         }
446     }
447 
448     /**
449      * Returns the signature of the given descriptor.  The signature does
450      * not include the return type.  For example, the signature of "(I)V"
451      * is "(I)".
452      */
getParamDescriptor(String decl)453     public static String getParamDescriptor(String decl) {
454         return decl.substring(0, decl.indexOf(')') + 1);
455     }
456 
457     /**
458      * Returns the <code>CtClass</code> object representing the return
459      * type specified by the given descriptor.
460      *
461      * @param desc descriptor
462      * @param cp   the class pool used for obtaining
463      *             a <code>CtClass</code> object.
464      */
getReturnType(String desc, ClassPool cp)465     public static CtClass getReturnType(String desc, ClassPool cp)
466         throws NotFoundException
467     {
468         int i = desc.indexOf(')');
469         if (i < 0)
470             return null;
471         CtClass[] type = new CtClass[1];
472         toCtClass(cp, desc, i + 1, type, 0);
473         return type[0];
474     }
475 
476     /**
477      * Returns the number of the prameters included in the given
478      * descriptor.
479      *
480      * @param desc descriptor
481      */
numOfParameters(String desc)482     public static int numOfParameters(String desc) {
483         int n = 0;
484         int i = 1;
485         for (;;) {
486             char c = desc.charAt(i);
487             if (c == ')')
488                 break;
489 
490             while (c == '[')
491                 c = desc.charAt(++i);
492 
493             if (c == 'L') {
494                 i = desc.indexOf(';', i) + 1;
495                 if (i <= 0)
496                     throw new IndexOutOfBoundsException("bad descriptor");
497             }
498             else
499                 ++i;
500 
501             ++n;
502         }
503 
504         return n;
505     }
506 
507     /**
508      * Returns a <code>CtClass</code> object representing the type
509      * specified by the given descriptor.
510      *
511      * <p>This method works even if the package-class separator is
512      * not <code>/</code> but <code>.</code> (period).  For example,
513      * it accepts <code>Ljava.lang.Object;</code>
514      * as well as <code>Ljava/lang/Object;</code>.
515      *
516      * @param desc descriptor.
517      * @param cp   the class pool used for obtaining
518      *             a <code>CtClass</code> object.
519      */
toCtClass(String desc, ClassPool cp)520     public static CtClass toCtClass(String desc, ClassPool cp)
521         throws NotFoundException
522     {
523         CtClass[] clazz = new CtClass[1];
524         int res = toCtClass(cp, desc, 0, clazz, 0);
525         if (res >= 0)
526             return clazz[0];
527         // maybe, you forgot to surround the class name with
528         // L and ;.  It violates the protocol, but I'm tolerant...
529         return cp.get(desc.replace('/', '.'));
530     }
531 
toCtClass(ClassPool cp, String desc, int i, CtClass[] args, int n)532     private static int toCtClass(ClassPool cp, String desc, int i,
533                                  CtClass[] args, int n)
534         throws NotFoundException
535     {
536         int i2;
537         String name;
538 
539         int arrayDim = 0;
540         char c = desc.charAt(i);
541         while (c == '[') {
542             ++arrayDim;
543             c = desc.charAt(++i);
544         }
545 
546         if (c == 'L') {
547             i2 = desc.indexOf(';', ++i);
548             name = desc.substring(i, i2++).replace('/', '.');
549         }
550         else {
551             CtClass type = toPrimitiveClass(c);
552             if (type == null)
553                 return -1; // error
554 
555             i2 = i + 1;
556             if (arrayDim == 0) {
557                 args[n] = type;
558                 return i2; // neither an array type or a class type
559             }
560             name = type.getName();
561         }
562 
563         if (arrayDim > 0) {
564             StringBuffer sbuf = new StringBuffer(name);
565             while (arrayDim-- > 0)
566                 sbuf.append("[]");
567 
568             name = sbuf.toString();
569         }
570 
571         args[n] = cp.get(name);
572         return i2;
573     }
574 
toPrimitiveClass(char c)575     static CtClass toPrimitiveClass(char c) {
576         CtClass type = null;
577         switch (c) {
578         case 'Z' :
579             type = CtClass.booleanType;
580             break;
581         case 'C' :
582             type = CtClass.charType;
583             break;
584         case 'B' :
585             type = CtClass.byteType;
586             break;
587         case 'S' :
588             type = CtClass.shortType;
589             break;
590         case 'I' :
591             type = CtClass.intType;
592             break;
593         case 'J' :
594             type = CtClass.longType;
595             break;
596         case 'F' :
597             type = CtClass.floatType;
598             break;
599         case 'D' :
600             type = CtClass.doubleType;
601             break;
602         case 'V' :
603             type = CtClass.voidType;
604             break;
605         }
606 
607         return type;
608     }
609 
610     /**
611      * Computes the dimension of the array represented by the given
612      * descriptor.  For example, if the descriptor is <code>"[[I"</code>,
613      * then this method returns 2.
614      *
615      * @param desc the descriptor.
616      * @return 0        if the descriptor does not represent an array type.
617      */
arrayDimension(String desc)618     public static int arrayDimension(String desc) {
619         int dim = 0;
620         while (desc.charAt(dim) == '[')
621             ++dim;
622 
623         return dim;
624     }
625 
626     /**
627      * Returns the descriptor of the type of the array component.
628      * For example, if the given descriptor is
629      * <code>"[[Ljava/lang/String;"</code> and the given dimension is 2,
630      * then this method returns <code>"Ljava/lang/String;"</code>.
631      *
632      * @param desc the descriptor.
633      * @param dim  the array dimension.
634      */
toArrayComponent(String desc, int dim)635     public static String toArrayComponent(String desc, int dim) {
636         return desc.substring(dim);
637     }
638 
639     /**
640      * Computes the data size specified by the given descriptor.
641      * For example, if the descriptor is "D", this method returns 2.
642      *
643      * <p>If the descriptor represents a method type, this method returns
644      * (the size of the returned value) - (the sum of the data sizes
645      * of all the parameters).  For example, if the descriptor is
646      * <code>"(I)D"</code>, then this method returns 1 (= 2 - 1).
647      *
648      * @param desc descriptor
649      */
dataSize(String desc)650     public static int dataSize(String desc) {
651         return dataSize(desc, true);
652     }
653 
654     /**
655      * Computes the data size of parameters.
656      * If one of the parameters is double type, the size of that parameter
657      * is 2 words.  For example, if the given descriptor is
658      *  <code>"(IJ)D"</code>, then this method returns 3.  The size of the
659      * return type is not computed.
660      *
661      * @param desc      a method descriptor.
662      */
paramSize(String desc)663     public static int paramSize(String desc) {
664         return -dataSize(desc, false);
665     }
666 
dataSize(String desc, boolean withRet)667     private static int dataSize(String desc, boolean withRet) {
668         int n = 0;
669         char c = desc.charAt(0);
670         if (c == '(') {
671             int i = 1;
672             for (;;) {
673                 c = desc.charAt(i);
674                 if (c == ')') {
675                     c = desc.charAt(i + 1);
676                     break;
677                 }
678 
679                 boolean array = false;
680                 while (c == '[') {
681                     array = true;
682                     c = desc.charAt(++i);
683                 }
684 
685                 if (c == 'L') {
686                     i = desc.indexOf(';', i) + 1;
687                     if (i <= 0)
688                         throw new IndexOutOfBoundsException("bad descriptor");
689                 }
690                 else
691                     ++i;
692 
693                 if (!array && (c == 'J' || c == 'D'))
694                     n -= 2;
695                 else
696                     --n;
697             }
698         }
699 
700         if (withRet)
701             if (c == 'J' || c == 'D')
702                 n += 2;
703             else if (c != 'V')
704                 ++n;
705 
706         return n;
707     }
708 
709     /**
710      * Returns a human-readable representation of the
711      * given descriptor.  For example, <code>Ljava/lang/Object;</code>
712      * is converted into <code>java.lang.Object</code>.
713      * <code>(I[I)V</code> is converted into <code>(int, int[])</code>
714      * (the return type is ignored).
715      */
toString(String desc)716     public static String toString(String desc) {
717         return PrettyPrinter.toString(desc);
718     }
719 
720     static class PrettyPrinter {
toString(String desc)721         static String toString(String desc) {
722             StringBuffer sbuf = new StringBuffer();
723             if (desc.charAt(0) == '(') {
724                 int pos = 1;
725                 sbuf.append('(');
726                 while (desc.charAt(pos) != ')') {
727                     if (pos > 1)
728                         sbuf.append(',');
729 
730                     pos = readType(sbuf, pos, desc);
731                 }
732 
733                 sbuf.append(')');
734             }
735             else
736                 readType(sbuf, 0, desc);
737 
738             return sbuf.toString();
739         }
740 
readType(StringBuffer sbuf, int pos, String desc)741         static int readType(StringBuffer sbuf, int pos, String desc) {
742             char c = desc.charAt(pos);
743             int arrayDim = 0;
744             while (c == '[') {
745                 arrayDim++;
746                 c = desc.charAt(++pos);
747             }
748 
749             if (c == 'L')
750                 while (true) {
751                     c = desc.charAt(++pos);
752                     if (c == ';')
753                         break;
754 
755                     if (c == '/')
756                         c = '.';
757 
758                     sbuf.append(c);
759                 }
760             else {
761                 CtClass t = toPrimitiveClass(c);
762                 sbuf.append(t.getName());
763             }
764 
765             while (arrayDim-- > 0)
766                 sbuf.append("[]");
767 
768             return pos + 1;
769         }
770     }
771 
772     /**
773      * An Iterator over a descriptor.
774      */
775     public static class Iterator {
776         private String desc;
777         private int index, curPos;
778         private boolean param;
779 
780         /**
781          * Constructs an iterator.
782          *
783          * @param s         descriptor.
784          */
Iterator(String s)785         public Iterator(String s) {
786             desc = s;
787             index = curPos = 0;
788             param = false;
789         }
790 
791         /**
792          * Returns true if the iteration has more elements.
793          */
hasNext()794         public boolean hasNext() {
795             return index < desc.length();
796         }
797 
798         /**
799          * Returns true if the current element is a parameter type.
800          */
isParameter()801         public boolean isParameter() { return param; }
802 
803         /**
804          * Returns the first character of the current element.
805          */
currentChar()806         public char currentChar() { return desc.charAt(curPos); }
807 
808         /**
809          * Returns true if the current element is double or long type.
810          */
is2byte()811         public boolean is2byte() {
812             char c = currentChar();
813             return c == 'D' || c == 'J';
814         }
815 
816         /**
817          * Returns the position of the next type character.
818          * That type character becomes a new current element.
819          */
next()820         public int next() {
821             int nextPos = index;
822             char c = desc.charAt(nextPos);
823             if (c == '(') {
824                 ++index;
825                 c = desc.charAt(++nextPos);
826                 param = true;
827             }
828 
829             if (c == ')') {
830                 ++index;
831                 c = desc.charAt(++nextPos);
832                 param = false;
833             }
834 
835             while (c == '[')
836                 c = desc.charAt(++nextPos);
837 
838             if (c == 'L') {
839                 nextPos = desc.indexOf(';', nextPos) + 1;
840                 if (nextPos <= 0)
841                     throw new IndexOutOfBoundsException("bad descriptor");
842             }
843             else
844                 ++nextPos;
845 
846             curPos = index;
847             index = nextPos;
848             return curPos;
849         }
850     }
851 }
852