| Index: frog/member.dart
|
| diff --git a/frog/member.dart b/frog/member.dart
|
| deleted file mode 100644
|
| index dd768b030d8968508c242d760a968c10d7bba26a..0000000000000000000000000000000000000000
|
| --- a/frog/member.dart
|
| +++ /dev/null
|
| @@ -1,1444 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -/** A formal parameter to a [Method]. */
|
| -class Parameter {
|
| - FormalNode definition;
|
| - Member method;
|
| -
|
| - String name;
|
| - Type type;
|
| - bool isInitializer = false;
|
| -
|
| - Value value;
|
| -
|
| - Parameter(this.definition, this.method);
|
| -
|
| - resolve() {
|
| - name = definition.name.name;
|
| - if (name.startsWith('this.')) {
|
| - name = name.substring(5);
|
| - isInitializer = true;
|
| - }
|
| -
|
| - type = method.resolveType(definition.type, false, true);
|
| -
|
| - if (definition.value != null) {
|
| - // To match VM, detect cases where value was not actually specified in
|
| - // code and don't signal errors.
|
| - // TODO(jimhug): Clean up after issue #352 is resolved.
|
| - if (!hasDefaultValue) return;
|
| -
|
| - if (method.name == ':call') {
|
| - // TODO(jimhug): Need simpler way to detect "true" function types vs.
|
| - // regular methods being used as function types for closures.
|
| - // TODO(sigmund): Disallow non-null default values for native calls?
|
| - var methodDef = method.definition;
|
| - if (methodDef.body == null && !method.isNative) {
|
| - world.error('default value not allowed on function type',
|
| - definition.span);
|
| - }
|
| - } else if (method.isAbstract) {
|
| - world.error('default value not allowed on abstract methods',
|
| - definition.span);
|
| - }
|
| - } else if (isInitializer && !method.isConstructor) {
|
| - world.error('initializer parameters only allowed on constructors',
|
| - definition.span);
|
| - }
|
| - }
|
| -
|
| - genValue(MethodMember method, CallingContext context) {
|
| - if (definition.value == null || value != null) return;
|
| -
|
| - // TODO(jmesserly): what should we do when context is not a MethodGenerator?
|
| - if (context is MethodGenerator) {
|
| - MethodGenerator gen = context;
|
| - value = definition.value.visit(gen);
|
| - if (!value.isConst) {
|
| - world.error('default parameter values must be constant', value.span);
|
| - }
|
| - value = value.convertTo(context, type);
|
| - }
|
| - }
|
| -
|
| - Parameter copyWithNewType(Member newMethod, Type newType) {
|
| - var ret = new Parameter(definition, newMethod);
|
| - ret.type = newType;
|
| - ret.name = name;
|
| - ret.isInitializer = isInitializer;
|
| - return ret;
|
| - }
|
| -
|
| - bool get isOptional() => definition != null && definition.value != null;
|
| -
|
| - /**
|
| - * Gets whether this named parameter has an explicit default value or relies
|
| - * on the implicit `null`.
|
| - */
|
| - bool get hasDefaultValue() =>
|
| - definition.value.span.start != definition.span.start;
|
| -}
|
| -
|
| -
|
| -class Member extends Element {
|
| - final Type declaringType;
|
| -
|
| - Member genericMember;
|
| -
|
| - // A root string for getter and setter names. This is used e.g. to ensure
|
| - // that fields with the same Dart name but different jsnames (due to native
|
| - // name directives) still have a common getter name. Is null when there is no
|
| - // renaming.
|
| - String _jsnameRoot;
|
| -
|
| - Member(String name, Type declaringType)
|
| - : this.declaringType = declaringType,
|
| - super(name, declaringType);
|
| -
|
| - String mangleJsName() {
|
| - var mangled = super.mangleJsName();
|
| - if (declaringType != null && declaringType.isTop) {
|
| - return JsNames.getValid(mangled);
|
| - } else {
|
| - // We don't need to mangle native member names unless
|
| - // they contain illegal characters.
|
| - return (isNative && !name.contains(':')) ? name : mangled;
|
| - }
|
| - }
|
| -
|
| - abstract bool get isStatic();
|
| - abstract Type get returnType();
|
| -
|
| - abstract bool get canGet();
|
| - abstract bool get canSet();
|
| -
|
| - Library get library() => declaringType.library;
|
| -
|
| - bool get isPrivate() => name !== null && name.startsWith('_');
|
| -
|
| - bool get isConstructor() => false;
|
| - bool get isField() => false;
|
| - bool get isMethod() => false;
|
| - bool get isProperty() => false;
|
| - bool get isAbstract() => false;
|
| -
|
| - bool get isFinal() => false;
|
| -
|
| - // TODO(jmesserly): these only makes sense on methods, but because of
|
| - // ConcreteMember we need to support them on Member.
|
| - bool get isConst() => false;
|
| - bool get isFactory() => false;
|
| -
|
| - bool get isOperator() => name.startsWith(':');
|
| - bool get isCallMethod() => name == ':call';
|
| -
|
| - bool get requiresPropertySyntax() => false;
|
| - bool _provideGetter = false;
|
| - bool _provideSetter = false;
|
| -
|
| - bool get isNative() => false;
|
| - String get constructorName() {
|
| - world.internalError('cannot be a constructor', span);
|
| - }
|
| -
|
| - void provideGetter() {}
|
| - void provideSetter() {}
|
| -
|
| - String get jsnameOfGetter() => 'get\$$jsnameRoot';
|
| - String get jsnameOfSetter() => 'set\$$jsnameRoot';
|
| - String get jsnameRoot() => _jsnameRoot != null ? _jsnameRoot : _jsname;
|
| -
|
| - Member get initDelegate() {
|
| - world.internalError('cannot have initializers', span);
|
| - }
|
| - void set initDelegate(ctor) {
|
| - world.internalError('cannot have initializers', span);
|
| - }
|
| -
|
| - Value computeValue() {
|
| - world.internalError('cannot have value', span);
|
| - }
|
| -
|
| - /**
|
| - * The inferred returnType. Right now this is just used to track
|
| - * non-nullable bools.
|
| - */
|
| - Type get inferredResult() {
|
| - var t = returnType;
|
| - if (t.isBool && (library.isCore || library.isCoreImpl)) {
|
| - // We trust our core libraries not to return null from bools.
|
| - // I hope this trust is well placed!
|
| - return world.nonNullBool;
|
| - }
|
| - return t;
|
| - }
|
| -
|
| - Definition get definition() => null;
|
| -
|
| - List<Parameter> get parameters() => [];
|
| -
|
| - MemberSet _preciseMemberSet, _potentialMemberSet;
|
| -
|
| - MemberSet get preciseMemberSet() {
|
| - if (_preciseMemberSet === null) {
|
| - _preciseMemberSet = new MemberSet(this);
|
| - }
|
| - return _preciseMemberSet;
|
| - }
|
| -
|
| - MemberSet get potentialMemberSet() {
|
| - // TODO(jimhug): This needs one more redesign - move to TypeSets...
|
| -
|
| - if (_potentialMemberSet === null) {
|
| - if (name == ':call') {
|
| - _potentialMemberSet = preciseMemberSet;
|
| - return _potentialMemberSet;
|
| - }
|
| -
|
| - final mems = new Set<Member>();
|
| - if (declaringType.isClass) mems.add(this);
|
| -
|
| - for (var subtype in declaringType.genericType.subtypes) {
|
| - if (!subtype.isClass) continue;
|
| - var mem = subtype.members[name];
|
| - if (mem !== null) {
|
| - if (mem.isDefinedOn(declaringType)) {
|
| - mems.add(mem);
|
| - }
|
| - } else if (!declaringType.isClass) {
|
| - // Handles weird interface case.
|
| - mem = subtype.getMember(name);
|
| - if (mem !== null && mem.isDefinedOn(declaringType)) {
|
| - mems.add(mem);
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (mems.length != 0) {
|
| - // TODO(jimhug): This hack needs to be rationalized.
|
| - for (var mem in mems) {
|
| - if (declaringType.genericType != declaringType &&
|
| - mem.genericMember != null && mems.contains(mem.genericMember)) {
|
| - //world.info('skip ${name} on ${mem.genericMember.declaringType.name}' +
|
| - // ' because we have on ${mem.declaringType.name} for ${declaringType.name}');
|
| - mems.remove(mem.genericMember);
|
| - }
|
| - }
|
| -
|
| -
|
| - for (var mem in mems) {
|
| - if (_potentialMemberSet === null) {
|
| - _potentialMemberSet = new MemberSet(mem);
|
| - } else {
|
| - _potentialMemberSet.add(mem);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return _potentialMemberSet;
|
| - }
|
| -
|
| - // If I have an object of [type] could I be invoking this member?
|
| - bool isDefinedOn(Type type) {
|
| - if (type.isClass) {
|
| - if (declaringType.isSubtypeOf(type)) {
|
| - return true;
|
| - } else if (type.isSubtypeOf(declaringType)) {
|
| - // maybe - but not if overridden somewhere
|
| - // TODO(jimhug): This lookup is not great for perf of this method.
|
| - return type.getMember(name) == this;
|
| - } else {
|
| - return false;
|
| - }
|
| - } else {
|
| - if (declaringType.isSubtypeOf(type)) {
|
| - return true;
|
| - } else {
|
| - // If this is an interface, the actual implementation may
|
| - // come from a class that does not implement this interface.
|
| - for (var t in declaringType.subtypes) {
|
| - if (t.isSubtypeOf(type) && t.getMember(name) == this) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| -
|
| - abstract Value _get(CallingContext context, Node node, Value target);
|
| -
|
| - abstract Value _set(CallingContext context, Node node, Value target,
|
| - Value value);
|
| -
|
| -
|
| - bool canInvoke(CallingContext context, Arguments args) {
|
| - // Any gettable member whose return type is callable can be "invoked".
|
| - if (canGet && (isField || isProperty)) {
|
| - return this.returnType.isFunction || this.returnType.isVar ||
|
| - this.returnType.getCallMethod() != null;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - Value invoke(CallingContext context, Node node, Value target,
|
| - Arguments args) {
|
| - var newTarget = _get(context, node, target);
|
| - return newTarget.invoke(context, ':call', node, args);
|
| - }
|
| -
|
| - bool override(Member other) {
|
| - if (isStatic) {
|
| - world.error('static members cannot hide parent members',
|
| - span, other.span);
|
| - return false;
|
| - } else if (other.isStatic) {
|
| - world.error('cannot override static member', span, other.span);
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - String get generatedFactoryName() {
|
| - assert(this.isFactory);
|
| - String prefix = '${declaringType.genericType.jsname}.${constructorName}\$';
|
| - if (name == '') {
|
| - return '${prefix}factory';
|
| - } else {
|
| - return '${prefix}$name\$factory';
|
| - }
|
| - }
|
| -
|
| - int hashCode() {
|
| - final typeCode = declaringType == null ? 1 : declaringType.hashCode();
|
| - final nameCode = isConstructor ? constructorName.hashCode() :
|
| - name.hashCode();
|
| - return (typeCode << 4) ^ nameCode;
|
| - }
|
| -
|
| - bool operator ==(other) {
|
| - return other is Member && isConstructor == other.isConstructor &&
|
| - declaringType == other.declaringType && (isConstructor ?
|
| - constructorName == other.constructorName : name == other.name);
|
| - }
|
| -
|
| - /** Overriden to ensure that type arguments aren't used in static mems. */
|
| - Type resolveType(TypeReference node, bool typeErrors, bool allowTypeParams) {
|
| - allowTypeParams = allowTypeParams && !(isStatic && !isFactory);
|
| -
|
| - return super.resolveType(node, typeErrors, allowTypeParams);
|
| - }
|
| -
|
| - // TODO(jimhug): Make this abstract.
|
| - Member makeConcrete(Type concreteType) {
|
| - world.internalError('cannot make this concrete', span);
|
| - }
|
| -}
|
| -
|
| -
|
| -/**
|
| - * Types are treated as first class members of their library's top type.
|
| - */
|
| -// TODO(jmesserly): perhaps Type should extend Member, but that can get
|
| -// complicated.
|
| -class TypeMember extends Member {
|
| - final DefinedType type;
|
| -
|
| - TypeMember(DefinedType type)
|
| - : super(type.name, type.library.topType),
|
| - this.type = type;
|
| -
|
| - SourceSpan get span() => type.definition === null ? null : type.definition.span;
|
| -
|
| - bool get isStatic() => true;
|
| -
|
| - // If this really becomes first class, this should return typeof(Type)
|
| - Type get returnType() => world.varType;
|
| -
|
| - bool canInvoke(CallingContext context, Arguments args) => false;
|
| - bool get canGet() => true;
|
| - bool get canSet() => false;
|
| -
|
| - Value _get(CallingContext context, Node node, Value target) {
|
| - return new TypeValue(type, node.span);
|
| - }
|
| -
|
| - Value _set(CallingContext context, Node node, Value target, Value value) {
|
| - world.error('cannot set type', node.span);
|
| - }
|
| -
|
| - Value invoke(CallingContext context, Node node, Value target,
|
| - Arguments args) {
|
| - world.error('cannot invoke type', node.span);
|
| - }
|
| -}
|
| -
|
| -/** Represents a Dart field from source code. */
|
| -class FieldMember extends Member {
|
| - final VariableDefinition definition;
|
| - final Expression value;
|
| -
|
| - Type type;
|
| - Value _computedValue;
|
| -
|
| - bool isStatic;
|
| - bool isFinal;
|
| - final bool isNative;
|
| -
|
| - // TODO(jimhug): Better notion of fields that need special handling...
|
| - bool get overridesProperty() {
|
| - if (isStatic) return false;
|
| -
|
| - if (declaringType.parent != null) {
|
| - var p = declaringType.parent.getProperty(name);
|
| - if (p != null && p.isProperty) return true;
|
| - if (p is FieldMember && p != this) return p.overridesProperty;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool override(Member other) {
|
| - if (!super.override(other)) return false;
|
| -
|
| - // According to the specification, fields can override properties
|
| - // and other fields.
|
| - if (other.isProperty || other.isField) {
|
| - // TODO(jimhug):
|
| - // other.returnType.ensureAssignableFrom(returnType, null, true);
|
| - return true;
|
| - // TODO(jimhug): Merge in overridesProperty logic here.
|
| - } else {
|
| - world.error('field can only override field or property',
|
| - span, other.span);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - void provideGetter() {
|
| - _provideGetter = true;
|
| - if (genericMember !== null) {
|
| - genericMember.provideGetter();
|
| - }
|
| - }
|
| -
|
| - void provideSetter() {
|
| - _provideSetter = true;
|
| - if (genericMember !== null) {
|
| - genericMember.provideSetter();
|
| - }
|
| - }
|
| -
|
| - FieldMember(String name, Type declaringType, this.definition, this.value,
|
| - [bool this.isNative = false])
|
| - : super(name, declaringType);
|
| -
|
| - Member makeConcrete(Type concreteType) {
|
| - var ret = new FieldMember(name, concreteType, definition, value);
|
| - ret.genericMember = this;
|
| - ret._jsname = _jsname;
|
| - return ret;
|
| - }
|
| -
|
| - SourceSpan get span() => definition == null ? null : definition.span;
|
| -
|
| - Type get returnType() => type;
|
| -
|
| - bool get canGet() => true;
|
| - bool get canSet() => !isFinal;
|
| -
|
| - bool get isField() => true;
|
| -
|
| - resolve() {
|
| - isStatic = declaringType.isTop;
|
| - isFinal = false;
|
| - if (definition.modifiers != null) {
|
| - for (var mod in definition.modifiers) {
|
| - if (mod.kind == TokenKind.STATIC) {
|
| - if (isStatic) {
|
| - world.error('duplicate static modifier', mod.span);
|
| - }
|
| - isStatic = true;
|
| - } else if (mod.kind == TokenKind.FINAL) {
|
| - if (isFinal) {
|
| - world.error('duplicate final modifier', mod.span);
|
| - }
|
| - isFinal = true;
|
| - } else {
|
| - world.error('${mod} modifier not allowed on field', mod.span);
|
| - }
|
| - }
|
| - }
|
| - type = resolveType(definition.type, false, true);
|
| -
|
| - if (isStatic && isFinal && value == null) {
|
| - world.error('static final field is missing initializer', span);
|
| - }
|
| -
|
| - if (declaringType.isClass) library._addMember(this);
|
| - }
|
| -
|
| -
|
| - bool _computing = false;
|
| - /** Generates the initial value for this field, if any. Marks it as used. */
|
| - Value computeValue() {
|
| - if (value == null) return null;
|
| -
|
| - if (_computedValue == null) {
|
| - if (_computing) {
|
| - world.error('circular reference', value.span);
|
| - return null;
|
| - }
|
| - _computing = true;
|
| - var finalMethod = new MethodMember('final_context', declaringType, null);
|
| - finalMethod.isStatic = true;
|
| - var finalGen = new MethodGenerator(finalMethod, null);
|
| - _computedValue = value.visit(finalGen);
|
| - if (!_computedValue.isConst) {
|
| - if (isStatic) {
|
| - world.error(
|
| - 'non constant static field must be initialized in functions',
|
| - value.span);
|
| - } else {
|
| - world.error(
|
| - 'non constant field must be initialized in constructor',
|
| - value.span);
|
| - }
|
| - }
|
| -
|
| -
|
| - if (isStatic) {
|
| - if (isFinal && _computedValue.isConst) {
|
| - ; // keep const as is here
|
| - } else {
|
| - _computedValue = world.gen.globalForStaticField(
|
| - this, _computedValue, [_computedValue]);
|
| - }
|
| - }
|
| - _computing = false;
|
| - }
|
| - return _computedValue;
|
| - }
|
| -
|
| - Value _get(CallingContext context, Node node, Value target) {
|
| - if (!context.needsCode) {
|
| - return new PureStaticValue(type, node.span, isStatic && isFinal);
|
| - }
|
| -
|
| - if (isNative && returnType != null) {
|
| - returnType.markUsed();
|
| - if (returnType is DefinedType) {
|
| - // TODO(jmesserly): this handles native fields that return types like
|
| - // "List". Is there a better solution for fields? Unlike methods we have
|
| - // no good way to annotate them.
|
| - var defaultType = returnType.genericType.defaultType;
|
| - if (defaultType != null && defaultType.isNative) {
|
| - defaultType.markUsed();
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (isStatic) {
|
| - // TODO(jmesserly): can we avoid generating the whole type?
|
| - declaringType.markUsed();
|
| -
|
| - // Make sure to compute the value of all static fields, even if we don't
|
| - // use this value immediately.
|
| - var cv = computeValue();
|
| - if (isFinal) {
|
| - return cv;
|
| - }
|
| - world.gen.hasStatics = true;
|
| - if (declaringType.isTop) {
|
| - return new Value(type, '\$globals.$jsname', node.span);
|
| - } else if (declaringType.isNative) {
|
| - if (declaringType.isHiddenNativeType) {
|
| - // TODO: Could warn at parse time.
|
| - world.error('static field of hidden native type is inaccessible',
|
| - node.span);
|
| - }
|
| - return new Value(type, '${declaringType.jsname}.$jsname', node.span);
|
| - } else {
|
| - return new Value(type,
|
| - '\$globals.${declaringType.jsname}_$jsname', node.span);
|
| - }
|
| - }
|
| - return new Value(type, '${target.code}.$jsname', node.span);
|
| - }
|
| -
|
| - Value _set(CallingContext context, Node node, Value target, Value value) {
|
| - if (!context.needsCode) {
|
| - // TODO(jimhug): Add type checks here.
|
| - return new PureStaticValue(type, node.span);
|
| - }
|
| -
|
| - var lhs = _get(context, node, target);
|
| - value = value.convertTo(context, type);
|
| - return new Value(type, '${lhs.code} = ${value.code}', node.span);
|
| - }
|
| -}
|
| -
|
| -class PropertyMember extends Member {
|
| - MethodMember getter;
|
| - MethodMember setter;
|
| -
|
| - Member _overriddenField;
|
| -
|
| - // TODO(jimhug): What is the right span for this beast?
|
| - SourceSpan get span() => getter != null ? getter.span : null;
|
| -
|
| - bool get canGet() => getter != null;
|
| - bool get canSet() => setter != null;
|
| -
|
| - // If the property is just a declaration in an interface, continue to allow
|
| - // field syntax in the generated code.
|
| - bool get requiresPropertySyntax() => declaringType.isClass;
|
| -
|
| - // When overriding native fields, we still provide a field syntax to ensure
|
| - // that native functions will find the appropriate property implementation.
|
| - // TODO(sigmund): should check for this transitively...
|
| - bool get needsFieldSyntax() =>
|
| - _overriddenField != null &&
|
| - _overriddenField.isNative &&
|
| - // Can't put property on hidden native class...
|
| - !_overriddenField.declaringType.isHiddenNativeType
|
| - ;
|
| -
|
| - // TODO(jimhug): Union of getter and setters sucks!
|
| - bool get isStatic() => getter == null ? setter.isStatic : getter.isStatic;
|
| -
|
| - bool get isProperty() => true;
|
| -
|
| - Type get returnType() {
|
| - return getter == null ? setter.returnType : getter.returnType;
|
| - }
|
| -
|
| - PropertyMember(String name, Type declaringType): super(name, declaringType);
|
| -
|
| - Member makeConcrete(Type concreteType) {
|
| - var ret = new PropertyMember(name, concreteType);
|
| - if (getter !== null) ret.getter = getter.makeConcrete(concreteType);
|
| - if (setter !== null) ret.setter = setter.makeConcrete(concreteType);
|
| - ret._jsname = _jsname;
|
| - return ret;
|
| - }
|
| -
|
| - bool override(Member other) {
|
| - if (!super.override(other)) return false;
|
| -
|
| - // properties can override other properties and fields
|
| - if (other.isProperty || other.isField) {
|
| - // TODO(jimhug):
|
| - // other.returnType.ensureAssignableFrom(returnType, null, true);
|
| - if (other.isProperty) addFromParent(other);
|
| - else _overriddenField = other;
|
| - return true;
|
| - } else {
|
| - world.error('property can only override field or property',
|
| - span, other.span);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - Value _get(CallingContext context, Node node, Value target) {
|
| - if (getter == null) {
|
| - if (_overriddenField != null) {
|
| - return _overriddenField._get(context, node, target);
|
| - }
|
| - return target.invokeNoSuchMethod(context, 'get:$name', node);
|
| - }
|
| - return getter.invoke(context, node, target, Arguments.EMPTY);
|
| - }
|
| -
|
| - Value _set(CallingContext context, Node node, Value target, Value value) {
|
| - if (setter == null) {
|
| - if (_overriddenField != null) {
|
| - return _overriddenField._set(context, node, target, value);
|
| - }
|
| - return target.invokeNoSuchMethod(context, 'set:$name', node,
|
| - new Arguments(null, [value]));
|
| - }
|
| - return setter.invoke(context, node, target, new Arguments(null, [value]));
|
| - }
|
| -
|
| - addFromParent(Member parentMember) {
|
| - final parent = parentMember;
|
| -
|
| - if (getter == null) getter = parent.getter;
|
| - if (setter == null) setter = parent.setter;
|
| - }
|
| -
|
| - resolve() {
|
| - if (getter != null) {
|
| - getter.resolve();
|
| - if (getter.parameters.length != 0) {
|
| - world.error('getter methods should take no arguments',
|
| - getter.definition.span);
|
| - }
|
| - if (getter.returnType.isVoid) {
|
| - world.warning('getter methods should not be void',
|
| - getter.definition.returnType.span);
|
| - }
|
| - }
|
| - if (setter != null) {
|
| - setter.resolve();
|
| - if (setter.parameters.length != 1) {
|
| - world.error('setter methods should take a single argument',
|
| - setter.definition.span);
|
| - }
|
| - // Not issue warning if setter is implicitly dynamic (returnType == null),
|
| - // but do if it is explicit (returnType.isVar)
|
| - if (!setter.returnType.isVoid && setter.definition.returnType != null) {
|
| - world.warning('setter methods should be void',
|
| - setter.definition.returnType.span);
|
| - }
|
| - }
|
| -
|
| - if (declaringType.isClass) library._addMember(this);
|
| - }
|
| -}
|
| -
|
| -
|
| -/** Represents a Dart method or top-level function. */
|
| -class MethodMember extends Member {
|
| - FunctionDefinition definition;
|
| - Type returnType;
|
| - List<Parameter> parameters;
|
| -
|
| - MethodData _methodData;
|
| -
|
| - Type _functionType;
|
| - bool isStatic = false;
|
| - bool isAbstract = false;
|
| -
|
| - // Note: these two modifiers are only legal on constructors
|
| - bool isConst = false;
|
| - bool isFactory = false;
|
| -
|
| - /** True if this is a function defined inside another method. */
|
| - final bool isLambda;
|
| -
|
| - /**
|
| - * True if we should provide info on optional parameters for use by runtime
|
| - * dispatch.
|
| - */
|
| - bool _provideOptionalParamInfo = false;
|
| -
|
| - /*
|
| - * When this is a constructor, contains any other constructor called during
|
| - * initialization (if any).
|
| - */
|
| - Member initDelegate;
|
| -
|
| - bool _hasNativeBody = false;
|
| -
|
| - static final kIdentifierRegExp = const RegExp(@'^[a-zA-Z][a-zA-Z_$0-9]*$');
|
| -
|
| - MethodMember(String name, Type declaringType, this.definition)
|
| - : isLambda = false, super(name, declaringType) {
|
| - if (isNative) {
|
| - // Parse the native string. The the native string can be a native name
|
| - // (identifier) or a chunk of JavaScript code.
|
| - //
|
| - // foo() native 'bar'; // The native method is called 'bar'.
|
| - // foo() native 'return 1'; // Defines method with native implementation.
|
| - //
|
| - if (kIdentifierRegExp.hasMatch(definition.nativeBody)) {
|
| - _jsname = definition.nativeBody;
|
| - // Prevent the compiler from using the name for a regular Dart member.
|
| - world._addHazardousMemberName(_jsname);
|
| - }
|
| - _hasNativeBody = definition.nativeBody != '' &&
|
| - definition.nativeBody != _jsname;
|
| - }
|
| - }
|
| -
|
| - MethodMember.lambda(String name, Type declaringType, this.definition)
|
| - : isLambda = true, super(name, declaringType);
|
| -
|
| - Member makeConcrete(Type concreteType) {
|
| - var _name = isConstructor ? concreteType.name : name;
|
| - var ret = new MethodMember(_name, concreteType, definition);
|
| - ret.genericMember = this;
|
| - ret._jsname = _jsname;
|
| - return ret;
|
| - }
|
| -
|
| - MethodData get methodData() {
|
| - if (genericMember !== null) return genericMember.dynamic.methodData;
|
| -
|
| - if (_methodData === null) {
|
| - _methodData = new MethodData(this);
|
| - }
|
| - return _methodData;
|
| - }
|
| -
|
| - bool get isConstructor() => name == declaringType.name;
|
| - bool get isMethod() => !isConstructor;
|
| -
|
| - bool get isNative() {
|
| - if (definition == null) return false;
|
| - return definition.nativeBody != null;
|
| - }
|
| -
|
| - bool get hasNativeBody() => _hasNativeBody;
|
| -
|
| - bool get canGet() => true;
|
| - bool get canSet() => false;
|
| -
|
| - bool get requiresPropertySyntax() => true;
|
| -
|
| - SourceSpan get span() => definition == null ? null : definition.span;
|
| -
|
| - String get constructorName() {
|
| - var returnType = definition.returnType;
|
| - if (returnType == null) return '';
|
| - if (returnType is GenericTypeReference) {
|
| - return '';
|
| - }
|
| -
|
| - // TODO(jmesserly): make this easier?
|
| - if (returnType.names != null) {
|
| - return returnType.names[0].name;
|
| - } else if (returnType.name != null) {
|
| - return returnType.name.name;
|
| - }
|
| - world.internalError('no valid constructor name', definition.span);
|
| - }
|
| -
|
| - Type get functionType() {
|
| - if (_functionType == null) {
|
| - _functionType = library.getOrAddFunctionType(declaringType, name,
|
| - definition, methodData);
|
| - // TODO(jimhug): Better resolution checks.
|
| - if (parameters == null) {
|
| - resolve();
|
| - }
|
| - }
|
| - return _functionType;
|
| - }
|
| -
|
| - bool override(Member other) {
|
| - if (!super.override(other)) return false;
|
| -
|
| - // methods can only override other methods
|
| - if (other.isMethod) {
|
| - // TODO(jimhug):
|
| - // other.returnType.ensureAssignableFrom(returnType, null, true);
|
| - // TODO(jimhug): Check for further parameter compatibility.
|
| - return true;
|
| - } else {
|
| - world.error('method can only override methods', span, other.span);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - bool canInvoke(CallingContext context, Arguments args) {
|
| - int bareCount = args.bareCount;
|
| -
|
| - if (bareCount > parameters.length) return false;
|
| -
|
| - if (bareCount == parameters.length) {
|
| - if (bareCount != args.length) return false;
|
| - } else {
|
| - if (!parameters[bareCount].isOptional) return false;
|
| -
|
| - for (int i = bareCount; i < args.length; i++) {
|
| - if (indexOfParameter(args.getName(i)) < 0) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| -
|
| - return true;
|
| - }
|
| -
|
| - // TODO(jmesserly): might need to make this faster
|
| - /** Gets the index of an optional parameter. */
|
| - int indexOfParameter(String name) {
|
| - for (int i = 0; i < parameters.length; i++) {
|
| - final p = parameters[i];
|
| - if (p.isOptional && p.name == name) {
|
| - return i;
|
| - }
|
| - }
|
| - return -1;
|
| - }
|
| -
|
| - void provideGetter() { _provideGetter = true; }
|
| - void provideSetter() { _provideSetter = true; }
|
| -
|
| - Value _set(CallingContext context, Node node, Value target, Value value) {
|
| - world.error('cannot set method', node.span);
|
| - }
|
| -
|
| - Value _get(CallingContext context, Node node, Value target) {
|
| - if (!context.needsCode) {
|
| - return new PureStaticValue(functionType, node.span);
|
| - }
|
| -
|
| - // TODO(jimhug): Would prefer to invoke!
|
| - declaringType.genMethod(this);
|
| - _provideOptionalParamInfo = true;
|
| - if (isStatic) {
|
| - // ensure the type is generated.
|
| - // TODO(sigmund): can we avoid generating the entire type, but only what
|
| - // we need?
|
| - declaringType.markUsed();
|
| - var type = declaringType.isTop ? '' : '${declaringType.jsname}.';
|
| - return new Value(functionType, '$type$jsname', node.span);
|
| - }
|
| - _provideGetter = true;
|
| - return new Value(functionType, '${target.code}.$jsnameOfGetter()', node.span);
|
| - }
|
| -
|
| - /**
|
| - * Checks if the named arguments are in their natural or 'home' positions,
|
| - * i.e. they may be passed directly without inserting, deleting or moving the
|
| - * arguments to correspond with the parameters.
|
| - */
|
| - bool namesInHomePositions(Arguments args) {
|
| - if (!args.hasNames) return true;
|
| -
|
| - for (int i = args.bareCount; i < args.values.length; i++) {
|
| - if (i >= parameters.length) {
|
| - return false;
|
| - }
|
| - if (args.getName(i) != parameters[i].name) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool namesInOrder(Arguments args) {
|
| - if (!args.hasNames) return true;
|
| -
|
| - int lastParameter = null;
|
| - for (int i = args.bareCount; i < parameters.length; i++) {
|
| - var p = args.getIndexOfName(parameters[i].name);
|
| - // Only worry about parameters that needTemps. Otherwise it's fine to
|
| - // reorder.
|
| - if (p >= 0 && args.values[p].needsTemp) {
|
| - if (lastParameter != null && lastParameter > p) {
|
| - return false;
|
| - }
|
| - lastParameter = p;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /** Returns true if any of the arguments will need conversion. */
|
| - // TODO(jmesserly): I don't like how this is coupled to invoke
|
| - bool needsArgumentConversion(Arguments args) {
|
| - int bareCount = args.bareCount;
|
| - for (int i = 0; i < bareCount; i++) {
|
| - var arg = args.values[i];
|
| - if (arg.needsConversion(parameters[i].type)) {
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - if (bareCount < parameters.length) {
|
| - for (int i = bareCount; i < parameters.length; i++) {
|
| - var arg = args.getValue(parameters[i].name);
|
| - if (arg != null && arg.needsConversion(parameters[i].type)) {
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| -
|
| - return false;
|
| - }
|
| -
|
| - /** Returns true if any of the parameters are optional. */
|
| - bool hasOptionalParameters() {
|
| - return parameters.some((Parameter p) => p.isOptional);
|
| - }
|
| -
|
| - String _tooManyArgumentsMsg(int actual, int expected) {
|
| - return hasOptionalParameters()
|
| - ? 'too many arguments, expected at most $expected but found $actual'
|
| - : _wrongArgumentCountMsg(actual, expected);
|
| - }
|
| -
|
| - String _tooFewArgumentsMsg(int actual, int expected) {
|
| - return hasOptionalParameters()
|
| - ? 'too few arguments, expected at least $expected but found $actual'
|
| - : _wrongArgumentCountMsg(actual, expected);
|
| - }
|
| -
|
| - String _wrongArgumentCountMsg(int actual, int expected) {
|
| - return 'wrong number of arguments, expected $expected but found $actual';
|
| - }
|
| -
|
| - Value _argError(CallingContext context, Node node, Value target,
|
| - Arguments args, String msg, int argIndex) {
|
| - if (context.showWarnings) {
|
| - SourceSpan span;
|
| - if ((args.nodes == null) || (argIndex >= args.nodes.length)) {
|
| - span = node.span;
|
| - } else {
|
| - span = args.nodes[argIndex].span;
|
| - }
|
| - if (isStatic || isConstructor) {
|
| - world.error(msg, span);
|
| - } else {
|
| - world.warning(msg, span);
|
| - }
|
| - }
|
| - return target.invokeNoSuchMethod(context, name, node, args);
|
| - }
|
| -
|
| - genParameterValues(CallingContext context) {
|
| - // TODO(jimhug): Is this the right context?
|
| - if (context.needsCode) {
|
| - for (var p in parameters) p.genValue(this, context);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Invokes this method on the given [target] with the given [args].
|
| - * [node] provides a [SourceSpan] for any error messages.
|
| - */
|
| - Value invoke(CallingContext context, Node node, Value target,
|
| - Arguments args) {
|
| -
|
| - var argValues = <Value>[];
|
| - int bareCount = args.bareCount;
|
| - for (int i = 0; i < bareCount; i++) {
|
| - var arg = args.values[i];
|
| - if (i >= parameters.length) {
|
| - var msg = _tooManyArgumentsMsg(args.length, parameters.length);
|
| - return _argError(context, node, target, args, msg, i);
|
| - }
|
| - argValues.add(arg.convertTo(context, parameters[i].type));
|
| - }
|
| -
|
| - int namedArgsUsed = 0;
|
| - if (bareCount < parameters.length) {
|
| - genParameterValues(context);
|
| -
|
| - for (int i = bareCount; i < parameters.length; i++) {
|
| - var param = parameters[i];
|
| - var arg = args.getValue(param.name);
|
| - if (arg == null) {
|
| - arg = param.value;
|
| - if (arg == null) {
|
| - // TODO(jmesserly): should we be use the actual constant value here?
|
| - arg = new PureStaticValue(param.type, param.definition.span, true);
|
| - }
|
| - } else {
|
| - arg = arg.convertTo(context, parameters[i].type);
|
| - namedArgsUsed++;
|
| - }
|
| -
|
| - if (arg == null || !parameters[i].isOptional) {
|
| - var msg = _tooFewArgumentsMsg(Math.min(i, args.length), i + 1);
|
| - return _argError(context, node, target, args, msg, i);
|
| - } else {
|
| - argValues.add(arg);
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (namedArgsUsed < args.nameCount) {
|
| - // Find the unused argument name
|
| - var seen = new Set<String>();
|
| - for (int i = bareCount; i < args.length; i++) {
|
| - var name = args.getName(i);
|
| - if (seen.contains(name)) {
|
| - return _argError(context, node, target, args,
|
| - 'duplicate argument "$name"', i);
|
| - }
|
| - seen.add(name);
|
| - int p = indexOfParameter(name);
|
| - if (p < 0) {
|
| - return _argError(context, node, target, args,
|
| - 'method does not have optional parameter "$name"', i);
|
| - } else if (p < bareCount) {
|
| - return _argError(context, node, target, args,
|
| - 'argument "$name" passed as positional and named',
|
| - // Given that the named was mentioned explicitly, highlight the
|
| - // positional location instead:
|
| - p);
|
| - }
|
| - }
|
| - world.internalError('wrong named arguments calling $name', node.span);
|
| - }
|
| -
|
| - if (!context.needsCode) {
|
| - return new PureStaticValue(returnType, node.span);
|
| - }
|
| -
|
| - declaringType.genMethod(this);
|
| -
|
| - if (isStatic || isFactory) {
|
| - // TODO(sigmund): can we avoid generating the entire type, but only what
|
| - // we need?
|
| - declaringType.markUsed();
|
| - }
|
| -
|
| - // TODO(jmesserly): get rid of this in favor of using the native method
|
| - // "bodies" to tell the compiler about valid return types.
|
| - if (isNative && returnType != null) returnType.markUsed();
|
| -
|
| - if (!namesInOrder(args)) {
|
| - // Names aren't in order. For now, use a var call because it's an
|
| - // easy way to get the right eval order for out of order arguments.
|
| - // TODO(jmesserly): temps would be better.
|
| - return context.findMembers(name).invokeOnVar(context, node, target, args);
|
| - }
|
| -
|
| - var argsCode = argValues.map((v) => v.code);
|
| - if (!target.isType && (isConstructor || target.isSuper)) {
|
| - argsCode.insertRange(0, 1, 'this');
|
| - }
|
| - if (bareCount < parameters.length) {
|
| - Arguments.removeTrailingNulls(argsCode);
|
| - }
|
| - var argsString = Strings.join(argsCode, ', ');
|
| -
|
| - if (isConstructor) {
|
| - return _invokeConstructor(context, node, target, args, argsString);
|
| - }
|
| -
|
| - if (target.isSuper) {
|
| - return new Value(inferredResult,
|
| - '${declaringType.jsname}.prototype.$jsname.call($argsString)',
|
| - node.span);
|
| - }
|
| -
|
| - if (isOperator) {
|
| - return _invokeBuiltin(context, node, target, args, argsCode);
|
| - }
|
| -
|
| - if (isFactory) {
|
| - assert(target.isType);
|
| - return new Value(target.type, '$generatedFactoryName($argsString)',
|
| - node !== null ? node.span : null);
|
| - }
|
| -
|
| - if (isStatic) {
|
| - if (declaringType.isTop) {
|
| - return new Value(inferredResult,
|
| - '$jsname($argsString)', node !== null ? node.span : null);
|
| - }
|
| - return new Value(inferredResult,
|
| - '${declaringType.jsname}.$jsname($argsString)', node.span);
|
| - }
|
| -
|
| - // TODO(jmesserly): factor this better
|
| - if (name == 'get:typeName' && declaringType.library.isDomOrHtml) {
|
| - world.gen.corejs.ensureTypeNameOf();
|
| - }
|
| -
|
| - var code = '${target.code}.$jsname($argsString)';
|
| - return new Value(inferredResult, code, node.span);
|
| - }
|
| -
|
| - Value _invokeConstructor(CallingContext context, Node node,
|
| - Value target, Arguments args, argsString) {
|
| - declaringType.markUsed();
|
| -
|
| - String ctor = constructorName;
|
| - if (ctor != '') ctor = '.${ctor}\$ctor';
|
| -
|
| - final span = node != null ? node.span : target.span;
|
| - if (!target.isType) {
|
| - // initializer call to another constructor
|
| - var code = '${declaringType.nativeName}${ctor}.call($argsString)';
|
| - return new Value(target.type, code, span);
|
| - } else {
|
| - // Start of abstract interpretation to replace const hacks goes here
|
| - // TODO(jmesserly): using the "node" here feels really hacky
|
| - if (isConst && node is NewExpression && node.dynamic.isConst) {
|
| - // TODO(jimhug): Embedding JSSyntaxRegExp works around an annoying
|
| - // issue with tracking native constructors for const objects.
|
| - if (isNative || declaringType.name == 'JSSyntaxRegExp') {
|
| - // check that all args are const?
|
| - var code = 'new ${declaringType.nativeName}${ctor}($argsString)';
|
| - return world.gen.globalForConst(new Value(target.type, code, span),
|
| - [args.values]);
|
| - }
|
| - var newType = declaringType;
|
| - var newObject = new ObjectValue(true, newType, span);
|
| - newObject.initFields();
|
| - _evalConstConstructor(newObject, args);
|
| - return world.gen.globalForConst(newObject, [args.values]);
|
| - } else {
|
| - var code = 'new ${declaringType.nativeName}${ctor}($argsString)';
|
| - return new Value(target.type, code, span);
|
| - }
|
| - }
|
| - }
|
| -
|
| - _evalConstConstructor(Value newObject, Arguments args) {
|
| - declaringType.markUsed();
|
| - methodData.eval(this, newObject, args);
|
| - }
|
| -
|
| - Value _invokeBuiltin(CallingContext context, Node node, Value target,
|
| - Arguments args, argsCode) {
|
| - // Handle some fast paths for Number, String, List and DOM.
|
| - if (target.type.isNum) {
|
| - // TODO(jimhug): This fails in bad ways when argsCode[1] is not num.
|
| - // TODO(jimhug): What about null?
|
| - var code = null;
|
| - if (args.length == 0) {
|
| - if (name == ':negate') {
|
| - code = '-${target.code}';
|
| - } else if (name == ':bit_not') {
|
| - code = '~${target.code}';
|
| - }
|
| - } else if (args.length == 1 && args.values[0].type.isNum) {
|
| - if (name == ':truncdiv' || name == ':mod') {
|
| - world.gen.corejs.useOperator(name);
|
| - code = '$jsname\$(${target.code}, ${argsCode[0]})';
|
| - } else {
|
| - var op = TokenKind.rawOperatorFromMethod(name);
|
| - code = '${target.code} $op ${argsCode[0]}';
|
| - }
|
| - }
|
| - if (code !== null) {
|
| - return new Value(inferredResult, code, node.span);
|
| - }
|
| - } else if (target.type.isString) {
|
| - if (name == ':index' && args.values[0].type.isNum) {
|
| - return new Value(declaringType, '${target.code}[${argsCode[0]}]',
|
| - node.span);
|
| - } else if (name == ':add' && args.values[0].type.isNum) {
|
| - return new Value(declaringType, '${target.code} + ${argsCode[0]}',
|
| - node.span);
|
| - }
|
| - } else if (declaringType.isNative && options.disableBoundsChecks) {
|
| - if (args.length > 0 && args.values[0].type.isNum) {
|
| - if (name == ':index') {
|
| - return new Value(returnType,
|
| - '${target.code}[${argsCode[0]}]', node.span);
|
| - } else if (name == ':setindex') {
|
| - return new Value(returnType,
|
| - '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // TODO(jimhug): Optimize null on lhs as well.
|
| - if (name == ':eq' || name == ':ne') {
|
| - final op = name == ':eq' ? '==' : '!=';
|
| -
|
| - if (name == ':ne') {
|
| - // Ensure == is generated.
|
| - target.invoke(context, ':eq', node, args);
|
| - }
|
| -
|
| - // Optimize test when null is on the rhs.
|
| - if (argsCode[0] == 'null') {
|
| - return new Value(inferredResult, '${target.code} $op null', node.span);
|
| - } else if (target.type.isNum || target.type.isString) {
|
| - // TODO(jimhug): Maybe check rhs.
|
| - return new Value(inferredResult, '${target.code} $op ${argsCode[0]}',
|
| - node.span);
|
| - }
|
| - world.gen.corejs.useOperator(name);
|
| - // TODO(jimhug): Should be able to use faster path sometimes here!
|
| - return new Value(inferredResult,
|
| - '$jsname\$(${target.code}, ${argsCode[0]})', node.span);
|
| - }
|
| -
|
| - if (isCallMethod) {
|
| - declaringType.markUsed();
|
| - return new Value(inferredResult,
|
| - '${target.code}(${Strings.join(argsCode, ", ")})', node.span);
|
| - }
|
| -
|
| - // TODO(jimhug): Reconcile with MethodSet version - ideally just eliminate
|
| - if (name == ':index') {
|
| - world.gen.corejs.useIndex = true;
|
| - } else if (name == ':setindex') {
|
| - world.gen.corejs.useSetIndex = true;
|
| - } else {
|
| - world.gen.corejs.useOperator(name);
|
| - var argsString = argsCode.length == 0 ? '' : ', ${argsCode[0]}';
|
| - return new Value(returnType, '$jsname\$(${target.code}${argsString})',
|
| - node.span);
|
| - }
|
| -
|
| - // Fall back to normal method invocation.
|
| - var argsString = Strings.join(argsCode, ', ');
|
| - return new Value(inferredResult, '${target.code}.$jsname($argsString)',
|
| - node.span);
|
| - }
|
| -
|
| - resolve() {
|
| - // TODO(jimhug): work through side-by-side with spec
|
| - isStatic = declaringType.isTop;
|
| - isConst = false;
|
| - isFactory = false;
|
| - isAbstract = !declaringType.isClass;
|
| - if (definition.modifiers != null) {
|
| - for (var mod in definition.modifiers) {
|
| - if (mod.kind == TokenKind.STATIC) {
|
| - if (isStatic) {
|
| - world.error('duplicate static modifier', mod.span);
|
| - }
|
| - isStatic = true;
|
| - } else if (isConstructor && mod.kind == TokenKind.CONST) {
|
| - if (isConst) {
|
| - world.error('duplicate const modifier', mod.span);
|
| - }
|
| - if (isFactory) {
|
| - world.error('const factory not allowed', mod.span);
|
| - }
|
| - isConst = true;
|
| - } else if (mod.kind == TokenKind.FACTORY) {
|
| - if (isFactory) {
|
| - world.error('duplicate factory modifier', mod.span);
|
| - }
|
| - if (isConst) {
|
| - world.error('const factory not allowed', mod.span);
|
| - }
|
| - if (isStatic) {
|
| - world.error('static factory not allowed', mod.span);
|
| - }
|
| - isFactory = true;
|
| - } else if (mod.kind == TokenKind.ABSTRACT) {
|
| - if (isAbstract) {
|
| - if (declaringType.isClass) {
|
| - world.error('duplicate abstract modifier', mod.span);
|
| - } else if (!isCallMethod) {
|
| - world.error('abstract modifier not allowed on interface members',
|
| - mod.span);
|
| - }
|
| - }
|
| - isAbstract = true;
|
| - } else {
|
| - world.error('${mod} modifier not allowed on method', mod.span);
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (isFactory) {
|
| - isStatic = true;
|
| - }
|
| -
|
| - // TODO(jimhug): need a better annotation for being an operator method
|
| - if (isOperator && isStatic && !isCallMethod) {
|
| - world.error('operator method may not be static "${name}"', span);
|
| - }
|
| -
|
| - if (isAbstract) {
|
| - if (definition.body != null &&
|
| - declaringType.definition is! FunctionTypeDefinition) {
|
| - // TODO(jimhug): Creating function types for concrete methods is
|
| - // steadily feeling uglier...
|
| - world.error('abstract method cannot have a body', span);
|
| - }
|
| - if (isStatic &&
|
| - declaringType.definition is! FunctionTypeDefinition) {
|
| - world.error('static method cannot be abstract', span);
|
| - }
|
| - } else {
|
| - if (definition.body == null && !isConstructor && !isNative) {
|
| - world.error('method needs a body', span);
|
| - }
|
| - }
|
| -
|
| - if (isConstructor && !isFactory) {
|
| - returnType = declaringType;
|
| - } else {
|
| - // This is the one and only place we allow void.
|
| - if (definition.returnType is SimpleTypeReference &&
|
| - definition.returnType.dynamic.type == world.voidType) {
|
| - returnType = world.voidType;
|
| - } else {
|
| - returnType = resolveType(definition.returnType, false, !isStatic);
|
| - }
|
| - }
|
| - parameters = [];
|
| - for (var formal in definition.formals) {
|
| - // TODO(jimhug): Clean up construction of Parameters.
|
| - var param = new Parameter(formal, this);
|
| - param.resolve();
|
| - parameters.add(param);
|
| - }
|
| -
|
| - if (!isLambda && declaringType.isClass) {
|
| - library._addMember(this);
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -/**
|
| - * A [FactoryMap] maps type names to a list of factory constructors.
|
| - * The constructors list is actually a map that maps factory names to
|
| - * [MethodMember]. The reason why we need both indirections are:
|
| - * 1) A class can define factory methods for multiple interfaces.
|
| - * 2) A factory constructor can have a name.
|
| - *
|
| - * For example:
|
| - *
|
| - * [:
|
| - * interface I factory A {
|
| - * I();
|
| - * I.foo();
|
| - * }
|
| - *
|
| - * interface I2 factory A {
|
| - * I2();
|
| - * }
|
| - *
|
| - * class A {
|
| - * factory I() { ... } // Member1
|
| - * factory I.foo() { ... } // Member2
|
| - * factory I2() { ... } // Member3
|
| - * factory A() { ... } // Member4
|
| - * }
|
| - * :]
|
| - *
|
| - * The [:factories:] field of A will be a [FactoryMap] that looks
|
| - * like:
|
| - * { "I" : { "": Member1, "foo": Member2 },
|
| - * "I2" : { "": Member3 },
|
| - * "A" : { "", Member4 }
|
| - * }
|
| - */
|
| -class FactoryMap {
|
| - Map<String, Map<String, Member>> factories;
|
| -
|
| - FactoryMap() : factories = {};
|
| -
|
| - // Returns the factories defined for [type].
|
| - Map<String, Member> getFactoriesFor(String typeName) {
|
| - var ret = factories[typeName];
|
| - if (ret == null) {
|
| - ret = {};
|
| - factories[typeName] = ret;
|
| - }
|
| - return ret;
|
| - }
|
| -
|
| - void addFactory(String typeName, String name, Member member) {
|
| - getFactoriesFor(typeName)[name] = member;
|
| - }
|
| -
|
| - Member getFactory(String typeName, String name) {
|
| - return getFactoriesFor(typeName)[name];
|
| - }
|
| -
|
| - void forEach(void f(Member member)) {
|
| - factories.forEach((_, Map constructors) {
|
| - constructors.forEach((_, Member member) {
|
| - f(member);
|
| - });
|
| - });
|
| - }
|
| -
|
| - bool isEmpty() {
|
| - return factories.getValues()
|
| - .every((Map constructors) => constructors.isEmpty());
|
| - }
|
| -}
|
|
|