From 5b4d55d6e532e3c2cd7284fe951fc792108a62af Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 17 Jan 2022 08:18:19 -0800 Subject: [PATCH] Use a WeakMap to associate indexes to Positions until we decide whether to make index an actual property in the next minor release. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/error.js | 7 ++++--- packages/babel-parser/src/parser/expression.js | 17 ++++++++++++----- packages/babel-parser/src/parser/node.js | 11 ++++++----- packages/babel-parser/src/parser/util.js | 7 ++++--- packages/babel-parser/src/plugins/flow/index.js | 4 ++-- packages/babel-parser/src/plugins/jsx/index.js | 5 +++-- packages/babel-parser/src/tokenizer/index.js | 17 ++++++++++++----- .../babel-parser/src/util/expression-scope.js | 8 +++++--- packages/babel-parser/src/util/location.js | 13 ++++++++++--- 9 files changed, 58 insertions(+), 31 deletions(-) diff --git a/packages/babel-parser/src/parser/error.js b/packages/babel-parser/src/parser/error.js index db9e3de14d19..c8d6c4eeef51 100644 --- a/packages/babel-parser/src/parser/error.js +++ b/packages/babel-parser/src/parser/error.js @@ -1,6 +1,6 @@ // @flow /* eslint sort-keys: "error" */ -import { type Position } from "../util/location"; +import { type Position, indexes } from "../util/location"; import CommentsParser from "./comments"; import { type ErrorCode, ErrorCodes } from "./error-codes"; import { type Node } from "../types"; @@ -111,7 +111,8 @@ export default class ParserError extends CommentsParser { { code, template }: ErrorTemplate, ...params: any ): Error | empty { - const pos = loc.index; + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + const pos: number = indexes.get(loc); const message = template.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; @@ -138,7 +139,7 @@ export default class ParserError extends CommentsParser { errorTemplate: string, ...params: any ): Error | empty { - const pos = loc.index; + const pos = indexes.get(loc); const message = errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 9759b69544cd..a5671162f971 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -44,7 +44,11 @@ import { isIdentifierStart, canBeReservedWord, } from "../util/identifier"; -import { Position, createPositionWithColumnOffset } from "../util/location"; +import { + indexes, + Position, + createPositionWithColumnOffset, +} from "../util/location"; import * as charCodes from "charcodes"; import { BIND_OUTSIDE, @@ -313,13 +317,15 @@ export default class ExpressionParser extends LValParser { if ( refExpressionErrors.doubleProtoLoc != null && - refExpressionErrors.doubleProtoLoc.index >= startPos + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(refExpressionErrors.doubleProtoLoc) >= startPos ) { refExpressionErrors.doubleProtoLoc = null; // reset because double __proto__ is valid in assignment expression } if ( refExpressionErrors.shorthandAssignLoc != null && - refExpressionErrors.shorthandAssignLoc.index >= startPos + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(refExpressionErrors.shorthandAssignLoc) >= startPos ) { refExpressionErrors.shorthandAssignLoc = null; // reset because shorthand default was used correctly } @@ -912,7 +918,7 @@ export default class ExpressionParser extends LValParser { return ( base.type === "Identifier" && base.name === "async" && - this.state.lastTokEndLoc.index === base.end && + indexes.get(this.state.lastTokEndLoc) === base.end && !this.canInsertSemicolon() && // check there are no escape sequences, such as \u{61}sync base.end - base.start === 5 && @@ -1764,7 +1770,8 @@ export default class ExpressionParser extends LValParser { this.takeSurroundingComments( val, startPos, - this.state.lastTokEndLoc.index, + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(this.state.lastTokEndLoc), ); return val; diff --git a/packages/babel-parser/src/parser/node.js b/packages/babel-parser/src/parser/node.js index b12c533ce96b..6c7805a2276d 100644 --- a/packages/babel-parser/src/parser/node.js +++ b/packages/babel-parser/src/parser/node.js @@ -2,7 +2,7 @@ import type Parser from "./index"; import UtilParser from "./util"; -import { SourceLocation, type Position } from "../util/location"; +import { indexes, type Position, SourceLocation } from "../util/location"; import type { Comment, Node as NodeType, NodeBase } from "../types"; // Start an AST node, attaching a start offset. @@ -126,9 +126,9 @@ export class NodeUtils extends UtilParser { ); } node.type = type; - node.end = endLoc.index; + node.end = indexes.get(endLoc); node.loc.end = endLoc; - if (this.options.ranges) node.range[1] = endLoc.index; + if (this.options.ranges) node.range[1] = node.end; if (this.options.attachComment) this.processComment(node); return node; } @@ -143,9 +143,10 @@ export class NodeUtils extends UtilParser { node: NodeBase, endLoc?: Position = this.state.lastTokEndLoc, ): void { - node.end = endLoc.index; + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + node.end = indexes.get(endLoc); node.loc.end = endLoc; - if (this.options.ranges) node.range[1] = endLoc.index; + if (this.options.ranges) node.range[1] = node.end; } /** diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 3818fbc6838e..6f1cecae9845 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -1,6 +1,6 @@ // @flow -import { type Position } from "../util/location"; +import { indexes, type Position } from "../util/location"; import { tokenIsLiteralPropertyName, tokenLabelName, @@ -120,7 +120,7 @@ export default class UtilParser extends Tokenizer { hasPrecedingLineBreak(): boolean { return lineBreak.test( - this.input.slice(this.state.lastTokEndLoc.index, this.state.start), + this.input.slice(indexes.get(this.state.lastTokEndLoc), this.state.start), ); } @@ -152,7 +152,8 @@ export default class UtilParser extends Tokenizer { // Throws if the current token and the prev one are separated by a space. assertNoSpace(message: string = "Unexpected space."): void { - if (this.state.start > this.state.lastTokEndLoc.index) { + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + if (this.state.start > indexes.get(this.state.lastTokEndLoc)) { /* eslint-disable @babel/development-internal/dry-error-messages */ this.raise( { diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 07ea2109d1e8..93d67ce4e42f 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -17,7 +17,7 @@ import { tokenIsFlowInterfaceOrTypeOrOpaque, } from "../../tokenizer/types"; import * as N from "../../types"; -import { Position } from "../../util/location"; +import { indexes, Position } from "../../util/location"; import { types as tc } from "../../tokenizer/context"; import * as charCodes from "charcodes"; import { isIteratorStart } from "../../util/identifier"; @@ -269,7 +269,7 @@ export default (superClass: Class): Class => this.next(); // eat `%` this.expectContextual(tt._checks); // Force '%' and 'checks' to be adjacent - if (this.state.lastTokStart > moduloLoc.index + 1) { + if (this.state.lastTokStart > indexes.get(moduloLoc) + 1) { this.raise(FlowErrors.UnexpectedSpaceBetweenModuloChecks, { at: moduloLoc, }); diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 257fb76ed5a8..4d6af8d2f3bc 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -18,7 +18,7 @@ import { import { TokContext, types as tc } from "../../tokenizer/context"; import * as N from "../../types"; import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; -import type { Position } from "../../util/location"; +import { indexes, type Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; @@ -327,7 +327,8 @@ export default (superClass: Class): Class => jsxParseEmptyExpression(): N.JSXEmptyExpression { const node = this.startNodeAt( - this.state.lastTokEndLoc.index, + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(this.state.lastTokEndLoc), this.state.lastTokEndLoc, ); return this.finishNodeAt(node, "JSXEmptyExpression", this.state.startLoc); diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 1e198600f865..f9597200fe74 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -3,7 +3,11 @@ /*:: declare var invariant; */ import type { Options } from "../options"; -import { Position, createPositionWithColumnOffset } from "../util/location"; +import { + indexes, + Position, + createPositionWithColumnOffset, +} from "../util/location"; import * as N from "../types"; import * as charCodes from "charcodes"; import { isIdentifierStart, isIdentifierChar } from "../util/identifier"; @@ -1249,7 +1253,7 @@ export default class Tokenizer extends ParserErrors { if (isBigInt) { const str = this.input - .slice(startLoc.index, this.state.pos) + .slice(indexes.get(startLoc), this.state.pos) .replace(/[_n]/g, ""); this.finishToken(tt.bigint, str); return; @@ -1494,10 +1498,12 @@ export default class Tokenizer extends ParserErrors { } recordStrictModeErrors(message: ErrorTemplate, loc: Position) { - if (this.state.strict && !this.state.strictErrors.has(loc.index)) { + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + const index: number = indexes.get(loc); + if (this.state.strict && !this.state.strictErrors.has(index)) { this.raise(message, { at: loc }); } else { - this.state.strictErrors.set(loc.index, { loc, message }); + this.state.strictErrors.set(index, { loc, message }); } } @@ -1607,7 +1613,8 @@ export default class Tokenizer extends ParserErrors { if (throwOnInvalid) { this.raise(Errors.InvalidEscapeSequence, { at: codeLoc }); } else { - this.state.pos = codeLoc.index - 1; + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + this.state.pos = indexes.get(codeLoc) - 1; } } return n; diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 2cc38db8c121..a4f42259a6dd 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -1,7 +1,7 @@ // @flow import type { ErrorData, ErrorTemplate, raiseFunction } from "../parser/error"; -import { Position } from "./location"; +import { indexes, Position } from "./location"; /*:: declare var invariant; */ /** @@ -80,10 +80,12 @@ class ArrowHeadParsingScope extends ExpressionScope { super(type); } recordDeclarationError(message: ErrorTemplate, loc: Position) { - this.errors.set(loc.index, { message, loc }); + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + this.errors.set(indexes.get(loc), { message, loc }); } clearDeclarationError(loc: Position) { - this.errors.delete(loc.index); + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + this.errors.delete(indexes.get(loc)); } iterateErrors(iterator: (data: ErrorData) => void) { this.errors.forEach(iterator); diff --git a/packages/babel-parser/src/util/location.js b/packages/babel-parser/src/util/location.js index 49ace09998d0..e1f21d86226f 100644 --- a/packages/babel-parser/src/util/location.js +++ b/packages/babel-parser/src/util/location.js @@ -17,7 +17,8 @@ export class Position { this.column = col; // this.index = index; - Object.defineProperty(this, "index", { enumerable: false, value: index }); + // Object.defineProperty(this, "index", { enumerable: false, value: index }); + indexes.set(this, index); } } @@ -34,6 +35,8 @@ export class SourceLocation { } } +export const indexes: WeakMap = new WeakMap(); + /** * creates a new position with a non-zero column offset from the given position. * This function should be only be used when we create AST node out of the token @@ -49,6 +52,10 @@ export function createPositionWithColumnOffset( position: Position, columnOffset: number, ) { - const { line, column, index } = position; - return new Position(line, column + columnOffset, index + columnOffset); + const { line, column } = position; + return new Position( + line, + column + columnOffset, + indexes.get(position) + columnOffset, + ); }