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 |