| Index: frog/member.dart
|
| diff --git a/frog/member.dart b/frog/member.dart
|
| index 5d34ab7969397e42088dc305f39f985cab6ee4ef..fae4246fb186d657105f49f7edb9ca04dc611bd1 100644
|
| --- a/frog/member.dart
|
| +++ b/frog/member.dart
|
| @@ -22,7 +22,7 @@ class Parameter {
|
| isInitializer = true;
|
| }
|
|
|
| - type = method.resolveType(definition.type, false);
|
| + type = method.resolveType(definition.type, false, true);
|
|
|
| if (definition.value != null) {
|
| // To match VM, detect cases where value was not actually specified in
|
| @@ -49,7 +49,7 @@ class Parameter {
|
| }
|
| }
|
|
|
| - genValue(MethodMember method, MethodGenerator context) {
|
| + genValue(MethodMember method, CallingContext context) {
|
| if (definition.value == null || value != null) return;
|
|
|
| if (context == null) { // interface method
|
| @@ -84,11 +84,10 @@ class Parameter {
|
| class Member extends Element {
|
| final Type declaringType;
|
|
|
| - bool isGenerated;
|
| - MethodGenerator generator;
|
| + Member genericMember;
|
|
|
| Member(String name, Type declaringType)
|
| - : isGenerated = false, this.declaringType = declaringType,
|
| + : this.declaringType = declaringType,
|
| super(name, declaringType);
|
|
|
| abstract bool get isStatic();
|
| @@ -119,16 +118,12 @@ class Member extends Element {
|
|
|
| bool get requiresPropertySyntax() => false;
|
| bool _providePropertySyntax = false;
|
| - bool get requiresFieldSyntax() => false;
|
| - bool _provideFieldSyntax = false;
|
|
|
| bool get isNative() => false;
|
| String get constructorName() {
|
| world.internalError('can not be a constructor', span);
|
| }
|
|
|
| - // Don't display an error here; we'll get a better error later.
|
| - void provideFieldSyntax() {}
|
| void providePropertySyntax() {}
|
|
|
| Member get initDelegate() {
|
| @@ -170,31 +165,45 @@ class Member extends Element {
|
| }
|
|
|
| MemberSet get potentialMemberSet() {
|
| + // TODO(jimhug): This needs one more redesign - move to TypeSets...
|
| +
|
| if (_potentialMemberSet === null) {
|
| - if (declaringType.isObject) {
|
| - _potentialMemberSet = world._members[name];
|
| + if (name == ':call') {
|
| + _potentialMemberSet = preciseMemberSet;
|
| return _potentialMemberSet;
|
| }
|
|
|
| final mems = new Set<Member>();
|
| if (declaringType.isClass) mems.add(this);
|
|
|
| -
|
| - for (var subtype in declaringType.subtypes) {
|
| + for (var subtype in declaringType.genericType.subtypes) {
|
| if (!subtype.isClass) continue;
|
| var mem = subtype.members[name];
|
| if (mem !== null) {
|
| - mems.add(mem);
|
| + if (mem.isDefinedOn(declaringType)) {
|
| + mems.add(mem);
|
| + }
|
| } else if (!declaringType.isClass) {
|
| // Handles weird interface case.
|
| mem = subtype.getMember(name);
|
| - if (mem !== null) {
|
| + 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);
|
| @@ -214,9 +223,8 @@ class Member extends Element {
|
| return true;
|
| } else if (type.isSubtypeOf(declaringType)) {
|
| // maybe - but not if overridden somewhere
|
| - // !!! horrible hack for today - awful perf props
|
| + // TODO(jimhug): This lookup is not great for perf of this method.
|
| return type.getMember(name) == this;
|
| - //return true;
|
| } else {
|
| return false;
|
| }
|
| @@ -236,23 +244,25 @@ class Member extends Element {
|
| }
|
| }
|
|
|
| - // TODO(jmesserly): isDynamic isn't a great name for this, something better?
|
| - abstract Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic]);
|
| + abstract Value _get(CallingContext context, Node node, Value target);
|
|
|
| - abstract Value _set(MethodGenerator context, Node node, Value target,
|
| - Value value, [bool isDynamic]);
|
| + abstract Value _set(CallingContext context, Node node, Value target,
|
| + Value value);
|
|
|
| - bool canInvoke(MethodGenerator context, Arguments args) {
|
| - // No source location needed because canInvoke may not produce errors.
|
| - return canGet &&
|
| - new Value(returnType, null, null).canInvoke(context, ':call', args);
|
| +
|
| + 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(MethodGenerator context, Node node, Value target, Arguments args,
|
| - [bool isDynamic=false]) {
|
| - var newTarget = _get(context, node, target, isDynamic);
|
| - return newTarget.invoke(context, ':call', node, args, isDynamic);
|
| + 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) {
|
| @@ -269,7 +279,7 @@ class Member extends Element {
|
|
|
| String get generatedFactoryName() {
|
| assert(this.isFactory);
|
| - String prefix = '${declaringType.jsname}.${constructorName}\$';
|
| + String prefix = '${declaringType.genericType.jsname}.${constructorName}\$';
|
| if (name == '') {
|
| return '${prefix}factory';
|
| } else {
|
| @@ -289,6 +299,18 @@ class Member extends Element {
|
| 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('can not make this concrete', span);
|
| + }
|
| }
|
|
|
|
|
| @@ -311,24 +333,20 @@ class TypeMember extends Member {
|
| // If this really becomes first class, this should return typeof(Type)
|
| Type get returnType() => world.varType;
|
|
|
| - bool canInvoke(MethodGenerator context, Arguments args) => false;
|
| + bool canInvoke(CallingContext context, Arguments args) => false;
|
| bool get canGet() => true;
|
| bool get canSet() => false;
|
|
|
| - bool get requiresFieldSyntax() => true;
|
| -
|
| - Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic=false]) {
|
| + Value _get(CallingContext context, Node node, Value target) {
|
| return new TypeValue(type, node.span);
|
| }
|
|
|
| - Value _set(MethodGenerator context, Node node, Value target, Value value,
|
| - [bool isDynamic=false]) {
|
| + Value _set(CallingContext context, Node node, Value target, Value value) {
|
| world.error('cannot set type', node.span);
|
| }
|
|
|
| - Value invoke(MethodGenerator context, Node node, Value target, Arguments args,
|
| - [bool isDynamic=false]) {
|
| + Value invoke(CallingContext context, Node node, Value target,
|
| + Arguments args) {
|
| world.error('cannot invoke type', node.span);
|
| }
|
| }
|
| @@ -373,12 +391,24 @@ class FieldMember extends Member {
|
| }
|
| }
|
|
|
| - void provideFieldSyntax() {} // Nothing to do.
|
| - void providePropertySyntax() { _providePropertySyntax = true; }
|
| + void providePropertySyntax() {
|
| + _providePropertySyntax = true;
|
| + if (genericMember !== null) {
|
| + genericMember.providePropertySyntax();
|
| + }
|
| + }
|
|
|
| FieldMember(String name, Type declaringType, this.definition, this.value)
|
| : super(name, declaringType), isNative = false;
|
|
|
| + 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;
|
| @@ -408,17 +438,13 @@ class FieldMember extends Member {
|
| }
|
| }
|
| }
|
| - type = resolveType(definition.type, false);
|
| - if (isStatic && !isFactory && type.hasTypeParams) {
|
| - world.error('using type parameter in static context',
|
| - definition.type.span);
|
| - }
|
| + type = resolveType(definition.type, false, true);
|
|
|
| if (isStatic && isFinal && value == null) {
|
| world.error('static final field is missing initializer', span);
|
| }
|
|
|
| - library._addMember(this);
|
| + if (declaringType.isClass) library._addMember(this);
|
| }
|
|
|
|
|
| @@ -463,8 +489,11 @@ class FieldMember extends Member {
|
| return _computedValue;
|
| }
|
|
|
| - Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic=false]) {
|
| + 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) {
|
| @@ -511,10 +540,14 @@ class FieldMember extends Member {
|
| return new Value(type, '${target.code}.$jsname', node.span);
|
| }
|
|
|
| - Value _set(MethodGenerator context, Node node, Value target, Value value,
|
| - [bool isDynamic=false]) {
|
| - var lhs = _get(context, node, target, isDynamic);
|
| - value = value.convertTo(context, type, isDynamic);
|
| + 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);
|
| }
|
| }
|
| @@ -535,15 +568,11 @@ class PropertyMember extends Member {
|
| // field syntax in the generated code.
|
| bool get requiresPropertySyntax() => declaringType.isClass;
|
|
|
| - void provideFieldSyntax() { _provideFieldSyntax = true; }
|
| - void providePropertySyntax() {
|
| - // 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...
|
| - if (_overriddenField != null && _overriddenField.isNative) {
|
| - provideFieldSyntax();
|
| - }
|
| - }
|
| + // 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;
|
|
|
| // TODO(jimhug): Union of getter and setters sucks!
|
| bool get isStatic() => getter == null ? setter.isStatic : getter.isStatic;
|
| @@ -556,6 +585,14 @@ class PropertyMember extends Member {
|
|
|
| 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;
|
|
|
| @@ -573,39 +610,29 @@ class PropertyMember extends Member {
|
| }
|
| }
|
|
|
| - Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic=false]) {
|
| + Value _get(CallingContext context, Node node, Value target) {
|
| if (getter == null) {
|
| if (_overriddenField != null) {
|
| - return _overriddenField._get(context, node, target, isDynamic);
|
| + return _overriddenField._get(context, node, target);
|
| }
|
| return target.invokeNoSuchMethod(context, 'get:$name', node);
|
| }
|
| return getter.invoke(context, node, target, Arguments.EMPTY);
|
| }
|
|
|
| - Value _set(MethodGenerator context, Node node, Value target, Value value,
|
| - [bool isDynamic=false]) {
|
| + Value _set(CallingContext context, Node node, Value target, Value value) {
|
| if (setter == null) {
|
| if (_overriddenField != null) {
|
| - return _overriddenField._set(context, node, target, value, isDynamic);
|
| + 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]),
|
| - isDynamic);
|
| + return setter.invoke(context, node, target, new Arguments(null, [value]));
|
| }
|
|
|
| addFromParent(Member parentMember) {
|
| - // TODO(jimhug): Egregious Hack!
|
| - PropertyMember parent;
|
| - if (parentMember is ConcreteMember) {
|
| - ConcreteMember c = parentMember;
|
| - parent = c.baseMember;
|
| - } else {
|
| - parent = parentMember;
|
| - }
|
| + final parent = parentMember;
|
|
|
| if (getter == null) getter = parent.getter;
|
| if (setter == null) setter = parent.setter;
|
| @@ -637,116 +664,7 @@ class PropertyMember extends Member {
|
| }
|
| }
|
|
|
| - library._addMember(this);
|
| - }
|
| -}
|
| -
|
| -
|
| -class ConcreteMember extends Member {
|
| - final Member baseMember;
|
| - Type returnType;
|
| - List<Parameter> parameters;
|
| -
|
| - ConcreteMember(String name, ConcreteType declaringType, this.baseMember)
|
| - : super(name, declaringType) {
|
| - parameters = [];
|
| - returnType = baseMember.returnType.resolveTypeParams(declaringType);
|
| - // TODO(jimhug): Optimize not creating new array if no new param types.
|
| - for (var p in baseMember.parameters) {
|
| - var newType = p.type.resolveTypeParams(declaringType);
|
| - if (newType != p.type) {
|
| - parameters.add(p.copyWithNewType(this, newType));
|
| - } else {
|
| - parameters.add(p);
|
| - }
|
| - }
|
| - }
|
| -
|
| - SourceSpan get span() => baseMember.span;
|
| -
|
| - bool get isStatic() => baseMember.isStatic;
|
| - bool get isAbstract() => baseMember.isAbstract;
|
| - bool get isConst() => baseMember.isConst;
|
| - bool get isFactory() => baseMember.isFactory;
|
| - bool get isFinal() => baseMember.isFinal;
|
| - bool get isNative() => baseMember.isNative;
|
| -
|
| - String get jsname() => baseMember.jsname;
|
| - set jsname(String name) =>
|
| - world.internalError('bad set of jsname on ConcreteMember');
|
| -
|
| -
|
| - bool get canGet() => baseMember.canGet;
|
| - bool get canSet() => baseMember.canSet;
|
| - bool canInvoke(MethodGenerator context, Arguments args) =>
|
| - baseMember.canInvoke(context, args);
|
| -
|
| - bool get isField() => baseMember.isField;
|
| - bool get isMethod() => baseMember.isMethod;
|
| - bool get isProperty() => baseMember.isProperty;
|
| -
|
| - bool get requiresPropertySyntax() => baseMember.requiresPropertySyntax;
|
| - bool get requiresFieldSyntax() => baseMember.requiresFieldSyntax;
|
| -
|
| - void provideFieldSyntax() => baseMember.provideFieldSyntax();
|
| - void providePropertySyntax() => baseMember.providePropertySyntax();
|
| -
|
| - bool get isConstructor() => name == declaringType.name;
|
| -
|
| - String get constructorName() => baseMember.constructorName;
|
| -
|
| - Definition get definition() => baseMember.definition;
|
| -
|
| - // TODO(sigmund): this is EGREGIOUS
|
| - Member get initDelegate() => baseMember.initDelegate;
|
| - void set initDelegate(ctor) { baseMember.initDelegate = ctor; }
|
| -
|
| - Type resolveType(TypeReference node, bool isRequired) {
|
| - var type = baseMember.resolveType(node, isRequired);
|
| - return type.resolveTypeParams(declaringType);
|
| - }
|
| -
|
| - Value computeValue() => baseMember.computeValue();
|
| -
|
| - // TODO(jimhug): Add support for type params.
|
| - bool override(Member other) => baseMember.override(other);
|
| -
|
| - Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic=false]) {
|
| - Value ret = baseMember._get(context, node, target, isDynamic);
|
| - return new Value(inferredResult, ret.code, node.span);
|
| - }
|
| -
|
| - Value _set(MethodGenerator context, Node node, Value target, Value value,
|
| - [bool isDynamic=false]) {
|
| - // TODO(jimhug): Check arg types in context of concrete type.
|
| - Value ret = baseMember._set(context, node, target, value, isDynamic);
|
| - return new Value(returnType, ret.code, node.span);
|
| - }
|
| -
|
| - _evalConstConstructor(ObjectValue newObject, Arguments args) {
|
| - // TODO(jimhug): Concrete type probably matters somehow here
|
| - return baseMember.dynamic._evalConstConstructor(newObject, args);
|
| - }
|
| -
|
| -
|
| - Value invoke(MethodGenerator context, Node node, Value target, Arguments args,
|
| - [bool isDynamic=false]) {
|
| - // TODO(jimhug): Check arg types in context of concrete type.
|
| - // TODO(jmesserly): I think what needs to happen is to move MethodMember's
|
| - // invoke so that it's run against the "parameters" and "returnType" of the
|
| - // ConcreteMember instead.
|
| - Value ret = baseMember.invoke(context, node, target, args, isDynamic);
|
| - var code = ret.code;
|
| - if (isConstructor) {
|
| - // TODO(jimhug): Egregious hack - won't live through the year.
|
| - code = code.replaceFirst(
|
| - declaringType.genericType.jsname, declaringType.jsname);
|
| - }
|
| - if (baseMember is MethodMember) {
|
| - declaringType.genMethod(this);
|
| - }
|
| - return new Value(inferredResult, code, node.span);
|
| + if (declaringType.isClass) library._addMember(this);
|
| }
|
| }
|
|
|
| @@ -757,6 +675,8 @@ class MethodMember extends Member {
|
| Type returnType;
|
| List<Parameter> parameters;
|
|
|
| + MethodData _methodData;
|
| +
|
| Type _functionType;
|
| bool isStatic = false;
|
| bool isAbstract = false;
|
| @@ -783,6 +703,23 @@ class MethodMember extends Member {
|
| MethodMember(String name, Type declaringType, this.definition)
|
| : 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;
|
|
|
| @@ -813,8 +750,8 @@ class MethodMember extends Member {
|
|
|
| Type get functionType() {
|
| if (_functionType == null) {
|
| - _functionType =
|
| - library.getOrAddFunctionType(declaringType, name, definition);
|
| + _functionType = library.getOrAddFunctionType(declaringType, name,
|
| + definition, methodData);
|
| // TODO(jimhug): Better resolution checks.
|
| if (parameters == null) {
|
| resolve();
|
| @@ -838,7 +775,7 @@ class MethodMember extends Member {
|
| }
|
| }
|
|
|
| - bool canInvoke(MethodGenerator context, Arguments args) {
|
| + bool canInvoke(CallingContext context, Arguments args) {
|
| int bareCount = args.bareCount;
|
|
|
| if (bareCount > parameters.length) return false;
|
| @@ -870,16 +807,17 @@ class MethodMember extends Member {
|
| return -1;
|
| }
|
|
|
| - void provideFieldSyntax() { _provideFieldSyntax = true; }
|
| void providePropertySyntax() { _providePropertySyntax = true; }
|
|
|
| - Value _set(MethodGenerator context, Node node, Value target, Value value,
|
| - [bool isDynamic=false]) {
|
| + Value _set(CallingContext context, Node node, Value target, Value value) {
|
| world.error('cannot set method', node.span);
|
| }
|
|
|
| - Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic=false]) {
|
| + 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;
|
| @@ -944,7 +882,6 @@ class MethodMember extends Member {
|
| }
|
|
|
| if (bareCount < parameters.length) {
|
| - genParameterValues();
|
| for (int i = bareCount; i < parameters.length; i++) {
|
| var arg = args.getValue(parameters[i].name);
|
| if (arg != null && arg.needsConversion(parameters[i].type)) {
|
| @@ -961,7 +898,7 @@ class MethodMember extends Member {
|
| '${atLeast ? "at least " : ""}$expected but found $actual';
|
| }
|
|
|
| - Value _argError(MethodGenerator context, Node node, Value target,
|
| + Value _argError(CallingContext context, Node node, Value target,
|
| Arguments args, String msg, int argIndex) {
|
| SourceSpan span;
|
| if ((args.nodes == null) || (argIndex >= args.nodes.length)) {
|
| @@ -977,21 +914,20 @@ class MethodMember extends Member {
|
| return target.invokeNoSuchMethod(context, name, node, args);
|
| }
|
|
|
| - genParameterValues() {
|
| - // Pure lazy?
|
| - for (var p in parameters) p.genValue(this, generator);
|
| + genParameterValues(CallingContext context) {
|
| + // TODO(jimhug): Is this the right context?
|
| + 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(MethodGenerator context, Node node, Value target,
|
| - Arguments args, [bool isDynamic=false]) {
|
| - // TODO(jimhug): Fix this hack for ensuring a method is resolved.
|
| - if (parameters == null) {
|
| - world.info('surprised to need to resolve: ${declaringType.name}.$name');
|
| - resolve();
|
| + Value invoke(CallingContext context, Node node, Value target,
|
| + Arguments args) {
|
| + if (!context.needsCode) {
|
| + // TODO(jimhug): Add argument type and name checks here.
|
| + return new PureStaticValue(returnType, node.span);
|
| }
|
|
|
| declaringType.genMethod(this);
|
| @@ -1025,20 +961,20 @@ class MethodMember extends Member {
|
| var msg = _argCountMsg(args.length, parameters.length);
|
| return _argError(context, node, target, args, msg, i);
|
| }
|
| - arg = arg.convertTo(context, parameters[i].type, isDynamic);
|
| + arg = arg.convertTo(context, parameters[i].type);
|
| argsCode.add(arg.code);
|
| }
|
|
|
| int namedArgsUsed = 0;
|
| if (bareCount < parameters.length) {
|
| - genParameterValues();
|
| + genParameterValues(context);
|
|
|
| for (int i = bareCount; i < parameters.length; i++) {
|
| var arg = args.getValue(parameters[i].name);
|
| if (arg == null) {
|
| arg = parameters[i].value;
|
| } else {
|
| - arg = arg.convertTo(context, parameters[i].type, isDynamic);
|
| + arg = arg.convertTo(context, parameters[i].type);
|
| namedArgsUsed++;
|
| }
|
|
|
| @@ -1090,7 +1026,7 @@ class MethodMember extends Member {
|
| }
|
|
|
| if (isOperator) {
|
| - return _invokeBuiltin(context, node, target, args, argsCode, isDynamic);
|
| + return _invokeBuiltin(context, node, target, args, argsCode);
|
| }
|
|
|
| if (isFactory) {
|
| @@ -1117,7 +1053,7 @@ class MethodMember extends Member {
|
| return new Value(inferredResult, code, node.span);
|
| }
|
|
|
| - Value _invokeConstructor(MethodGenerator context, Node node,
|
| + Value _invokeConstructor(CallingContext context, Node node,
|
| Value target, Arguments args, argsString) {
|
| declaringType.markUsed();
|
|
|
| @@ -1155,45 +1091,52 @@ class MethodMember extends Member {
|
|
|
| _evalConstConstructor(Value newObject, Arguments args) {
|
| declaringType.markUsed();
|
| - var generator = new MethodGenerator(this, null);
|
| - generator.evalBody(newObject, args);
|
| + methodData.eval(this, newObject, args);
|
| }
|
|
|
| - Value _invokeBuiltin(MethodGenerator context, Node node, Value target,
|
| - Arguments args, argsCode, bool isDynamic) {
|
| + Value _invokeBuiltin(CallingContext context, Node node, Value target,
|
| + Arguments args, argsCode) {
|
| // Handle some fast paths for Number, String, List and DOM.
|
| - if (declaringType.isNum) {
|
| + if (target.type.isNum) {
|
| // TODO(jimhug): This fails in bad ways when argsCode[1] is not num.
|
| // TODO(jimhug): What about null?
|
| - var code;
|
| - if (name == ':negate') {
|
| - code = '-${target.code}';
|
| - } else if (name == ':bit_not') {
|
| - code = '~${target.code}';
|
| - } else 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]}';
|
| + 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]}';
|
| + }
|
| }
|
| -
|
| - return new Value(inferredResult, code, node.span);
|
| - } else if (declaringType.isString) {
|
| - if (name == ':index') {
|
| + 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') {
|
| + } else if (name == ':add' && args.values[0].type.isNum) {
|
| return new Value(declaringType, '${target.code} + ${argsCode[0]}',
|
| node.span);
|
| }
|
| } else if (declaringType.isNative) {
|
| - 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);
|
| + if (args.length > 0 && args.values[0].type.isNum) {
|
| + // TODO(jimhug): make more accurate/reliable
|
| + 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);
|
| + }
|
| }
|
| }
|
|
|
| @@ -1203,7 +1146,7 @@ class MethodMember extends Member {
|
|
|
| if (name == ':ne') {
|
| // Ensure == is generated.
|
| - target.invoke(context, ':eq', node, args, isDynamic);
|
| + target.invoke(context, ':eq', node, args);
|
| }
|
|
|
| // Optimize test when null is on the rhs.
|
| @@ -1226,10 +1169,16 @@ class MethodMember extends Member {
|
| '${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.
|
| @@ -1316,7 +1265,12 @@ class MethodMember extends Member {
|
| returnType = declaringType;
|
| } else {
|
| // This is the one and only place we allow void.
|
| - returnType = resolveType(definition.returnType, false, allowVoid: true);
|
| + 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) {
|
| @@ -1326,310 +1280,13 @@ class MethodMember extends Member {
|
| parameters.add(param);
|
| }
|
|
|
| - if (!isLambda) {
|
| + if (!isLambda && declaringType.isClass) {
|
| library._addMember(this);
|
| }
|
| }
|
| -
|
| - /** Overriden to ensure that type arguments aren't used in static methods. */
|
| - Type resolveType(TypeReference node, bool typeErrors,
|
| - [bool allowVoid = false]) {
|
| - Type t = super.resolveType(node, typeErrors);
|
| - if (isStatic && !isFactory && t is ParameterType) {
|
| - world.error('using type parameter in static context.', node.span);
|
| - }
|
| - if (!allowVoid && t.isVoid) {
|
| - world.error('"void" only allowed as return type', node.span);
|
| - }
|
| - return t;
|
| - }
|
| }
|
|
|
|
|
| -class MemberSet {
|
| - final String name;
|
| - final List<Member> members;
|
| - final String jsname;
|
| - final bool isVar;
|
| -
|
| - MemberSet(Member member, [bool isVar=false]):
|
| - name = member.name, members = [member], jsname = member.jsname,
|
| - isVar = isVar;
|
| -
|
| - toString() => '$name:${members.length}';
|
| -
|
| - // TODO(jimhug): Still working towards the right logic for conflicts...
|
| - bool get containsProperties() => members.some((m) => m is PropertyMember);
|
| - bool get containsMethods() => members.some((m) => m is MethodMember);
|
| -
|
| -
|
| - void add(Member member) => members.add(member);
|
| -
|
| - // TODO(jimhug): Always false, or is this needed?
|
| - bool get isStatic() => members.length == 1 && members[0].isStatic;
|
| - bool get isOperator() => members[0].isOperator;
|
| -
|
| - bool canInvoke(MethodGenerator context, Arguments args) =>
|
| - members.some((m) => m.canInvoke(context, args));
|
| -
|
| - Value _makeError(Node node, Value target, String action) {
|
| - if (!target.type.isVar) {
|
| - world.warning('could not find applicable $action for "$name"', node.span);
|
| - }
|
| - return new Value(world.varType,
|
| - '${target.code}.$jsname() /*no applicable $action*/', node.span);
|
| - }
|
| -
|
| - bool _treatAsField;
|
| - bool get treatAsField() {
|
| - if (_treatAsField == null) {
|
| - // If this is the global MemberSet from world, always bind dynamically.
|
| - // Note: we need this for proper noSuchMethod and REPL behavior.
|
| - _treatAsField = !isVar && (members.some((m) => m.requiresFieldSyntax)
|
| - || members.every((m) => !m.requiresPropertySyntax));
|
| -
|
| - for (var member in members) {
|
| - if (_treatAsField) {
|
| - member.provideFieldSyntax();
|
| - } else {
|
| - member.providePropertySyntax();
|
| - }
|
| - }
|
| - }
|
| - return _treatAsField;
|
| - }
|
| -
|
| - Value _get(MethodGenerator context, Node node, Value target,
|
| - [bool isDynamic=false]) {
|
| - // If this is the global MemberSet from world, always bind dynamically.
|
| - // Note: we need this for proper noSuchMethod and REPL behavior.
|
| - Value returnValue;
|
| - if (members.length == 1 && !isVar) {
|
| - return members[0]._get(context, node, target, isDynamic);
|
| - }
|
| -
|
| -
|
| - final targets = members.filter((m) => m.canGet);
|
| - if (isVar) {
|
| - targets.forEach((m) => m._get(context, node, target, isDynamic: true));
|
| - returnValue = new Value(_foldTypes(targets), null, node.span);
|
| - } else {
|
| - if (members.length == 1) {
|
| - return members[0]._get(context, node, target, isDynamic);
|
| - } else if (targets.length == 1) {
|
| - return targets[0]._get(context, node, target, isDynamic);
|
| - }
|
| -
|
| - for (var member in targets) {
|
| - final value = member._get(context, node, target, isDynamic:true);
|
| - returnValue = _tryUnion(returnValue, value, node);
|
| - }
|
| - if (returnValue == null) {
|
| - return _makeError(node, target, 'getter');
|
| - }
|
| - }
|
| -
|
| - if (returnValue.code == null) {
|
| - if (treatAsField) {
|
| - return new Value(returnValue.type, '${target.code}.$jsname',
|
| - node.span);
|
| - } else {
|
| - return new Value(returnValue.type, '${target.code}.get\$$jsname()',
|
| - node.span);
|
| - }
|
| - }
|
| - return returnValue;
|
| - }
|
| -
|
| - Value _set(MethodGenerator context, Node node, Value target, Value value,
|
| - [bool isDynamic=false]) {
|
| - // If this is the global MemberSet from world, always bind dynamically.
|
| - // Note: we need this for proper noSuchMethod and REPL behavior.
|
| - if (members.length == 1 && !isVar) {
|
| - return members[0]._set(context, node, target, value, isDynamic);
|
| - }
|
| -
|
| - Value returnValue;
|
| - final targets = members.filter((m) => m.canSet);
|
| - if (isVar) {
|
| - targets.forEach((m) =>
|
| - m._set(context, node, target, value, isDynamic: true));
|
| - returnValue = new Value(_foldTypes(targets), null, node.span);
|
| - } else {
|
| - if (members.length == 1) {
|
| - return members[0]._set(context, node, target, value, isDynamic);
|
| - } else if (targets.length == 1) {
|
| - return targets[0]._set(context, node, target, value, isDynamic);
|
| - }
|
| -
|
| - for (var member in targets) {
|
| - final res = member._set(context, node, target, value, isDynamic:true);
|
| - returnValue = _tryUnion(returnValue, res, node);
|
| - }
|
| - if (returnValue == null) {
|
| - return _makeError(node, target, 'setter');
|
| - }
|
| - }
|
| -
|
| - if (returnValue.code == null) {
|
| - if (treatAsField) {
|
| - return new Value(returnValue.type,
|
| - '${target.code}.$jsname = ${value.code}', node.span);
|
| - } else {
|
| - return new Value(returnValue.type,
|
| - '${target.code}.set\$$jsname(${value.code})', node.span);
|
| - }
|
| - }
|
| - return returnValue;
|
| - }
|
| -
|
| - Value invoke(MethodGenerator context, Node node, Value target,
|
| - Arguments args, [bool isDynamic=false]) {
|
| - // If this is the global MemberSet from world, always bind dynamically.
|
| - // Note: we need this for proper noSuchMethod and REPL behavior.
|
| - if (isVar && !isOperator) {
|
| - return invokeOnVar(context, node, target, args);
|
| - }
|
| -
|
| - if (members.length == 1 && !isVar) {
|
| - return members[0].invoke(context, node, target, args, isDynamic);
|
| - }
|
| -
|
| - final targets = members.filter((m) => m.canInvoke(context, args));
|
| - if (targets.length == 1) {
|
| - return targets[0].invoke(context, node, target, args, isDynamic);
|
| - }
|
| -
|
| - Value returnValue = null;
|
| - if (targets.length < 1000) {
|
| - for (var member in targets) {
|
| - final res = member.invoke(context, node, target, args, isDynamic:true);
|
| - // TODO(jmesserly): If the code has different type checks, it will fail to
|
| - // unify and go through a dynamic stub. Good so far. However, we'll end
|
| - // up with a bogus unused temp generated (usually "var $0"). We need a way
|
| - // to throw away temps when we throw away the code.
|
| - returnValue = _tryUnion(returnValue, res, node);
|
| - }
|
| -
|
| - if (returnValue == null) {
|
| - return _makeError(node, target, 'method');
|
| - }
|
| - } else {
|
| - returnValue = new Value(world.varType, null, node.span);
|
| - }
|
| -
|
| - if (returnValue.code == null) {
|
| - if (name == ':call') {
|
| - // TODO(jmesserly): reconcile this with similar code in Value
|
| - return target._varCall(context, node, args);
|
| - } else if (isOperator) {
|
| - // TODO(jmesserly): make operators less special.
|
| - return invokeSpecial(target, args, returnValue.type);
|
| - } else {
|
| - return invokeOnVar(context, node, target, args);
|
| - }
|
| - }
|
| -
|
| - return returnValue;
|
| - }
|
| -
|
| - Value invokeSpecial(Value target, Arguments args, Type returnType) {
|
| - assert(name.startsWith(':'));
|
| - assert(!args.hasNames);
|
| - // TODO(jimhug): We need to do this a little bit more like get and set on
|
| - // properties. We should check the set of members for something
|
| - // like "requiresNativeIndexer" and "requiresDartIndexer" to
|
| - // decide on a strategy.
|
| -
|
| - var argsString = args.getCode();
|
| - // Most operator calls need to be emitted as function calls, so we don't
|
| - // box numbers accidentally. Indexing is the exception.
|
| - if (name == ':index' || name == ':setindex') {
|
| - // TODO(jimhug): should not need this test both here and in invoke
|
| - if (name == ':index') {
|
| - world.gen.corejs.useIndex = true;
|
| - } else if (name == ':setindex') {
|
| - world.gen.corejs.useSetIndex = true;
|
| - }
|
| - return new Value(returnType, '${target.code}.$jsname($argsString)',
|
| - target.span);
|
| - } else {
|
| - if (argsString.length > 0) argsString = ', $argsString';
|
| - world.gen.corejs.useOperator(name);
|
| - return new Value(returnType, '$jsname(${target.code}$argsString)',
|
| - target.span);
|
| - }
|
| - }
|
| -
|
| - Value invokeOnVar(MethodGenerator context, Node node, Value target,
|
| - Arguments args) {
|
| - context.counters.dynamicMethodCalls++;
|
| - var member = getVarMember(context, node, args);
|
| - return member.invoke(context, node, target, args);
|
| - }
|
| -
|
| - Value _union(Value x, Value y, Node node) {
|
| - var result = _tryUnion(x, y, node);
|
| - if (result.code == null) {
|
| - world.internalError('mismatched code for $name (${x.code}, ${y.code})',
|
| - node.span);
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - // TODO(jimhug): This is icky - but this whole class needs cleanup.
|
| - Value _tryUnion(Value x, Value y, Node node) {
|
| - if (x == null) return y;
|
| - var type = Type.union(x.type, y.type);
|
| - if (x.code == y.code) {
|
| - if (type == x.type) {
|
| - return x;
|
| - } else if (x.isConst || y.isConst) {
|
| - world.internalError("unexpected: union of const values ");
|
| - } else {
|
| - return Value.union(x, y);
|
| - }
|
| - } else {
|
| - return new Value(type, null, node.span);
|
| - }
|
| - }
|
| -
|
| - dumpAllMembers() {
|
| - for (var member in members) {
|
| - world.warning('hard-multi $name on ${member.declaringType.name}',
|
| - member.span);
|
| - }
|
| - }
|
| -
|
| - VarMember getVarMember(MethodGenerator context, Node node, Arguments args) {
|
| - if (world.objectType.varStubs == null) {
|
| - world.objectType.varStubs = {};
|
| - }
|
| -
|
| - var stubName = _getCallStubName(name, args);
|
| - var stub = world.objectType.varStubs[stubName];
|
| - if (stub == null) {
|
| - // Ensure that we're making stub with all possible members of this name.
|
| - // We need this canonicalization step because only one VarMemberSet can
|
| - // live on Object.prototype
|
| - // TODO(jmesserly): this is ugly--we're throwing away type information!
|
| - // The right solution is twofold:
|
| - // 1. put stubs on a more precise type when possible
|
| - // 2. merge VarMemberSets together if necessary
|
| - final mset = context.findMembers(name).members;
|
| -
|
| - final targets = mset.filter((m) => m.canInvoke(context, args));
|
| - stub = new VarMethodSet(name, stubName, targets, args,
|
| - _foldTypes(targets));
|
| - world.objectType.varStubs[stubName] = stub;
|
| - }
|
| - return stub;
|
| - }
|
| -
|
| - Type _foldTypes(List<Member> targets) =>
|
| - reduce(map(targets, (t) => t.returnType), Type.union, world.varType);
|
| -}
|
| -
|
| /**
|
| * A [FactoryMap] maps type names to a list of factory constructors.
|
| * The constructors list is actually a map that maps factory names to
|
|
|