OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 12 matching lines...) Expand all Loading... | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 | 28 |
29 function inherits(childCtor, parentCtor) { | 29 function inherits(childCtor, parentCtor) { |
30 childCtor.prototype.__proto__ = parentCtor.prototype; | 30 childCtor.prototype.__proto__ = parentCtor.prototype; |
31 }; | 31 }; |
32 | 32 |
33 | |
Jakob Kummerow
2013/08/23 13:08:35
nit: leave this line in please.
Daniel Kurka
2013/08/23 13:31:43
Done.
| |
34 function V8Profile(separateIc) { | 33 function V8Profile(separateIc) { |
35 Profile.call(this); | 34 Profile.call(this); |
36 if (!separateIc) { | 35 if (!separateIc) { |
37 this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); }; | 36 this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); }; |
38 } | 37 } |
39 }; | 38 }; |
40 inherits(V8Profile, Profile); | 39 inherits(V8Profile, Profile); |
41 | 40 |
42 | 41 |
43 V8Profile.IC_RE = | 42 V8Profile.IC_RE = |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
146 | 145 |
147 | 146 |
148 function TickProcessor( | 147 function TickProcessor( |
149 cppEntriesProvider, | 148 cppEntriesProvider, |
150 separateIc, | 149 separateIc, |
151 callGraphSize, | 150 callGraphSize, |
152 ignoreUnknown, | 151 ignoreUnknown, |
153 stateFilter, | 152 stateFilter, |
154 snapshotLogProcessor, | 153 snapshotLogProcessor, |
155 distortion, | 154 distortion, |
156 range) { | 155 range, |
156 sourceMap) { | |
157 LogReader.call(this, { | 157 LogReader.call(this, { |
158 'shared-library': { parsers: [null, parseInt, parseInt], | 158 'shared-library': { parsers: [null, parseInt, parseInt], |
159 processor: this.processSharedLibrary }, | 159 processor: this.processSharedLibrary }, |
160 'code-creation': { | 160 'code-creation': { |
161 parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'], | 161 parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'], |
162 processor: this.processCodeCreation }, | 162 processor: this.processCodeCreation }, |
163 'code-move': { parsers: [parseInt, parseInt], | 163 'code-move': { parsers: [parseInt, parseInt], |
164 processor: this.processCodeMove }, | 164 processor: this.processCodeMove }, |
165 'code-delete': { parsers: [parseInt], | 165 'code-delete': { parsers: [parseInt], |
166 processor: this.processCodeDelete }, | 166 processor: this.processCodeDelete }, |
(...skipping 22 matching lines...) Expand all Loading... | |
189 // Obsolete row types. | 189 // Obsolete row types. |
190 'code-allocate': null, | 190 'code-allocate': null, |
191 'begin-code-region': null, | 191 'begin-code-region': null, |
192 'end-code-region': null }); | 192 'end-code-region': null }); |
193 | 193 |
194 this.cppEntriesProvider_ = cppEntriesProvider; | 194 this.cppEntriesProvider_ = cppEntriesProvider; |
195 this.callGraphSize_ = callGraphSize; | 195 this.callGraphSize_ = callGraphSize; |
196 this.ignoreUnknown_ = ignoreUnknown; | 196 this.ignoreUnknown_ = ignoreUnknown; |
197 this.stateFilter_ = stateFilter; | 197 this.stateFilter_ = stateFilter; |
198 this.snapshotLogProcessor_ = snapshotLogProcessor; | 198 this.snapshotLogProcessor_ = snapshotLogProcessor; |
199 this.sourceMap = sourceMap; | |
199 this.deserializedEntriesNames_ = []; | 200 this.deserializedEntriesNames_ = []; |
200 var ticks = this.ticks_ = | 201 var ticks = this.ticks_ = |
201 { total: 0, unaccounted: 0, excluded: 0, gc: 0 }; | 202 { total: 0, unaccounted: 0, excluded: 0, gc: 0 }; |
202 | 203 |
203 distortion = parseInt(distortion); | 204 distortion = parseInt(distortion); |
204 // Convert picoseconds to nanoseconds. | 205 // Convert picoseconds to nanoseconds. |
205 this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000); | 206 this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000); |
206 this.distortion = 0; | 207 this.distortion = 0; |
207 var rangelimits = range ? range.split(",") : []; | 208 var rangelimits = range ? range.split(",") : []; |
208 var range_start = parseInt(rangelimits[0]); | 209 var range_start = parseInt(rangelimits[0]); |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
537 profile, filterP, func) { | 538 profile, filterP, func) { |
538 for (var i = 0, n = profile.length; i < n; ++i) { | 539 for (var i = 0, n = profile.length; i < n; ++i) { |
539 var rec = profile[i]; | 540 var rec = profile[i]; |
540 if (!filterP(rec.internalFuncName)) { | 541 if (!filterP(rec.internalFuncName)) { |
541 continue; | 542 continue; |
542 } | 543 } |
543 func(rec); | 544 func(rec); |
544 } | 545 } |
545 }; | 546 }; |
546 | 547 |
548 TickProcessor.prototype.getLineAndColumn = function(name) { | |
549 var re = /:([0-9]+):([0-9]+)$/; | |
550 var array = re.exec(name); | |
551 if (!array) { | |
552 return null; | |
Jakob Kummerow
2013/08/23 13:08:35
nit: indentation
Daniel Kurka
2013/08/23 13:31:43
Done.
| |
553 } | |
554 return {line: array[1], column: array[2]}; | |
555 } | |
556 | |
557 TickProcessor.prototype.hasSourceMap = function() { | |
558 return this.sourceMap != null; | |
559 }; | |
560 | |
561 | |
562 TickProcessor.prototype.formatFunctionName = function(funcName) { | |
563 if (!this.hasSourceMap()) { | |
564 return funcName; | |
565 } | |
566 var lc = this.getLineAndColumn(funcName); | |
567 if (lc == null) { | |
568 return funcName; | |
569 } | |
570 // in source maps lines and columns are zero based | |
571 var lineNumber = lc.line - 1; | |
572 var column = lc.column - 1; | |
573 var entry = this.sourceMap.findEntry(lineNumber, column); | |
574 var sourceFile = entry[2]; | |
575 var sourceLine = entry[3] + 1; | |
576 var sourceColumn = entry[4] + 1; | |
577 | |
578 return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName; | |
579 }; | |
547 | 580 |
548 TickProcessor.prototype.printEntries = function( | 581 TickProcessor.prototype.printEntries = function( |
549 profile, nonLibTicks, filterP) { | 582 profile, nonLibTicks, filterP) { |
583 var that = this; | |
550 this.processProfile(profile, filterP, function (rec) { | 584 this.processProfile(profile, filterP, function (rec) { |
551 if (rec.selfTime == 0) return; | 585 if (rec.selfTime == 0) return; |
552 var nonLibPct = nonLibTicks != null ? | 586 var nonLibPct = nonLibTicks != null ? |
553 rec.selfTime * 100.0 / nonLibTicks : 0.0; | 587 rec.selfTime * 100.0 / nonLibTicks : 0.0; |
588 var funcName = that.formatFunctionName(rec.internalFuncName); | |
589 | |
554 print(' ' + padLeft(rec.selfTime, 5) + ' ' + | 590 print(' ' + padLeft(rec.selfTime, 5) + ' ' + |
555 padLeft(rec.selfPercent.toFixed(1), 5) + '% ' + | 591 padLeft(rec.selfPercent.toFixed(1), 5) + '% ' + |
556 padLeft(nonLibPct.toFixed(1), 5) + '% ' + | 592 padLeft(nonLibPct.toFixed(1), 5) + '% ' + |
557 rec.internalFuncName); | 593 funcName); |
558 }); | 594 }); |
559 }; | 595 }; |
560 | 596 |
561 | 597 |
562 TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) { | 598 TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) { |
563 var self = this; | 599 var self = this; |
564 var indent = opt_indent || 0; | 600 var indent = opt_indent || 0; |
565 var indentStr = padLeft('', indent); | 601 var indentStr = padLeft('', indent); |
566 this.processProfile(profile, function() { return true; }, function (rec) { | 602 this.processProfile(profile, function() { return true; }, function (rec) { |
567 // Cut off too infrequent callers. | 603 // Cut off too infrequent callers. |
568 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return; | 604 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return; |
605 var funcName = self.formatFunctionName(rec.internalFuncName); | |
569 print(' ' + padLeft(rec.totalTime, 5) + ' ' + | 606 print(' ' + padLeft(rec.totalTime, 5) + ' ' + |
570 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' + | 607 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' + |
571 indentStr + rec.internalFuncName); | 608 indentStr + funcName); |
572 // Limit backtrace depth. | 609 // Limit backtrace depth. |
573 if (indent < 2 * self.callGraphSize_) { | 610 if (indent < 2 * self.callGraphSize_) { |
574 self.printHeavyProfile(rec.children, indent + 2); | 611 self.printHeavyProfile(rec.children, indent + 2); |
575 } | 612 } |
576 // Delimit top-level functions. | 613 // Delimit top-level functions. |
577 if (indent == 0) { | 614 if (indent == 0) { |
578 print(''); | 615 print(''); |
579 } | 616 } |
580 }); | 617 }); |
581 }; | 618 }; |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
818 'Specify that we are running on Mac OS X platform'], | 855 'Specify that we are running on Mac OS X platform'], |
819 '--nm': ['nm', 'nm', | 856 '--nm': ['nm', 'nm', |
820 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'], | 857 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'], |
821 '--target': ['targetRootFS', '', | 858 '--target': ['targetRootFS', '', |
822 'Specify the target root directory for cross environment'], | 859 'Specify the target root directory for cross environment'], |
823 '--snapshot-log': ['snapshotLogFileName', 'snapshot.log', | 860 '--snapshot-log': ['snapshotLogFileName', 'snapshot.log', |
824 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)'], | 861 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)'], |
825 '--range': ['range', 'auto,auto', | 862 '--range': ['range', 'auto,auto', |
826 'Specify the range limit as [start],[end]'], | 863 'Specify the range limit as [start],[end]'], |
827 '--distortion': ['distortion', 0, | 864 '--distortion': ['distortion', 0, |
828 'Specify the logging overhead in picoseconds'] | 865 'Specify the logging overhead in picoseconds'], |
866 '--sourceMap': ['sourceMap', null, | |
Jakob Kummerow
2013/08/23 13:08:35
For consistency with other arg names, let's call t
Daniel Kurka
2013/08/23 13:31:43
Done.
| |
867 'Specify the source map that should be used for output'] | |
Jakob Kummerow
2013/08/23 13:08:35
nit: indentation
Daniel Kurka
2013/08/23 13:31:43
Done.
| |
829 }; | 868 }; |
830 this.argsDispatch_['--js'] = this.argsDispatch_['-j']; | 869 this.argsDispatch_['--js'] = this.argsDispatch_['-j']; |
831 this.argsDispatch_['--gc'] = this.argsDispatch_['-g']; | 870 this.argsDispatch_['--gc'] = this.argsDispatch_['-g']; |
832 this.argsDispatch_['--compiler'] = this.argsDispatch_['-c']; | 871 this.argsDispatch_['--compiler'] = this.argsDispatch_['-c']; |
833 this.argsDispatch_['--other'] = this.argsDispatch_['-o']; | 872 this.argsDispatch_['--other'] = this.argsDispatch_['-o']; |
834 this.argsDispatch_['--external'] = this.argsDispatch_['-e']; | 873 this.argsDispatch_['--external'] = this.argsDispatch_['-e']; |
835 }; | 874 }; |
836 | 875 |
837 | 876 |
838 ArgumentsProcessor.DEFAULTS = { | 877 ArgumentsProcessor.DEFAULTS = { |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
904 if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) { | 943 if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) { |
905 synonims.push(synArg); | 944 synonims.push(synArg); |
906 delete this.argsDispatch_[synArg]; | 945 delete this.argsDispatch_[synArg]; |
907 } | 946 } |
908 } | 947 } |
909 print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]); | 948 print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]); |
910 } | 949 } |
911 quit(2); | 950 quit(2); |
912 }; | 951 }; |
913 | 952 |
953 | |
954 /** | |
955 * Implements Source Map V3 model. See http://code.google.com/p/closure-compiler /wiki/SourceMaps | |
Jakob Kummerow
2013/08/23 13:08:35
This section looks a lot like http://src.chromium.
Daniel Kurka
2013/08/23 13:31:43
Actually we only need a modified load method
The
| |
956 * for format description. | |
957 * @constructor | |
958 * @param {string} sourceMappingURL | |
959 * @param {SourceMapV3} payload | |
960 */ | |
961 SourceMap = function(sourceMappingURL, payload) | |
962 { | |
963 if (!SourceMap.prototype._base64Map) { | |
964 const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx yz0123456789+/"; | |
965 SourceMap.prototype._base64Map = {}; | |
966 for (var i = 0; i < base64Digits.length; ++i) | |
967 SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i; | |
968 } | |
969 | |
970 this._sourceMappingURL = sourceMappingURL; | |
971 this._reverseMappingsBySourceURL = {}; | |
972 this._mappings = []; | |
973 this._sources = {}; | |
974 this._sourceContentByURL = {}; | |
975 this._parseMappingPayload(payload); | |
976 } | |
977 | |
978 /** | |
979 * @param {string} sourceMapURL | |
980 */ | |
981 SourceMap.load = function(sourceMapURL) | |
982 { | |
983 var content = readFile(sourceMapURL); | |
984 var sourceMapObject = /** @type {SourceMapV3} */ (JSON.parse(content)); | |
985 return new SourceMap(sourceMapURL, sourceMapObject); | |
986 } | |
987 | |
988 SourceMap.prototype = { | |
989 /** | |
990 * @return {Array.<string>} | |
991 */ | |
992 sources: function() | |
993 { | |
994 return Object.keys(this._sources); | |
995 }, | |
996 | |
997 /** | |
998 * @param {SourceMapV3} mappingPayload | |
999 */ | |
1000 _parseMappingPayload: function(mappingPayload) | |
1001 { | |
1002 if (mappingPayload.sections) | |
1003 this._parseSections(mappingPayload.sections); | |
1004 else | |
1005 this._parseMap(mappingPayload, 0, 0); | |
1006 }, | |
1007 | |
1008 /** | |
1009 * @param {Array.<SourceMapV3.Section>} sections | |
1010 */ | |
1011 _parseSections: function(sections) | |
1012 { | |
1013 for (var i = 0; i < sections.length; ++i) { | |
1014 var section = sections[i]; | |
1015 this._parseMap(section.map, section.offset.line, section.offset.colu mn); | |
1016 } | |
1017 }, | |
1018 | |
1019 /** | |
1020 * @param {number} lineNumber in compiled resource | |
1021 * @param {number} columnNumber in compiled resource | |
1022 * @return {?Array} | |
1023 */ | |
1024 findEntry: function(lineNumber, columnNumber) | |
1025 { | |
1026 var first = 0; | |
1027 var count = this._mappings.length; | |
1028 while (count > 1) { | |
1029 var step = count >> 1; | |
1030 var middle = first + step; | |
1031 var mapping = this._mappings[middle]; | |
1032 if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNum ber < mapping[1])) | |
1033 count = step; | |
1034 else { | |
1035 first = middle; | |
1036 count -= step; | |
1037 } | |
1038 } | |
1039 var entry = this._mappings[first]; | |
1040 if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0 ] && columnNumber < entry[1]))) | |
1041 return null; | |
1042 return entry; | |
1043 }, | |
1044 | |
1045 /** | |
1046 * @override | |
1047 */ | |
1048 _parseMap: function(map, lineNumber, columnNumber) | |
1049 { | |
1050 var sourceIndex = 0; | |
1051 var sourceLineNumber = 0; | |
1052 var sourceColumnNumber = 0; | |
1053 var nameIndex = 0; | |
1054 | |
1055 var sources = []; | |
1056 var originalToCanonicalURLMap = {}; | |
1057 for (var i = 0; i < map.sources.length; ++i) { | |
1058 var originalSourceURL = map.sources[i]; | |
1059 var sourceRoot = map.sourceRoot || ""; | |
1060 if (sourceRoot && !sourceRoot.endsWith("/")) | |
1061 sourceRoot += "/"; | |
1062 var href = sourceRoot + originalSourceURL; | |
1063 var url = href; | |
1064 originalToCanonicalURLMap[originalSourceURL] = url; | |
1065 sources.push(url); | |
1066 this._sources[url] = true; | |
1067 | |
1068 if (map.sourcesContent && map.sourcesContent[i]) | |
1069 this._sourceContentByURL[url] = map.sourcesContent[i]; | |
1070 } | |
1071 | |
1072 var stringCharIterator = new SourceMap.StringCharIterator(map.mappings); | |
1073 var sourceURL = sources[sourceIndex]; | |
1074 | |
1075 while (true) { | |
1076 if (stringCharIterator.peek() === ",") | |
1077 stringCharIterator.next(); | |
1078 else { | |
1079 while (stringCharIterator.peek() === ";") { | |
1080 lineNumber += 1; | |
1081 columnNumber = 0; | |
1082 stringCharIterator.next(); | |
1083 } | |
1084 if (!stringCharIterator.hasNext()) | |
1085 break; | |
1086 } | |
1087 | |
1088 columnNumber += this._decodeVLQ(stringCharIterator); | |
1089 if (this._isSeparator(stringCharIterator.peek())) { | |
1090 this._mappings.push([lineNumber, columnNumber]); | |
1091 continue; | |
1092 } | |
1093 | |
1094 var sourceIndexDelta = this._decodeVLQ(stringCharIterator); | |
1095 if (sourceIndexDelta) { | |
1096 sourceIndex += sourceIndexDelta; | |
1097 sourceURL = sources[sourceIndex]; | |
1098 } | |
1099 sourceLineNumber += this._decodeVLQ(stringCharIterator); | |
1100 sourceColumnNumber += this._decodeVLQ(stringCharIterator); | |
1101 if (!this._isSeparator(stringCharIterator.peek())) | |
1102 nameIndex += this._decodeVLQ(stringCharIterator); | |
1103 | |
1104 this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLine Number, sourceColumnNumber]); | |
1105 } | |
1106 | |
1107 for (var i = 0; i < this._mappings.length; ++i) { | |
1108 var mapping = this._mappings[i]; | |
1109 var url = mapping[2]; | |
1110 if (!url) | |
1111 continue; | |
1112 if (!this._reverseMappingsBySourceURL[url]) | |
1113 this._reverseMappingsBySourceURL[url] = []; | |
1114 var reverseMappings = this._reverseMappingsBySourceURL[url]; | |
1115 var sourceLine = mapping[3]; | |
1116 if (!reverseMappings[sourceLine]) | |
1117 reverseMappings[sourceLine] = [mapping[0], mapping[1]]; | |
1118 } | |
1119 }, | |
1120 | |
1121 /** | |
1122 * @param {string} char | |
1123 * @return {boolean} | |
1124 */ | |
1125 _isSeparator: function(char) | |
1126 { | |
1127 return char === "," || char === ";"; | |
1128 }, | |
1129 | |
1130 /** | |
1131 * @param {SourceMap.StringCharIterator} stringCharIterator | |
1132 * @return {number} | |
1133 */ | |
1134 _decodeVLQ: function(stringCharIterator) | |
1135 { | |
1136 // Read unsigned value. | |
1137 var result = 0; | |
1138 var shift = 0; | |
1139 do { | |
1140 var digit = this._base64Map[stringCharIterator.next()]; | |
1141 result += (digit & this._VLQ_BASE_MASK) << shift; | |
1142 shift += this._VLQ_BASE_SHIFT; | |
1143 } while (digit & this._VLQ_CONTINUATION_MASK); | |
1144 | |
1145 // Fix the sign. | |
1146 var negative = result & 1; | |
1147 result >>= 1; | |
1148 return negative ? -result : result; | |
1149 }, | |
1150 | |
1151 _VLQ_BASE_SHIFT: 5, | |
1152 _VLQ_BASE_MASK: (1 << 5) - 1, | |
1153 _VLQ_CONTINUATION_MASK: 1 << 5 | |
1154 } | |
1155 | |
1156 /** | |
1157 * @constructor | |
1158 * @param {string} string | |
1159 */ | |
1160 SourceMap.StringCharIterator = function(string) | |
1161 { | |
1162 this._string = string; | |
1163 this._position = 0; | |
1164 } | |
1165 | |
1166 SourceMap.StringCharIterator.prototype = { | |
1167 /** | |
1168 * @return {string} | |
1169 */ | |
1170 next: function() | |
1171 { | |
1172 return this._string.charAt(this._position++); | |
1173 }, | |
1174 | |
1175 /** | |
1176 * @return {string} | |
1177 */ | |
1178 peek: function() | |
1179 { | |
1180 return this._string.charAt(this._position); | |
1181 }, | |
1182 | |
1183 /** | |
1184 * @return {boolean} | |
1185 */ | |
1186 hasNext: function() | |
1187 { | |
1188 return this._position < this._string.length; | |
1189 } | |
1190 } | |
OLD | NEW |