scopes.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. import { StringReader, StringWriter } from './strings';
  2. import { comma, decodeInteger, encodeInteger, hasMoreVlq, semicolon } from './vlq';
  3. const EMPTY: any[] = [];
  4. type Line = number;
  5. type Column = number;
  6. type Kind = number;
  7. type Name = number;
  8. type Var = number;
  9. type SourcesIndex = number;
  10. type ScopesIndex = number;
  11. type Mix<A, B, O> = (A & O) | (B & O);
  12. export type OriginalScope = Mix<
  13. [Line, Column, Line, Column, Kind],
  14. [Line, Column, Line, Column, Kind, Name],
  15. { vars: Var[] }
  16. >;
  17. export type GeneratedRange = Mix<
  18. [Line, Column, Line, Column],
  19. [Line, Column, Line, Column, SourcesIndex, ScopesIndex],
  20. {
  21. callsite: CallSite | null;
  22. bindings: Binding[];
  23. isScope: boolean;
  24. }
  25. >;
  26. export type CallSite = [SourcesIndex, Line, Column];
  27. type Binding = BindingExpressionRange[];
  28. export type BindingExpressionRange = [Name] | [Name, Line, Column];
  29. export function decodeOriginalScopes(input: string): OriginalScope[] {
  30. const { length } = input;
  31. const reader = new StringReader(input);
  32. const scopes: OriginalScope[] = [];
  33. const stack: OriginalScope[] = [];
  34. let line = 0;
  35. for (; reader.pos < length; reader.pos++) {
  36. line = decodeInteger(reader, line);
  37. const column = decodeInteger(reader, 0);
  38. if (!hasMoreVlq(reader, length)) {
  39. const last = stack.pop()!;
  40. last[2] = line;
  41. last[3] = column;
  42. continue;
  43. }
  44. const kind = decodeInteger(reader, 0);
  45. const fields = decodeInteger(reader, 0);
  46. const hasName = fields & 0b0001;
  47. const scope: OriginalScope = (
  48. hasName ? [line, column, 0, 0, kind, decodeInteger(reader, 0)] : [line, column, 0, 0, kind]
  49. ) as OriginalScope;
  50. let vars: Var[] = EMPTY;
  51. if (hasMoreVlq(reader, length)) {
  52. vars = [];
  53. do {
  54. const varsIndex = decodeInteger(reader, 0);
  55. vars.push(varsIndex);
  56. } while (hasMoreVlq(reader, length));
  57. }
  58. scope.vars = vars;
  59. scopes.push(scope);
  60. stack.push(scope);
  61. }
  62. return scopes;
  63. }
  64. export function encodeOriginalScopes(scopes: OriginalScope[]): string {
  65. const writer = new StringWriter();
  66. for (let i = 0; i < scopes.length; ) {
  67. i = _encodeOriginalScopes(scopes, i, writer, [0]);
  68. }
  69. return writer.flush();
  70. }
  71. function _encodeOriginalScopes(
  72. scopes: OriginalScope[],
  73. index: number,
  74. writer: StringWriter,
  75. state: [
  76. number, // GenColumn
  77. ],
  78. ): number {
  79. const scope = scopes[index];
  80. const { 0: startLine, 1: startColumn, 2: endLine, 3: endColumn, 4: kind, vars } = scope;
  81. if (index > 0) writer.write(comma);
  82. state[0] = encodeInteger(writer, startLine, state[0]);
  83. encodeInteger(writer, startColumn, 0);
  84. encodeInteger(writer, kind, 0);
  85. const fields = scope.length === 6 ? 0b0001 : 0;
  86. encodeInteger(writer, fields, 0);
  87. if (scope.length === 6) encodeInteger(writer, scope[5], 0);
  88. for (const v of vars) {
  89. encodeInteger(writer, v, 0);
  90. }
  91. for (index++; index < scopes.length; ) {
  92. const next = scopes[index];
  93. const { 0: l, 1: c } = next;
  94. if (l > endLine || (l === endLine && c >= endColumn)) {
  95. break;
  96. }
  97. index = _encodeOriginalScopes(scopes, index, writer, state);
  98. }
  99. writer.write(comma);
  100. state[0] = encodeInteger(writer, endLine, state[0]);
  101. encodeInteger(writer, endColumn, 0);
  102. return index;
  103. }
  104. export function decodeGeneratedRanges(input: string): GeneratedRange[] {
  105. const { length } = input;
  106. const reader = new StringReader(input);
  107. const ranges: GeneratedRange[] = [];
  108. const stack: GeneratedRange[] = [];
  109. let genLine = 0;
  110. let definitionSourcesIndex = 0;
  111. let definitionScopeIndex = 0;
  112. let callsiteSourcesIndex = 0;
  113. let callsiteLine = 0;
  114. let callsiteColumn = 0;
  115. let bindingLine = 0;
  116. let bindingColumn = 0;
  117. do {
  118. const semi = reader.indexOf(';');
  119. let genColumn = 0;
  120. for (; reader.pos < semi; reader.pos++) {
  121. genColumn = decodeInteger(reader, genColumn);
  122. if (!hasMoreVlq(reader, semi)) {
  123. const last = stack.pop()!;
  124. last[2] = genLine;
  125. last[3] = genColumn;
  126. continue;
  127. }
  128. const fields = decodeInteger(reader, 0);
  129. const hasDefinition = fields & 0b0001;
  130. const hasCallsite = fields & 0b0010;
  131. const hasScope = fields & 0b0100;
  132. let callsite: CallSite | null = null;
  133. let bindings: Binding[] = EMPTY;
  134. let range: GeneratedRange;
  135. if (hasDefinition) {
  136. const defSourcesIndex = decodeInteger(reader, definitionSourcesIndex);
  137. definitionScopeIndex = decodeInteger(
  138. reader,
  139. definitionSourcesIndex === defSourcesIndex ? definitionScopeIndex : 0,
  140. );
  141. definitionSourcesIndex = defSourcesIndex;
  142. range = [genLine, genColumn, 0, 0, defSourcesIndex, definitionScopeIndex] as GeneratedRange;
  143. } else {
  144. range = [genLine, genColumn, 0, 0] as GeneratedRange;
  145. }
  146. range.isScope = !!hasScope;
  147. if (hasCallsite) {
  148. const prevCsi = callsiteSourcesIndex;
  149. const prevLine = callsiteLine;
  150. callsiteSourcesIndex = decodeInteger(reader, callsiteSourcesIndex);
  151. const sameSource = prevCsi === callsiteSourcesIndex;
  152. callsiteLine = decodeInteger(reader, sameSource ? callsiteLine : 0);
  153. callsiteColumn = decodeInteger(
  154. reader,
  155. sameSource && prevLine === callsiteLine ? callsiteColumn : 0,
  156. );
  157. callsite = [callsiteSourcesIndex, callsiteLine, callsiteColumn];
  158. }
  159. range.callsite = callsite;
  160. if (hasMoreVlq(reader, semi)) {
  161. bindings = [];
  162. do {
  163. bindingLine = genLine;
  164. bindingColumn = genColumn;
  165. const expressionsCount = decodeInteger(reader, 0);
  166. let expressionRanges: BindingExpressionRange[];
  167. if (expressionsCount < -1) {
  168. expressionRanges = [[decodeInteger(reader, 0)]];
  169. for (let i = -1; i > expressionsCount; i--) {
  170. const prevBl = bindingLine;
  171. bindingLine = decodeInteger(reader, bindingLine);
  172. bindingColumn = decodeInteger(reader, bindingLine === prevBl ? bindingColumn : 0);
  173. const expression = decodeInteger(reader, 0);
  174. expressionRanges.push([expression, bindingLine, bindingColumn]);
  175. }
  176. } else {
  177. expressionRanges = [[expressionsCount]];
  178. }
  179. bindings.push(expressionRanges);
  180. } while (hasMoreVlq(reader, semi));
  181. }
  182. range.bindings = bindings;
  183. ranges.push(range);
  184. stack.push(range);
  185. }
  186. genLine++;
  187. reader.pos = semi + 1;
  188. } while (reader.pos < length);
  189. return ranges;
  190. }
  191. export function encodeGeneratedRanges(ranges: GeneratedRange[]): string {
  192. if (ranges.length === 0) return '';
  193. const writer = new StringWriter();
  194. for (let i = 0; i < ranges.length; ) {
  195. i = _encodeGeneratedRanges(ranges, i, writer, [0, 0, 0, 0, 0, 0, 0]);
  196. }
  197. return writer.flush();
  198. }
  199. function _encodeGeneratedRanges(
  200. ranges: GeneratedRange[],
  201. index: number,
  202. writer: StringWriter,
  203. state: [
  204. number, // GenLine
  205. number, // GenColumn
  206. number, // DefSourcesIndex
  207. number, // DefScopesIndex
  208. number, // CallSourcesIndex
  209. number, // CallLine
  210. number, // CallColumn
  211. ],
  212. ): number {
  213. const range = ranges[index];
  214. const {
  215. 0: startLine,
  216. 1: startColumn,
  217. 2: endLine,
  218. 3: endColumn,
  219. isScope,
  220. callsite,
  221. bindings,
  222. } = range;
  223. if (state[0] < startLine) {
  224. catchupLine(writer, state[0], startLine);
  225. state[0] = startLine;
  226. state[1] = 0;
  227. } else if (index > 0) {
  228. writer.write(comma);
  229. }
  230. state[1] = encodeInteger(writer, range[1], state[1]);
  231. const fields =
  232. (range.length === 6 ? 0b0001 : 0) | (callsite ? 0b0010 : 0) | (isScope ? 0b0100 : 0);
  233. encodeInteger(writer, fields, 0);
  234. if (range.length === 6) {
  235. const { 4: sourcesIndex, 5: scopesIndex } = range;
  236. if (sourcesIndex !== state[2]) {
  237. state[3] = 0;
  238. }
  239. state[2] = encodeInteger(writer, sourcesIndex, state[2]);
  240. state[3] = encodeInteger(writer, scopesIndex, state[3]);
  241. }
  242. if (callsite) {
  243. const { 0: sourcesIndex, 1: callLine, 2: callColumn } = range.callsite!;
  244. if (sourcesIndex !== state[4]) {
  245. state[5] = 0;
  246. state[6] = 0;
  247. } else if (callLine !== state[5]) {
  248. state[6] = 0;
  249. }
  250. state[4] = encodeInteger(writer, sourcesIndex, state[4]);
  251. state[5] = encodeInteger(writer, callLine, state[5]);
  252. state[6] = encodeInteger(writer, callColumn, state[6]);
  253. }
  254. if (bindings) {
  255. for (const binding of bindings) {
  256. if (binding.length > 1) encodeInteger(writer, -binding.length, 0);
  257. const expression = binding[0][0];
  258. encodeInteger(writer, expression, 0);
  259. let bindingStartLine = startLine;
  260. let bindingStartColumn = startColumn;
  261. for (let i = 1; i < binding.length; i++) {
  262. const expRange = binding[i];
  263. bindingStartLine = encodeInteger(writer, expRange[1]!, bindingStartLine);
  264. bindingStartColumn = encodeInteger(writer, expRange[2]!, bindingStartColumn);
  265. encodeInteger(writer, expRange[0]!, 0);
  266. }
  267. }
  268. }
  269. for (index++; index < ranges.length; ) {
  270. const next = ranges[index];
  271. const { 0: l, 1: c } = next;
  272. if (l > endLine || (l === endLine && c >= endColumn)) {
  273. break;
  274. }
  275. index = _encodeGeneratedRanges(ranges, index, writer, state);
  276. }
  277. if (state[0] < endLine) {
  278. catchupLine(writer, state[0], endLine);
  279. state[0] = endLine;
  280. state[1] = 0;
  281. } else {
  282. writer.write(comma);
  283. }
  284. state[1] = encodeInteger(writer, endColumn, state[1]);
  285. return index;
  286. }
  287. function catchupLine(writer: StringWriter, lastLine: number, line: number) {
  288. do {
  289. writer.write(semicolon);
  290. } while (++lastLine < line);
  291. }