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

Side by Side Diff: lib/dartdoc/frog/block_scope.dart

Issue 10696191: Frog removed from dartdoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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
6 class BlockScope {
7 MethodGenerator enclosingMethod;
8 BlockScope parent;
9
10 // TODO(jimhug): Using a list or tree-based map may improve perf; the list
11 // is normally small.
12 CopyOnWriteMap<String, VariableValue> _vars;
13
14 /** Used JS names, if different from the Dart name. */
15 Set<String> _jsNames;
16
17 /**
18 * Variables in this method that have been captured by lambdas.
19 * Don't reuse the names in child blocks.
20 */
21 Set<String> _closedOver;
22
23 /** If we are in a catch block, this is the exception variable to rethrow. */
24 String rethrow;
25
26 /**
27 * True if the block is reentrant while the current method is executing.
28 * This is only used for the blocks within loops.
29 */
30 bool reentrant;
31
32 /** Tracks the node that this scope is associated with, for debugging */
33 Node node;
34
35 /** True if we should try to infer types for this block. */
36 bool inferTypes;
37
38 BlockScope(this.enclosingMethod, this.parent, this.node,
39 [bool reentrant = false])
40 : this.reentrant = reentrant,
41 _vars = new CopyOnWriteMap<String, VariableValue>(),
42 _jsNames = new Set<String>() {
43
44 if (isMethodScope) {
45 _closedOver = new Set<String>();
46 } else {
47 // Blocks within a reentrant block are also reentrant.
48 this.reentrant = reentrant || parent.reentrant;
49 }
50 inferTypes = options.inferTypes && (parent == null || parent.inferTypes);
51 }
52
53 /** See the [snapshot] method for a description. */
54 BlockScope._snapshot(BlockScope original)
55 : enclosingMethod = original.enclosingMethod,
56 parent = original.parent == null ? null : original.parent.snapshot(),
57 _vars = original._vars.clone(),
58 node = original.node,
59 inferTypes = original.inferTypes,
60 rethrow = original.rethrow,
61 // TODO(jmesserly): are these this right?
62 _jsNames = original._jsNames,
63 _closedOver = original._closedOver;
64
65 /** True if this is the top level scope of the method. */
66 bool get isMethodScope() {
67 return parent == null || parent.enclosingMethod != enclosingMethod;
68 }
69
70 /**
71 * Gets the method scope associated with this block scope (possibly itself).
72 */
73 BlockScope get methodScope() {
74 var s = this;
75 while (!s.isMethodScope) s = s.parent;
76 return s;
77 }
78
79 VariableValue lookup(String name) {
80 for (var s = this; s != null; s = s.parent) {
81 VariableValue ret = s._vars[name];
82 if (ret != null) return _capture(s, ret);
83 }
84 return null;
85 }
86
87 void inferAssign(String name, Value value) {
88 if (inferTypes) assign(name, value);
89 }
90
91 void assign(String name, Value value) {
92 for (var s = this; s != null; s = s.parent) {
93 var existing = s._vars[name];
94 if (existing != null) {
95 s._vars[name] = existing.replaceValue(value);
96 return;
97 }
98 }
99 world.internalError("assigning variable '${name}' that doesn't exist.");
100 }
101
102 Value _capture(BlockScope other, Value value) {
103 // If this variable is from a different method, it means we closed over
104 // it in the child lambda. Time for some bookeeping!
105 if (other.enclosingMethod != enclosingMethod) {
106 // Make sure the parent method doesn't reuse this variable to mean
107 // something else.
108 other.methodScope._closedOver.add(value.code);
109
110 // If the scope we found this variable in is reentrant, remember the
111 // variable. The lambda we're in will capture it with Function.bind.
112 if (enclosingMethod.captures != null && other.reentrant) {
113 enclosingMethod.captures.add(value.code);
114 }
115 }
116 return value;
117 }
118
119 /**
120 * Returns true if we can't use this name because we would be shadowing
121 * another name in the JS that we might need to access later.
122 */
123 bool _isDefinedInParent(String name) {
124 if (isMethodScope && _closedOver.contains(name)) return true;
125
126 for (var s = parent; s != null; s = s.parent) {
127 if (s._vars.containsKey(name)) return true;
128 if (s._jsNames.contains(name)) return true;
129 // Don't reuse a name that's been closed over
130 if (s.isMethodScope && s._closedOver.contains(name)) return true;
131 }
132
133 // Ensure that we don't shadow another name that would've been accessible,
134 // like top level names.
135 // (This lookup might report errors, which is a bit strange.
136 // But probably harmless since we have to pay for the lookup anyway.)
137 // TODO(jmesserly): does this work right if JS name of the top-level thing
138 // is different from Dart name?
139 final type = enclosingMethod.method.declaringType;
140 if (type.library.lookup(name, null) != null) return true;
141
142 // Nobody else needs this name. It's safe to reuse.
143 return false;
144 }
145
146
147 VariableValue create(String name, Type type, SourceSpan span,
148 [bool isFinal = false, bool isParameter = false]) {
149
150 var jsName = world.toJsIdentifier(name);
151 if (_vars.containsKey(name)) {
152 world.error('duplicate name "$name"', span);
153 }
154
155 // Make sure variables don't shadow any names we might need to access.
156 if (!isParameter) {
157 int index = 0;
158 while (_isDefinedInParent(jsName)) {
159 jsName = '$name${index++}';
160 }
161 }
162
163 var ret = new VariableValue(type, jsName, span, isFinal);
164 _vars[name] = ret;
165 if (name != jsName) _jsNames.add(jsName);
166 return ret;
167 }
168
169 Value declareParameter(Parameter p) {
170 return create(p.name, p.type, p.definition.span, isParameter:true);
171 }
172
173 /** Declares a variable in the current scope for this identifier. */
174 Value declare(DeclaredIdentifier id) {
175 var type = enclosingMethod.method.resolveType(id.type, false, true);
176 return create(id.name.name, type, id.span);
177 }
178
179 /**
180 * Finds the first lexically enclosing catch block, if any, and returns its
181 * exception variable.
182 */
183 String getRethrow() {
184 var scope = this;
185 while (scope.rethrow == null && scope.parent != null) {
186 scope = scope.parent;
187 }
188 return scope.rethrow;
189 }
190
191 /**
192 * Creates a snapshot of an existing BlockScope. Both the original and the
193 * returned copy are writable. Clones all the way to the root node.
194 */
195 // TODO(jmesserly): this might need to be optimized.
196 BlockScope snapshot() => new BlockScope._snapshot(this);
197
198 /**
199 * Unifies variable values with the ones in [other]. Returns `true` if
200 * anything changed, `false` otherwise.
201 */
202 bool unionWith(BlockScope other) {
203 bool changed = false;
204 if (parent != null) {
205 changed = parent.unionWith(other.parent);
206 }
207
208 // Optimization: check if the copy-on-write maps are the same
209 if (_vars._map !== other._vars._map) {
210 other._vars.forEach((String key, VariableValue otherVar) {
211 VariableValue myVar = _vars[key];
212 Value v = Value.union(myVar.value, otherVar.value);
213 if (myVar.value !== v) {
214 _vars[key] = myVar.replaceValue(v);
215 changed = true;
216 }
217 });
218 }
219
220 return changed;
221 }
222 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698