| Index: lib/src/codegen/js_names.dart
|
| diff --git a/lib/src/codegen/js_names.dart b/lib/src/codegen/js_names.dart
|
| deleted file mode 100644
|
| index 867d85b0e89bd769a0e6b8348ba11a175f6258fa..0000000000000000000000000000000000000000
|
| --- a/lib/src/codegen/js_names.dart
|
| +++ /dev/null
|
| @@ -1,303 +0,0 @@
|
| -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -import 'dart:collection';
|
| -
|
| -import '../js/js_ast.dart';
|
| -import 'package:dev_compiler/src/options.dart';
|
| -
|
| -/// Unique instance for temporary variables. Will be renamed consistently
|
| -/// across the entire file. Different instances will be named differently
|
| -/// even if they have the same name, this makes it safe to use in code
|
| -/// generation without needing global knowledge. See [JSNamer].
|
| -///
|
| -// TODO(jmesserly): move into js_ast? add a boolean to Identifier?
|
| -class TemporaryId extends Identifier {
|
| - TemporaryId(String name) : super(name);
|
| -}
|
| -
|
| -/// Creates a qualified identifier, without determining for sure if it needs to
|
| -/// be qualified until [setQualified] is called.
|
| -///
|
| -/// This expression is transparent to visiting after [setQualified].
|
| -class MaybeQualifiedId extends Expression {
|
| - Expression _expr;
|
| -
|
| - final Identifier qualifier;
|
| - final Expression name;
|
| -
|
| - MaybeQualifiedId(this.qualifier, this.name) {
|
| - _expr = new PropertyAccess(qualifier, name);
|
| - }
|
| -
|
| - /// Helper to create an [Identifier] from something that starts as a property.
|
| - static identifier(LiteralString propertyName) =>
|
| - new Identifier(propertyName.valueWithoutQuotes);
|
| -
|
| - void setQualified(bool qualified) {
|
| - if (!qualified && name is LiteralString) {
|
| - _expr = identifier(name);
|
| - }
|
| - }
|
| -
|
| - int get precedenceLevel => _expr.precedenceLevel;
|
| -
|
| - accept(NodeVisitor visitor) => _expr.accept(visitor);
|
| -
|
| - void visitChildren(NodeVisitor visitor) => _expr.visitChildren(visitor);
|
| -}
|
| -
|
| -/// This class has two purposes:
|
| -///
|
| -/// * rename JS identifiers to avoid keywords.
|
| -/// * rename temporary variables to avoid colliding with user-specified names,
|
| -/// or other temporaries
|
| -///
|
| -/// Each instance of [TemporaryId] is treated as a unique variable, with its
|
| -/// `name` field simply the suggestion of what name to use. By contrast
|
| -/// [Identifiers] are never renamed unless they are an invalid identifier, like
|
| -/// `function` or `instanceof`, and their `name` field controls whether they
|
| -/// refer to the same variable.
|
| -class TemporaryNamer extends LocalNamer {
|
| - _FunctionScope scope;
|
| -
|
| - TemporaryNamer(Node node) : scope = new _RenameVisitor.build(node).rootScope;
|
| -
|
| - String getName(Identifier node) {
|
| - var rename = scope.renames[identifierKey(node)];
|
| - if (rename != null) return rename;
|
| - return node.name;
|
| - }
|
| -
|
| - void enterScope(FunctionExpression node) {
|
| - scope = scope.functions[node];
|
| - }
|
| -
|
| - void leaveScope() {
|
| - scope = scope.parent;
|
| - }
|
| -}
|
| -
|
| -/// Represents a complete function scope in JS.
|
| -///
|
| -/// We don't currently track ES6 block scopes, because we don't represent them
|
| -/// in js_ast yet.
|
| -class _FunctionScope {
|
| - /// The parent scope.
|
| - final _FunctionScope parent;
|
| -
|
| - /// All names declared in this scope.
|
| - final declared = new HashSet<Object>();
|
| -
|
| - /// All names [declared] in this scope or its [parent]s, that is used in this
|
| - /// scope and/or children. This is exactly the set of variable names we must
|
| - /// not collide with inside this scope.
|
| - final used = new HashSet<String>();
|
| -
|
| - /// Nested functions, these are visited after everything else so the names
|
| - /// they might need are in scope.
|
| - final functions = new Map<FunctionExpression, _FunctionScope>();
|
| -
|
| - /// New names assigned for temps and identifiers.
|
| - final renames = new HashMap<Object, String>();
|
| -
|
| - _FunctionScope(this.parent);
|
| -}
|
| -
|
| -/// Collects all names used in the visited tree.
|
| -class _RenameVisitor extends VariableDeclarationVisitor {
|
| - final pendingRenames = new Map<Object, Set<_FunctionScope>>();
|
| -
|
| - final _FunctionScope rootScope = new _FunctionScope(null);
|
| - _FunctionScope scope;
|
| -
|
| - _RenameVisitor.build(Node root) {
|
| - scope = rootScope;
|
| - root.accept(this);
|
| - _finishFunctions();
|
| - _finishNames();
|
| - }
|
| -
|
| - declare(Identifier node) {
|
| - var id = identifierKey(node);
|
| - var notAlreadyDeclared = scope.declared.add(id);
|
| - // Normal identifiers can be declared multiple times, because we don't
|
| - // implement block scope yet. However temps should only be declared once.
|
| - assert(notAlreadyDeclared || node is! TemporaryId);
|
| - _markUsed(node, id, scope);
|
| - }
|
| -
|
| - visitIdentifier(Identifier node) {
|
| - var id = identifierKey(node);
|
| -
|
| - // Find where the node was declared.
|
| - var declScope = scope;
|
| - while (declScope != null && !declScope.declared.contains(id)) {
|
| - declScope = declScope.parent;
|
| - }
|
| - if (declScope == null) {
|
| - // Assume it comes from the global scope.
|
| - declScope = rootScope;
|
| - declScope.declared.add(id);
|
| - }
|
| - _markUsed(node, id, declScope);
|
| - }
|
| -
|
| - _markUsed(Identifier node, Object id, _FunctionScope declScope) {
|
| - // If it needs rename, we can't add it to the used name set yet, instead we
|
| - // will record all scopes it is visible in.
|
| - Set<_FunctionScope> usedIn = null;
|
| - var rename = declScope != rootScope && needsRename(node);
|
| - if (rename) {
|
| - usedIn = pendingRenames.putIfAbsent(id, () => new HashSet());
|
| - }
|
| - for (var s = scope, end = declScope.parent; s != end; s = s.parent) {
|
| - if (usedIn != null) {
|
| - usedIn.add(s);
|
| - } else {
|
| - s.used.add(node.name);
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitFunctionExpression(FunctionExpression node) {
|
| - // Visit nested functions after all identifiers are declared.
|
| - scope.functions[node] = new _FunctionScope(scope);
|
| - }
|
| -
|
| - void _finishFunctions() {
|
| - scope.functions.forEach((FunctionExpression f, _FunctionScope s) {
|
| - scope = s;
|
| - super.visitFunctionExpression(f);
|
| - _finishFunctions();
|
| - scope = scope.parent;
|
| - });
|
| - }
|
| -
|
| - void _finishNames() {
|
| - var allNames = new Set<String>();
|
| - pendingRenames.forEach((id, scopes) {
|
| - allNames.clear();
|
| - for (var s in scopes) allNames.addAll(s.used);
|
| -
|
| - var name = _findName(id, allNames);
|
| - for (var s in scopes) {
|
| - s.used.add(name);
|
| - s.renames[id] = name;
|
| - }
|
| - });
|
| - }
|
| -
|
| - static String _findName(Object id, Set<String> usedNames) {
|
| - String name;
|
| - bool valid;
|
| - if (id is TemporaryId) {
|
| - name = id.name;
|
| - valid = !invalidVariableName(name);
|
| - } else {
|
| - name = id;
|
| - valid = false;
|
| - }
|
| -
|
| - // Try to use the temp's name, otherwise rename.
|
| - String candidate;
|
| - if (valid && !usedNames.contains(name)) {
|
| - candidate = name;
|
| - } else {
|
| - // This assumes that collisions are rare, hence linear search.
|
| - // If collisions become common we need a better search.
|
| - // TODO(jmesserly): what's the most readable scheme here? Maybe 1-letter
|
| - // names in some cases?
|
| - candidate = name == 'function' ? 'func' : '${name}\$';
|
| - for (int i = 0; usedNames.contains(candidate); i++) {
|
| - candidate = '${name}\$$i';
|
| - }
|
| - }
|
| - return candidate;
|
| - }
|
| -}
|
| -
|
| -bool needsRename(Identifier node) =>
|
| - node is TemporaryId || node.allowRename && invalidVariableName(node.name);
|
| -
|
| -Object /*String|TemporaryId*/ identifierKey(Identifier node) =>
|
| - node is TemporaryId ? node : node.name;
|
| -
|
| -/// Returns true for invalid JS variable names, such as keywords.
|
| -/// Also handles invalid variable names in strict mode, like "arguments".
|
| -bool invalidVariableName(String keyword, {bool strictMode: true}) {
|
| - switch (keyword) {
|
| - // http://www.ecma-international.org/ecma-262/6.0/#sec-future-reserved-words
|
| - case "await":
|
| -
|
| - case "break":
|
| - case "case":
|
| - case "catch":
|
| - case "class":
|
| - case "const":
|
| - case "continue":
|
| - case "debugger":
|
| - case "default":
|
| - case "delete":
|
| - case "do":
|
| - case "else":
|
| - case "enum":
|
| - case "export":
|
| - case "extends":
|
| - case "finally":
|
| - case "for":
|
| - case "function":
|
| - case "if":
|
| - case "import":
|
| - case "in":
|
| - case "instanceof":
|
| - case "let":
|
| - case "new":
|
| - case "return":
|
| - case "super":
|
| - case "switch":
|
| - case "this":
|
| - case "throw":
|
| - case "try":
|
| - case "typeof":
|
| - case "var":
|
| - case "void":
|
| - case "while":
|
| - case "with":
|
| - return true;
|
| - case "arguments":
|
| - case "eval":
|
| - // http://www.ecma-international.org/ecma-262/6.0/#sec-future-reserved-words
|
| - // http://www.ecma-international.org/ecma-262/6.0/#sec-identifiers-static-semantics-early-errors
|
| - case "implements":
|
| - case "interface":
|
| - case "let":
|
| - case "package":
|
| - case "private":
|
| - case "protected":
|
| - case "public":
|
| - case "static":
|
| - case "yield":
|
| - return strictMode;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -/// Returns true for invalid static field names in strict mode.
|
| -/// In particular, "caller" "callee" and "arguments" cannot be used.
|
| -bool invalidStaticFieldName(String name) {
|
| - switch (name) {
|
| - case "arguments":
|
| - case "caller":
|
| - case "callee":
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -/// We cannot destructure named params that clash with JS reserved names:
|
| -/// see discussion in https://github.com/dart-lang/dev_compiler/issues/392.
|
| -bool canDestructureNamedParams(Iterable<String> names, CodegenOptions options) {
|
| - return options.destructureNamedParams && !names.any(invalidVariableName);
|
| -}
|
|
|