source: webkit/trunk/JavaScriptCore/kjs/lexer.cpp@ 9582

Last change on this file since 9582 was 7507, checked in by mjs, 21 years ago

Reviewed by Richard.

<rdar://problem/3493140> REGRESSION (85-100): cedille displays %-escaped in JavaScript message at hotmail.com

  • kjs/function.cpp: (KJS::GlobalFuncImp::call): Replace our escape() and unescape() implementations with ones from KDE KJS, which have the proper latin-1 behavior to match Win IE.
  • kjs/lexer.cpp: (Lexer::isHexDigit): Made static and non-const.
  • kjs/lexer.h:
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.5 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2000 Harri Porten ([email protected])
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <ctype.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <assert.h>
32
33#include "value.h"
34#include "object.h"
35#include "types.h"
36#include "interpreter.h"
37#include "nodes.h"
38#include "lexer.h"
39#include "identifier.h"
40#include "lookup.h"
41#include "internal.h"
42
43// we can't specify the namespace in yacc's C output, so do it here
44using namespace KJS;
45
46static Lexer *currLexer = 0;
47
48#ifndef KDE_USE_FINAL
49#include "grammar.h"
50#endif
51
52#include "lexer.lut.h"
53
54extern YYLTYPE kjsyylloc; // global bison variable holding token info
55
56// a bridge for yacc from the C world to C++
57int kjsyylex()
58{
59 return Lexer::curr()->lex();
60}
61
62Lexer::Lexer()
63 : yylineno(1),
64 size8(128), size16(128), restrKeyword(false),
65 eatNextIdentifier(false), stackToken(-1), lastToken(-1), pos(0),
66 code(0), length(0),
67#ifndef KJS_PURE_ECMA
68 bol(true),
69#endif
70 current(0), next1(0), next2(0), next3(0),
71 strings(0), numStrings(0), stringsCapacity(0),
72 identifiers(0), numIdentifiers(0), identifiersCapacity(0)
73{
74 // allocate space for read buffers
75 buffer8 = new char[size8];
76 buffer16 = new UChar[size16];
77 currLexer = this;
78}
79
80Lexer::~Lexer()
81{
82 doneParsing();
83 delete [] buffer8;
84 delete [] buffer16;
85}
86
87Lexer *Lexer::curr()
88{
89 if (!currLexer) {
90 // create singleton instance
91 currLexer = new Lexer();
92 }
93 return currLexer;
94}
95
96#ifdef KJS_DEBUG_MEM
97void Lexer::globalClear()
98{
99 delete currLexer;
100 currLexer = 0L;
101}
102#endif
103
104void Lexer::setCode(const UString &sourceURL, int startingLineNumber, const UChar *c, unsigned int len)
105{
106 yylineno = 1 + startingLineNumber;
107 m_sourceURL = sourceURL;
108 restrKeyword = false;
109 delimited = false;
110 eatNextIdentifier = false;
111 stackToken = -1;
112 lastToken = -1;
113 pos = 0;
114 code = c;
115 length = len;
116 skipLF = false;
117 skipCR = false;
118 error = false;
119#ifndef KJS_PURE_ECMA
120 bol = true;
121#endif
122
123 // read first characters
124 current = (length > 0) ? code[0].uc : 0;
125 next1 = (length > 1) ? code[1].uc : 0;
126 next2 = (length > 2) ? code[2].uc : 0;
127 next3 = (length > 3) ? code[3].uc : 0;
128}
129
130void Lexer::shift(unsigned int p)
131{
132 while (p--) {
133 pos++;
134 current = next1;
135 next1 = next2;
136 next2 = next3;
137 next3 = (pos + 3 < length) ? code[pos+3].uc : 0;
138 }
139}
140
141// called on each new line
142void Lexer::nextLine()
143{
144 yylineno++;
145#ifndef KJS_PURE_ECMA
146 bol = true;
147#endif
148}
149
150void Lexer::setDone(State s)
151{
152 state = s;
153 done = true;
154}
155
156int Lexer::lex()
157{
158 int token = 0;
159 state = Start;
160 unsigned short stringType = 0; // either single or double quotes
161 pos8 = pos16 = 0;
162 done = false;
163 terminator = false;
164 skipLF = false;
165 skipCR = false;
166
167 // did we push a token on the stack previously ?
168 // (after an automatic semicolon insertion)
169 if (stackToken >= 0) {
170 setDone(Other);
171 token = stackToken;
172 stackToken = 0;
173 }
174
175 while (!done) {
176 if (skipLF && current != '\n') // found \r but not \n afterwards
177 skipLF = false;
178 if (skipCR && current != '\r') // found \n but not \r afterwards
179 skipCR = false;
180 if (skipLF || skipCR) // found \r\n or \n\r -> eat the second one
181 {
182 skipLF = false;
183 skipCR = false;
184 shift(1);
185 }
186 switch (state) {
187 case Start:
188 if (isWhiteSpace()) {
189 // do nothing
190 } else if (current == '/' && next1 == '/') {
191 shift(1);
192 state = InSingleLineComment;
193 } else if (current == '/' && next1 == '*') {
194 shift(1);
195 state = InMultiLineComment;
196 } else if (current == 0) {
197 if (!terminator && !delimited) {
198 // automatic semicolon insertion if program incomplete
199 token = ';';
200 stackToken = 0;
201 setDone(Other);
202 } else
203 setDone(Eof);
204 } else if (isLineTerminator()) {
205 nextLine();
206 terminator = true;
207 if (restrKeyword) {
208 token = ';';
209 setDone(Other);
210 }
211 } else if (current == '"' || current == '\'') {
212 state = InString;
213 stringType = current;
214 } else if (isIdentLetter(current)) {
215 record16(current);
216 state = InIdentifier;
217 } else if (current == '0') {
218 record8(current);
219 state = InNum0;
220 } else if (isDecimalDigit(current)) {
221 record8(current);
222 state = InNum;
223 } else if (current == '.' && isDecimalDigit(next1)) {
224 record8(current);
225 state = InDecimal;
226#ifndef KJS_PURE_ECMA
227 // <!-- marks the beginning of a line comment (for www usage)
228 } else if (current == '<' && next1 == '!' &&
229 next2 == '-' && next3 == '-') {
230 shift(3);
231 state = InSingleLineComment;
232 // same for -->
233 } else if (bol && current == '-' && next1 == '-' && next2 == '>') {
234 shift(2);
235 state = InSingleLineComment;
236#endif
237 } else {
238 token = matchPunctuator(current, next1, next2, next3);
239 if (token != -1) {
240 setDone(Other);
241 } else {
242 // cerr << "encountered unknown character" << endl;
243 setDone(Bad);
244 }
245 }
246 break;
247 case InString:
248 if (current == stringType) {
249 shift(1);
250 setDone(String);
251 } else if (current == 0 || isLineTerminator()) {
252 setDone(Bad);
253 } else if (current == '\\') {
254 state = InEscapeSequence;
255 } else {
256 record16(current);
257 }
258 break;
259 // Escape Sequences inside of strings
260 case InEscapeSequence:
261 if (isOctalDigit(current)) {
262 if (current >= '0' && current <= '3' &&
263 isOctalDigit(next1) && isOctalDigit(next2)) {
264 record16(convertOctal(current, next1, next2));
265 shift(2);
266 state = InString;
267 } else if (isOctalDigit(current) && isOctalDigit(next1)) {
268 record16(convertOctal('0', current, next1));
269 shift(1);
270 state = InString;
271 } else if (isOctalDigit(current)) {
272 record16(convertOctal('0', '0', current));
273 state = InString;
274 } else {
275 setDone(Bad);
276 }
277 } else if (current == 'x')
278 state = InHexEscape;
279 else if (current == 'u')
280 state = InUnicodeEscape;
281 else if (isLineTerminator()) {
282 nextLine();
283 state = InString;
284 } else {
285 record16(singleEscape(current));
286 state = InString;
287 }
288 break;
289 case InHexEscape:
290 if (isHexDigit(current) && isHexDigit(next1)) {
291 state = InString;
292 record16(convertHex(current, next1));
293 shift(1);
294 } else if (current == stringType) {
295 record16('x');
296 shift(1);
297 setDone(String);
298 } else {
299 record16('x');
300 record16(current);
301 state = InString;
302 }
303 break;
304 case InUnicodeEscape:
305 if (isHexDigit(current) && isHexDigit(next1) &&
306 isHexDigit(next2) && isHexDigit(next3)) {
307 record16(convertUnicode(current, next1, next2, next3));
308 shift(3);
309 state = InString;
310 } else if (current == stringType) {
311 record16('u');
312 shift(1);
313 setDone(String);
314 } else {
315 setDone(Bad);
316 }
317 break;
318 case InSingleLineComment:
319 if (isLineTerminator()) {
320 nextLine();
321 terminator = true;
322 if (restrKeyword) {
323 token = ';';
324 setDone(Other);
325 } else
326 state = Start;
327 } else if (current == 0) {
328 setDone(Eof);
329 }
330 break;
331 case InMultiLineComment:
332 if (current == 0) {
333 setDone(Bad);
334 } else if (isLineTerminator()) {
335 nextLine();
336 } else if (current == '*' && next1 == '/') {
337 state = Start;
338 shift(1);
339 }
340 break;
341 case InIdentifier:
342 if (isIdentLetter(current) || isDecimalDigit(current)) {
343 record16(current);
344 break;
345 }
346 setDone(Identifier);
347 break;
348 case InNum0:
349 if (current == 'x' || current == 'X') {
350 record8(current);
351 state = InHex;
352 } else if (current == '.') {
353 record8(current);
354 state = InDecimal;
355 } else if (current == 'e' || current == 'E') {
356 record8(current);
357 state = InExponentIndicator;
358 } else if (isOctalDigit(current)) {
359 record8(current);
360 state = InOctal;
361 } else if (isDecimalDigit(current)) {
362 record8(current);
363 state = InDecimal;
364 } else {
365 setDone(Number);
366 }
367 break;
368 case InHex:
369 if (isHexDigit(current)) {
370 record8(current);
371 } else {
372 setDone(Hex);
373 }
374 break;
375 case InOctal:
376 if (isOctalDigit(current)) {
377 record8(current);
378 }
379 else if (isDecimalDigit(current)) {
380 record8(current);
381 state = InDecimal;
382 } else
383 setDone(Octal);
384 break;
385 case InNum:
386 if (isDecimalDigit(current)) {
387 record8(current);
388 } else if (current == '.') {
389 record8(current);
390 state = InDecimal;
391 } else if (current == 'e' || current == 'E') {
392 record8(current);
393 state = InExponentIndicator;
394 } else
395 setDone(Number);
396 break;
397 case InDecimal:
398 if (isDecimalDigit(current)) {
399 record8(current);
400 } else if (current == 'e' || current == 'E') {
401 record8(current);
402 state = InExponentIndicator;
403 } else
404 setDone(Number);
405 break;
406 case InExponentIndicator:
407 if (current == '+' || current == '-') {
408 record8(current);
409 } else if (isDecimalDigit(current)) {
410 record8(current);
411 state = InExponent;
412 } else
413 setDone(Bad);
414 break;
415 case InExponent:
416 if (isDecimalDigit(current)) {
417 record8(current);
418 } else
419 setDone(Number);
420 break;
421 default:
422 assert(!"Unhandled state in switch statement");
423 }
424
425 // move on to the next character
426 if (!done)
427 shift(1);
428#ifndef KJS_PURE_ECMA
429 if (state != Start && state != InSingleLineComment)
430 bol = false;
431#endif
432 }
433
434 // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
435 if ((state == Number || state == Octal || state == Hex)
436 && isIdentLetter(current))
437 state = Bad;
438
439 // terminate string
440 buffer8[pos8] = '\0';
441
442#ifdef KJS_DEBUG_LEX
443 fprintf(stderr, "line: %d ", lineNo());
444 fprintf(stderr, "yytext (%x): ", buffer8[0]);
445 fprintf(stderr, "%s ", buffer8);
446#endif
447
448 double dval = 0;
449 if (state == Number) {
450 dval = strtod(buffer8, 0L);
451 } else if (state == Hex) { // scan hex numbers
452 const char *p = buffer8 + 2;
453 while (char c = *p++) {
454 dval *= 16;
455 dval += convertHex(c);
456 }
457 state = Number;
458 } else if (state == Octal) { // scan octal number
459 const char *p = buffer8 + 1;
460 while (char c = *p++) {
461 dval *= 8;
462 dval += c - '0';
463 }
464 state = Number;
465 }
466
467#ifdef KJS_DEBUG_LEX
468 switch (state) {
469 case Eof:
470 printf("(EOF)\n");
471 break;
472 case Other:
473 printf("(Other)\n");
474 break;
475 case Identifier:
476 printf("(Identifier)/(Keyword)\n");
477 break;
478 case String:
479 printf("(String)\n");
480 break;
481 case Number:
482 printf("(Number)\n");
483 break;
484 default:
485 printf("(unknown)");
486 }
487#endif
488
489 if (state != Identifier && eatNextIdentifier)
490 eatNextIdentifier = false;
491
492 restrKeyword = false;
493 delimited = false;
494 kjsyylloc.first_line = yylineno; // ???
495 kjsyylloc.last_line = yylineno;
496
497 switch (state) {
498 case Eof:
499 token = 0;
500 break;
501 case Other:
502 if(token == '}' || token == ';') {
503 delimited = true;
504 }
505 break;
506 case Identifier:
507 if ((token = Lookup::find(&mainTable, buffer16, pos16)) < 0) {
508 // Lookup for keyword failed, means this is an identifier
509 // Apply anonymous-function hack below (eat the identifier)
510 if (eatNextIdentifier) {
511 eatNextIdentifier = false;
512 token = lex();
513 break;
514 }
515 kjsyylval.ident = makeIdentifier(buffer16, pos16);
516 token = IDENT;
517 break;
518 }
519
520 eatNextIdentifier = false;
521 // Hack for "f = function somename() { ... }", too hard to get into the grammar
522 if (token == FUNCTION && lastToken == '=' )
523 eatNextIdentifier = true;
524
525 if (token == CONTINUE || token == BREAK ||
526 token == RETURN || token == THROW)
527 restrKeyword = true;
528 break;
529 case String:
530 kjsyylval.ustr = makeUString(buffer16, pos16);
531 token = STRING;
532 break;
533 case Number:
534 kjsyylval.dval = dval;
535 token = NUMBER;
536 break;
537 case Bad:
538 fprintf(stderr, "yylex: ERROR.\n");
539 error = true;
540 return -1;
541 default:
542 assert(!"unhandled numeration value in switch");
543 error = true;
544 return -1;
545 }
546 lastToken = token;
547 return token;
548}
549
550bool Lexer::isWhiteSpace() const
551{
552 return (current == ' ' || current == '\t' ||
553 current == 0x0b || current == 0x0c || current == 0xa0);
554}
555
556bool Lexer::isLineTerminator()
557{
558 bool cr = (current == '\r');
559 bool lf = (current == '\n');
560 if (cr)
561 skipLF = true;
562 else if (lf)
563 skipCR = true;
564 return cr || lf;
565}
566
567bool Lexer::isIdentLetter(unsigned short c)
568{
569 /* TODO: allow other legitimate unicode chars */
570 return (c >= 'a' && c <= 'z' ||
571 c >= 'A' && c <= 'Z' ||
572 c == '$' || c == '_');
573}
574
575bool Lexer::isDecimalDigit(unsigned short c)
576{
577 return (c >= '0' && c <= '9');
578}
579
580bool Lexer::isHexDigit(unsigned short c)
581{
582 return (c >= '0' && c <= '9' ||
583 c >= 'a' && c <= 'f' ||
584 c >= 'A' && c <= 'F');
585}
586
587bool Lexer::isOctalDigit(unsigned short c) const
588{
589 return (c >= '0' && c <= '7');
590}
591
592int Lexer::matchPunctuator(unsigned short c1, unsigned short c2,
593 unsigned short c3, unsigned short c4)
594{
595 if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') {
596 shift(4);
597 return URSHIFTEQUAL;
598 } else if (c1 == '=' && c2 == '=' && c3 == '=') {
599 shift(3);
600 return STREQ;
601 } else if (c1 == '!' && c2 == '=' && c3 == '=') {
602 shift(3);
603 return STRNEQ;
604 } else if (c1 == '>' && c2 == '>' && c3 == '>') {
605 shift(3);
606 return URSHIFT;
607 } else if (c1 == '<' && c2 == '<' && c3 == '=') {
608 shift(3);
609 return LSHIFTEQUAL;
610 } else if (c1 == '>' && c2 == '>' && c3 == '=') {
611 shift(3);
612 return RSHIFTEQUAL;
613 } else if (c1 == '<' && c2 == '=') {
614 shift(2);
615 return LE;
616 } else if (c1 == '>' && c2 == '=') {
617 shift(2);
618 return GE;
619 } else if (c1 == '!' && c2 == '=') {
620 shift(2);
621 return NE;
622 } else if (c1 == '+' && c2 == '+') {
623 shift(2);
624 if (terminator)
625 return AUTOPLUSPLUS;
626 else
627 return PLUSPLUS;
628 } else if (c1 == '-' && c2 == '-') {
629 shift(2);
630 if (terminator)
631 return AUTOMINUSMINUS;
632 else
633 return MINUSMINUS;
634 } else if (c1 == '=' && c2 == '=') {
635 shift(2);
636 return EQEQ;
637 } else if (c1 == '+' && c2 == '=') {
638 shift(2);
639 return PLUSEQUAL;
640 } else if (c1 == '-' && c2 == '=') {
641 shift(2);
642 return MINUSEQUAL;
643 } else if (c1 == '*' && c2 == '=') {
644 shift(2);
645 return MULTEQUAL;
646 } else if (c1 == '/' && c2 == '=') {
647 shift(2);
648 return DIVEQUAL;
649 } else if (c1 == '&' && c2 == '=') {
650 shift(2);
651 return ANDEQUAL;
652 } else if (c1 == '^' && c2 == '=') {
653 shift(2);
654 return XOREQUAL;
655 } else if (c1 == '%' && c2 == '=') {
656 shift(2);
657 return MODEQUAL;
658 } else if (c1 == '|' && c2 == '=') {
659 shift(2);
660 return OREQUAL;
661 } else if (c1 == '<' && c2 == '<') {
662 shift(2);
663 return LSHIFT;
664 } else if (c1 == '>' && c2 == '>') {
665 shift(2);
666 return RSHIFT;
667 } else if (c1 == '&' && c2 == '&') {
668 shift(2);
669 return AND;
670 } else if (c1 == '|' && c2 == '|') {
671 shift(2);
672 return OR;
673 }
674
675 switch(c1) {
676 case '=':
677 case '>':
678 case '<':
679 case ',':
680 case '!':
681 case '~':
682 case '?':
683 case ':':
684 case '.':
685 case '+':
686 case '-':
687 case '*':
688 case '/':
689 case '&':
690 case '|':
691 case '^':
692 case '%':
693 case '(':
694 case ')':
695 case '{':
696 case '}':
697 case '[':
698 case ']':
699 case ';':
700 shift(1);
701 return static_cast<int>(c1);
702 default:
703 return -1;
704 }
705}
706
707unsigned short Lexer::singleEscape(unsigned short c) const
708{
709 switch(c) {
710 case 'b':
711 return 0x08;
712 case 't':
713 return 0x09;
714 case 'n':
715 return 0x0A;
716 case 'v':
717 return 0x0B;
718 case 'f':
719 return 0x0C;
720 case 'r':
721 return 0x0D;
722 case '"':
723 return 0x22;
724 case '\'':
725 return 0x27;
726 case '\\':
727 return 0x5C;
728 default:
729 return c;
730 }
731}
732
733unsigned short Lexer::convertOctal(unsigned short c1, unsigned short c2,
734 unsigned short c3) const
735{
736 return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0');
737}
738
739unsigned char Lexer::convertHex(unsigned short c)
740{
741 if (c >= '0' && c <= '9')
742 return (c - '0');
743 else if (c >= 'a' && c <= 'f')
744 return (c - 'a' + 10);
745 else
746 return (c - 'A' + 10);
747}
748
749unsigned char Lexer::convertHex(unsigned short c1, unsigned short c2)
750{
751 return ((convertHex(c1) << 4) + convertHex(c2));
752}
753
754UChar Lexer::convertUnicode(unsigned short c1, unsigned short c2,
755 unsigned short c3, unsigned short c4)
756{
757 return UChar((convertHex(c1) << 4) + convertHex(c2),
758 (convertHex(c3) << 4) + convertHex(c4));
759}
760
761void Lexer::record8(unsigned short c)
762{
763 assert(c <= 0xff);
764
765 // enlarge buffer if full
766 if (pos8 >= size8 - 1) {
767 char *tmp = new char[2 * size8];
768 memcpy(tmp, buffer8, size8 * sizeof(char));
769 delete [] buffer8;
770 buffer8 = tmp;
771 size8 *= 2;
772 }
773
774 buffer8[pos8++] = (char) c;
775}
776
777void Lexer::record16(UChar c)
778{
779 // enlarge buffer if full
780 if (pos16 >= size16 - 1) {
781 UChar *tmp = new UChar[2 * size16];
782 memcpy(tmp, buffer16, size16 * sizeof(UChar));
783 delete [] buffer16;
784 buffer16 = tmp;
785 size16 *= 2;
786 }
787
788 buffer16[pos16++] = c;
789}
790
791bool Lexer::scanRegExp()
792{
793 pos16 = 0;
794 bool lastWasEscape = false;
795 bool inBrackets = false;
796
797 while (1) {
798 if (isLineTerminator() || current == 0)
799 return false;
800 else if (current != '/' || lastWasEscape == true || inBrackets == true)
801 {
802 // keep track of '[' and ']'
803 if ( !lastWasEscape ) {
804 if ( current == '[' && !inBrackets )
805 inBrackets = true;
806 if ( current == ']' && inBrackets )
807 inBrackets = false;
808 }
809 record16(current);
810 lastWasEscape =
811 !lastWasEscape && (current == '\\');
812 }
813 else { // end of regexp
814 pattern = UString(buffer16, pos16);
815 pos16 = 0;
816 shift(1);
817 break;
818 }
819 shift(1);
820 }
821
822 while (isIdentLetter(current)) {
823 record16(current);
824 shift(1);
825 }
826 flags = UString(buffer16, pos16);
827
828 return true;
829}
830
831
832void Lexer::doneParsing()
833{
834 for (unsigned i = 0; i < numIdentifiers; i++) {
835 delete identifiers[i];
836 }
837 free (identifiers);
838 identifiers = 0;
839 numIdentifiers = 0;
840 identifiersCapacity = 0;
841
842 for (unsigned i = 0; i < numStrings; i++) {
843 delete strings[i];
844 }
845 free (strings);
846 strings = 0;
847 numStrings = 0;
848 stringsCapacity = 0;
849}
850
851const int initialCapacity = 64;
852const int growthFactor = 2;
853
854Identifier *Lexer::makeIdentifier(UChar *buffer, unsigned int pos)
855{
856 if (numIdentifiers == identifiersCapacity) {
857 identifiersCapacity = (identifiersCapacity == 0) ? initialCapacity : identifiersCapacity *growthFactor;
858 identifiers = (KJS::Identifier **)realloc(identifiers, sizeof(KJS::Identifier *) * identifiersCapacity);
859 }
860
861 KJS::Identifier *identifier = new KJS::Identifier(buffer16, pos16);
862 identifiers[numIdentifiers++] = identifier;
863 return identifier;
864}
865
866UString *Lexer::makeUString(UChar *buffer, unsigned int pos)
867{
868 if (numStrings == stringsCapacity) {
869 stringsCapacity = (stringsCapacity == 0) ? initialCapacity : stringsCapacity *growthFactor;
870 strings = (UString **)realloc(strings, sizeof(UString *) * stringsCapacity);
871 }
872
873 UString *string = new UString(buffer16, pos16);
874 strings[numStrings++] = string;
875 return string;
876}
Note: See TracBrowser for help on using the repository browser.