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

Side by Side Diff: lib/src/codegen/js_names.dart

Issue 1030063004: more care around generated names, fixes #60 #82 and #97 (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library dev_compiler.src.codegen.js_names;
6
7 import 'package:dev_compiler/src/js/js_ast.dart';
8 import 'package:dev_compiler/src/js/keywords.dart';
9
10 /// Marker subclass for temporary variables.
11 /// We treat these as being in a different scope from other identifiers, and
12 /// rename them so they don't collide. See [JSNamer].
13 // TODO(jmesserly): move into js_ast? add a boolean to Identifier?
14 class JSTemporary extends Identifier {
15 JSTemporary(String name) : super(name);
16 }
17
18 /// This class has two purposes:
19 /// * rename JS identifiers to avoid keywords.
20 /// * rename temporary variables to avoid colliding with user-specified names.
21 ///
22 /// We use a very simple algorithm:
23 /// * collect all names
24 /// * visit the tree, choosing unique names for things we need to rename
25 ///
26 /// We assume the scoping is already correct (if the isTemporary bit is
27 /// considered as part of the name), so we can do both of these renames
28 /// globally, with no regard to where the name was declared or which precise
29 /// identifier it was bound to.
30 // TODO(jmesserly): some future transforms, like ES6->5, might want a name bound
31 // tree, but we can defer that until needed.
32 class JSNamer extends LocalNamer {
33 final usedNames = new Set<String>();
34 final renames = new Map<String, String>();
35
36 JSNamer(Node node) {
37 node.accept(new _NameCollector(usedNames));
38 }
39
40 String getName(Identifier node) {
41 var name = node.name;
42 if (node is JSTemporary) {
43 return _rename(name, valid: true);
44 } else if (isJsKeyword(name)) {
45 return _rename(name, valid: false);
46 }
47 return name;
48 }
49
50 String _rename(String name, {bool valid}) {
51 var candidate = renames[name];
52 if (candidate != null) return candidate;
53
54 // Try to use the temp's name, otherwise rename.
55 if (valid && usedNames.add(name)) {
56 candidate = name;
57 } else {
58 // This assumes that collisions are rare, hence linear search.
59 // If collisions become common we need a better search.
60 // TODO(jmesserly): what's the most readable scheme here? Maybe 1-letter
61 // names in some cases?
62 candidate = name == 'function' ? 'func' : '${name}\$';
63 for (int i = 0; !usedNames.add(candidate); i++) {
64 candidate = '${name}\$$i';
65 }
66 }
67 return renames[name] = candidate;
68 }
69
70 void enterScope(Node node) {}
71 void leaveScope() {}
72 }
73
74 /// Collects all names used in the visited tree.
75 class _NameCollector extends BaseVisitor {
76 final Set<String> names;
77 _NameCollector(this.names);
78 visitIdentifier(Identifier node) {
79 if (node is! JSTemporary) names.add(node.name);
80 }
81 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698