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

Side by Side Diff: lib/dartdoc/frog/member.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 /** A formal parameter to a [Method]. */
6 class Parameter {
7 FormalNode definition;
8 Member method;
9
10 String name;
11 Type type;
12 bool isInitializer = false;
13
14 Value value;
15
16 Parameter(this.definition, this.method);
17
18 resolve() {
19 name = definition.name.name;
20 if (name.startsWith('this.')) {
21 name = name.substring(5);
22 isInitializer = true;
23 }
24
25 type = method.resolveType(definition.type, false, true);
26
27 if (definition.value != null) {
28 // To match VM, detect cases where value was not actually specified in
29 // code and don't signal errors.
30 // TODO(jimhug): Clean up after issue #352 is resolved.
31 if (!hasDefaultValue) return;
32
33 if (method.name == ':call') {
34 // TODO(jimhug): Need simpler way to detect "true" function types vs.
35 // regular methods being used as function types for closures.
36 // TODO(sigmund): Disallow non-null default values for native calls?
37 var methodDef = method.definition;
38 if (methodDef.body == null && !method.isNative) {
39 world.error('default value not allowed on function type',
40 definition.span);
41 }
42 } else if (method.isAbstract) {
43 world.error('default value not allowed on abstract methods',
44 definition.span);
45 }
46 } else if (isInitializer && !method.isConstructor) {
47 world.error('initializer parameters only allowed on constructors',
48 definition.span);
49 }
50 }
51
52 genValue(MethodMember method, CallingContext context) {
53 if (definition.value == null || value != null) return;
54
55 // TODO(jmesserly): what should we do when context is not a MethodGenerator?
56 if (context is MethodGenerator) {
57 MethodGenerator gen = context;
58 value = definition.value.visit(gen);
59 if (!value.isConst) {
60 world.error('default parameter values must be constant', value.span);
61 }
62 value = value.convertTo(context, type);
63 }
64 }
65
66 Parameter copyWithNewType(Member newMethod, Type newType) {
67 var ret = new Parameter(definition, newMethod);
68 ret.type = newType;
69 ret.name = name;
70 ret.isInitializer = isInitializer;
71 return ret;
72 }
73
74 bool get isOptional() => definition != null && definition.value != null;
75
76 /**
77 * Gets whether this named parameter has an explicit default value or relies
78 * on the implicit `null`.
79 */
80 bool get hasDefaultValue() =>
81 definition.value.span.start != definition.span.start;
82 }
83
84
85 class Member extends Element {
86 final Type declaringType;
87
88 Member genericMember;
89
90 // A root string for getter and setter names. This is used e.g. to ensure
91 // that fields with the same Dart name but different jsnames (due to native
92 // name directives) still have a common getter name. Is null when there is no
93 // renaming.
94 String _jsnameRoot;
95
96 Member(String name, Type declaringType)
97 : this.declaringType = declaringType,
98 super(name, declaringType);
99
100 String mangleJsName() {
101 var mangled = super.mangleJsName();
102 if (declaringType != null && declaringType.isTop) {
103 return JsNames.getValid(mangled);
104 } else {
105 // We don't need to mangle native member names unless
106 // they contain illegal characters.
107 return (isNative && !name.contains(':')) ? name : mangled;
108 }
109 }
110
111 abstract bool get isStatic();
112 abstract Type get returnType();
113
114 abstract bool get canGet();
115 abstract bool get canSet();
116
117 Library get library() => declaringType.library;
118
119 bool get isPrivate() => name !== null && name.startsWith('_');
120
121 bool get isConstructor() => false;
122 bool get isField() => false;
123 bool get isMethod() => false;
124 bool get isProperty() => false;
125 bool get isAbstract() => false;
126
127 bool get isFinal() => false;
128
129 // TODO(jmesserly): these only makes sense on methods, but because of
130 // ConcreteMember we need to support them on Member.
131 bool get isConst() => false;
132 bool get isFactory() => false;
133
134 bool get isOperator() => name.startsWith(':');
135 bool get isCallMethod() => name == ':call';
136
137 bool get requiresPropertySyntax() => false;
138 bool _provideGetter = false;
139 bool _provideSetter = false;
140
141 bool get isNative() => false;
142 String get constructorName() {
143 world.internalError('cannot be a constructor', span);
144 }
145
146 void provideGetter() {}
147 void provideSetter() {}
148
149 String get jsnameOfGetter() => 'get\$$jsnameRoot';
150 String get jsnameOfSetter() => 'set\$$jsnameRoot';
151 String get jsnameRoot() => _jsnameRoot != null ? _jsnameRoot : _jsname;
152
153 Member get initDelegate() {
154 world.internalError('cannot have initializers', span);
155 }
156 void set initDelegate(ctor) {
157 world.internalError('cannot have initializers', span);
158 }
159
160 Value computeValue() {
161 world.internalError('cannot have value', span);
162 }
163
164 /**
165 * The inferred returnType. Right now this is just used to track
166 * non-nullable bools.
167 */
168 Type get inferredResult() {
169 var t = returnType;
170 if (t.isBool && (library.isCore || library.isCoreImpl)) {
171 // We trust our core libraries not to return null from bools.
172 // I hope this trust is well placed!
173 return world.nonNullBool;
174 }
175 return t;
176 }
177
178 Definition get definition() => null;
179
180 List<Parameter> get parameters() => [];
181
182 MemberSet _preciseMemberSet, _potentialMemberSet;
183
184 MemberSet get preciseMemberSet() {
185 if (_preciseMemberSet === null) {
186 _preciseMemberSet = new MemberSet(this);
187 }
188 return _preciseMemberSet;
189 }
190
191 MemberSet get potentialMemberSet() {
192 // TODO(jimhug): This needs one more redesign - move to TypeSets...
193
194 if (_potentialMemberSet === null) {
195 if (name == ':call') {
196 _potentialMemberSet = preciseMemberSet;
197 return _potentialMemberSet;
198 }
199
200 final mems = new Set<Member>();
201 if (declaringType.isClass) mems.add(this);
202
203 for (var subtype in declaringType.genericType.subtypes) {
204 if (!subtype.isClass) continue;
205 var mem = subtype.members[name];
206 if (mem !== null) {
207 if (mem.isDefinedOn(declaringType)) {
208 mems.add(mem);
209 }
210 } else if (!declaringType.isClass) {
211 // Handles weird interface case.
212 mem = subtype.getMember(name);
213 if (mem !== null && mem.isDefinedOn(declaringType)) {
214 mems.add(mem);
215 }
216 }
217 }
218
219 if (mems.length != 0) {
220 // TODO(jimhug): This hack needs to be rationalized.
221 for (var mem in mems) {
222 if (declaringType.genericType != declaringType &&
223 mem.genericMember != null && mems.contains(mem.genericMember)) {
224 //world.info('skip ${name} on ${mem.genericMember.declaringType.name }' +
225 // ' because we have on ${mem.declaringType.name} for ${declaringTy pe.name}');
226 mems.remove(mem.genericMember);
227 }
228 }
229
230
231 for (var mem in mems) {
232 if (_potentialMemberSet === null) {
233 _potentialMemberSet = new MemberSet(mem);
234 } else {
235 _potentialMemberSet.add(mem);
236 }
237 }
238 }
239 }
240 return _potentialMemberSet;
241 }
242
243 // If I have an object of [type] could I be invoking this member?
244 bool isDefinedOn(Type type) {
245 if (type.isClass) {
246 if (declaringType.isSubtypeOf(type)) {
247 return true;
248 } else if (type.isSubtypeOf(declaringType)) {
249 // maybe - but not if overridden somewhere
250 // TODO(jimhug): This lookup is not great for perf of this method.
251 return type.getMember(name) == this;
252 } else {
253 return false;
254 }
255 } else {
256 if (declaringType.isSubtypeOf(type)) {
257 return true;
258 } else {
259 // If this is an interface, the actual implementation may
260 // come from a class that does not implement this interface.
261 for (var t in declaringType.subtypes) {
262 if (t.isSubtypeOf(type) && t.getMember(name) == this) {
263 return true;
264 }
265 }
266 return false;
267 }
268 }
269 }
270
271 abstract Value _get(CallingContext context, Node node, Value target);
272
273 abstract Value _set(CallingContext context, Node node, Value target,
274 Value value);
275
276
277 bool canInvoke(CallingContext context, Arguments args) {
278 // Any gettable member whose return type is callable can be "invoked".
279 if (canGet && (isField || isProperty)) {
280 return this.returnType.isFunction || this.returnType.isVar ||
281 this.returnType.getCallMethod() != null;
282 }
283 return false;
284 }
285
286 Value invoke(CallingContext context, Node node, Value target,
287 Arguments args) {
288 var newTarget = _get(context, node, target);
289 return newTarget.invoke(context, ':call', node, args);
290 }
291
292 bool override(Member other) {
293 if (isStatic) {
294 world.error('static members cannot hide parent members',
295 span, other.span);
296 return false;
297 } else if (other.isStatic) {
298 world.error('cannot override static member', span, other.span);
299 return false;
300 }
301 return true;
302 }
303
304 String get generatedFactoryName() {
305 assert(this.isFactory);
306 String prefix = '${declaringType.genericType.jsname}.${constructorName}\$';
307 if (name == '') {
308 return '${prefix}factory';
309 } else {
310 return '${prefix}$name\$factory';
311 }
312 }
313
314 int hashCode() {
315 final typeCode = declaringType == null ? 1 : declaringType.hashCode();
316 final nameCode = isConstructor ? constructorName.hashCode() :
317 name.hashCode();
318 return (typeCode << 4) ^ nameCode;
319 }
320
321 bool operator ==(other) {
322 return other is Member && isConstructor == other.isConstructor &&
323 declaringType == other.declaringType && (isConstructor ?
324 constructorName == other.constructorName : name == other.name);
325 }
326
327 /** Overriden to ensure that type arguments aren't used in static mems. */
328 Type resolveType(TypeReference node, bool typeErrors, bool allowTypeParams) {
329 allowTypeParams = allowTypeParams && !(isStatic && !isFactory);
330
331 return super.resolveType(node, typeErrors, allowTypeParams);
332 }
333
334 // TODO(jimhug): Make this abstract.
335 Member makeConcrete(Type concreteType) {
336 world.internalError('cannot make this concrete', span);
337 }
338 }
339
340
341 /**
342 * Types are treated as first class members of their library's top type.
343 */
344 // TODO(jmesserly): perhaps Type should extend Member, but that can get
345 // complicated.
346 class TypeMember extends Member {
347 final DefinedType type;
348
349 TypeMember(DefinedType type)
350 : super(type.name, type.library.topType),
351 this.type = type;
352
353 SourceSpan get span() => type.definition === null ? null : type.definition.spa n;
354
355 bool get isStatic() => true;
356
357 // If this really becomes first class, this should return typeof(Type)
358 Type get returnType() => world.varType;
359
360 bool canInvoke(CallingContext context, Arguments args) => false;
361 bool get canGet() => true;
362 bool get canSet() => false;
363
364 Value _get(CallingContext context, Node node, Value target) {
365 return new TypeValue(type, node.span);
366 }
367
368 Value _set(CallingContext context, Node node, Value target, Value value) {
369 world.error('cannot set type', node.span);
370 }
371
372 Value invoke(CallingContext context, Node node, Value target,
373 Arguments args) {
374 world.error('cannot invoke type', node.span);
375 }
376 }
377
378 /** Represents a Dart field from source code. */
379 class FieldMember extends Member {
380 final VariableDefinition definition;
381 final Expression value;
382
383 Type type;
384 Value _computedValue;
385
386 bool isStatic;
387 bool isFinal;
388 final bool isNative;
389
390 // TODO(jimhug): Better notion of fields that need special handling...
391 bool get overridesProperty() {
392 if (isStatic) return false;
393
394 if (declaringType.parent != null) {
395 var p = declaringType.parent.getProperty(name);
396 if (p != null && p.isProperty) return true;
397 if (p is FieldMember && p != this) return p.overridesProperty;
398 }
399 return false;
400 }
401
402 bool override(Member other) {
403 if (!super.override(other)) return false;
404
405 // According to the specification, fields can override properties
406 // and other fields.
407 if (other.isProperty || other.isField) {
408 // TODO(jimhug):
409 // other.returnType.ensureAssignableFrom(returnType, null, true);
410 return true;
411 // TODO(jimhug): Merge in overridesProperty logic here.
412 } else {
413 world.error('field can only override field or property',
414 span, other.span);
415 return false;
416 }
417 }
418
419 void provideGetter() {
420 _provideGetter = true;
421 if (genericMember !== null) {
422 genericMember.provideGetter();
423 }
424 }
425
426 void provideSetter() {
427 _provideSetter = true;
428 if (genericMember !== null) {
429 genericMember.provideSetter();
430 }
431 }
432
433 FieldMember(String name, Type declaringType, this.definition, this.value,
434 [bool this.isNative = false])
435 : super(name, declaringType);
436
437 Member makeConcrete(Type concreteType) {
438 var ret = new FieldMember(name, concreteType, definition, value);
439 ret.genericMember = this;
440 ret._jsname = _jsname;
441 return ret;
442 }
443
444 SourceSpan get span() => definition == null ? null : definition.span;
445
446 Type get returnType() => type;
447
448 bool get canGet() => true;
449 bool get canSet() => !isFinal;
450
451 bool get isField() => true;
452
453 resolve() {
454 isStatic = declaringType.isTop;
455 isFinal = false;
456 if (definition.modifiers != null) {
457 for (var mod in definition.modifiers) {
458 if (mod.kind == TokenKind.STATIC) {
459 if (isStatic) {
460 world.error('duplicate static modifier', mod.span);
461 }
462 isStatic = true;
463 } else if (mod.kind == TokenKind.FINAL) {
464 if (isFinal) {
465 world.error('duplicate final modifier', mod.span);
466 }
467 isFinal = true;
468 } else {
469 world.error('${mod} modifier not allowed on field', mod.span);
470 }
471 }
472 }
473 type = resolveType(definition.type, false, true);
474
475 if (isStatic && isFinal && value == null) {
476 world.error('static final field is missing initializer', span);
477 }
478
479 if (declaringType.isClass) library._addMember(this);
480 }
481
482
483 bool _computing = false;
484 /** Generates the initial value for this field, if any. Marks it as used. */
485 Value computeValue() {
486 if (value == null) return null;
487
488 if (_computedValue == null) {
489 if (_computing) {
490 world.error('circular reference', value.span);
491 return null;
492 }
493 _computing = true;
494 var finalMethod = new MethodMember('final_context', declaringType, null);
495 finalMethod.isStatic = true;
496 var finalGen = new MethodGenerator(finalMethod, null);
497 _computedValue = value.visit(finalGen);
498 if (!_computedValue.isConst) {
499 if (isStatic) {
500 world.error(
501 'non constant static field must be initialized in functions',
502 value.span);
503 } else {
504 world.error(
505 'non constant field must be initialized in constructor',
506 value.span);
507 }
508 }
509
510
511 if (isStatic) {
512 if (isFinal && _computedValue.isConst) {
513 ; // keep const as is here
514 } else {
515 _computedValue = world.gen.globalForStaticField(
516 this, _computedValue, [_computedValue]);
517 }
518 }
519 _computing = false;
520 }
521 return _computedValue;
522 }
523
524 Value _get(CallingContext context, Node node, Value target) {
525 if (!context.needsCode) {
526 return new PureStaticValue(type, node.span, isStatic && isFinal);
527 }
528
529 if (isNative && returnType != null) {
530 returnType.markUsed();
531 if (returnType is DefinedType) {
532 // TODO(jmesserly): this handles native fields that return types like
533 // "List". Is there a better solution for fields? Unlike methods we have
534 // no good way to annotate them.
535 var defaultType = returnType.genericType.defaultType;
536 if (defaultType != null && defaultType.isNative) {
537 defaultType.markUsed();
538 }
539 }
540 }
541
542 if (isStatic) {
543 // TODO(jmesserly): can we avoid generating the whole type?
544 declaringType.markUsed();
545
546 // Make sure to compute the value of all static fields, even if we don't
547 // use this value immediately.
548 var cv = computeValue();
549 if (isFinal) {
550 return cv;
551 }
552 world.gen.hasStatics = true;
553 if (declaringType.isTop) {
554 return new Value(type, '\$globals.$jsname', node.span);
555 } else if (declaringType.isNative) {
556 if (declaringType.isHiddenNativeType) {
557 // TODO: Could warn at parse time.
558 world.error('static field of hidden native type is inaccessible',
559 node.span);
560 }
561 return new Value(type, '${declaringType.jsname}.$jsname', node.span);
562 } else {
563 return new Value(type,
564 '\$globals.${declaringType.jsname}_$jsname', node.span);
565 }
566 }
567 return new Value(type, '${target.code}.$jsname', node.span);
568 }
569
570 Value _set(CallingContext context, Node node, Value target, Value value) {
571 if (!context.needsCode) {
572 // TODO(jimhug): Add type checks here.
573 return new PureStaticValue(type, node.span);
574 }
575
576 var lhs = _get(context, node, target);
577 value = value.convertTo(context, type);
578 return new Value(type, '${lhs.code} = ${value.code}', node.span);
579 }
580 }
581
582 class PropertyMember extends Member {
583 MethodMember getter;
584 MethodMember setter;
585
586 Member _overriddenField;
587
588 // TODO(jimhug): What is the right span for this beast?
589 SourceSpan get span() => getter != null ? getter.span : null;
590
591 bool get canGet() => getter != null;
592 bool get canSet() => setter != null;
593
594 // If the property is just a declaration in an interface, continue to allow
595 // field syntax in the generated code.
596 bool get requiresPropertySyntax() => declaringType.isClass;
597
598 // When overriding native fields, we still provide a field syntax to ensure
599 // that native functions will find the appropriate property implementation.
600 // TODO(sigmund): should check for this transitively...
601 bool get needsFieldSyntax() =>
602 _overriddenField != null &&
603 _overriddenField.isNative &&
604 // Can't put property on hidden native class...
605 !_overriddenField.declaringType.isHiddenNativeType
606 ;
607
608 // TODO(jimhug): Union of getter and setters sucks!
609 bool get isStatic() => getter == null ? setter.isStatic : getter.isStatic;
610
611 bool get isProperty() => true;
612
613 Type get returnType() {
614 return getter == null ? setter.returnType : getter.returnType;
615 }
616
617 PropertyMember(String name, Type declaringType): super(name, declaringType);
618
619 Member makeConcrete(Type concreteType) {
620 var ret = new PropertyMember(name, concreteType);
621 if (getter !== null) ret.getter = getter.makeConcrete(concreteType);
622 if (setter !== null) ret.setter = setter.makeConcrete(concreteType);
623 ret._jsname = _jsname;
624 return ret;
625 }
626
627 bool override(Member other) {
628 if (!super.override(other)) return false;
629
630 // properties can override other properties and fields
631 if (other.isProperty || other.isField) {
632 // TODO(jimhug):
633 // other.returnType.ensureAssignableFrom(returnType, null, true);
634 if (other.isProperty) addFromParent(other);
635 else _overriddenField = other;
636 return true;
637 } else {
638 world.error('property can only override field or property',
639 span, other.span);
640 return false;
641 }
642 }
643
644 Value _get(CallingContext context, Node node, Value target) {
645 if (getter == null) {
646 if (_overriddenField != null) {
647 return _overriddenField._get(context, node, target);
648 }
649 return target.invokeNoSuchMethod(context, 'get:$name', node);
650 }
651 return getter.invoke(context, node, target, Arguments.EMPTY);
652 }
653
654 Value _set(CallingContext context, Node node, Value target, Value value) {
655 if (setter == null) {
656 if (_overriddenField != null) {
657 return _overriddenField._set(context, node, target, value);
658 }
659 return target.invokeNoSuchMethod(context, 'set:$name', node,
660 new Arguments(null, [value]));
661 }
662 return setter.invoke(context, node, target, new Arguments(null, [value]));
663 }
664
665 addFromParent(Member parentMember) {
666 final parent = parentMember;
667
668 if (getter == null) getter = parent.getter;
669 if (setter == null) setter = parent.setter;
670 }
671
672 resolve() {
673 if (getter != null) {
674 getter.resolve();
675 if (getter.parameters.length != 0) {
676 world.error('getter methods should take no arguments',
677 getter.definition.span);
678 }
679 if (getter.returnType.isVoid) {
680 world.warning('getter methods should not be void',
681 getter.definition.returnType.span);
682 }
683 }
684 if (setter != null) {
685 setter.resolve();
686 if (setter.parameters.length != 1) {
687 world.error('setter methods should take a single argument',
688 setter.definition.span);
689 }
690 // Not issue warning if setter is implicitly dynamic (returnType == null),
691 // but do if it is explicit (returnType.isVar)
692 if (!setter.returnType.isVoid && setter.definition.returnType != null) {
693 world.warning('setter methods should be void',
694 setter.definition.returnType.span);
695 }
696 }
697
698 if (declaringType.isClass) library._addMember(this);
699 }
700 }
701
702
703 /** Represents a Dart method or top-level function. */
704 class MethodMember extends Member {
705 FunctionDefinition definition;
706 Type returnType;
707 List<Parameter> parameters;
708
709 MethodData _methodData;
710
711 Type _functionType;
712 bool isStatic = false;
713 bool isAbstract = false;
714
715 // Note: these two modifiers are only legal on constructors
716 bool isConst = false;
717 bool isFactory = false;
718
719 /** True if this is a function defined inside another method. */
720 final bool isLambda;
721
722 /**
723 * True if we should provide info on optional parameters for use by runtime
724 * dispatch.
725 */
726 bool _provideOptionalParamInfo = false;
727
728 /*
729 * When this is a constructor, contains any other constructor called during
730 * initialization (if any).
731 */
732 Member initDelegate;
733
734 bool _hasNativeBody = false;
735
736 static final kIdentifierRegExp = const RegExp(@'^[a-zA-Z][a-zA-Z_$0-9]*$');
737
738 MethodMember(String name, Type declaringType, this.definition)
739 : isLambda = false, super(name, declaringType) {
740 if (isNative) {
741 // Parse the native string. The the native string can be a native name
742 // (identifier) or a chunk of JavaScript code.
743 //
744 // foo() native 'bar'; // The native method is called 'bar'.
745 // foo() native 'return 1'; // Defines method with native implementation.
746 //
747 if (kIdentifierRegExp.hasMatch(definition.nativeBody)) {
748 _jsname = definition.nativeBody;
749 // Prevent the compiler from using the name for a regular Dart member.
750 world._addHazardousMemberName(_jsname);
751 }
752 _hasNativeBody = definition.nativeBody != '' &&
753 definition.nativeBody != _jsname;
754 }
755 }
756
757 MethodMember.lambda(String name, Type declaringType, this.definition)
758 : isLambda = true, super(name, declaringType);
759
760 Member makeConcrete(Type concreteType) {
761 var _name = isConstructor ? concreteType.name : name;
762 var ret = new MethodMember(_name, concreteType, definition);
763 ret.genericMember = this;
764 ret._jsname = _jsname;
765 return ret;
766 }
767
768 MethodData get methodData() {
769 if (genericMember !== null) return genericMember.dynamic.methodData;
770
771 if (_methodData === null) {
772 _methodData = new MethodData(this);
773 }
774 return _methodData;
775 }
776
777 bool get isConstructor() => name == declaringType.name;
778 bool get isMethod() => !isConstructor;
779
780 bool get isNative() {
781 if (definition == null) return false;
782 return definition.nativeBody != null;
783 }
784
785 bool get hasNativeBody() => _hasNativeBody;
786
787 bool get canGet() => true;
788 bool get canSet() => false;
789
790 bool get requiresPropertySyntax() => true;
791
792 SourceSpan get span() => definition == null ? null : definition.span;
793
794 String get constructorName() {
795 var returnType = definition.returnType;
796 if (returnType == null) return '';
797 if (returnType is GenericTypeReference) {
798 return '';
799 }
800
801 // TODO(jmesserly): make this easier?
802 if (returnType.names != null) {
803 return returnType.names[0].name;
804 } else if (returnType.name != null) {
805 return returnType.name.name;
806 }
807 world.internalError('no valid constructor name', definition.span);
808 }
809
810 Type get functionType() {
811 if (_functionType == null) {
812 _functionType = library.getOrAddFunctionType(declaringType, name,
813 definition, methodData);
814 // TODO(jimhug): Better resolution checks.
815 if (parameters == null) {
816 resolve();
817 }
818 }
819 return _functionType;
820 }
821
822 bool override(Member other) {
823 if (!super.override(other)) return false;
824
825 // methods can only override other methods
826 if (other.isMethod) {
827 // TODO(jimhug):
828 // other.returnType.ensureAssignableFrom(returnType, null, true);
829 // TODO(jimhug): Check for further parameter compatibility.
830 return true;
831 } else {
832 world.error('method can only override methods', span, other.span);
833 return false;
834 }
835 }
836
837 bool canInvoke(CallingContext context, Arguments args) {
838 int bareCount = args.bareCount;
839
840 if (bareCount > parameters.length) return false;
841
842 if (bareCount == parameters.length) {
843 if (bareCount != args.length) return false;
844 } else {
845 if (!parameters[bareCount].isOptional) return false;
846
847 for (int i = bareCount; i < args.length; i++) {
848 if (indexOfParameter(args.getName(i)) < 0) {
849 return false;
850 }
851 }
852 }
853
854 return true;
855 }
856
857 // TODO(jmesserly): might need to make this faster
858 /** Gets the index of an optional parameter. */
859 int indexOfParameter(String name) {
860 for (int i = 0; i < parameters.length; i++) {
861 final p = parameters[i];
862 if (p.isOptional && p.name == name) {
863 return i;
864 }
865 }
866 return -1;
867 }
868
869 void provideGetter() { _provideGetter = true; }
870 void provideSetter() { _provideSetter = true; }
871
872 Value _set(CallingContext context, Node node, Value target, Value value) {
873 world.error('cannot set method', node.span);
874 }
875
876 Value _get(CallingContext context, Node node, Value target) {
877 if (!context.needsCode) {
878 return new PureStaticValue(functionType, node.span);
879 }
880
881 // TODO(jimhug): Would prefer to invoke!
882 declaringType.genMethod(this);
883 _provideOptionalParamInfo = true;
884 if (isStatic) {
885 // ensure the type is generated.
886 // TODO(sigmund): can we avoid generating the entire type, but only what
887 // we need?
888 declaringType.markUsed();
889 var type = declaringType.isTop ? '' : '${declaringType.jsname}.';
890 return new Value(functionType, '$type$jsname', node.span);
891 }
892 _provideGetter = true;
893 return new Value(functionType, '${target.code}.$jsnameOfGetter()', node.span );
894 }
895
896 /**
897 * Checks if the named arguments are in their natural or 'home' positions,
898 * i.e. they may be passed directly without inserting, deleting or moving the
899 * arguments to correspond with the parameters.
900 */
901 bool namesInHomePositions(Arguments args) {
902 if (!args.hasNames) return true;
903
904 for (int i = args.bareCount; i < args.values.length; i++) {
905 if (i >= parameters.length) {
906 return false;
907 }
908 if (args.getName(i) != parameters[i].name) {
909 return false;
910 }
911 }
912 return true;
913 }
914
915 bool namesInOrder(Arguments args) {
916 if (!args.hasNames) return true;
917
918 int lastParameter = null;
919 for (int i = args.bareCount; i < parameters.length; i++) {
920 var p = args.getIndexOfName(parameters[i].name);
921 // Only worry about parameters that needTemps. Otherwise it's fine to
922 // reorder.
923 if (p >= 0 && args.values[p].needsTemp) {
924 if (lastParameter != null && lastParameter > p) {
925 return false;
926 }
927 lastParameter = p;
928 }
929 }
930 return true;
931 }
932
933 /** Returns true if any of the arguments will need conversion. */
934 // TODO(jmesserly): I don't like how this is coupled to invoke
935 bool needsArgumentConversion(Arguments args) {
936 int bareCount = args.bareCount;
937 for (int i = 0; i < bareCount; i++) {
938 var arg = args.values[i];
939 if (arg.needsConversion(parameters[i].type)) {
940 return true;
941 }
942 }
943
944 if (bareCount < parameters.length) {
945 for (int i = bareCount; i < parameters.length; i++) {
946 var arg = args.getValue(parameters[i].name);
947 if (arg != null && arg.needsConversion(parameters[i].type)) {
948 return true;
949 }
950 }
951 }
952
953 return false;
954 }
955
956 /** Returns true if any of the parameters are optional. */
957 bool hasOptionalParameters() {
958 return parameters.some((Parameter p) => p.isOptional);
959 }
960
961 String _tooManyArgumentsMsg(int actual, int expected) {
962 return hasOptionalParameters()
963 ? 'too many arguments, expected at most $expected but found $actual'
964 : _wrongArgumentCountMsg(actual, expected);
965 }
966
967 String _tooFewArgumentsMsg(int actual, int expected) {
968 return hasOptionalParameters()
969 ? 'too few arguments, expected at least $expected but found $actual'
970 : _wrongArgumentCountMsg(actual, expected);
971 }
972
973 String _wrongArgumentCountMsg(int actual, int expected) {
974 return 'wrong number of arguments, expected $expected but found $actual';
975 }
976
977 Value _argError(CallingContext context, Node node, Value target,
978 Arguments args, String msg, int argIndex) {
979 if (context.showWarnings) {
980 SourceSpan span;
981 if ((args.nodes == null) || (argIndex >= args.nodes.length)) {
982 span = node.span;
983 } else {
984 span = args.nodes[argIndex].span;
985 }
986 if (isStatic || isConstructor) {
987 world.error(msg, span);
988 } else {
989 world.warning(msg, span);
990 }
991 }
992 return target.invokeNoSuchMethod(context, name, node, args);
993 }
994
995 genParameterValues(CallingContext context) {
996 // TODO(jimhug): Is this the right context?
997 if (context.needsCode) {
998 for (var p in parameters) p.genValue(this, context);
999 }
1000 }
1001
1002 /**
1003 * Invokes this method on the given [target] with the given [args].
1004 * [node] provides a [SourceSpan] for any error messages.
1005 */
1006 Value invoke(CallingContext context, Node node, Value target,
1007 Arguments args) {
1008
1009 var argValues = <Value>[];
1010 int bareCount = args.bareCount;
1011 for (int i = 0; i < bareCount; i++) {
1012 var arg = args.values[i];
1013 if (i >= parameters.length) {
1014 var msg = _tooManyArgumentsMsg(args.length, parameters.length);
1015 return _argError(context, node, target, args, msg, i);
1016 }
1017 argValues.add(arg.convertTo(context, parameters[i].type));
1018 }
1019
1020 int namedArgsUsed = 0;
1021 if (bareCount < parameters.length) {
1022 genParameterValues(context);
1023
1024 for (int i = bareCount; i < parameters.length; i++) {
1025 var param = parameters[i];
1026 var arg = args.getValue(param.name);
1027 if (arg == null) {
1028 arg = param.value;
1029 if (arg == null) {
1030 // TODO(jmesserly): should we be use the actual constant value here?
1031 arg = new PureStaticValue(param.type, param.definition.span, true);
1032 }
1033 } else {
1034 arg = arg.convertTo(context, parameters[i].type);
1035 namedArgsUsed++;
1036 }
1037
1038 if (arg == null || !parameters[i].isOptional) {
1039 var msg = _tooFewArgumentsMsg(Math.min(i, args.length), i + 1);
1040 return _argError(context, node, target, args, msg, i);
1041 } else {
1042 argValues.add(arg);
1043 }
1044 }
1045 }
1046
1047 if (namedArgsUsed < args.nameCount) {
1048 // Find the unused argument name
1049 var seen = new Set<String>();
1050 for (int i = bareCount; i < args.length; i++) {
1051 var name = args.getName(i);
1052 if (seen.contains(name)) {
1053 return _argError(context, node, target, args,
1054 'duplicate argument "$name"', i);
1055 }
1056 seen.add(name);
1057 int p = indexOfParameter(name);
1058 if (p < 0) {
1059 return _argError(context, node, target, args,
1060 'method does not have optional parameter "$name"', i);
1061 } else if (p < bareCount) {
1062 return _argError(context, node, target, args,
1063 'argument "$name" passed as positional and named',
1064 // Given that the named was mentioned explicitly, highlight the
1065 // positional location instead:
1066 p);
1067 }
1068 }
1069 world.internalError('wrong named arguments calling $name', node.span);
1070 }
1071
1072 if (!context.needsCode) {
1073 return new PureStaticValue(returnType, node.span);
1074 }
1075
1076 declaringType.genMethod(this);
1077
1078 if (isStatic || isFactory) {
1079 // TODO(sigmund): can we avoid generating the entire type, but only what
1080 // we need?
1081 declaringType.markUsed();
1082 }
1083
1084 // TODO(jmesserly): get rid of this in favor of using the native method
1085 // "bodies" to tell the compiler about valid return types.
1086 if (isNative && returnType != null) returnType.markUsed();
1087
1088 if (!namesInOrder(args)) {
1089 // Names aren't in order. For now, use a var call because it's an
1090 // easy way to get the right eval order for out of order arguments.
1091 // TODO(jmesserly): temps would be better.
1092 return context.findMembers(name).invokeOnVar(context, node, target, args);
1093 }
1094
1095 var argsCode = argValues.map((v) => v.code);
1096 if (!target.isType && (isConstructor || target.isSuper)) {
1097 argsCode.insertRange(0, 1, 'this');
1098 }
1099 if (bareCount < parameters.length) {
1100 Arguments.removeTrailingNulls(argsCode);
1101 }
1102 var argsString = Strings.join(argsCode, ', ');
1103
1104 if (isConstructor) {
1105 return _invokeConstructor(context, node, target, args, argsString);
1106 }
1107
1108 if (target.isSuper) {
1109 return new Value(inferredResult,
1110 '${declaringType.jsname}.prototype.$jsname.call($argsString)',
1111 node.span);
1112 }
1113
1114 if (isOperator) {
1115 return _invokeBuiltin(context, node, target, args, argsCode);
1116 }
1117
1118 if (isFactory) {
1119 assert(target.isType);
1120 return new Value(target.type, '$generatedFactoryName($argsString)',
1121 node !== null ? node.span : null);
1122 }
1123
1124 if (isStatic) {
1125 if (declaringType.isTop) {
1126 return new Value(inferredResult,
1127 '$jsname($argsString)', node !== null ? node.span : null);
1128 }
1129 return new Value(inferredResult,
1130 '${declaringType.jsname}.$jsname($argsString)', node.span);
1131 }
1132
1133 // TODO(jmesserly): factor this better
1134 if (name == 'get:typeName' && declaringType.library.isDomOrHtml) {
1135 world.gen.corejs.ensureTypeNameOf();
1136 }
1137
1138 var code = '${target.code}.$jsname($argsString)';
1139 return new Value(inferredResult, code, node.span);
1140 }
1141
1142 Value _invokeConstructor(CallingContext context, Node node,
1143 Value target, Arguments args, argsString) {
1144 declaringType.markUsed();
1145
1146 String ctor = constructorName;
1147 if (ctor != '') ctor = '.${ctor}\$ctor';
1148
1149 final span = node != null ? node.span : target.span;
1150 if (!target.isType) {
1151 // initializer call to another constructor
1152 var code = '${declaringType.nativeName}${ctor}.call($argsString)';
1153 return new Value(target.type, code, span);
1154 } else {
1155 // Start of abstract interpretation to replace const hacks goes here
1156 // TODO(jmesserly): using the "node" here feels really hacky
1157 if (isConst && node is NewExpression && node.dynamic.isConst) {
1158 // TODO(jimhug): Embedding JSSyntaxRegExp works around an annoying
1159 // issue with tracking native constructors for const objects.
1160 if (isNative || declaringType.name == 'JSSyntaxRegExp') {
1161 // check that all args are const?
1162 var code = 'new ${declaringType.nativeName}${ctor}($argsString)';
1163 return world.gen.globalForConst(new Value(target.type, code, span),
1164 [args.values]);
1165 }
1166 var newType = declaringType;
1167 var newObject = new ObjectValue(true, newType, span);
1168 newObject.initFields();
1169 _evalConstConstructor(newObject, args);
1170 return world.gen.globalForConst(newObject, [args.values]);
1171 } else {
1172 var code = 'new ${declaringType.nativeName}${ctor}($argsString)';
1173 return new Value(target.type, code, span);
1174 }
1175 }
1176 }
1177
1178 _evalConstConstructor(Value newObject, Arguments args) {
1179 declaringType.markUsed();
1180 methodData.eval(this, newObject, args);
1181 }
1182
1183 Value _invokeBuiltin(CallingContext context, Node node, Value target,
1184 Arguments args, argsCode) {
1185 // Handle some fast paths for Number, String, List and DOM.
1186 if (target.type.isNum) {
1187 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num.
1188 // TODO(jimhug): What about null?
1189 var code = null;
1190 if (args.length == 0) {
1191 if (name == ':negate') {
1192 code = '-${target.code}';
1193 } else if (name == ':bit_not') {
1194 code = '~${target.code}';
1195 }
1196 } else if (args.length == 1 && args.values[0].type.isNum) {
1197 if (name == ':truncdiv' || name == ':mod') {
1198 world.gen.corejs.useOperator(name);
1199 code = '$jsname\$(${target.code}, ${argsCode[0]})';
1200 } else {
1201 var op = TokenKind.rawOperatorFromMethod(name);
1202 code = '${target.code} $op ${argsCode[0]}';
1203 }
1204 }
1205 if (code !== null) {
1206 return new Value(inferredResult, code, node.span);
1207 }
1208 } else if (target.type.isString) {
1209 if (name == ':index' && args.values[0].type.isNum) {
1210 return new Value(declaringType, '${target.code}[${argsCode[0]}]',
1211 node.span);
1212 } else if (name == ':add' && args.values[0].type.isNum) {
1213 return new Value(declaringType, '${target.code} + ${argsCode[0]}',
1214 node.span);
1215 }
1216 } else if (declaringType.isNative && options.disableBoundsChecks) {
1217 if (args.length > 0 && args.values[0].type.isNum) {
1218 if (name == ':index') {
1219 return new Value(returnType,
1220 '${target.code}[${argsCode[0]}]', node.span);
1221 } else if (name == ':setindex') {
1222 return new Value(returnType,
1223 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span);
1224 }
1225 }
1226 }
1227
1228 // TODO(jimhug): Optimize null on lhs as well.
1229 if (name == ':eq' || name == ':ne') {
1230 final op = name == ':eq' ? '==' : '!=';
1231
1232 if (name == ':ne') {
1233 // Ensure == is generated.
1234 target.invoke(context, ':eq', node, args);
1235 }
1236
1237 // Optimize test when null is on the rhs.
1238 if (argsCode[0] == 'null') {
1239 return new Value(inferredResult, '${target.code} $op null', node.span);
1240 } else if (target.type.isNum || target.type.isString) {
1241 // TODO(jimhug): Maybe check rhs.
1242 return new Value(inferredResult, '${target.code} $op ${argsCode[0]}',
1243 node.span);
1244 }
1245 world.gen.corejs.useOperator(name);
1246 // TODO(jimhug): Should be able to use faster path sometimes here!
1247 return new Value(inferredResult,
1248 '$jsname\$(${target.code}, ${argsCode[0]})', node.span);
1249 }
1250
1251 if (isCallMethod) {
1252 declaringType.markUsed();
1253 return new Value(inferredResult,
1254 '${target.code}(${Strings.join(argsCode, ", ")})', node.span);
1255 }
1256
1257 // TODO(jimhug): Reconcile with MethodSet version - ideally just eliminate
1258 if (name == ':index') {
1259 world.gen.corejs.useIndex = true;
1260 } else if (name == ':setindex') {
1261 world.gen.corejs.useSetIndex = true;
1262 } else {
1263 world.gen.corejs.useOperator(name);
1264 var argsString = argsCode.length == 0 ? '' : ', ${argsCode[0]}';
1265 return new Value(returnType, '$jsname\$(${target.code}${argsString})',
1266 node.span);
1267 }
1268
1269 // Fall back to normal method invocation.
1270 var argsString = Strings.join(argsCode, ', ');
1271 return new Value(inferredResult, '${target.code}.$jsname($argsString)',
1272 node.span);
1273 }
1274
1275 resolve() {
1276 // TODO(jimhug): work through side-by-side with spec
1277 isStatic = declaringType.isTop;
1278 isConst = false;
1279 isFactory = false;
1280 isAbstract = !declaringType.isClass;
1281 if (definition.modifiers != null) {
1282 for (var mod in definition.modifiers) {
1283 if (mod.kind == TokenKind.STATIC) {
1284 if (isStatic) {
1285 world.error('duplicate static modifier', mod.span);
1286 }
1287 isStatic = true;
1288 } else if (isConstructor && mod.kind == TokenKind.CONST) {
1289 if (isConst) {
1290 world.error('duplicate const modifier', mod.span);
1291 }
1292 if (isFactory) {
1293 world.error('const factory not allowed', mod.span);
1294 }
1295 isConst = true;
1296 } else if (mod.kind == TokenKind.FACTORY) {
1297 if (isFactory) {
1298 world.error('duplicate factory modifier', mod.span);
1299 }
1300 if (isConst) {
1301 world.error('const factory not allowed', mod.span);
1302 }
1303 if (isStatic) {
1304 world.error('static factory not allowed', mod.span);
1305 }
1306 isFactory = true;
1307 } else if (mod.kind == TokenKind.ABSTRACT) {
1308 if (isAbstract) {
1309 if (declaringType.isClass) {
1310 world.error('duplicate abstract modifier', mod.span);
1311 } else if (!isCallMethod) {
1312 world.error('abstract modifier not allowed on interface members',
1313 mod.span);
1314 }
1315 }
1316 isAbstract = true;
1317 } else {
1318 world.error('${mod} modifier not allowed on method', mod.span);
1319 }
1320 }
1321 }
1322
1323 if (isFactory) {
1324 isStatic = true;
1325 }
1326
1327 // TODO(jimhug): need a better annotation for being an operator method
1328 if (isOperator && isStatic && !isCallMethod) {
1329 world.error('operator method may not be static "${name}"', span);
1330 }
1331
1332 if (isAbstract) {
1333 if (definition.body != null &&
1334 declaringType.definition is! FunctionTypeDefinition) {
1335 // TODO(jimhug): Creating function types for concrete methods is
1336 // steadily feeling uglier...
1337 world.error('abstract method cannot have a body', span);
1338 }
1339 if (isStatic &&
1340 declaringType.definition is! FunctionTypeDefinition) {
1341 world.error('static method cannot be abstract', span);
1342 }
1343 } else {
1344 if (definition.body == null && !isConstructor && !isNative) {
1345 world.error('method needs a body', span);
1346 }
1347 }
1348
1349 if (isConstructor && !isFactory) {
1350 returnType = declaringType;
1351 } else {
1352 // This is the one and only place we allow void.
1353 if (definition.returnType is SimpleTypeReference &&
1354 definition.returnType.dynamic.type == world.voidType) {
1355 returnType = world.voidType;
1356 } else {
1357 returnType = resolveType(definition.returnType, false, !isStatic);
1358 }
1359 }
1360 parameters = [];
1361 for (var formal in definition.formals) {
1362 // TODO(jimhug): Clean up construction of Parameters.
1363 var param = new Parameter(formal, this);
1364 param.resolve();
1365 parameters.add(param);
1366 }
1367
1368 if (!isLambda && declaringType.isClass) {
1369 library._addMember(this);
1370 }
1371 }
1372 }
1373
1374
1375 /**
1376 * A [FactoryMap] maps type names to a list of factory constructors.
1377 * The constructors list is actually a map that maps factory names to
1378 * [MethodMember]. The reason why we need both indirections are:
1379 * 1) A class can define factory methods for multiple interfaces.
1380 * 2) A factory constructor can have a name.
1381 *
1382 * For example:
1383 *
1384 * [:
1385 * interface I factory A {
1386 * I();
1387 * I.foo();
1388 * }
1389 *
1390 * interface I2 factory A {
1391 * I2();
1392 * }
1393 *
1394 * class A {
1395 * factory I() { ... } // Member1
1396 * factory I.foo() { ... } // Member2
1397 * factory I2() { ... } // Member3
1398 * factory A() { ... } // Member4
1399 * }
1400 * :]
1401 *
1402 * The [:factories:] field of A will be a [FactoryMap] that looks
1403 * like:
1404 * { "I" : { "": Member1, "foo": Member2 },
1405 * "I2" : { "": Member3 },
1406 * "A" : { "", Member4 }
1407 * }
1408 */
1409 class FactoryMap {
1410 Map<String, Map<String, Member>> factories;
1411
1412 FactoryMap() : factories = {};
1413
1414 // Returns the factories defined for [type].
1415 Map<String, Member> getFactoriesFor(String typeName) {
1416 var ret = factories[typeName];
1417 if (ret == null) {
1418 ret = {};
1419 factories[typeName] = ret;
1420 }
1421 return ret;
1422 }
1423
1424 void addFactory(String typeName, String name, Member member) {
1425 getFactoriesFor(typeName)[name] = member;
1426 }
1427
1428 Member getFactory(String typeName, String name) {
1429 return getFactoriesFor(typeName)[name];
1430 }
1431
1432 void forEach(void f(Member member)) {
1433 factories.forEach((_, Map constructors) {
1434 constructors.forEach((_, Member member) {
1435 f(member);
1436 });
1437 });
1438 }
1439
1440 bool isEmpty() {
1441 return factories.getValues()
1442 .every((Map constructors) => constructors.isEmpty());
1443 }
1444 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698