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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: lib/dartdoc/frog/member.dart
diff --git a/lib/dartdoc/frog/member.dart b/lib/dartdoc/frog/member.dart
deleted file mode 100644
index dd768b030d8968508c242d760a968c10d7bba26a..0000000000000000000000000000000000000000
--- a/lib/dartdoc/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());
- }
-}

Powered by Google App Engine
This is Rietveld 408576698