OLD | NEW |
---|---|
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 class LibraryImport { | 5 class LibraryImport { |
6 String prefix; | 6 final String prefix; |
7 Library library; | 7 final Library library; |
8 LibraryImport(this.library, [this.prefix = null]); | 8 final SourceSpan span; |
9 LibraryImport(this.library, [this.prefix, this.span]); | |
10 } | |
11 | |
12 // TODO(jimhug): Make this more useful for good error messages. | |
13 class AmbiguousMember extends Member { | |
14 List<Member> members; | |
15 AmbiguousMember(String name, this.members): super(name, null); | |
9 } | 16 } |
10 | 17 |
11 | 18 |
12 /** Represents a Dart library. */ | 19 /** Represents a Dart library. */ |
13 class Library extends Element { | 20 class Library extends Element { |
14 final SourceFile baseSource; | 21 final SourceFile baseSource; |
15 Map<String, DefinedType> types; | 22 Map<String, DefinedType> types; |
16 List<LibraryImport> imports; | 23 List<LibraryImport> imports; |
17 String sourceDir; | 24 String sourceDir; |
18 List<SourceFile> natives; | 25 List<SourceFile> natives; |
19 List<SourceFile> sources; | 26 List<SourceFile> sources; |
20 | 27 |
28 Map<String, Member> _topNames; | |
21 Map<String, MemberSet> _privateMembers; | 29 Map<String, MemberSet> _privateMembers; |
22 | 30 |
23 /** The type that holds top level types in the library. */ | 31 /** The type that holds top level types in the library. */ |
24 DefinedType topType; | 32 DefinedType topType; |
25 | 33 |
26 /** Set to true by [WorldGenerator] once this type has been written. */ | 34 /** Set to true by [WorldGenerator] once this type has been written. */ |
27 bool isWritten = false; | 35 bool isWritten = false; |
28 | 36 |
29 Library(this.baseSource) : super(null, null) { | 37 Library(this.baseSource) : super(null, null) { |
30 sourceDir = dirname(baseSource.filename); | 38 sourceDir = dirname(baseSource.filename); |
(...skipping 21 matching lines...) Expand all Loading... | |
52 String makeFullPath(String filename) { | 60 String makeFullPath(String filename) { |
53 if (filename.startsWith('dart:')) return filename; | 61 if (filename.startsWith('dart:')) return filename; |
54 // TODO(jmesserly): replace with node.js path.resolve | 62 // TODO(jmesserly): replace with node.js path.resolve |
55 if (filename.startsWith('/')) return filename; | 63 if (filename.startsWith('/')) return filename; |
56 if (filename.startsWith('file:///')) return filename; | 64 if (filename.startsWith('file:///')) return filename; |
57 if (filename.startsWith('http://')) return filename; | 65 if (filename.startsWith('http://')) return filename; |
58 return joinPaths(sourceDir, filename); | 66 return joinPaths(sourceDir, filename); |
59 } | 67 } |
60 | 68 |
61 /** Adds an import to the library. */ | 69 /** Adds an import to the library. */ |
62 addImport(String fullname, String prefix) { | 70 addImport(String fullname, String prefix, SourceSpan span) { |
63 var newLib = world.getOrAddLibrary(fullname); | 71 var newLib = world.getOrAddLibrary(fullname); |
64 imports.add(new LibraryImport(newLib, prefix)); | 72 // Special exemption in spec to ensure core is only imported once |
Jennifer Messerly
2012/01/12 01:58:33
Is this to avoid our implicit import of dart:core
| |
73 if (newLib.isCore) return; | |
74 imports.add(new LibraryImport(newLib, prefix, span)); | |
65 return newLib; | 75 return newLib; |
66 } | 76 } |
67 | 77 |
68 addNative(String fullname) { | 78 addNative(String fullname) { |
69 natives.add(world.reader.readFile(fullname)); | 79 natives.add(world.reader.readFile(fullname)); |
70 } | 80 } |
71 | 81 |
72 MemberSet _findMembers(String name) { | 82 MemberSet _findMembers(String name) { |
73 if (name.startsWith('_')) { | 83 if (name.startsWith('_')) { |
74 return _privateMembers[name]; | 84 return _privateMembers[name]; |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
156 return null; | 166 return null; |
157 } | 167 } |
158 | 168 |
159 // The type we got back was the "top level" library type. | 169 // The type we got back was the "top level" library type. |
160 // Now perform a lookup in that library for the next name. | 170 // Now perform a lookup in that library for the next name. |
161 return result.library.findTypeByName(type.names[0].name); | 171 return result.library.findTypeByName(type.names[0].name); |
162 } | 172 } |
163 return result; | 173 return result; |
164 } | 174 } |
165 | 175 |
176 // TODO(jimhug): Should be merged with new lookup method's logic. | |
166 Type findTypeByName(String name) { | 177 Type findTypeByName(String name) { |
167 var ret = types[name]; | 178 var ret = types[name]; |
168 | 179 |
169 // Check all imports even if ret != null to detect conflicting names. | 180 // Check all imports even if ret != null to detect conflicting names. |
170 // TODO(jimhug): Only do this on first lookup. | 181 // TODO(jimhug): Only do this on first lookup. |
171 for (var imported in imports) { | 182 for (var imported in imports) { |
172 var newRet = null; | 183 var newRet = null; |
173 if (imported.prefix == null) { | 184 if (imported.prefix == null) { |
174 newRet = imported.library.types[name]; | 185 newRet = imported.library.types[name]; |
175 } else if (imported.prefix == name) { | 186 } else if (imported.prefix == name) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 | 220 |
210 static String _getDottedName(NameTypeReference type) { | 221 static String _getDottedName(NameTypeReference type) { |
211 if (type.names != null) { | 222 if (type.names != null) { |
212 var names = map(type.names, (n) => n.name); | 223 var names = map(type.names, (n) => n.name); |
213 return type.name.name + '.' + Strings.join(names, '.'); | 224 return type.name.name + '.' + Strings.join(names, '.'); |
214 } else { | 225 } else { |
215 return type.name.name; | 226 return type.name.name; |
216 } | 227 } |
217 } | 228 } |
218 | 229 |
219 Member lookup(String name, SourceSpan span) { | 230 Member lookup(String name, SourceSpan span) { |
Jennifer Messerly
2012/01/12 01:58:33
Member lookup(String name, SourceSpan span) => _to
| |
220 var retType = findTypeByName(name); | 231 return _topNames[name]; |
221 var ret = null; | |
222 | |
223 if (retType != null) { | |
224 ret = retType.typeMember; | |
225 } | |
226 | |
227 var newRet = topType.getMember(name); | |
228 // TODO(jimhug): Shares too much code with body of loop. | |
229 if (newRet != null) { | |
230 if (ret != null && ret != newRet) { | |
231 world.error('conflicting members for "$name"', | |
232 span, ret.span, newRet.span); | |
233 } else { | |
234 ret = newRet; | |
235 } | |
236 } | |
237 | |
238 // Check all imports even if ret != null to detect conflicting names. | |
239 // TODO(jimhug): Only do this on first lookup. | |
240 for (var imported in imports) { | |
241 if (imported.prefix == null) { | |
242 newRet = imported.library.topType.getMember(name); | |
243 if (newRet != null) { | |
244 if (ret != null && ret != newRet) { | |
245 world.error('conflicting members for "$name"', | |
246 span, ret.span, newRet.span); | |
247 } else { | |
248 ret = newRet; | |
249 } | |
250 } | |
251 } | |
252 } | |
253 return ret; | |
254 } | 232 } |
255 | 233 |
256 resolve() { | 234 resolve() { |
257 if (name == null) { | 235 if (name == null) { |
258 // TODO(jimhug): More fodder for io library. | 236 // TODO(jimhug): More fodder for io library. |
259 name = baseSource.filename; | 237 name = baseSource.filename; |
260 var index = name.lastIndexOf('/', name.length); | 238 var index = name.lastIndexOf('/', name.length); |
261 if (index >= 0) { | 239 if (index >= 0) { |
262 name = name.substring(index+1); | 240 name = name.substring(index+1); |
263 } | 241 } |
264 index = name.indexOf('.'); | 242 index = name.indexOf('.'); |
265 if (index > 0) { | 243 if (index > 0) { |
266 name = name.substring(0, index); | 244 name = name.substring(0, index); |
267 } | 245 } |
268 } | 246 } |
269 // TODO(jimhug): Expand to handle all illegal id characters | 247 // TODO(jimhug): Expand to handle all illegal id characters |
270 _jsname = | 248 _jsname = |
271 name.replaceAll('.', '_').replaceAll(':', '_').replaceAll(' ', '_'); | 249 name.replaceAll('.', '_').replaceAll(':', '_').replaceAll(' ', '_'); |
272 | 250 |
273 for (var type in types.getValues()) { | 251 for (var type in types.getValues()) { |
274 type.resolve(); | 252 type.resolve(); |
275 } | 253 } |
276 } | 254 } |
277 | 255 |
256 _addTopName(String name, Member member, [SourceSpan localSpan]) { | |
257 var existing = _topNames[name]; | |
258 if (existing === null) { | |
259 _topNames[name] = member; | |
260 } else { | |
261 if (existing is AmbiguousMember) { | |
262 existing.members.add(member); | |
263 } else { | |
264 var newMember = new AmbiguousMember(name, [existing, member]); | |
265 world.error('conflicting members for "$name"', | |
266 existing.span, member.span, localSpan); | |
267 _topNames[name] = newMember; | |
268 } | |
269 } | |
270 } | |
271 | |
272 _addTopNames(Library lib) { | |
273 for (var member in lib.topType.members.getValues()) { | |
274 if (member.isPrivate && lib != this) continue; | |
275 _addTopName(member.name, member); | |
276 } | |
277 for (var type in lib.types.getValues()) { | |
278 if (!type.isTop) { | |
279 if (lib != this && type.typeMember.isPrivate) continue; | |
280 _addTopName(type.name, type.typeMember); | |
281 } | |
282 } | |
283 } | |
284 | |
285 /** | |
286 * This method will check for any conflicts in top-level names in this | |
287 * library. It will also build up a map from top-level names to a single | |
288 * member to be used for future lookups both to keep error messages clean | |
289 * and as a minor perf optimization. | |
290 */ | |
291 postResolveChecks() { | |
292 _topNames = {}; | |
293 // check for conflicts between top-level names | |
294 _addTopNames(this); | |
295 for (var imported in imports) { | |
296 if (imported.prefix == null) { | |
297 _addTopNames(imported.library); | |
298 } else { | |
299 _addTopName(imported.prefix, imported.library.topType.typeMember, | |
300 imported.span); | |
301 } | |
302 } | |
303 } | |
304 | |
278 visitSources() { | 305 visitSources() { |
279 var visitor = new _LibraryVisitor(this); | 306 var visitor = new _LibraryVisitor(this); |
280 visitor.addSource(baseSource); | 307 visitor.addSource(baseSource); |
281 } | 308 } |
282 | 309 |
283 toString() => baseSource.filename; | 310 toString() => baseSource.filename; |
284 | 311 |
285 int hashCode() => baseSource.filename.hashCode(); | 312 int hashCode() => baseSource.filename.hashCode(); |
286 | 313 |
287 bool operator ==(other) => other is Library && | 314 bool operator ==(other) => other is Library && |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
383 if (prefix == '') prefix = null; | 410 if (prefix == '') prefix = null; |
384 | 411 |
385 var filename = library.makeFullPath(name); | 412 var filename = library.makeFullPath(name); |
386 | 413 |
387 if (library.imports.some((li) => li.library.baseSource == filename)) { | 414 if (library.imports.some((li) => li.library.baseSource == filename)) { |
388 // TODO(jimhug): Can you import a lib twice with different prefixes? | 415 // TODO(jimhug): Can you import a lib twice with different prefixes? |
389 world.error('duplicate import of "$name"', node.span); | 416 world.error('duplicate import of "$name"', node.span); |
390 return; | 417 return; |
391 } | 418 } |
392 | 419 |
393 var newLib = library.addImport(filename, prefix); | 420 var newLib = library.addImport(filename, prefix, node.span); |
394 // TODO(jimhug): Add check that imported library has a #library | 421 // TODO(jimhug): Add check that imported library has a #library |
395 break; | 422 break; |
396 | 423 |
397 case "source": | 424 case "source": |
398 seenSource = true; | 425 seenSource = true; |
399 name = getSingleStringArg(node); | 426 name = getSingleStringArg(node); |
400 addSourceFromName(name, node.span); | 427 addSourceFromName(name, node.span); |
401 if (seenResource) { | 428 if (seenResource) { |
402 world.error('#sources must come before any #resource', node.span); | 429 world.error('#sources must come before any #resource', node.span); |
403 } | 430 } |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 | 510 |
484 void visitFunctionDefinition(FunctionDefinition node) { | 511 void visitFunctionDefinition(FunctionDefinition node) { |
485 currentType.addMethod(node.name.name, node); | 512 currentType.addMethod(node.name.name, node); |
486 } | 513 } |
487 | 514 |
488 void visitFunctionTypeDefinition(FunctionTypeDefinition node) { | 515 void visitFunctionTypeDefinition(FunctionTypeDefinition node) { |
489 var type = library.addType(node.func.name.name, node, false); | 516 var type = library.addType(node.func.name.name, node, false); |
490 type.addMethod(':call', node.func); | 517 type.addMethod(':call', node.func); |
491 } | 518 } |
492 } | 519 } |
OLD | NEW |