Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Unified Diff: lib/src/codegen/js_names.dart

Issue 1879373004: Implement modular compilation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/codegen/js_module_item_order.dart ('k') | lib/src/codegen/js_printer.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
-}
« no previous file with comments | « lib/src/codegen/js_module_item_order.dart ('k') | lib/src/codegen/js_printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698