| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** A formal parameter to a [Method]. */ | 5 /** A formal parameter to a [Method]. */ |
| 6 class Parameter { | 6 class Parameter { |
| 7 FormalNode definition; | 7 FormalNode definition; |
| 8 Member method; | 8 Member method; |
| 9 | 9 |
| 10 String name; | 10 String name; |
| 11 Type type; | 11 Type type; |
| 12 bool isInitializer = false; | 12 bool isInitializer = false; |
| 13 | 13 |
| 14 Value value; | 14 Value value; |
| 15 | 15 |
| 16 Parameter(this.definition, this.method); | 16 Parameter(this.definition, this.method); |
| 17 | 17 |
| 18 resolve() { | 18 resolve() { |
| 19 name = definition.name.name; | 19 name = definition.name.name; |
| 20 if (name.startsWith('this.')) { | 20 if (name.startsWith('this.')) { |
| 21 name = name.substring(5); | 21 name = name.substring(5); |
| 22 isInitializer = true; | 22 isInitializer = true; |
| 23 } | 23 } |
| 24 | 24 |
| 25 type = method.resolveType(definition.type, false); | 25 type = method.resolveType(definition.type, false, true); |
| 26 | 26 |
| 27 if (definition.value != null) { | 27 if (definition.value != null) { |
| 28 // To match VM, detect cases where value was not actually specified in | 28 // To match VM, detect cases where value was not actually specified in |
| 29 // code and don't signal errors. | 29 // code and don't signal errors. |
| 30 // TODO(jimhug): Clean up after issue #352 is resolved. | 30 // TODO(jimhug): Clean up after issue #352 is resolved. |
| 31 if (!hasDefaultValue) return; | 31 if (!hasDefaultValue) return; |
| 32 | 32 |
| 33 if (method.name == ':call') { | 33 if (method.name == ':call') { |
| 34 // TODO(jimhug): Need simpler way to detect "true" function types vs. | 34 // TODO(jimhug): Need simpler way to detect "true" function types vs. |
| 35 // regular methods being used as function types for closures. | 35 // regular methods being used as function types for closures. |
| 36 // TODO(sigmund): Disallow non-null default values for native calls? | 36 // TODO(sigmund): Disallow non-null default values for native calls? |
| 37 var methodDef = method.definition; | 37 var methodDef = method.definition; |
| 38 if (methodDef.body == null && !method.isNative) { | 38 if (methodDef.body == null && !method.isNative) { |
| 39 world.error('default value not allowed on function type', | 39 world.error('default value not allowed on function type', |
| 40 definition.span); | 40 definition.span); |
| 41 } | 41 } |
| 42 } else if (method.isAbstract) { | 42 } else if (method.isAbstract) { |
| 43 world.error('default value not allowed on abstract methods', | 43 world.error('default value not allowed on abstract methods', |
| 44 definition.span); | 44 definition.span); |
| 45 } | 45 } |
| 46 } else if (isInitializer && !method.isConstructor) { | 46 } else if (isInitializer && !method.isConstructor) { |
| 47 world.error('initializer parameters only allowed on constructors', | 47 world.error('initializer parameters only allowed on constructors', |
| 48 definition.span); | 48 definition.span); |
| 49 } | 49 } |
| 50 } | 50 } |
| 51 | 51 |
| 52 genValue(MethodMember method, MethodGenerator context) { | 52 genValue(MethodMember method, CallingContext context) { |
| 53 if (definition.value == null || value != null) return; | 53 if (definition.value == null || value != null) return; |
| 54 | 54 |
| 55 if (context == null) { // interface method | 55 if (context == null) { // interface method |
| 56 context = new MethodGenerator(method, null); | 56 context = new MethodGenerator(method, null); |
| 57 } | 57 } |
| 58 value = definition.value.visit(context); | 58 value = definition.value.visit(context); |
| 59 if (!value.isConst) { | 59 if (!value.isConst) { |
| 60 world.error('default parameter values must be constant', value.span); | 60 world.error('default parameter values must be constant', value.span); |
| 61 } | 61 } |
| 62 value = value.convertTo(context, type); | 62 value = value.convertTo(context, type); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 77 * on the implicit `null`. | 77 * on the implicit `null`. |
| 78 */ | 78 */ |
| 79 bool get hasDefaultValue() => | 79 bool get hasDefaultValue() => |
| 80 definition.value.span.start != definition.span.start; | 80 definition.value.span.start != definition.span.start; |
| 81 } | 81 } |
| 82 | 82 |
| 83 | 83 |
| 84 class Member extends Element { | 84 class Member extends Element { |
| 85 final Type declaringType; | 85 final Type declaringType; |
| 86 | 86 |
| 87 bool isGenerated; | 87 Member genericMember; |
| 88 MethodGenerator generator; | |
| 89 | 88 |
| 90 Member(String name, Type declaringType) | 89 Member(String name, Type declaringType) |
| 91 : isGenerated = false, this.declaringType = declaringType, | 90 : this.declaringType = declaringType, |
| 92 super(name, declaringType); | 91 super(name, declaringType); |
| 93 | 92 |
| 94 abstract bool get isStatic(); | 93 abstract bool get isStatic(); |
| 95 abstract Type get returnType(); | 94 abstract Type get returnType(); |
| 96 | 95 |
| 97 abstract bool get canGet(); | 96 abstract bool get canGet(); |
| 98 abstract bool get canSet(); | 97 abstract bool get canSet(); |
| 99 | 98 |
| 100 Library get library() => declaringType.library; | 99 Library get library() => declaringType.library; |
| 101 | 100 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 112 // TODO(jmesserly): these only makes sense on methods, but because of | 111 // TODO(jmesserly): these only makes sense on methods, but because of |
| 113 // ConcreteMember we need to support them on Member. | 112 // ConcreteMember we need to support them on Member. |
| 114 bool get isConst() => false; | 113 bool get isConst() => false; |
| 115 bool get isFactory() => false; | 114 bool get isFactory() => false; |
| 116 | 115 |
| 117 bool get isOperator() => name.startsWith(':'); | 116 bool get isOperator() => name.startsWith(':'); |
| 118 bool get isCallMethod() => name == ':call'; | 117 bool get isCallMethod() => name == ':call'; |
| 119 | 118 |
| 120 bool get requiresPropertySyntax() => false; | 119 bool get requiresPropertySyntax() => false; |
| 121 bool _providePropertySyntax = false; | 120 bool _providePropertySyntax = false; |
| 122 bool get requiresFieldSyntax() => false; | |
| 123 bool _provideFieldSyntax = false; | |
| 124 | 121 |
| 125 bool get isNative() => false; | 122 bool get isNative() => false; |
| 126 String get constructorName() { | 123 String get constructorName() { |
| 127 world.internalError('can not be a constructor', span); | 124 world.internalError('can not be a constructor', span); |
| 128 } | 125 } |
| 129 | 126 |
| 130 // Don't display an error here; we'll get a better error later. | |
| 131 void provideFieldSyntax() {} | |
| 132 void providePropertySyntax() {} | 127 void providePropertySyntax() {} |
| 133 | 128 |
| 134 Member get initDelegate() { | 129 Member get initDelegate() { |
| 135 world.internalError('cannot have initializers', span); | 130 world.internalError('cannot have initializers', span); |
| 136 } | 131 } |
| 137 void set initDelegate(ctor) { | 132 void set initDelegate(ctor) { |
| 138 world.internalError('cannot have initializers', span); | 133 world.internalError('cannot have initializers', span); |
| 139 } | 134 } |
| 140 | 135 |
| 141 Value computeValue() { | 136 Value computeValue() { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 163 MemberSet _preciseMemberSet, _potentialMemberSet; | 158 MemberSet _preciseMemberSet, _potentialMemberSet; |
| 164 | 159 |
| 165 MemberSet get preciseMemberSet() { | 160 MemberSet get preciseMemberSet() { |
| 166 if (_preciseMemberSet === null) { | 161 if (_preciseMemberSet === null) { |
| 167 _preciseMemberSet = new MemberSet(this); | 162 _preciseMemberSet = new MemberSet(this); |
| 168 } | 163 } |
| 169 return _preciseMemberSet; | 164 return _preciseMemberSet; |
| 170 } | 165 } |
| 171 | 166 |
| 172 MemberSet get potentialMemberSet() { | 167 MemberSet get potentialMemberSet() { |
| 168 // TODO(jimhug): This needs one more redesign - move to TypeSets... |
| 169 |
| 173 if (_potentialMemberSet === null) { | 170 if (_potentialMemberSet === null) { |
| 174 if (declaringType.isObject) { | 171 if (name == ':call') { |
| 175 _potentialMemberSet = world._members[name]; | 172 _potentialMemberSet = preciseMemberSet; |
| 176 return _potentialMemberSet; | 173 return _potentialMemberSet; |
| 177 } | 174 } |
| 178 | 175 |
| 179 final mems = new Set<Member>(); | 176 final mems = new Set<Member>(); |
| 180 if (declaringType.isClass) mems.add(this); | 177 if (declaringType.isClass) mems.add(this); |
| 181 | 178 |
| 182 | 179 for (var subtype in declaringType.genericType.subtypes) { |
| 183 for (var subtype in declaringType.subtypes) { | |
| 184 if (!subtype.isClass) continue; | 180 if (!subtype.isClass) continue; |
| 185 var mem = subtype.members[name]; | 181 var mem = subtype.members[name]; |
| 186 if (mem !== null) { | 182 if (mem !== null) { |
| 187 mems.add(mem); | 183 if (mem.isDefinedOn(declaringType)) { |
| 184 mems.add(mem); |
| 185 } |
| 188 } else if (!declaringType.isClass) { | 186 } else if (!declaringType.isClass) { |
| 189 // Handles weird interface case. | 187 // Handles weird interface case. |
| 190 mem = subtype.getMember(name); | 188 mem = subtype.getMember(name); |
| 191 if (mem !== null) { | 189 if (mem !== null && mem.isDefinedOn(declaringType)) { |
| 192 mems.add(mem); | 190 mems.add(mem); |
| 193 } | 191 } |
| 194 } | 192 } |
| 195 } | 193 } |
| 196 | 194 |
| 197 if (mems.length != 0) { | 195 if (mems.length != 0) { |
| 196 // TODO(jimhug): This hack needs to be rationalized. |
| 197 for (var mem in mems) { |
| 198 if (declaringType.genericType != declaringType && |
| 199 mem.genericMember != null && mems.contains(mem.genericMember)) { |
| 200 //world.info('skip ${name} on ${mem.genericMember.declaringType.name
}' + |
| 201 // ' because we have on ${mem.declaringType.name} for ${declaringTy
pe.name}'); |
| 202 mems.remove(mem.genericMember); |
| 203 } |
| 204 } |
| 205 |
| 206 |
| 198 for (var mem in mems) { | 207 for (var mem in mems) { |
| 199 if (_potentialMemberSet === null) { | 208 if (_potentialMemberSet === null) { |
| 200 _potentialMemberSet = new MemberSet(mem); | 209 _potentialMemberSet = new MemberSet(mem); |
| 201 } else { | 210 } else { |
| 202 _potentialMemberSet.add(mem); | 211 _potentialMemberSet.add(mem); |
| 203 } | 212 } |
| 204 } | 213 } |
| 205 } | 214 } |
| 206 } | 215 } |
| 207 return _potentialMemberSet; | 216 return _potentialMemberSet; |
| 208 } | 217 } |
| 209 | 218 |
| 210 // If I have an object of [type] could I be invoking this member? | 219 // If I have an object of [type] could I be invoking this member? |
| 211 bool isDefinedOn(Type type) { | 220 bool isDefinedOn(Type type) { |
| 212 if (type.isClass) { | 221 if (type.isClass) { |
| 213 if (declaringType.isSubtypeOf(type)) { | 222 if (declaringType.isSubtypeOf(type)) { |
| 214 return true; | 223 return true; |
| 215 } else if (type.isSubtypeOf(declaringType)) { | 224 } else if (type.isSubtypeOf(declaringType)) { |
| 216 // maybe - but not if overridden somewhere | 225 // maybe - but not if overridden somewhere |
| 217 // !!! horrible hack for today - awful perf props | 226 // TODO(jimhug): This lookup is not great for perf of this method. |
| 218 return type.getMember(name) == this; | 227 return type.getMember(name) == this; |
| 219 //return true; | |
| 220 } else { | 228 } else { |
| 221 return false; | 229 return false; |
| 222 } | 230 } |
| 223 } else { | 231 } else { |
| 224 if (declaringType.isSubtypeOf(type)) { | 232 if (declaringType.isSubtypeOf(type)) { |
| 225 return true; | 233 return true; |
| 226 } else { | 234 } else { |
| 227 // If this is an interface, the actual implementation may | 235 // If this is an interface, the actual implementation may |
| 228 // come from a class that does not implement this interface. | 236 // come from a class that does not implement this interface. |
| 229 for (var t in declaringType.subtypes) { | 237 for (var t in declaringType.subtypes) { |
| 230 if (t.isSubtypeOf(type) && t.getMember(name) == this) { | 238 if (t.isSubtypeOf(type) && t.getMember(name) == this) { |
| 231 return true; | 239 return true; |
| 232 } | 240 } |
| 233 } | 241 } |
| 234 return false; | 242 return false; |
| 235 } | 243 } |
| 236 } | 244 } |
| 237 } | 245 } |
| 238 | 246 |
| 239 // TODO(jmesserly): isDynamic isn't a great name for this, something better? | 247 abstract Value _get(CallingContext context, Node node, Value target); |
| 240 abstract Value _get(MethodGenerator context, Node node, Value target, | |
| 241 [bool isDynamic]); | |
| 242 | 248 |
| 243 abstract Value _set(MethodGenerator context, Node node, Value target, | 249 abstract Value _set(CallingContext context, Node node, Value target, |
| 244 Value value, [bool isDynamic]); | 250 Value value); |
| 245 | 251 |
| 246 bool canInvoke(MethodGenerator context, Arguments args) { | 252 |
| 247 // No source location needed because canInvoke may not produce errors. | 253 bool canInvoke(CallingContext context, Arguments args) { |
| 248 return canGet && | 254 // Any gettable member whose return type is callable can be "invoked". |
| 249 new Value(returnType, null, null).canInvoke(context, ':call', args); | 255 if (canGet && (isField || isProperty)) { |
| 256 return this.returnType.isFunction || this.returnType.isVar || |
| 257 this.returnType.getCallMethod() != null; |
| 258 } |
| 259 return false; |
| 250 } | 260 } |
| 251 | 261 |
| 252 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, | 262 Value invoke(CallingContext context, Node node, Value target, |
| 253 [bool isDynamic=false]) { | 263 Arguments args) { |
| 254 var newTarget = _get(context, node, target, isDynamic); | 264 var newTarget = _get(context, node, target); |
| 255 return newTarget.invoke(context, ':call', node, args, isDynamic); | 265 return newTarget.invoke(context, ':call', node, args); |
| 256 } | 266 } |
| 257 | 267 |
| 258 bool override(Member other) { | 268 bool override(Member other) { |
| 259 if (isStatic) { | 269 if (isStatic) { |
| 260 world.error('static members can not hide parent members', | 270 world.error('static members can not hide parent members', |
| 261 span, other.span); | 271 span, other.span); |
| 262 return false; | 272 return false; |
| 263 } else if (other.isStatic) { | 273 } else if (other.isStatic) { |
| 264 world.error('can not override static member', span, other.span); | 274 world.error('can not override static member', span, other.span); |
| 265 return false; | 275 return false; |
| 266 } | 276 } |
| 267 return true; | 277 return true; |
| 268 } | 278 } |
| 269 | 279 |
| 270 String get generatedFactoryName() { | 280 String get generatedFactoryName() { |
| 271 assert(this.isFactory); | 281 assert(this.isFactory); |
| 272 String prefix = '${declaringType.jsname}.${constructorName}\$'; | 282 String prefix = '${declaringType.genericType.jsname}.${constructorName}\$'; |
| 273 if (name == '') { | 283 if (name == '') { |
| 274 return '${prefix}factory'; | 284 return '${prefix}factory'; |
| 275 } else { | 285 } else { |
| 276 return '${prefix}$name\$factory'; | 286 return '${prefix}$name\$factory'; |
| 277 } | 287 } |
| 278 } | 288 } |
| 279 | 289 |
| 280 int hashCode() { | 290 int hashCode() { |
| 281 final typeCode = declaringType == null ? 1 : declaringType.hashCode(); | 291 final typeCode = declaringType == null ? 1 : declaringType.hashCode(); |
| 282 final nameCode = isConstructor ? constructorName.hashCode() : | 292 final nameCode = isConstructor ? constructorName.hashCode() : |
| 283 name.hashCode(); | 293 name.hashCode(); |
| 284 return (typeCode << 4) ^ nameCode; | 294 return (typeCode << 4) ^ nameCode; |
| 285 } | 295 } |
| 286 | 296 |
| 287 bool operator ==(other) { | 297 bool operator ==(other) { |
| 288 return other is Member && isConstructor == other.isConstructor && | 298 return other is Member && isConstructor == other.isConstructor && |
| 289 declaringType == other.declaringType && (isConstructor ? | 299 declaringType == other.declaringType && (isConstructor ? |
| 290 constructorName == other.constructorName : name == other.name); | 300 constructorName == other.constructorName : name == other.name); |
| 291 } | 301 } |
| 302 |
| 303 /** Overriden to ensure that type arguments aren't used in static mems. */ |
| 304 Type resolveType(TypeReference node, bool typeErrors, bool allowTypeParams) { |
| 305 allowTypeParams = allowTypeParams && !(isStatic && !isFactory); |
| 306 |
| 307 return super.resolveType(node, typeErrors, allowTypeParams); |
| 308 } |
| 309 |
| 310 // TODO(jimhug): Make this abstract. |
| 311 Member makeConcrete(Type concreteType) { |
| 312 world.internalError('can not make this concrete', span); |
| 313 } |
| 292 } | 314 } |
| 293 | 315 |
| 294 | 316 |
| 295 /** | 317 /** |
| 296 * Types are treated as first class members of their library's top type. | 318 * Types are treated as first class members of their library's top type. |
| 297 */ | 319 */ |
| 298 // TODO(jmesserly): perhaps Type should extend Member, but that can get | 320 // TODO(jmesserly): perhaps Type should extend Member, but that can get |
| 299 // complicated. | 321 // complicated. |
| 300 class TypeMember extends Member { | 322 class TypeMember extends Member { |
| 301 final DefinedType type; | 323 final DefinedType type; |
| 302 | 324 |
| 303 TypeMember(DefinedType type) | 325 TypeMember(DefinedType type) |
| 304 : super(type.name, type.library.topType), | 326 : super(type.name, type.library.topType), |
| 305 this.type = type; | 327 this.type = type; |
| 306 | 328 |
| 307 SourceSpan get span() => type.definition === null ? null : type.definition.spa
n; | 329 SourceSpan get span() => type.definition === null ? null : type.definition.spa
n; |
| 308 | 330 |
| 309 bool get isStatic() => true; | 331 bool get isStatic() => true; |
| 310 | 332 |
| 311 // If this really becomes first class, this should return typeof(Type) | 333 // If this really becomes first class, this should return typeof(Type) |
| 312 Type get returnType() => world.varType; | 334 Type get returnType() => world.varType; |
| 313 | 335 |
| 314 bool canInvoke(MethodGenerator context, Arguments args) => false; | 336 bool canInvoke(CallingContext context, Arguments args) => false; |
| 315 bool get canGet() => true; | 337 bool get canGet() => true; |
| 316 bool get canSet() => false; | 338 bool get canSet() => false; |
| 317 | 339 |
| 318 bool get requiresFieldSyntax() => true; | 340 Value _get(CallingContext context, Node node, Value target) { |
| 319 | |
| 320 Value _get(MethodGenerator context, Node node, Value target, | |
| 321 [bool isDynamic=false]) { | |
| 322 return new TypeValue(type, node.span); | 341 return new TypeValue(type, node.span); |
| 323 } | 342 } |
| 324 | 343 |
| 325 Value _set(MethodGenerator context, Node node, Value target, Value value, | 344 Value _set(CallingContext context, Node node, Value target, Value value) { |
| 326 [bool isDynamic=false]) { | |
| 327 world.error('cannot set type', node.span); | 345 world.error('cannot set type', node.span); |
| 328 } | 346 } |
| 329 | 347 |
| 330 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, | 348 Value invoke(CallingContext context, Node node, Value target, |
| 331 [bool isDynamic=false]) { | 349 Arguments args) { |
| 332 world.error('cannot invoke type', node.span); | 350 world.error('cannot invoke type', node.span); |
| 333 } | 351 } |
| 334 } | 352 } |
| 335 | 353 |
| 336 /** Represents a Dart field from source code. */ | 354 /** Represents a Dart field from source code. */ |
| 337 class FieldMember extends Member { | 355 class FieldMember extends Member { |
| 338 final VariableDefinition definition; | 356 final VariableDefinition definition; |
| 339 final Expression value; | 357 final Expression value; |
| 340 | 358 |
| 341 Type type; | 359 Type type; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 366 // other.returnType.ensureAssignableFrom(returnType, null, true); | 384 // other.returnType.ensureAssignableFrom(returnType, null, true); |
| 367 return true; | 385 return true; |
| 368 // TODO(jimhug): Merge in overridesProperty logic here. | 386 // TODO(jimhug): Merge in overridesProperty logic here. |
| 369 } else { | 387 } else { |
| 370 world.error('field can not override anything but property', | 388 world.error('field can not override anything but property', |
| 371 span, other.span); | 389 span, other.span); |
| 372 return false; | 390 return false; |
| 373 } | 391 } |
| 374 } | 392 } |
| 375 | 393 |
| 376 void provideFieldSyntax() {} // Nothing to do. | 394 void providePropertySyntax() { |
| 377 void providePropertySyntax() { _providePropertySyntax = true; } | 395 _providePropertySyntax = true; |
| 396 if (genericMember !== null) { |
| 397 genericMember.providePropertySyntax(); |
| 398 } |
| 399 } |
| 378 | 400 |
| 379 FieldMember(String name, Type declaringType, this.definition, this.value) | 401 FieldMember(String name, Type declaringType, this.definition, this.value) |
| 380 : super(name, declaringType), isNative = false; | 402 : super(name, declaringType), isNative = false; |
| 381 | 403 |
| 404 Member makeConcrete(Type concreteType) { |
| 405 var ret = new FieldMember(name, concreteType, definition, value); |
| 406 ret.genericMember = this; |
| 407 ret._jsname = _jsname; |
| 408 return ret; |
| 409 } |
| 410 |
| 411 |
| 382 SourceSpan get span() => definition == null ? null : definition.span; | 412 SourceSpan get span() => definition == null ? null : definition.span; |
| 383 | 413 |
| 384 Type get returnType() => type; | 414 Type get returnType() => type; |
| 385 | 415 |
| 386 bool get canGet() => true; | 416 bool get canGet() => true; |
| 387 bool get canSet() => !isFinal; | 417 bool get canSet() => !isFinal; |
| 388 | 418 |
| 389 bool get isField() => true; | 419 bool get isField() => true; |
| 390 | 420 |
| 391 resolve() { | 421 resolve() { |
| 392 isStatic = declaringType.isTop; | 422 isStatic = declaringType.isTop; |
| 393 isFinal = false; | 423 isFinal = false; |
| 394 if (definition.modifiers != null) { | 424 if (definition.modifiers != null) { |
| 395 for (var mod in definition.modifiers) { | 425 for (var mod in definition.modifiers) { |
| 396 if (mod.kind == TokenKind.STATIC) { | 426 if (mod.kind == TokenKind.STATIC) { |
| 397 if (isStatic) { | 427 if (isStatic) { |
| 398 world.error('duplicate static modifier', mod.span); | 428 world.error('duplicate static modifier', mod.span); |
| 399 } | 429 } |
| 400 isStatic = true; | 430 isStatic = true; |
| 401 } else if (mod.kind == TokenKind.FINAL) { | 431 } else if (mod.kind == TokenKind.FINAL) { |
| 402 if (isFinal) { | 432 if (isFinal) { |
| 403 world.error('duplicate final modifier', mod.span); | 433 world.error('duplicate final modifier', mod.span); |
| 404 } | 434 } |
| 405 isFinal = true; | 435 isFinal = true; |
| 406 } else { | 436 } else { |
| 407 world.error('${mod} modifier not allowed on field', mod.span); | 437 world.error('${mod} modifier not allowed on field', mod.span); |
| 408 } | 438 } |
| 409 } | 439 } |
| 410 } | 440 } |
| 411 type = resolveType(definition.type, false); | 441 type = resolveType(definition.type, false, true); |
| 412 if (isStatic && !isFactory && type.hasTypeParams) { | |
| 413 world.error('using type parameter in static context', | |
| 414 definition.type.span); | |
| 415 } | |
| 416 | 442 |
| 417 if (isStatic && isFinal && value == null) { | 443 if (isStatic && isFinal && value == null) { |
| 418 world.error('static final field is missing initializer', span); | 444 world.error('static final field is missing initializer', span); |
| 419 } | 445 } |
| 420 | 446 |
| 421 library._addMember(this); | 447 if (declaringType.isClass) library._addMember(this); |
| 422 } | 448 } |
| 423 | 449 |
| 424 | 450 |
| 425 bool _computing = false; | 451 bool _computing = false; |
| 426 /** Generates the initial value for this field, if any. Marks it as used. */ | 452 /** Generates the initial value for this field, if any. Marks it as used. */ |
| 427 Value computeValue() { | 453 Value computeValue() { |
| 428 if (value == null) return null; | 454 if (value == null) return null; |
| 429 | 455 |
| 430 if (_computedValue == null) { | 456 if (_computedValue == null) { |
| 431 if (_computing) { | 457 if (_computing) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 456 } else { | 482 } else { |
| 457 _computedValue = world.gen.globalForStaticField( | 483 _computedValue = world.gen.globalForStaticField( |
| 458 this, _computedValue, [_computedValue]); | 484 this, _computedValue, [_computedValue]); |
| 459 } | 485 } |
| 460 } | 486 } |
| 461 _computing = false; | 487 _computing = false; |
| 462 } | 488 } |
| 463 return _computedValue; | 489 return _computedValue; |
| 464 } | 490 } |
| 465 | 491 |
| 466 Value _get(MethodGenerator context, Node node, Value target, | 492 Value _get(CallingContext context, Node node, Value target) { |
| 467 [bool isDynamic=false]) { | 493 if (!context.needsCode) { |
| 494 return new PureStaticValue(type, node.span, isStatic && isFinal); |
| 495 } |
| 496 |
| 468 if (isNative && returnType != null) { | 497 if (isNative && returnType != null) { |
| 469 returnType.markUsed(); | 498 returnType.markUsed(); |
| 470 if (returnType is DefinedType) { | 499 if (returnType is DefinedType) { |
| 471 // TODO(jmesserly): this handles native fields that return types like | 500 // TODO(jmesserly): this handles native fields that return types like |
| 472 // "List". Is there a better solution for fields? Unlike methods we have | 501 // "List". Is there a better solution for fields? Unlike methods we have |
| 473 // no good way to annotate them. | 502 // no good way to annotate them. |
| 474 var defaultType = returnType.genericType.defaultType; | 503 var defaultType = returnType.genericType.defaultType; |
| 475 if (defaultType != null && defaultType.isNative) { | 504 if (defaultType != null && defaultType.isNative) { |
| 476 defaultType.markUsed(); | 505 defaultType.markUsed(); |
| 477 } | 506 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 504 } | 533 } |
| 505 return new Value(type, '${declaringType.jsname}.$jsname', node.span); | 534 return new Value(type, '${declaringType.jsname}.$jsname', node.span); |
| 506 } else { | 535 } else { |
| 507 return new Value(type, | 536 return new Value(type, |
| 508 '\$globals.${declaringType.jsname}_$jsname', node.span); | 537 '\$globals.${declaringType.jsname}_$jsname', node.span); |
| 509 } | 538 } |
| 510 } | 539 } |
| 511 return new Value(type, '${target.code}.$jsname', node.span); | 540 return new Value(type, '${target.code}.$jsname', node.span); |
| 512 } | 541 } |
| 513 | 542 |
| 514 Value _set(MethodGenerator context, Node node, Value target, Value value, | 543 Value _set(CallingContext context, Node node, Value target, Value value) { |
| 515 [bool isDynamic=false]) { | 544 if (!context.needsCode) { |
| 516 var lhs = _get(context, node, target, isDynamic); | 545 // TODO(jimhug): Add type checks here. |
| 517 value = value.convertTo(context, type, isDynamic); | 546 return new PureStaticValue(type, node.span); |
| 547 } |
| 548 |
| 549 var lhs = _get(context, node, target); |
| 550 value = value.convertTo(context, type); |
| 518 return new Value(type, '${lhs.code} = ${value.code}', node.span); | 551 return new Value(type, '${lhs.code} = ${value.code}', node.span); |
| 519 } | 552 } |
| 520 } | 553 } |
| 521 | 554 |
| 522 class PropertyMember extends Member { | 555 class PropertyMember extends Member { |
| 523 MethodMember getter; | 556 MethodMember getter; |
| 524 MethodMember setter; | 557 MethodMember setter; |
| 525 | 558 |
| 526 Member _overriddenField; | 559 Member _overriddenField; |
| 527 | 560 |
| 528 // TODO(jimhug): What is the right span for this beast? | 561 // TODO(jimhug): What is the right span for this beast? |
| 529 SourceSpan get span() => getter != null ? getter.span : null; | 562 SourceSpan get span() => getter != null ? getter.span : null; |
| 530 | 563 |
| 531 bool get canGet() => getter != null; | 564 bool get canGet() => getter != null; |
| 532 bool get canSet() => setter != null; | 565 bool get canSet() => setter != null; |
| 533 | 566 |
| 534 // If the property is just a declaration in an interface, continue to allow | 567 // If the property is just a declaration in an interface, continue to allow |
| 535 // field syntax in the generated code. | 568 // field syntax in the generated code. |
| 536 bool get requiresPropertySyntax() => declaringType.isClass; | 569 bool get requiresPropertySyntax() => declaringType.isClass; |
| 537 | 570 |
| 538 void provideFieldSyntax() { _provideFieldSyntax = true; } | 571 // when overriding native fields, we still provide a field syntax to ensure |
| 539 void providePropertySyntax() { | 572 // that native functions will find the appropriate property implementation. |
| 540 // when overriding native fields, we still provide a field syntax to ensure | 573 // TODO(sigmund): should check for this transitively... |
| 541 // that native functions will find the appropriate property implementation. | 574 bool get needsFieldSyntax() => |
| 542 // TODO(sigmund): should check for this transitively... | 575 _overriddenField != null && _overriddenField.isNative; |
| 543 if (_overriddenField != null && _overriddenField.isNative) { | |
| 544 provideFieldSyntax(); | |
| 545 } | |
| 546 } | |
| 547 | 576 |
| 548 // TODO(jimhug): Union of getter and setters sucks! | 577 // TODO(jimhug): Union of getter and setters sucks! |
| 549 bool get isStatic() => getter == null ? setter.isStatic : getter.isStatic; | 578 bool get isStatic() => getter == null ? setter.isStatic : getter.isStatic; |
| 550 | 579 |
| 551 bool get isProperty() => true; | 580 bool get isProperty() => true; |
| 552 | 581 |
| 553 Type get returnType() { | 582 Type get returnType() { |
| 554 return getter == null ? setter.returnType : getter.returnType; | 583 return getter == null ? setter.returnType : getter.returnType; |
| 555 } | 584 } |
| 556 | 585 |
| 557 PropertyMember(String name, Type declaringType): super(name, declaringType); | 586 PropertyMember(String name, Type declaringType): super(name, declaringType); |
| 558 | 587 |
| 588 Member makeConcrete(Type concreteType) { |
| 589 var ret = new PropertyMember(name, concreteType); |
| 590 if (getter !== null) ret.getter = getter.makeConcrete(concreteType); |
| 591 if (setter !== null) ret.setter = setter.makeConcrete(concreteType); |
| 592 ret._jsname = _jsname; |
| 593 return ret; |
| 594 } |
| 595 |
| 559 bool override(Member other) { | 596 bool override(Member other) { |
| 560 if (!super.override(other)) return false; | 597 if (!super.override(other)) return false; |
| 561 | 598 |
| 562 // properties can override other properties and fields | 599 // properties can override other properties and fields |
| 563 if (other.isProperty || other.isField) { | 600 if (other.isProperty || other.isField) { |
| 564 // TODO(jimhug): | 601 // TODO(jimhug): |
| 565 // other.returnType.ensureAssignableFrom(returnType, null, true); | 602 // other.returnType.ensureAssignableFrom(returnType, null, true); |
| 566 if (other.isProperty) addFromParent(other); | 603 if (other.isProperty) addFromParent(other); |
| 567 else _overriddenField = other; | 604 else _overriddenField = other; |
| 568 return true; | 605 return true; |
| 569 } else { | 606 } else { |
| 570 world.error('property can only override field or property', | 607 world.error('property can only override field or property', |
| 571 span, other.span); | 608 span, other.span); |
| 572 return false; | 609 return false; |
| 573 } | 610 } |
| 574 } | 611 } |
| 575 | 612 |
| 576 Value _get(MethodGenerator context, Node node, Value target, | 613 Value _get(CallingContext context, Node node, Value target) { |
| 577 [bool isDynamic=false]) { | |
| 578 if (getter == null) { | 614 if (getter == null) { |
| 579 if (_overriddenField != null) { | 615 if (_overriddenField != null) { |
| 580 return _overriddenField._get(context, node, target, isDynamic); | 616 return _overriddenField._get(context, node, target); |
| 581 } | 617 } |
| 582 return target.invokeNoSuchMethod(context, 'get:$name', node); | 618 return target.invokeNoSuchMethod(context, 'get:$name', node); |
| 583 } | 619 } |
| 584 return getter.invoke(context, node, target, Arguments.EMPTY); | 620 return getter.invoke(context, node, target, Arguments.EMPTY); |
| 585 } | 621 } |
| 586 | 622 |
| 587 Value _set(MethodGenerator context, Node node, Value target, Value value, | 623 Value _set(CallingContext context, Node node, Value target, Value value) { |
| 588 [bool isDynamic=false]) { | |
| 589 if (setter == null) { | 624 if (setter == null) { |
| 590 if (_overriddenField != null) { | 625 if (_overriddenField != null) { |
| 591 return _overriddenField._set(context, node, target, value, isDynamic); | 626 return _overriddenField._set(context, node, target, value); |
| 592 } | 627 } |
| 593 return target.invokeNoSuchMethod(context, 'set:$name', node, | 628 return target.invokeNoSuchMethod(context, 'set:$name', node, |
| 594 new Arguments(null, [value])); | 629 new Arguments(null, [value])); |
| 595 } | 630 } |
| 596 return setter.invoke(context, node, target, new Arguments(null, [value]), | 631 return setter.invoke(context, node, target, new Arguments(null, [value])); |
| 597 isDynamic); | |
| 598 } | 632 } |
| 599 | 633 |
| 600 addFromParent(Member parentMember) { | 634 addFromParent(Member parentMember) { |
| 601 // TODO(jimhug): Egregious Hack! | 635 final parent = parentMember; |
| 602 PropertyMember parent; | |
| 603 if (parentMember is ConcreteMember) { | |
| 604 ConcreteMember c = parentMember; | |
| 605 parent = c.baseMember; | |
| 606 } else { | |
| 607 parent = parentMember; | |
| 608 } | |
| 609 | 636 |
| 610 if (getter == null) getter = parent.getter; | 637 if (getter == null) getter = parent.getter; |
| 611 if (setter == null) setter = parent.setter; | 638 if (setter == null) setter = parent.setter; |
| 612 } | 639 } |
| 613 | 640 |
| 614 resolve() { | 641 resolve() { |
| 615 if (getter != null) { | 642 if (getter != null) { |
| 616 getter.resolve(); | 643 getter.resolve(); |
| 617 if (getter.parameters.length != 0) { | 644 if (getter.parameters.length != 0) { |
| 618 world.error('getter methods should take no arguments', | 645 world.error('getter methods should take no arguments', |
| (...skipping 11 matching lines...) Expand all Loading... |
| 630 setter.definition.span); | 657 setter.definition.span); |
| 631 } | 658 } |
| 632 // Not issue warning if setter is implicitly dynamic (returnType == null), | 659 // Not issue warning if setter is implicitly dynamic (returnType == null), |
| 633 // but do if it is explicit (returnType.isVar) | 660 // but do if it is explicit (returnType.isVar) |
| 634 if (!setter.returnType.isVoid && setter.definition.returnType != null) { | 661 if (!setter.returnType.isVoid && setter.definition.returnType != null) { |
| 635 world.warning('setter methods should be void', | 662 world.warning('setter methods should be void', |
| 636 setter.definition.returnType.span); | 663 setter.definition.returnType.span); |
| 637 } | 664 } |
| 638 } | 665 } |
| 639 | 666 |
| 640 library._addMember(this); | 667 if (declaringType.isClass) library._addMember(this); |
| 641 } | 668 } |
| 642 } | 669 } |
| 643 | 670 |
| 644 | |
| 645 class ConcreteMember extends Member { | |
| 646 final Member baseMember; | |
| 647 Type returnType; | |
| 648 List<Parameter> parameters; | |
| 649 | |
| 650 ConcreteMember(String name, ConcreteType declaringType, this.baseMember) | |
| 651 : super(name, declaringType) { | |
| 652 parameters = []; | |
| 653 returnType = baseMember.returnType.resolveTypeParams(declaringType); | |
| 654 // TODO(jimhug): Optimize not creating new array if no new param types. | |
| 655 for (var p in baseMember.parameters) { | |
| 656 var newType = p.type.resolveTypeParams(declaringType); | |
| 657 if (newType != p.type) { | |
| 658 parameters.add(p.copyWithNewType(this, newType)); | |
| 659 } else { | |
| 660 parameters.add(p); | |
| 661 } | |
| 662 } | |
| 663 } | |
| 664 | |
| 665 SourceSpan get span() => baseMember.span; | |
| 666 | |
| 667 bool get isStatic() => baseMember.isStatic; | |
| 668 bool get isAbstract() => baseMember.isAbstract; | |
| 669 bool get isConst() => baseMember.isConst; | |
| 670 bool get isFactory() => baseMember.isFactory; | |
| 671 bool get isFinal() => baseMember.isFinal; | |
| 672 bool get isNative() => baseMember.isNative; | |
| 673 | |
| 674 String get jsname() => baseMember.jsname; | |
| 675 set jsname(String name) => | |
| 676 world.internalError('bad set of jsname on ConcreteMember'); | |
| 677 | |
| 678 | |
| 679 bool get canGet() => baseMember.canGet; | |
| 680 bool get canSet() => baseMember.canSet; | |
| 681 bool canInvoke(MethodGenerator context, Arguments args) => | |
| 682 baseMember.canInvoke(context, args); | |
| 683 | |
| 684 bool get isField() => baseMember.isField; | |
| 685 bool get isMethod() => baseMember.isMethod; | |
| 686 bool get isProperty() => baseMember.isProperty; | |
| 687 | |
| 688 bool get requiresPropertySyntax() => baseMember.requiresPropertySyntax; | |
| 689 bool get requiresFieldSyntax() => baseMember.requiresFieldSyntax; | |
| 690 | |
| 691 void provideFieldSyntax() => baseMember.provideFieldSyntax(); | |
| 692 void providePropertySyntax() => baseMember.providePropertySyntax(); | |
| 693 | |
| 694 bool get isConstructor() => name == declaringType.name; | |
| 695 | |
| 696 String get constructorName() => baseMember.constructorName; | |
| 697 | |
| 698 Definition get definition() => baseMember.definition; | |
| 699 | |
| 700 // TODO(sigmund): this is EGREGIOUS | |
| 701 Member get initDelegate() => baseMember.initDelegate; | |
| 702 void set initDelegate(ctor) { baseMember.initDelegate = ctor; } | |
| 703 | |
| 704 Type resolveType(TypeReference node, bool isRequired) { | |
| 705 var type = baseMember.resolveType(node, isRequired); | |
| 706 return type.resolveTypeParams(declaringType); | |
| 707 } | |
| 708 | |
| 709 Value computeValue() => baseMember.computeValue(); | |
| 710 | |
| 711 // TODO(jimhug): Add support for type params. | |
| 712 bool override(Member other) => baseMember.override(other); | |
| 713 | |
| 714 Value _get(MethodGenerator context, Node node, Value target, | |
| 715 [bool isDynamic=false]) { | |
| 716 Value ret = baseMember._get(context, node, target, isDynamic); | |
| 717 return new Value(inferredResult, ret.code, node.span); | |
| 718 } | |
| 719 | |
| 720 Value _set(MethodGenerator context, Node node, Value target, Value value, | |
| 721 [bool isDynamic=false]) { | |
| 722 // TODO(jimhug): Check arg types in context of concrete type. | |
| 723 Value ret = baseMember._set(context, node, target, value, isDynamic); | |
| 724 return new Value(returnType, ret.code, node.span); | |
| 725 } | |
| 726 | |
| 727 _evalConstConstructor(ObjectValue newObject, Arguments args) { | |
| 728 // TODO(jimhug): Concrete type probably matters somehow here | |
| 729 return baseMember.dynamic._evalConstConstructor(newObject, args); | |
| 730 } | |
| 731 | |
| 732 | |
| 733 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, | |
| 734 [bool isDynamic=false]) { | |
| 735 // TODO(jimhug): Check arg types in context of concrete type. | |
| 736 // TODO(jmesserly): I think what needs to happen is to move MethodMember's | |
| 737 // invoke so that it's run against the "parameters" and "returnType" of the | |
| 738 // ConcreteMember instead. | |
| 739 Value ret = baseMember.invoke(context, node, target, args, isDynamic); | |
| 740 var code = ret.code; | |
| 741 if (isConstructor) { | |
| 742 // TODO(jimhug): Egregious hack - won't live through the year. | |
| 743 code = code.replaceFirst( | |
| 744 declaringType.genericType.jsname, declaringType.jsname); | |
| 745 } | |
| 746 if (baseMember is MethodMember) { | |
| 747 declaringType.genMethod(this); | |
| 748 } | |
| 749 return new Value(inferredResult, code, node.span); | |
| 750 } | |
| 751 } | |
| 752 | |
| 753 | 671 |
| 754 /** Represents a Dart method or top-level function. */ | 672 /** Represents a Dart method or top-level function. */ |
| 755 class MethodMember extends Member { | 673 class MethodMember extends Member { |
| 756 FunctionDefinition definition; | 674 FunctionDefinition definition; |
| 757 Type returnType; | 675 Type returnType; |
| 758 List<Parameter> parameters; | 676 List<Parameter> parameters; |
| 759 | 677 |
| 678 MethodData _methodData; |
| 679 |
| 760 Type _functionType; | 680 Type _functionType; |
| 761 bool isStatic = false; | 681 bool isStatic = false; |
| 762 bool isAbstract = false; | 682 bool isAbstract = false; |
| 763 | 683 |
| 764 // Note: these two modifiers are only legal on constructors | 684 // Note: these two modifiers are only legal on constructors |
| 765 bool isConst = false; | 685 bool isConst = false; |
| 766 bool isFactory = false; | 686 bool isFactory = false; |
| 767 | 687 |
| 768 /** True if this is a function defined inside another method. */ | 688 /** True if this is a function defined inside another method. */ |
| 769 bool isLambda = false; | 689 bool isLambda = false; |
| 770 | 690 |
| 771 /** | 691 /** |
| 772 * True if we should provide info on optional parameters for use by runtime | 692 * True if we should provide info on optional parameters for use by runtime |
| 773 * dispatch. | 693 * dispatch. |
| 774 */ | 694 */ |
| 775 bool _provideOptionalParamInfo = false; | 695 bool _provideOptionalParamInfo = false; |
| 776 | 696 |
| 777 /* | 697 /* |
| 778 * When this is a constructor, contains any other constructor called during | 698 * When this is a constructor, contains any other constructor called during |
| 779 * initialization (if any). | 699 * initialization (if any). |
| 780 */ | 700 */ |
| 781 Member initDelegate; | 701 Member initDelegate; |
| 782 | 702 |
| 783 MethodMember(String name, Type declaringType, this.definition) | 703 MethodMember(String name, Type declaringType, this.definition) |
| 784 : super(name, declaringType); | 704 : super(name, declaringType); |
| 785 | 705 |
| 706 Member makeConcrete(Type concreteType) { |
| 707 var _name = isConstructor ? concreteType.name : name; |
| 708 var ret = new MethodMember(_name, concreteType, definition); |
| 709 ret.genericMember = this; |
| 710 ret._jsname = _jsname; |
| 711 return ret; |
| 712 } |
| 713 |
| 714 MethodData get methodData() { |
| 715 if (genericMember !== null) return genericMember.dynamic.methodData; |
| 716 |
| 717 if (_methodData === null) { |
| 718 _methodData = new MethodData(this); |
| 719 } |
| 720 return _methodData; |
| 721 } |
| 722 |
| 786 bool get isConstructor() => name == declaringType.name; | 723 bool get isConstructor() => name == declaringType.name; |
| 787 bool get isMethod() => !isConstructor; | 724 bool get isMethod() => !isConstructor; |
| 788 | 725 |
| 789 bool get isNative() => definition.nativeBody != null; | 726 bool get isNative() => definition.nativeBody != null; |
| 790 | 727 |
| 791 bool get canGet() => true; | 728 bool get canGet() => true; |
| 792 bool get canSet() => false; | 729 bool get canSet() => false; |
| 793 | 730 |
| 794 bool get requiresPropertySyntax() => true; | 731 bool get requiresPropertySyntax() => true; |
| 795 | 732 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 806 if (returnType.names != null) { | 743 if (returnType.names != null) { |
| 807 return returnType.names[0].name; | 744 return returnType.names[0].name; |
| 808 } else if (returnType.name != null) { | 745 } else if (returnType.name != null) { |
| 809 return returnType.name.name; | 746 return returnType.name.name; |
| 810 } | 747 } |
| 811 world.internalError('no valid constructor name', definition.span); | 748 world.internalError('no valid constructor name', definition.span); |
| 812 } | 749 } |
| 813 | 750 |
| 814 Type get functionType() { | 751 Type get functionType() { |
| 815 if (_functionType == null) { | 752 if (_functionType == null) { |
| 816 _functionType = | 753 _functionType = library.getOrAddFunctionType(declaringType, name, |
| 817 library.getOrAddFunctionType(declaringType, name, definition); | 754 definition, methodData); |
| 818 // TODO(jimhug): Better resolution checks. | 755 // TODO(jimhug): Better resolution checks. |
| 819 if (parameters == null) { | 756 if (parameters == null) { |
| 820 resolve(); | 757 resolve(); |
| 821 } | 758 } |
| 822 } | 759 } |
| 823 return _functionType; | 760 return _functionType; |
| 824 } | 761 } |
| 825 | 762 |
| 826 bool override(Member other) { | 763 bool override(Member other) { |
| 827 if (!super.override(other)) return false; | 764 if (!super.override(other)) return false; |
| 828 | 765 |
| 829 // methods can only override other methods | 766 // methods can only override other methods |
| 830 if (other.isMethod) { | 767 if (other.isMethod) { |
| 831 // TODO(jimhug): | 768 // TODO(jimhug): |
| 832 // other.returnType.ensureAssignableFrom(returnType, null, true); | 769 // other.returnType.ensureAssignableFrom(returnType, null, true); |
| 833 // TODO(jimhug): Check for further parameter compatibility. | 770 // TODO(jimhug): Check for further parameter compatibility. |
| 834 return true; | 771 return true; |
| 835 } else { | 772 } else { |
| 836 world.error('method can only override methods', span, other.span); | 773 world.error('method can only override methods', span, other.span); |
| 837 return false; | 774 return false; |
| 838 } | 775 } |
| 839 } | 776 } |
| 840 | 777 |
| 841 bool canInvoke(MethodGenerator context, Arguments args) { | 778 bool canInvoke(CallingContext context, Arguments args) { |
| 842 int bareCount = args.bareCount; | 779 int bareCount = args.bareCount; |
| 843 | 780 |
| 844 if (bareCount > parameters.length) return false; | 781 if (bareCount > parameters.length) return false; |
| 845 | 782 |
| 846 if (bareCount == parameters.length) { | 783 if (bareCount == parameters.length) { |
| 847 if (bareCount != args.length) return false; | 784 if (bareCount != args.length) return false; |
| 848 } else { | 785 } else { |
| 849 if (!parameters[bareCount].isOptional) return false; | 786 if (!parameters[bareCount].isOptional) return false; |
| 850 | 787 |
| 851 for (int i = bareCount; i < args.length; i++) { | 788 for (int i = bareCount; i < args.length; i++) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 863 int indexOfParameter(String name) { | 800 int indexOfParameter(String name) { |
| 864 for (int i = 0; i < parameters.length; i++) { | 801 for (int i = 0; i < parameters.length; i++) { |
| 865 final p = parameters[i]; | 802 final p = parameters[i]; |
| 866 if (p.isOptional && p.name == name) { | 803 if (p.isOptional && p.name == name) { |
| 867 return i; | 804 return i; |
| 868 } | 805 } |
| 869 } | 806 } |
| 870 return -1; | 807 return -1; |
| 871 } | 808 } |
| 872 | 809 |
| 873 void provideFieldSyntax() { _provideFieldSyntax = true; } | |
| 874 void providePropertySyntax() { _providePropertySyntax = true; } | 810 void providePropertySyntax() { _providePropertySyntax = true; } |
| 875 | 811 |
| 876 Value _set(MethodGenerator context, Node node, Value target, Value value, | 812 Value _set(CallingContext context, Node node, Value target, Value value) { |
| 877 [bool isDynamic=false]) { | |
| 878 world.error('cannot set method', node.span); | 813 world.error('cannot set method', node.span); |
| 879 } | 814 } |
| 880 | 815 |
| 881 Value _get(MethodGenerator context, Node node, Value target, | 816 Value _get(CallingContext context, Node node, Value target) { |
| 882 [bool isDynamic=false]) { | 817 if (!context.needsCode) { |
| 818 return new PureStaticValue(functionType, node.span); |
| 819 } |
| 820 |
| 883 // TODO(jimhug): Would prefer to invoke! | 821 // TODO(jimhug): Would prefer to invoke! |
| 884 declaringType.genMethod(this); | 822 declaringType.genMethod(this); |
| 885 _provideOptionalParamInfo = true; | 823 _provideOptionalParamInfo = true; |
| 886 if (isStatic) { | 824 if (isStatic) { |
| 887 // ensure the type is generated. | 825 // ensure the type is generated. |
| 888 // TODO(sigmund): can we avoid generating the entire type, but only what | 826 // TODO(sigmund): can we avoid generating the entire type, but only what |
| 889 // we need? | 827 // we need? |
| 890 declaringType.markUsed(); | 828 declaringType.markUsed(); |
| 891 var type = declaringType.isTop ? '' : '${declaringType.jsname}.'; | 829 var type = declaringType.isTop ? '' : '${declaringType.jsname}.'; |
| 892 return new Value(functionType, '$type$jsname', node.span); | 830 return new Value(functionType, '$type$jsname', node.span); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 937 bool needsArgumentConversion(Arguments args) { | 875 bool needsArgumentConversion(Arguments args) { |
| 938 int bareCount = args.bareCount; | 876 int bareCount = args.bareCount; |
| 939 for (int i = 0; i < bareCount; i++) { | 877 for (int i = 0; i < bareCount; i++) { |
| 940 var arg = args.values[i]; | 878 var arg = args.values[i]; |
| 941 if (arg.needsConversion(parameters[i].type)) { | 879 if (arg.needsConversion(parameters[i].type)) { |
| 942 return true; | 880 return true; |
| 943 } | 881 } |
| 944 } | 882 } |
| 945 | 883 |
| 946 if (bareCount < parameters.length) { | 884 if (bareCount < parameters.length) { |
| 947 genParameterValues(); | |
| 948 for (int i = bareCount; i < parameters.length; i++) { | 885 for (int i = bareCount; i < parameters.length; i++) { |
| 949 var arg = args.getValue(parameters[i].name); | 886 var arg = args.getValue(parameters[i].name); |
| 950 if (arg != null && arg.needsConversion(parameters[i].type)) { | 887 if (arg != null && arg.needsConversion(parameters[i].type)) { |
| 951 return true; | 888 return true; |
| 952 } | 889 } |
| 953 } | 890 } |
| 954 } | 891 } |
| 955 | 892 |
| 956 return false; | 893 return false; |
| 957 } | 894 } |
| 958 | 895 |
| 959 static String _argCountMsg(int actual, int expected, [bool atLeast=false]) { | 896 static String _argCountMsg(int actual, int expected, [bool atLeast=false]) { |
| 960 return 'wrong number of positional arguments, expected ' + | 897 return 'wrong number of positional arguments, expected ' + |
| 961 '${atLeast ? "at least " : ""}$expected but found $actual'; | 898 '${atLeast ? "at least " : ""}$expected but found $actual'; |
| 962 } | 899 } |
| 963 | 900 |
| 964 Value _argError(MethodGenerator context, Node node, Value target, | 901 Value _argError(CallingContext context, Node node, Value target, |
| 965 Arguments args, String msg, int argIndex) { | 902 Arguments args, String msg, int argIndex) { |
| 966 SourceSpan span; | 903 SourceSpan span; |
| 967 if ((args.nodes == null) || (argIndex >= args.nodes.length)) { | 904 if ((args.nodes == null) || (argIndex >= args.nodes.length)) { |
| 968 span = node.span; | 905 span = node.span; |
| 969 } else { | 906 } else { |
| 970 span = args.nodes[argIndex].span; | 907 span = args.nodes[argIndex].span; |
| 971 } | 908 } |
| 972 if (isStatic || isConstructor) { | 909 if (isStatic || isConstructor) { |
| 973 world.error(msg, span); | 910 world.error(msg, span); |
| 974 } else { | 911 } else { |
| 975 world.warning(msg, span); | 912 world.warning(msg, span); |
| 976 } | 913 } |
| 977 return target.invokeNoSuchMethod(context, name, node, args); | 914 return target.invokeNoSuchMethod(context, name, node, args); |
| 978 } | 915 } |
| 979 | 916 |
| 980 genParameterValues() { | 917 genParameterValues(CallingContext context) { |
| 981 // Pure lazy? | 918 // TODO(jimhug): Is this the right context? |
| 982 for (var p in parameters) p.genValue(this, generator); | 919 for (var p in parameters) p.genValue(this, context); |
| 983 } | 920 } |
| 984 | 921 |
| 985 /** | 922 /** |
| 986 * Invokes this method on the given [target] with the given [args]. | 923 * Invokes this method on the given [target] with the given [args]. |
| 987 * [node] provides a [SourceSpan] for any error messages. | 924 * [node] provides a [SourceSpan] for any error messages. |
| 988 */ | 925 */ |
| 989 Value invoke(MethodGenerator context, Node node, Value target, | 926 Value invoke(CallingContext context, Node node, Value target, |
| 990 Arguments args, [bool isDynamic=false]) { | 927 Arguments args) { |
| 991 // TODO(jimhug): Fix this hack for ensuring a method is resolved. | 928 if (!context.needsCode) { |
| 992 if (parameters == null) { | 929 // TODO(jimhug): Add argument type and name checks here. |
| 993 world.info('surprised to need to resolve: ${declaringType.name}.$name'); | 930 return new PureStaticValue(returnType, node.span); |
| 994 resolve(); | |
| 995 } | 931 } |
| 996 | 932 |
| 997 declaringType.genMethod(this); | 933 declaringType.genMethod(this); |
| 998 | 934 |
| 999 if (isStatic || isFactory) { | 935 if (isStatic || isFactory) { |
| 1000 // TODO(sigmund): can we avoid generating the entire type, but only what | 936 // TODO(sigmund): can we avoid generating the entire type, but only what |
| 1001 // we need? | 937 // we need? |
| 1002 declaringType.markUsed(); | 938 declaringType.markUsed(); |
| 1003 } | 939 } |
| 1004 | 940 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1018 argsCode.add('this'); | 954 argsCode.add('this'); |
| 1019 } | 955 } |
| 1020 | 956 |
| 1021 int bareCount = args.bareCount; | 957 int bareCount = args.bareCount; |
| 1022 for (int i = 0; i < bareCount; i++) { | 958 for (int i = 0; i < bareCount; i++) { |
| 1023 var arg = args.values[i]; | 959 var arg = args.values[i]; |
| 1024 if (i >= parameters.length) { | 960 if (i >= parameters.length) { |
| 1025 var msg = _argCountMsg(args.length, parameters.length); | 961 var msg = _argCountMsg(args.length, parameters.length); |
| 1026 return _argError(context, node, target, args, msg, i); | 962 return _argError(context, node, target, args, msg, i); |
| 1027 } | 963 } |
| 1028 arg = arg.convertTo(context, parameters[i].type, isDynamic); | 964 arg = arg.convertTo(context, parameters[i].type); |
| 1029 argsCode.add(arg.code); | 965 argsCode.add(arg.code); |
| 1030 } | 966 } |
| 1031 | 967 |
| 1032 int namedArgsUsed = 0; | 968 int namedArgsUsed = 0; |
| 1033 if (bareCount < parameters.length) { | 969 if (bareCount < parameters.length) { |
| 1034 genParameterValues(); | 970 genParameterValues(context); |
| 1035 | 971 |
| 1036 for (int i = bareCount; i < parameters.length; i++) { | 972 for (int i = bareCount; i < parameters.length; i++) { |
| 1037 var arg = args.getValue(parameters[i].name); | 973 var arg = args.getValue(parameters[i].name); |
| 1038 if (arg == null) { | 974 if (arg == null) { |
| 1039 arg = parameters[i].value; | 975 arg = parameters[i].value; |
| 1040 } else { | 976 } else { |
| 1041 arg = arg.convertTo(context, parameters[i].type, isDynamic); | 977 arg = arg.convertTo(context, parameters[i].type); |
| 1042 namedArgsUsed++; | 978 namedArgsUsed++; |
| 1043 } | 979 } |
| 1044 | 980 |
| 1045 if (arg == null || !parameters[i].isOptional) { | 981 if (arg == null || !parameters[i].isOptional) { |
| 1046 var msg = _argCountMsg(Math.min(i, args.length), i + 1, atLeast:true); | 982 var msg = _argCountMsg(Math.min(i, args.length), i + 1, atLeast:true); |
| 1047 return _argError(context, node, target, args, msg, i); | 983 return _argError(context, node, target, args, msg, i); |
| 1048 } else { | 984 } else { |
| 1049 argsCode.add(arg.code); | 985 argsCode.add(arg.code); |
| 1050 } | 986 } |
| 1051 } | 987 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1083 return _invokeConstructor(context, node, target, args, argsString); | 1019 return _invokeConstructor(context, node, target, args, argsString); |
| 1084 } | 1020 } |
| 1085 | 1021 |
| 1086 if (target.isSuper) { | 1022 if (target.isSuper) { |
| 1087 return new Value(inferredResult, | 1023 return new Value(inferredResult, |
| 1088 '${declaringType.jsname}.prototype.$jsname.call($argsString)', | 1024 '${declaringType.jsname}.prototype.$jsname.call($argsString)', |
| 1089 node.span); | 1025 node.span); |
| 1090 } | 1026 } |
| 1091 | 1027 |
| 1092 if (isOperator) { | 1028 if (isOperator) { |
| 1093 return _invokeBuiltin(context, node, target, args, argsCode, isDynamic); | 1029 return _invokeBuiltin(context, node, target, args, argsCode); |
| 1094 } | 1030 } |
| 1095 | 1031 |
| 1096 if (isFactory) { | 1032 if (isFactory) { |
| 1097 assert(target.isType); | 1033 assert(target.isType); |
| 1098 return new Value(target.type, '$generatedFactoryName($argsString)', | 1034 return new Value(target.type, '$generatedFactoryName($argsString)', |
| 1099 node !== null ? node.span : null); | 1035 node !== null ? node.span : null); |
| 1100 } | 1036 } |
| 1101 | 1037 |
| 1102 if (isStatic) { | 1038 if (isStatic) { |
| 1103 if (declaringType.isTop) { | 1039 if (declaringType.isTop) { |
| 1104 return new Value(inferredResult, | 1040 return new Value(inferredResult, |
| 1105 '$jsname($argsString)', node !== null ? node.span : null); | 1041 '$jsname($argsString)', node !== null ? node.span : null); |
| 1106 } | 1042 } |
| 1107 return new Value(inferredResult, | 1043 return new Value(inferredResult, |
| 1108 '${declaringType.jsname}.$jsname($argsString)', node.span); | 1044 '${declaringType.jsname}.$jsname($argsString)', node.span); |
| 1109 } | 1045 } |
| 1110 | 1046 |
| 1111 // TODO(jmesserly): factor this better | 1047 // TODO(jmesserly): factor this better |
| 1112 if (name == 'get:typeName' && declaringType.library.isDom) { | 1048 if (name == 'get:typeName' && declaringType.library.isDom) { |
| 1113 world.gen.corejs.ensureTypeNameOf(); | 1049 world.gen.corejs.ensureTypeNameOf(); |
| 1114 } | 1050 } |
| 1115 | 1051 |
| 1116 var code = '${target.code}.$jsname($argsString)'; | 1052 var code = '${target.code}.$jsname($argsString)'; |
| 1117 return new Value(inferredResult, code, node.span); | 1053 return new Value(inferredResult, code, node.span); |
| 1118 } | 1054 } |
| 1119 | 1055 |
| 1120 Value _invokeConstructor(MethodGenerator context, Node node, | 1056 Value _invokeConstructor(CallingContext context, Node node, |
| 1121 Value target, Arguments args, argsString) { | 1057 Value target, Arguments args, argsString) { |
| 1122 declaringType.markUsed(); | 1058 declaringType.markUsed(); |
| 1123 | 1059 |
| 1124 String ctor = constructorName; | 1060 String ctor = constructorName; |
| 1125 if (ctor != '') ctor = '.${ctor}\$ctor'; | 1061 if (ctor != '') ctor = '.${ctor}\$ctor'; |
| 1126 | 1062 |
| 1127 final span = node != null ? node.span : target.span; | 1063 final span = node != null ? node.span : target.span; |
| 1128 if (!target.isType) { | 1064 if (!target.isType) { |
| 1129 // initializer call to another constructor | 1065 // initializer call to another constructor |
| 1130 var code = '${declaringType.nativeName}${ctor}.call($argsString)'; | 1066 var code = '${declaringType.nativeName}${ctor}.call($argsString)'; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1148 return world.gen.globalForConst(newObject, [args.values]); | 1084 return world.gen.globalForConst(newObject, [args.values]); |
| 1149 } else { | 1085 } else { |
| 1150 var code = 'new ${declaringType.nativeName}${ctor}($argsString)'; | 1086 var code = 'new ${declaringType.nativeName}${ctor}($argsString)'; |
| 1151 return new Value(target.type, code, span); | 1087 return new Value(target.type, code, span); |
| 1152 } | 1088 } |
| 1153 } | 1089 } |
| 1154 } | 1090 } |
| 1155 | 1091 |
| 1156 _evalConstConstructor(Value newObject, Arguments args) { | 1092 _evalConstConstructor(Value newObject, Arguments args) { |
| 1157 declaringType.markUsed(); | 1093 declaringType.markUsed(); |
| 1158 var generator = new MethodGenerator(this, null); | 1094 methodData.eval(this, newObject, args); |
| 1159 generator.evalBody(newObject, args); | |
| 1160 } | 1095 } |
| 1161 | 1096 |
| 1162 Value _invokeBuiltin(MethodGenerator context, Node node, Value target, | 1097 Value _invokeBuiltin(CallingContext context, Node node, Value target, |
| 1163 Arguments args, argsCode, bool isDynamic) { | 1098 Arguments args, argsCode) { |
| 1164 // Handle some fast paths for Number, String, List and DOM. | 1099 // Handle some fast paths for Number, String, List and DOM. |
| 1165 if (declaringType.isNum) { | 1100 if (target.type.isNum) { |
| 1166 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num. | 1101 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num. |
| 1167 // TODO(jimhug): What about null? | 1102 // TODO(jimhug): What about null? |
| 1168 var code; | 1103 var code = null; |
| 1169 if (name == ':negate') { | 1104 if (args.length == 0) { |
| 1170 code = '-${target.code}'; | 1105 if (name == ':negate') { |
| 1171 } else if (name == ':bit_not') { | 1106 code = '-${target.code}'; |
| 1172 code = '~${target.code}'; | 1107 } else if (name == ':bit_not') { |
| 1173 } else if (name == ':truncdiv' || name == ':mod') { | 1108 code = '~${target.code}'; |
| 1174 world.gen.corejs.useOperator(name); | 1109 } |
| 1175 code = '$jsname(${target.code}, ${argsCode[0]})'; | 1110 } else if (args.length == 1 && args.values[0].type.isNum) { |
| 1176 } else { | 1111 if (name == ':truncdiv' || name == ':mod') { |
| 1177 var op = TokenKind.rawOperatorFromMethod(name); | 1112 world.gen.corejs.useOperator(name); |
| 1178 code = '${target.code} $op ${argsCode[0]}'; | 1113 code = '$jsname(${target.code}, ${argsCode[0]})'; |
| 1114 } else { |
| 1115 var op = TokenKind.rawOperatorFromMethod(name); |
| 1116 code = '${target.code} $op ${argsCode[0]}'; |
| 1117 } |
| 1179 } | 1118 } |
| 1180 | 1119 if (code !== null) { |
| 1181 return new Value(inferredResult, code, node.span); | 1120 return new Value(inferredResult, code, node.span); |
| 1182 } else if (declaringType.isString) { | 1121 } |
| 1183 if (name == ':index') { | 1122 } else if (target.type.isString) { |
| 1123 if (name == ':index' && args.values[0].type.isNum) { |
| 1184 return new Value(declaringType, '${target.code}[${argsCode[0]}]', | 1124 return new Value(declaringType, '${target.code}[${argsCode[0]}]', |
| 1185 node.span); | 1125 node.span); |
| 1186 } else if (name == ':add') { | 1126 } else if (name == ':add' && args.values[0].type.isNum) { |
| 1187 return new Value(declaringType, '${target.code} + ${argsCode[0]}', | 1127 return new Value(declaringType, '${target.code} + ${argsCode[0]}', |
| 1188 node.span); | 1128 node.span); |
| 1189 } | 1129 } |
| 1190 } else if (declaringType.isNative) { | 1130 } else if (declaringType.isNative) { |
| 1191 if (name == ':index') { | 1131 if (args.length > 0 && args.values[0].type.isNum) { |
| 1192 return | 1132 // TODO(jimhug): make more accurate/reliable |
| 1193 new Value(returnType, '${target.code}[${argsCode[0]}]', node.span); | 1133 if (name == ':index') { |
| 1194 } else if (name == ':setindex') { | 1134 return |
| 1195 return new Value(returnType, | 1135 new Value(returnType, '${target.code}[${argsCode[0]}]', node.span)
; |
| 1196 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span); | 1136 } else if (name == ':setindex') { |
| 1137 return new Value(returnType, |
| 1138 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span); |
| 1139 } |
| 1197 } | 1140 } |
| 1198 } | 1141 } |
| 1199 | 1142 |
| 1200 // TODO(jimhug): Optimize null on lhs as well. | 1143 // TODO(jimhug): Optimize null on lhs as well. |
| 1201 if (name == ':eq' || name == ':ne') { | 1144 if (name == ':eq' || name == ':ne') { |
| 1202 final op = name == ':eq' ? '==' : '!='; | 1145 final op = name == ':eq' ? '==' : '!='; |
| 1203 | 1146 |
| 1204 if (name == ':ne') { | 1147 if (name == ':ne') { |
| 1205 // Ensure == is generated. | 1148 // Ensure == is generated. |
| 1206 target.invoke(context, ':eq', node, args, isDynamic); | 1149 target.invoke(context, ':eq', node, args); |
| 1207 } | 1150 } |
| 1208 | 1151 |
| 1209 // Optimize test when null is on the rhs. | 1152 // Optimize test when null is on the rhs. |
| 1210 if (argsCode[0] == 'null') { | 1153 if (argsCode[0] == 'null') { |
| 1211 return new Value(inferredResult, '${target.code} $op null', node.span); | 1154 return new Value(inferredResult, '${target.code} $op null', node.span); |
| 1212 } else if (target.type.isNum || target.type.isString) { | 1155 } else if (target.type.isNum || target.type.isString) { |
| 1213 // TODO(jimhug): Maybe check rhs. | 1156 // TODO(jimhug): Maybe check rhs. |
| 1214 return new Value(inferredResult, '${target.code} $op ${argsCode[0]}', | 1157 return new Value(inferredResult, '${target.code} $op ${argsCode[0]}', |
| 1215 node.span); | 1158 node.span); |
| 1216 } | 1159 } |
| 1217 world.gen.corejs.useOperator(name); | 1160 world.gen.corejs.useOperator(name); |
| 1218 // TODO(jimhug): Should be able to use faster path sometimes here! | 1161 // TODO(jimhug): Should be able to use faster path sometimes here! |
| 1219 return new Value(inferredResult, | 1162 return new Value(inferredResult, |
| 1220 '$jsname(${target.code}, ${argsCode[0]})', node.span); | 1163 '$jsname(${target.code}, ${argsCode[0]})', node.span); |
| 1221 } | 1164 } |
| 1222 | 1165 |
| 1223 if (isCallMethod) { | 1166 if (isCallMethod) { |
| 1224 declaringType.markUsed(); | 1167 declaringType.markUsed(); |
| 1225 return new Value(inferredResult, | 1168 return new Value(inferredResult, |
| 1226 '${target.code}(${Strings.join(argsCode, ", ")})', node.span); | 1169 '${target.code}(${Strings.join(argsCode, ", ")})', node.span); |
| 1227 } | 1170 } |
| 1228 | 1171 |
| 1172 // TODO(jimhug): Reconcile with MethodSet version - ideally just eliminate |
| 1229 if (name == ':index') { | 1173 if (name == ':index') { |
| 1230 world.gen.corejs.useIndex = true; | 1174 world.gen.corejs.useIndex = true; |
| 1231 } else if (name == ':setindex') { | 1175 } else if (name == ':setindex') { |
| 1232 world.gen.corejs.useSetIndex = true; | 1176 world.gen.corejs.useSetIndex = true; |
| 1177 } else { |
| 1178 world.gen.corejs.useOperator(name); |
| 1179 var argsString = argsCode.length == 0 ? '' : ', ${argsCode[0]}'; |
| 1180 return new Value(returnType, '$jsname(${target.code}${argsString})', |
| 1181 node.span); |
| 1233 } | 1182 } |
| 1234 | 1183 |
| 1235 // Fall back to normal method invocation. | 1184 // Fall back to normal method invocation. |
| 1236 var argsString = Strings.join(argsCode, ', '); | 1185 var argsString = Strings.join(argsCode, ', '); |
| 1237 return new Value(inferredResult, '${target.code}.$jsname($argsString)', | 1186 return new Value(inferredResult, '${target.code}.$jsname($argsString)', |
| 1238 node.span); | 1187 node.span); |
| 1239 } | 1188 } |
| 1240 | 1189 |
| 1241 resolve() { | 1190 resolve() { |
| 1242 // TODO(jimhug): work through side-by-side with spec | 1191 // TODO(jimhug): work through side-by-side with spec |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1309 } else { | 1258 } else { |
| 1310 if (definition.body == null && !isConstructor && !isNative) { | 1259 if (definition.body == null && !isConstructor && !isNative) { |
| 1311 world.error('method needs a body', span); | 1260 world.error('method needs a body', span); |
| 1312 } | 1261 } |
| 1313 } | 1262 } |
| 1314 | 1263 |
| 1315 if (isConstructor && !isFactory) { | 1264 if (isConstructor && !isFactory) { |
| 1316 returnType = declaringType; | 1265 returnType = declaringType; |
| 1317 } else { | 1266 } else { |
| 1318 // This is the one and only place we allow void. | 1267 // This is the one and only place we allow void. |
| 1319 returnType = resolveType(definition.returnType, false, allowVoid: true); | 1268 if (definition.returnType is SimpleTypeReference && |
| 1269 definition.returnType.dynamic.type == world.voidType) { |
| 1270 returnType = world.voidType; |
| 1271 } else { |
| 1272 returnType = resolveType(definition.returnType, false, !isStatic); |
| 1273 } |
| 1320 } | 1274 } |
| 1321 parameters = []; | 1275 parameters = []; |
| 1322 for (var formal in definition.formals) { | 1276 for (var formal in definition.formals) { |
| 1323 // TODO(jimhug): Clean up construction of Parameters. | 1277 // TODO(jimhug): Clean up construction of Parameters. |
| 1324 var param = new Parameter(formal, this); | 1278 var param = new Parameter(formal, this); |
| 1325 param.resolve(); | 1279 param.resolve(); |
| 1326 parameters.add(param); | 1280 parameters.add(param); |
| 1327 } | 1281 } |
| 1328 | 1282 |
| 1329 if (!isLambda) { | 1283 if (!isLambda && declaringType.isClass) { |
| 1330 library._addMember(this); | 1284 library._addMember(this); |
| 1331 } | 1285 } |
| 1332 } | 1286 } |
| 1333 | |
| 1334 /** Overriden to ensure that type arguments aren't used in static methods. */ | |
| 1335 Type resolveType(TypeReference node, bool typeErrors, | |
| 1336 [bool allowVoid = false]) { | |
| 1337 Type t = super.resolveType(node, typeErrors); | |
| 1338 if (isStatic && !isFactory && t is ParameterType) { | |
| 1339 world.error('using type parameter in static context.', node.span); | |
| 1340 } | |
| 1341 if (!allowVoid && t.isVoid) { | |
| 1342 world.error('"void" only allowed as return type', node.span); | |
| 1343 } | |
| 1344 return t; | |
| 1345 } | |
| 1346 } | 1287 } |
| 1347 | 1288 |
| 1348 | 1289 |
| 1349 class MemberSet { | |
| 1350 final String name; | |
| 1351 final List<Member> members; | |
| 1352 final String jsname; | |
| 1353 final bool isVar; | |
| 1354 | |
| 1355 MemberSet(Member member, [bool isVar=false]): | |
| 1356 name = member.name, members = [member], jsname = member.jsname, | |
| 1357 isVar = isVar; | |
| 1358 | |
| 1359 toString() => '$name:${members.length}'; | |
| 1360 | |
| 1361 // TODO(jimhug): Still working towards the right logic for conflicts... | |
| 1362 bool get containsProperties() => members.some((m) => m is PropertyMember); | |
| 1363 bool get containsMethods() => members.some((m) => m is MethodMember); | |
| 1364 | |
| 1365 | |
| 1366 void add(Member member) => members.add(member); | |
| 1367 | |
| 1368 // TODO(jimhug): Always false, or is this needed? | |
| 1369 bool get isStatic() => members.length == 1 && members[0].isStatic; | |
| 1370 bool get isOperator() => members[0].isOperator; | |
| 1371 | |
| 1372 bool canInvoke(MethodGenerator context, Arguments args) => | |
| 1373 members.some((m) => m.canInvoke(context, args)); | |
| 1374 | |
| 1375 Value _makeError(Node node, Value target, String action) { | |
| 1376 if (!target.type.isVar) { | |
| 1377 world.warning('could not find applicable $action for "$name"', node.span); | |
| 1378 } | |
| 1379 return new Value(world.varType, | |
| 1380 '${target.code}.$jsname() /*no applicable $action*/', node.span); | |
| 1381 } | |
| 1382 | |
| 1383 bool _treatAsField; | |
| 1384 bool get treatAsField() { | |
| 1385 if (_treatAsField == null) { | |
| 1386 // If this is the global MemberSet from world, always bind dynamically. | |
| 1387 // Note: we need this for proper noSuchMethod and REPL behavior. | |
| 1388 _treatAsField = !isVar && (members.some((m) => m.requiresFieldSyntax) | |
| 1389 || members.every((m) => !m.requiresPropertySyntax)); | |
| 1390 | |
| 1391 for (var member in members) { | |
| 1392 if (_treatAsField) { | |
| 1393 member.provideFieldSyntax(); | |
| 1394 } else { | |
| 1395 member.providePropertySyntax(); | |
| 1396 } | |
| 1397 } | |
| 1398 } | |
| 1399 return _treatAsField; | |
| 1400 } | |
| 1401 | |
| 1402 Value _get(MethodGenerator context, Node node, Value target, | |
| 1403 [bool isDynamic=false]) { | |
| 1404 // If this is the global MemberSet from world, always bind dynamically. | |
| 1405 // Note: we need this for proper noSuchMethod and REPL behavior. | |
| 1406 Value returnValue; | |
| 1407 if (members.length == 1 && !isVar) { | |
| 1408 return members[0]._get(context, node, target, isDynamic); | |
| 1409 } | |
| 1410 | |
| 1411 | |
| 1412 final targets = members.filter((m) => m.canGet); | |
| 1413 if (isVar) { | |
| 1414 targets.forEach((m) => m._get(context, node, target, isDynamic: true)); | |
| 1415 returnValue = new Value(_foldTypes(targets), null, node.span); | |
| 1416 } else { | |
| 1417 if (members.length == 1) { | |
| 1418 return members[0]._get(context, node, target, isDynamic); | |
| 1419 } else if (targets.length == 1) { | |
| 1420 return targets[0]._get(context, node, target, isDynamic); | |
| 1421 } | |
| 1422 | |
| 1423 for (var member in targets) { | |
| 1424 final value = member._get(context, node, target, isDynamic:true); | |
| 1425 returnValue = _tryUnion(returnValue, value, node); | |
| 1426 } | |
| 1427 if (returnValue == null) { | |
| 1428 return _makeError(node, target, 'getter'); | |
| 1429 } | |
| 1430 } | |
| 1431 | |
| 1432 if (returnValue.code == null) { | |
| 1433 if (treatAsField) { | |
| 1434 return new Value(returnValue.type, '${target.code}.$jsname', | |
| 1435 node.span); | |
| 1436 } else { | |
| 1437 return new Value(returnValue.type, '${target.code}.get\$$jsname()', | |
| 1438 node.span); | |
| 1439 } | |
| 1440 } | |
| 1441 return returnValue; | |
| 1442 } | |
| 1443 | |
| 1444 Value _set(MethodGenerator context, Node node, Value target, Value value, | |
| 1445 [bool isDynamic=false]) { | |
| 1446 // If this is the global MemberSet from world, always bind dynamically. | |
| 1447 // Note: we need this for proper noSuchMethod and REPL behavior. | |
| 1448 if (members.length == 1 && !isVar) { | |
| 1449 return members[0]._set(context, node, target, value, isDynamic); | |
| 1450 } | |
| 1451 | |
| 1452 Value returnValue; | |
| 1453 final targets = members.filter((m) => m.canSet); | |
| 1454 if (isVar) { | |
| 1455 targets.forEach((m) => | |
| 1456 m._set(context, node, target, value, isDynamic: true)); | |
| 1457 returnValue = new Value(_foldTypes(targets), null, node.span); | |
| 1458 } else { | |
| 1459 if (members.length == 1) { | |
| 1460 return members[0]._set(context, node, target, value, isDynamic); | |
| 1461 } else if (targets.length == 1) { | |
| 1462 return targets[0]._set(context, node, target, value, isDynamic); | |
| 1463 } | |
| 1464 | |
| 1465 for (var member in targets) { | |
| 1466 final res = member._set(context, node, target, value, isDynamic:true); | |
| 1467 returnValue = _tryUnion(returnValue, res, node); | |
| 1468 } | |
| 1469 if (returnValue == null) { | |
| 1470 return _makeError(node, target, 'setter'); | |
| 1471 } | |
| 1472 } | |
| 1473 | |
| 1474 if (returnValue.code == null) { | |
| 1475 if (treatAsField) { | |
| 1476 return new Value(returnValue.type, | |
| 1477 '${target.code}.$jsname = ${value.code}', node.span); | |
| 1478 } else { | |
| 1479 return new Value(returnValue.type, | |
| 1480 '${target.code}.set\$$jsname(${value.code})', node.span); | |
| 1481 } | |
| 1482 } | |
| 1483 return returnValue; | |
| 1484 } | |
| 1485 | |
| 1486 Value invoke(MethodGenerator context, Node node, Value target, | |
| 1487 Arguments args, [bool isDynamic=false]) { | |
| 1488 // If this is the global MemberSet from world, always bind dynamically. | |
| 1489 // Note: we need this for proper noSuchMethod and REPL behavior. | |
| 1490 if (isVar && !isOperator) { | |
| 1491 return invokeOnVar(context, node, target, args); | |
| 1492 } | |
| 1493 | |
| 1494 if (members.length == 1 && !isVar) { | |
| 1495 return members[0].invoke(context, node, target, args, isDynamic); | |
| 1496 } | |
| 1497 | |
| 1498 final targets = members.filter((m) => m.canInvoke(context, args)); | |
| 1499 if (targets.length == 1) { | |
| 1500 return targets[0].invoke(context, node, target, args, isDynamic); | |
| 1501 } | |
| 1502 | |
| 1503 Value returnValue = null; | |
| 1504 if (targets.length < 1000) { | |
| 1505 for (var member in targets) { | |
| 1506 final res = member.invoke(context, node, target, args, isDynamic:true); | |
| 1507 // TODO(jmesserly): If the code has different type checks, it will fail
to | |
| 1508 // unify and go through a dynamic stub. Good so far. However, we'll end | |
| 1509 // up with a bogus unused temp generated (usually "var $0"). We need a w
ay | |
| 1510 // to throw away temps when we throw away the code. | |
| 1511 returnValue = _tryUnion(returnValue, res, node); | |
| 1512 } | |
| 1513 | |
| 1514 if (returnValue == null) { | |
| 1515 return _makeError(node, target, 'method'); | |
| 1516 } | |
| 1517 } else { | |
| 1518 returnValue = new Value(world.varType, null, node.span); | |
| 1519 } | |
| 1520 | |
| 1521 if (returnValue.code == null) { | |
| 1522 if (name == ':call') { | |
| 1523 // TODO(jmesserly): reconcile this with similar code in Value | |
| 1524 return target._varCall(context, node, args); | |
| 1525 } else if (isOperator) { | |
| 1526 // TODO(jmesserly): make operators less special. | |
| 1527 return invokeSpecial(target, args, returnValue.type); | |
| 1528 } else { | |
| 1529 return invokeOnVar(context, node, target, args); | |
| 1530 } | |
| 1531 } | |
| 1532 | |
| 1533 return returnValue; | |
| 1534 } | |
| 1535 | |
| 1536 Value invokeSpecial(Value target, Arguments args, Type returnType) { | |
| 1537 assert(name.startsWith(':')); | |
| 1538 assert(!args.hasNames); | |
| 1539 // TODO(jimhug): We need to do this a little bit more like get and set on | |
| 1540 // properties. We should check the set of members for something | |
| 1541 // like "requiresNativeIndexer" and "requiresDartIndexer" to | |
| 1542 // decide on a strategy. | |
| 1543 | |
| 1544 var argsString = args.getCode(); | |
| 1545 // Most operator calls need to be emitted as function calls, so we don't | |
| 1546 // box numbers accidentally. Indexing is the exception. | |
| 1547 if (name == ':index' || name == ':setindex') { | |
| 1548 // TODO(jimhug): should not need this test both here and in invoke | |
| 1549 if (name == ':index') { | |
| 1550 world.gen.corejs.useIndex = true; | |
| 1551 } else if (name == ':setindex') { | |
| 1552 world.gen.corejs.useSetIndex = true; | |
| 1553 } | |
| 1554 return new Value(returnType, '${target.code}.$jsname($argsString)', | |
| 1555 target.span); | |
| 1556 } else { | |
| 1557 if (argsString.length > 0) argsString = ', $argsString'; | |
| 1558 world.gen.corejs.useOperator(name); | |
| 1559 return new Value(returnType, '$jsname(${target.code}$argsString)', | |
| 1560 target.span); | |
| 1561 } | |
| 1562 } | |
| 1563 | |
| 1564 Value invokeOnVar(MethodGenerator context, Node node, Value target, | |
| 1565 Arguments args) { | |
| 1566 context.counters.dynamicMethodCalls++; | |
| 1567 var member = getVarMember(context, node, args); | |
| 1568 return member.invoke(context, node, target, args); | |
| 1569 } | |
| 1570 | |
| 1571 Value _union(Value x, Value y, Node node) { | |
| 1572 var result = _tryUnion(x, y, node); | |
| 1573 if (result.code == null) { | |
| 1574 world.internalError('mismatched code for $name (${x.code}, ${y.code})', | |
| 1575 node.span); | |
| 1576 } | |
| 1577 return result; | |
| 1578 } | |
| 1579 | |
| 1580 // TODO(jimhug): This is icky - but this whole class needs cleanup. | |
| 1581 Value _tryUnion(Value x, Value y, Node node) { | |
| 1582 if (x == null) return y; | |
| 1583 var type = Type.union(x.type, y.type); | |
| 1584 if (x.code == y.code) { | |
| 1585 if (type == x.type) { | |
| 1586 return x; | |
| 1587 } else if (x.isConst || y.isConst) { | |
| 1588 world.internalError("unexpected: union of const values "); | |
| 1589 } else { | |
| 1590 return Value.union(x, y); | |
| 1591 } | |
| 1592 } else { | |
| 1593 return new Value(type, null, node.span); | |
| 1594 } | |
| 1595 } | |
| 1596 | |
| 1597 dumpAllMembers() { | |
| 1598 for (var member in members) { | |
| 1599 world.warning('hard-multi $name on ${member.declaringType.name}', | |
| 1600 member.span); | |
| 1601 } | |
| 1602 } | |
| 1603 | |
| 1604 VarMember getVarMember(MethodGenerator context, Node node, Arguments args) { | |
| 1605 if (world.objectType.varStubs == null) { | |
| 1606 world.objectType.varStubs = {}; | |
| 1607 } | |
| 1608 | |
| 1609 var stubName = _getCallStubName(name, args); | |
| 1610 var stub = world.objectType.varStubs[stubName]; | |
| 1611 if (stub == null) { | |
| 1612 // Ensure that we're making stub with all possible members of this name. | |
| 1613 // We need this canonicalization step because only one VarMemberSet can | |
| 1614 // live on Object.prototype | |
| 1615 // TODO(jmesserly): this is ugly--we're throwing away type information! | |
| 1616 // The right solution is twofold: | |
| 1617 // 1. put stubs on a more precise type when possible | |
| 1618 // 2. merge VarMemberSets together if necessary | |
| 1619 final mset = context.findMembers(name).members; | |
| 1620 | |
| 1621 final targets = mset.filter((m) => m.canInvoke(context, args)); | |
| 1622 stub = new VarMethodSet(name, stubName, targets, args, | |
| 1623 _foldTypes(targets)); | |
| 1624 world.objectType.varStubs[stubName] = stub; | |
| 1625 } | |
| 1626 return stub; | |
| 1627 } | |
| 1628 | |
| 1629 Type _foldTypes(List<Member> targets) => | |
| 1630 reduce(map(targets, (t) => t.returnType), Type.union, world.varType); | |
| 1631 } | |
| 1632 | |
| 1633 /** | 1290 /** |
| 1634 * A [FactoryMap] maps type names to a list of factory constructors. | 1291 * A [FactoryMap] maps type names to a list of factory constructors. |
| 1635 * The constructors list is actually a map that maps factory names to | 1292 * The constructors list is actually a map that maps factory names to |
| 1636 * [MethodMember]. The reason why we need both indirections are: | 1293 * [MethodMember]. The reason why we need both indirections are: |
| 1637 * 1) A class can define factory methods for multiple interfaces. | 1294 * 1) A class can define factory methods for multiple interfaces. |
| 1638 * 2) A factory constructor can have a name. | 1295 * 2) A factory constructor can have a name. |
| 1639 * | 1296 * |
| 1640 * For example: | 1297 * For example: |
| 1641 * | 1298 * |
| 1642 * [: | 1299 * [: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1688 } | 1345 } |
| 1689 | 1346 |
| 1690 void forEach(void f(Member member)) { | 1347 void forEach(void f(Member member)) { |
| 1691 factories.forEach((_, Map constructors) { | 1348 factories.forEach((_, Map constructors) { |
| 1692 constructors.forEach((_, Member member) { | 1349 constructors.forEach((_, Member member) { |
| 1693 f(member); | 1350 f(member); |
| 1694 }); | 1351 }); |
| 1695 }); | 1352 }); |
| 1696 } | 1353 } |
| 1697 } | 1354 } |
| OLD | NEW |