Use set in parser scope (#13408)

* chore: rename benchmark

* perf: back parser scope names storage by set

* chore: add benchmark
This commit is contained in:
Huáng Jùnliàng
2021-06-01 10:35:19 -04:00
committed by GitHub
parent cbad50ac1d
commit b281fe352c
5 changed files with 85 additions and 46 deletions

View File

@@ -21,13 +21,13 @@ import { Errors, type raiseFunction } from "../parser/error";
// Start an AST node, attaching a start offset.
export class Scope {
flags: ScopeFlags;
// A list of var-declared names in the current lexical scope
var: string[] = [];
// A list of lexically-declared names in the current lexical scope
lexical: string[] = [];
// A list of lexically-declared FunctionDeclaration names in the current lexical scope
functions: string[] = [];
declare flags: ScopeFlags;
// A set of var-declared names in the current lexical scope
var: Set<string> = new Set();
// A set of lexically-declared names in the current lexical scope
lexical: Set<string> = new Set();
// A set of lexically-declared FunctionDeclaration names in the current lexical scope
functions: Set<string> = new Set();
constructor(flags: ScopeFlags) {
this.flags = flags;
@@ -104,9 +104,9 @@ export default class ScopeHandler<IScope: Scope = Scope> {
this.checkRedeclarationInScope(scope, name, bindingType, pos);
if (bindingType & BIND_SCOPE_FUNCTION) {
scope.functions.push(name);
scope.functions.add(name);
} else {
scope.lexical.push(name);
scope.lexical.add(name);
}
if (bindingType & BIND_SCOPE_LEXICAL) {
@@ -116,7 +116,7 @@ export default class ScopeHandler<IScope: Scope = Scope> {
for (let i = this.scopeStack.length - 1; i >= 0; --i) {
scope = this.scopeStack[i];
this.checkRedeclarationInScope(scope, name, bindingType, pos);
scope.var.push(name);
scope.var.add(name);
this.maybeExportDefined(scope, name);
if (scope.flags & SCOPE_VAR) break;
@@ -153,38 +153,41 @@ export default class ScopeHandler<IScope: Scope = Scope> {
if (bindingType & BIND_SCOPE_LEXICAL) {
return (
scope.lexical.indexOf(name) > -1 ||
scope.functions.indexOf(name) > -1 ||
scope.var.indexOf(name) > -1
scope.lexical.has(name) ||
scope.functions.has(name) ||
scope.var.has(name)
);
}
if (bindingType & BIND_SCOPE_FUNCTION) {
return (
scope.lexical.indexOf(name) > -1 ||
(!this.treatFunctionsAsVarInScope(scope) &&
scope.var.indexOf(name) > -1)
scope.lexical.has(name) ||
(!this.treatFunctionsAsVarInScope(scope) && scope.var.has(name))
);
}
return (
(scope.lexical.indexOf(name) > -1 &&
!(scope.flags & SCOPE_SIMPLE_CATCH && scope.lexical[0] === name)) ||
(!this.treatFunctionsAsVarInScope(scope) &&
scope.functions.indexOf(name) > -1)
(scope.lexical.has(name) &&
!(
scope.flags & SCOPE_SIMPLE_CATCH &&
scope.lexical.values().next().value === name
)) ||
(!this.treatFunctionsAsVarInScope(scope) && scope.functions.has(name))
);
}
checkLocalExport(id: N.Identifier) {
const { name } = id;
const topLevelScope = this.scopeStack[0];
if (
this.scopeStack[0].lexical.indexOf(id.name) === -1 &&
this.scopeStack[0].var.indexOf(id.name) === -1 &&
!topLevelScope.lexical.has(name) &&
!topLevelScope.var.has(name) &&
// In strict mode, scope.functions will always be empty.
// Modules are strict by default, but the `scriptMode` option
// can overwrite this behavior.
this.scopeStack[0].functions.indexOf(id.name) === -1
!topLevelScope.functions.has(name)
) {
this.undefinedExports.set(id.name, id.start);
this.undefinedExports.set(name, id.start);
}
}