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

Side by Side Diff: lib/dartdoc/frog/library.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 class LibraryImport {
6 final String prefix;
7 final Library library;
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);
16 }
17
18
19 /** Represents a Dart library. */
20 class Library extends Element {
21 final SourceFile baseSource;
22 Map<String, DefinedType> types;
23 List<LibraryImport> imports;
24 String sourceDir;
25 List<SourceFile> natives;
26 List<SourceFile> sources;
27
28 Map<String, Member> _topNames;
29 Map<String, MemberSet> _privateMembers;
30
31 /** The type that holds top level types in the library. */
32 DefinedType topType;
33
34 /** Set to true by [WorldGenerator] once this type has been written. */
35 bool isWritten = false;
36
37 Library(this.baseSource) : super(null, null) {
38 sourceDir = dirname(baseSource.filename);
39 topType = new DefinedType(null, this, null, true);
40 types = { '': topType };
41 imports = [];
42 natives = [];
43 sources = [];
44 _privateMembers = {};
45 }
46
47 Element get enclosingElement() => null;
48 Library get library() => this;
49
50 bool get isNative() => topType.isNative;
51
52 bool get isCore() => this == world.corelib;
53 bool get isCoreImpl() => this == world.coreimpl;
54
55 // TODO(jmesserly): we shouldn't be special casing DOM anywhere.
56 bool get isDomOrHtml() => this == world.dom || this == world.html;
57
58 SourceSpan get span() => new SourceSpan(baseSource, 0, 0);
59
60 String makeFullPath(String filename) {
61 if (filename.startsWith('dart:')) return filename;
62 if (filename.startsWith('package:')) return filename;
63 // TODO(jmesserly): replace with node.js path.resolve
64 if (filename.startsWith('/')) return filename;
65 if (filename.startsWith('file:///')) return filename;
66 if (filename.startsWith('http://')) return filename;
67 if (const RegExp('^[a-zA-Z]:/').hasMatch(filename)) return filename;
68 return joinPaths(sourceDir, filename);
69 }
70
71 /** Adds an import to the library. */
72 addImport(String fullname, String prefix, SourceSpan span) {
73 var newLib = world.getOrAddLibrary(fullname);
74 // Special exemption in spec to ensure core is only imported once
75 if (newLib.isCore) return;
76 imports.add(new LibraryImport(newLib, prefix, span));
77 return newLib;
78 }
79
80 addNative(String fullname) {
81 natives.add(world.reader.readFile(fullname));
82 }
83
84 MemberSet _findMembers(String name) {
85 if (name.startsWith('_')) {
86 return _privateMembers[name];
87 } else {
88 return world._members[name];
89 }
90 }
91
92 _addMember(Member member) {
93 if (member.isPrivate) {
94 if (member.isStatic) {
95 if (member.declaringType.isTop) {
96 world._addTopName(member);
97 }
98 } else {
99 var members = _privateMembers[member.name];
100 if (members == null) {
101 members = new MemberSet(member, isVar: true);
102 _privateMembers[member.name] = members;
103 } else {
104 members.add(member);
105 }
106 }
107 } else {
108 world._addMember(member);
109 }
110 }
111
112 // TODO(jimhug): Cache and share the types as interfaces!
113 Type getOrAddFunctionType(Element enclosingElement, String name,
114 FunctionDefinition func, MethodData data) {
115 // TODO(jimhug): This is redundant now that FunctionDef has type params.
116 final def = new FunctionTypeDefinition(func, null, func.span);
117 final type = new DefinedType(name, this, def, false);
118 type.addMethod(':call', func);
119 var m = type.members[':call'];
120 m.enclosingElement = enclosingElement;
121 m.resolve();
122 m._methodData = data;
123 // Function types implement the Function interface.
124 type.interfaces = [world.functionType];
125 return type;
126 }
127
128 /** Adds a type to the library. */
129 DefinedType addType(String name, Node definition, bool isClass) {
130 if (types.containsKey(name)) {
131 var existingType = types[name];
132 if ((isCore || isCoreImpl) && existingType.definition == null) {
133 // TODO(jimhug): Validate compatibility with natives.
134 existingType.setDefinition(definition);
135 } else {
136 world.warning('duplicate definition of $name', definition.span,
137 existingType.span);
138 }
139 } else {
140 types[name] = new DefinedType(name, this, definition, isClass);
141 }
142
143 return types[name];
144 }
145
146 Type findType(NameTypeReference type) {
147 Type result = findTypeByName(type.name.name);
148 if (result == null) return null;
149
150 if (type.names != null) {
151 if (type.names.length > 1) {
152 // TODO(jmesserly): can we ever get legitimate types with more than one
153 // name after the library prefix?
154 return null;
155 }
156
157 if (!result.isTop) {
158 // No inner type support. If we get first-class types, this should
159 // perform a lookup on the type.
160 return null;
161 }
162
163 // The type we got back was the "top level" library type.
164 // Now perform a lookup in that library for the next name.
165 return result.library.findTypeByName(type.names[0].name);
166 }
167 return result;
168 }
169
170 // TODO(jimhug): Should be merged with new lookup method's logic.
171 Type findTypeByName(String name) {
172 var ret = types[name];
173
174 // Check all imports even if ret != null to detect conflicting names.
175 // TODO(jimhug): Only do this on first lookup.
176 for (var imported in imports) {
177 var newRet = null;
178 if (imported.prefix == null) {
179 newRet = imported.library.types[name];
180 } else if (imported.prefix == name) {
181 newRet = imported.library.topType;
182 }
183 if (newRet != null) {
184 // TODO(jimhug): Should not need ret != newRet here or below.
185 if (ret != null && ret != newRet) {
186 world.error('conflicting types for "$name"', ret.span, newRet.span);
187 } else {
188 ret = newRet;
189 }
190 }
191 }
192 return ret;
193 }
194
195
196 // TODO(jimhug): Why is it okay to assume node is NameTypeReference in here?
197 Type resolveType(TypeReference node, bool typeErrors, bool allowTypeParams) {
198 if (node == null) return world.varType;
199
200 var ret = findType(node);
201
202 if (ret == null) {
203 var message = 'cannot find type ${_getDottedName(node)}';
204 if (typeErrors) {
205 world.error(message, node.span);
206 return world.objectType;
207 } else {
208 world.warning(message, node.span);
209 return world.varType;
210 }
211 }
212 return ret;
213 }
214
215 static String _getDottedName(NameTypeReference type) {
216 if (type.names != null) {
217 var names = map(type.names, (n) => n.name);
218 return '${type.name.name}.${Strings.join(names, ".")}';
219 } else {
220 return type.name.name;
221 }
222 }
223
224 Member lookup(String name, SourceSpan span) {
225 return _topNames[name];
226 }
227
228 resolve() {
229 if (name == null) {
230 // TODO(jimhug): More fodder for io library.
231 name = baseSource.filename;
232 var index = name.lastIndexOf('/', name.length);
233 if (index >= 0) {
234 name = name.substring(index+1);
235 }
236 index = name.indexOf('.');
237 if (index > 0) {
238 name = name.substring(0, index);
239 }
240 }
241 // TODO(jimhug): Expand to handle all illegal id characters
242 _jsname =
243 name.replaceAll('.', '_').replaceAll(':', '_').replaceAll(' ', '_');
244
245 for (var type in types.getValues()) {
246 type.resolve();
247 }
248 }
249
250 _addTopName(String name, Member member, [SourceSpan localSpan]) {
251 var existing = _topNames[name];
252 if (existing === null) {
253 _topNames[name] = member;
254 } else {
255 if (existing is AmbiguousMember) {
256 existing.members.add(member);
257 } else {
258 var newMember = new AmbiguousMember(name, [existing, member]);
259 world.error('conflicting members for "$name"',
260 existing.span, member.span, localSpan);
261 _topNames[name] = newMember;
262 }
263 }
264 }
265
266 _addTopNames(Library lib) {
267 for (var member in lib.topType.members.getValues()) {
268 if (member.isPrivate && lib != this) continue;
269 _addTopName(member.name, member);
270 }
271 for (var type in lib.types.getValues()) {
272 if (!type.isTop) {
273 if (lib != this && type.typeMember.isPrivate) continue;
274 _addTopName(type.name, type.typeMember);
275 }
276 }
277 }
278
279 /**
280 * This method will check for any conflicts in top-level names in this
281 * library. It will also build up a map from top-level names to a single
282 * member to be used for future lookups both to keep error messages clean
283 * and as a minor perf optimization.
284 */
285 postResolveChecks() {
286 _topNames = {};
287 // check for conflicts between top-level names
288 _addTopNames(this);
289 for (var imported in imports) {
290 if (imported.prefix == null) {
291 _addTopNames(imported.library);
292 } else {
293 _addTopName(imported.prefix, imported.library.topType.typeMember,
294 imported.span);
295 }
296 }
297 }
298
299 visitSources() {
300 var visitor = new _LibraryVisitor(this);
301 visitor.addSource(baseSource);
302 }
303
304 toString() => baseSource.filename;
305
306 int hashCode() => baseSource.filename.hashCode();
307
308 bool operator ==(other) => other is Library &&
309 other.baseSource.filename == baseSource.filename;
310 }
311
312
313 class _LibraryVisitor implements TreeVisitor {
314 final Library library;
315 DefinedType currentType;
316 List<SourceFile> sources;
317
318 bool seenImport = false;
319 bool seenSource = false;
320 bool seenResource = false;
321 bool isTop = true;
322
323 _LibraryVisitor(this.library) {
324 currentType = library.topType;
325 sources = [];
326 }
327
328 addSourceFromName(String name, SourceSpan span) {
329 var filename = library.makeFullPath(name);
330 if (filename == library.baseSource.filename) {
331 world.error('library cannot source itself', span);
332 return;
333 } else if (sources.some((s) => s.filename == filename)) {
334 world.error('file "$filename" has already been sourced', span);
335 return;
336 }
337
338 var source = world.readFile(library.makeFullPath(name));
339 sources.add(source);
340 }
341
342 addSource(SourceFile source) {
343 if (library.sources.some((s) => s.filename == source.filename)) {
344 // TODO(jimhug): good error location.
345 world.error('duplicate source file "${source.filename}"', null);
346 return;
347 }
348 library.sources.add(source);
349 final parser = new Parser(source, diet: options.dietParse);
350 final unit = parser.compilationUnit();
351
352 unit.forEach((def) => def.visit(this));
353
354 assert(sources.length == 0 || isTop);
355 isTop = false;
356 var newSources = sources;
357 sources = [];
358 for (var newSource in newSources) {
359 addSource(newSource);
360 }
361 }
362
363 void visitDirectiveDefinition(DirectiveDefinition node) {
364 if (!isTop) {
365 world.error('directives not allowed in sourced file', node.span);
366 return;
367 }
368
369 var name;
370 switch (node.name.name) {
371 case "library":
372 name = getSingleStringArg(node);
373 if (library.name == null) {
374 library.name = name;
375 if (seenImport || seenSource || seenResource) {
376 world.error('#library must be first directive in file', node.span);
377 }
378 } else {
379 world.error('already specified library name', node.span);
380 }
381 break;
382
383 case "import":
384 seenImport = true;
385 name = getFirstStringArg(node);
386 var prefix = tryGetNamedStringArg(node, 'prefix');
387 if (node.arguments.length > 2 ||
388 node.arguments.length == 2 && prefix == null) {
389 world.error(
390 'expected at most one "name" argument and one optional "prefix"'
391 ' but found ${node.arguments.length}', node.span);
392 } else if (prefix != null && prefix.indexOf('.') >= 0) {
393 world.error('library prefix canot contain "."', node.span);
394 } else if (seenSource || seenResource) {
395 world.error('#imports must come before any #source or #resource',
396 node.span);
397 }
398
399 // Empty prefix and no prefix are equivalent
400 if (prefix == '') prefix = null;
401
402 var filename = library.makeFullPath(name);
403
404 if (library.imports.some((li) => li.library.baseSource == filename)) {
405 // TODO(jimhug): Can you import a lib twice with different prefixes?
406 world.error('duplicate import of "$name"', node.span);
407 return;
408 }
409
410 var newLib = library.addImport(filename, prefix, node.span);
411 // TODO(jimhug): Add check that imported library has a #library
412 break;
413
414 case "source":
415 seenSource = true;
416 name = getSingleStringArg(node);
417 addSourceFromName(name, node.span);
418 if (seenResource) {
419 world.error('#sources must come before any #resource', node.span);
420 }
421 break;
422
423 case "native":
424 // TODO(jimhug): Fit this into spec?
425 name = getSingleStringArg(node);
426 library.addNative(library.makeFullPath(name));
427 break;
428
429 case "resource":
430 // TODO(jmesserly): should we do anything else here?
431 seenResource = true;
432 getFirstStringArg(node);
433 break;
434
435 default:
436 world.error('unknown directive: ${node.name.name}', node.span);
437 }
438 }
439
440 String getSingleStringArg(DirectiveDefinition node) {
441 if (node.arguments.length != 1) {
442 world.error(
443 'expected exactly one argument but found ${node.arguments.length}',
444 node.span);
445 }
446 return getFirstStringArg(node);
447 }
448
449 String getFirstStringArg(DirectiveDefinition node) {
450 if (node.arguments.length < 1) {
451 world.error(
452 'expected at least one argument but found ${node.arguments.length}',
453 node.span);
454 }
455 var arg = node.arguments[0];
456 if (arg.label != null) {
457 world.error('label not allowed for directive', node.span);
458 }
459 return _parseStringArgument(arg);
460 }
461
462 String tryGetNamedStringArg(DirectiveDefinition node, String argName) {
463 var args = node.arguments.filter(
464 (a) => a.label != null && a.label.name == argName);
465
466 if (args.length == 0) {
467 return null;
468 }
469 if (args.length > 1) {
470 world.error('expected at most one "${argName}" argument but found '
471 '${node.arguments.length}', node.span);
472 }
473 // Even though the collection has one arg, this is the easiest way to get
474 // the first item.
475 for (var arg in args) {
476 return _parseStringArgument(arg);
477 }
478 }
479
480 String _parseStringArgument(ArgumentNode arg) {
481 var expr = arg.value;
482 if (expr is! LiteralExpression || !expr.value.type.isString) {
483 world.error('expected string literal', expr.span);
484 }
485 return expr.value.actualValue;
486 }
487
488 void visitTypeDefinition(TypeDefinition node) {
489 var oldType = currentType;
490 currentType = library.addType(node.name.name, node, node.isClass);
491 for (var member in node.body) {
492 member.visit(this);
493 }
494 currentType = oldType;
495 }
496
497 void visitVariableDefinition(VariableDefinition node) {
498 currentType.addField(node);
499 }
500
501 void visitFunctionDefinition(FunctionDefinition node) {
502 currentType.addMethod(node.name.name, node);
503 }
504
505 void visitFunctionTypeDefinition(FunctionTypeDefinition node) {
506 var type = library.addType(node.func.name.name, node, false);
507 type.addMethod(':call', node.func);
508 }
509 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698