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 /** | 5 /** |
6 * Top level generator object for writing code and keeping track of | 6 * Top level generator object for writing code and keeping track of |
7 * dependencies. | 7 * dependencies. |
8 * | 8 * |
9 * Should have two compilation models, but only one implemented so far. | 9 * Should have two compilation models, but only one implemented so far. |
10 * | 10 * |
11 * 1. Do a top-level resolution of all types and their members. | 11 * 1. Do a top-level resolution of all types and their members. |
12 * 2. Start from main and walk the call-graph compiling members as needed. | 12 * 2. Start from main and walk the call-graph compiling members as needed. |
13 * 2a. That includes compiling overriding methods and calling methods by | 13 * 2a. That includes compiling overriding methods and calling methods by |
14 * selector when invoked on var. | 14 * selector when invoked on var. |
15 * 3. Spit out all required code. | 15 * 3. Spit out all required code. |
16 */ | 16 */ |
17 class WorldGenerator { | 17 class WorldGenerator { |
18 MethodMember main; | 18 MethodMember main; |
19 CodeWriter writer; | 19 CodeWriter writer; |
20 CodeWriter _mixins; | 20 CodeWriter _mixins; |
21 | 21 |
22 CallingContext mainContext; | |
23 | |
22 /** | 24 /** |
23 * Whether the app has any static fields used. Note this could still be true | 25 * Whether the app has any static fields used. Note this could still be true |
24 * and [globals] be empty if no static field has a default initialization. | 26 * and [globals] be empty if no static field has a default initialization. |
25 */ | 27 */ |
26 bool hasStatics = false; | 28 bool hasStatics = false; |
27 | 29 |
28 /** Global const and static field initializations. */ | 30 /** Global const and static field initializations. */ |
29 Map<String, GlobalValue> globals; | 31 Map<String, GlobalValue> globals; |
30 CoreJs corejs; | 32 CoreJs corejs; |
31 | 33 |
32 /** */ | 34 /** */ |
33 Set<Type> typesWithDynamicDispatch; | 35 Set<Type> typesWithDynamicDispatch; |
34 | 36 |
35 WorldGenerator(this.main, this.writer) | 37 WorldGenerator(this.main, this.writer) |
36 : globals = {}, corejs = new CoreJs(); | 38 : globals = {}, corejs = new CoreJs(); |
37 | 39 |
40 analyze() { | |
41 // Walk all code and find all NewExpressions - to determine possible types | |
42 int nlibs=0, ntypes=0, nmems=0, nnews=0; | |
43 //Set<Type> newedTypes = new Set<Type>(); | |
44 for (var lib in world.libraries.getValues()) { | |
45 nlibs += 1; | |
46 for (var type in lib.types.getValues()) { | |
47 ntypes += 1; | |
48 var allMembers = []; | |
49 allMembers.addAll(type.constructors.getValues()); | |
50 allMembers.addAll(type.members.getValues()); | |
51 type.factories.forEach((f) => allMembers.add(f)); | |
52 for (var m in allMembers) { | |
53 if (m.isAbstract || !m.isMethod) continue; | |
54 | |
55 m.methodData.analyze(); | |
56 } | |
57 } | |
58 } | |
59 } | |
60 | |
38 run() { | 61 run() { |
39 var metaGen = new MethodGenerator(main, null); | 62 mainContext = new MethodGenerator(main, null); |
40 var mainTarget = new TypeValue(main.declaringType, main.span); | 63 var mainTarget = new TypeValue(main.declaringType, main.span); |
41 var mainCall = main.invoke(metaGen, null, mainTarget, Arguments.EMPTY); | 64 var mainCall = main.invoke(mainContext, null, mainTarget, Arguments.EMPTY); |
42 main.declaringType.markUsed(); | 65 main.declaringType.markUsed(); |
43 | 66 |
44 if (options.compileAll) { | 67 if (options.compileAll) { |
45 markLibrariesUsed( | 68 markLibrariesUsed( |
46 [world.coreimpl, world.corelib, main.declaringType.library]); | 69 [world.coreimpl, world.corelib, main.declaringType.library]); |
47 } | 70 } |
48 | 71 |
49 // These are essentially always used through literals - just include them | 72 // These are essentially always used through literals - just include them |
50 world.numImplType.markUsed(); | 73 world.numImplType.markUsed(); |
51 world.stringImplType.markUsed(); | 74 world.stringImplType.markUsed(); |
52 | 75 |
53 // Only include isolate-specific code if isolates are used. | 76 // Only include isolate-specific code if isolates are used. |
54 if (world.corelib.types['Isolate'].isUsed | 77 if (world.corelib.types['Isolate'].isUsed |
55 || world.coreimpl.types['ReceivePortImpl'].isUsed) { | 78 || world.coreimpl.types['ReceivePortImpl'].isUsed) { |
56 | 79 |
57 // Generate callbacks from JS to isolate code if needed | 80 // Generate callbacks from JS to isolate code if needed |
58 if (corejs.useWrap0 || corejs.useWrap1) { | 81 if (corejs.useWrap0 || corejs.useWrap1) { |
59 genMethod(world.coreimpl.types['IsolateContext'].getMember('eval')); | 82 genMethod(world.coreimpl.types['IsolateContext'].getMember('eval')); |
60 genMethod(world.coreimpl.types['EventLoop'].getMember('run')); | 83 genMethod(world.coreimpl.types['EventLoop'].getMember('run')); |
61 } | 84 } |
62 | 85 |
63 corejs.useIsolates = true; | 86 corejs.useIsolates = true; |
64 MethodMember isolateMain = | 87 MethodMember isolateMain = |
65 world.coreimpl.lookup('startRootIsolate', main.span); | 88 world.coreimpl.lookup('startRootIsolate', main.span); |
66 var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); | 89 var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); |
67 mainCall = isolateMain.invoke(metaGen, null, isolateMainTarget, | 90 mainCall = isolateMain.invoke(mainContext, null, isolateMainTarget, |
68 new Arguments(null, [main._get(metaGen, main.definition, null)])); | 91 new Arguments(null, [main._get(mainContext, main.definition, null)])); |
69 } | 92 } |
70 | 93 |
71 writeTypes(world.coreimpl); | 94 writeTypes(world.coreimpl); |
72 writeTypes(world.corelib); | 95 writeTypes(world.corelib); |
73 | 96 |
74 // Write the main library. This will cause all libraries to be written in | 97 // Write the main library. This will cause all libraries to be written in |
75 // the topographic sort order. | 98 // the topographic sort order. |
76 writeTypes(main.declaringType.library); | 99 writeTypes(main.declaringType.library); |
77 | 100 |
78 // Write out any inherited concrete members. | 101 // Write out any inherited concrete members. |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
201 for (var type in orderedTypes) { | 224 for (var type in orderedTypes) { |
202 if ((type.library.isDom || type.isHiddenNativeType) && | 225 if ((type.library.isDom || type.isHiddenNativeType) && |
203 type.isClass) { | 226 type.isClass) { |
204 type.markUsed(); | 227 type.markUsed(); |
205 } | 228 } |
206 } | 229 } |
207 | 230 |
208 for (var type in orderedTypes) { | 231 for (var type in orderedTypes) { |
209 if (type.isUsed && type.isClass) { | 232 if (type.isUsed && type.isClass) { |
210 writeType(type); | 233 writeType(type); |
211 | 234 // TODO(jimhug): Performance is terrible if we use current |
212 if (type.isGeneric) { | 235 // reified generics approach for reified generic Arrays. |
236 if (type.isGeneric && type !== world.listFactoryType) { | |
213 for (var ct in _orderValues(type._concreteTypes)) { | 237 for (var ct in _orderValues(type._concreteTypes)) { |
214 writeType(ct); | 238 if (ct.isUsed) writeType(ct); |
215 } | 239 } |
216 } | 240 } |
217 } else if (type.isFunction && type.varStubs.length > 0) { | 241 } else if (type.isFunction && type.varStubs.length > 0) { |
218 // Emit stubs on "Function" or hidden types if needed | 242 // Emit stubs on "Function" or hidden types if needed |
219 writer.comment('// ********** Code for ${type.jsname} **************'); | 243 writer.comment('// ********** Code for ${type.jsname} **************'); |
220 _writeDynamicStubs(type); | 244 _writeDynamicStubs(type); |
221 } | 245 } |
222 // Type check functions for builtin JS types | 246 // Type check functions for builtin JS types |
223 if (type.typeCheckCode != null) { | 247 if (type.typeCheckCode != null) { |
224 writer.writeln(type.typeCheckCode); | 248 writer.writeln(type.typeCheckCode); |
225 } | 249 } |
226 } | 250 } |
227 } | 251 } |
228 | 252 |
229 genMethod(Member meth, [MethodGenerator enclosingMethod=null]) { | 253 genMethod(MethodMember meth) { |
230 if (!meth.isGenerated && !meth.isAbstract && meth.definition != null) { | 254 meth.methodData.run(meth); |
231 new MethodGenerator(meth, enclosingMethod).run(); | |
232 } | |
233 } | 255 } |
234 | 256 |
235 String _prototypeOf(Type type, String name) { | 257 String _prototypeOf(Type type, String name) { |
236 if (type.isSingletonNative) { | 258 if (type.isSingletonNative) { |
237 // e.g. window.console.log$1 | 259 // e.g. window.console.log$1 |
238 return '${type.jsname}.$name'; | 260 return '${type.jsname}.$name'; |
239 } else if (type.isHiddenNativeType) { | 261 } else if (type.isHiddenNativeType) { |
240 corejs.ensureDynamicProto(); | 262 corejs.ensureDynamicProto(); |
241 _usedDynamicDispatchOnType(type); | 263 _usedDynamicDispatchOnType(type); |
242 return '\$dynamic("$name").${type.definition.nativeType.name}'; | 264 return '\$dynamic("$name").${type.definition.nativeType.name}'; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
306 writeType(Type type) { | 328 writeType(Type type) { |
307 if (type.isWritten) return; | 329 if (type.isWritten) return; |
308 | 330 |
309 type.isWritten = true; | 331 type.isWritten = true; |
310 // Ensure parent has been written before the child. Important ordering for | 332 // Ensure parent has been written before the child. Important ordering for |
311 // IE when we're using $inherits, since we don't have __proto__ available. | 333 // IE when we're using $inherits, since we don't have __proto__ available. |
312 if (type.parent != null && !type.isNative) { | 334 if (type.parent != null && !type.isNative) { |
313 writeType(type.parent); | 335 writeType(type.parent); |
314 } | 336 } |
315 | 337 |
316 // TODO(jimhug): Workaround for problems with reified generic Array. | |
317 if (type.name != null && type is ConcreteType && | |
318 type.library == world.coreimpl && | |
319 type.name.startsWith('ListFactory')) { | |
320 writer.writeln('${type.jsname} = ${type.genericType.jsname};'); | |
321 return; | |
322 } | |
323 | |
324 var typeName = type.jsname != null ? type.jsname : 'top level'; | 338 var typeName = type.jsname != null ? type.jsname : 'top level'; |
325 writer.comment('// ********** Code for ${typeName} **************'); | 339 writer.comment('// ********** Code for ${typeName} **************'); |
326 if (type.isNative && !type.isTop) { | 340 if (type.isNative && !type.isTop && !type.isConcreteGeneric) { |
327 var nativeName = type.definition.nativeType.name; | 341 var nativeName = type.definition.nativeType.name; |
328 if (nativeName == '') { | 342 if (nativeName == '') { |
329 writer.writeln('function ${type.jsname}() {}'); | 343 writer.writeln('function ${type.jsname}() {}'); |
330 } else if (type.jsname != nativeName) { | 344 } else if (type.jsname != nativeName) { |
331 if (type.isHiddenNativeType) { | 345 if (type.isHiddenNativeType) { |
332 if (_typeNeedsHolderForStaticMethods(type)) { | 346 if (_typeNeedsHolderForStaticMethods(type)) { |
333 // This is a holder for static methods. | 347 // This is a holder for static methods. |
334 writer.writeln('var ${type.jsname} = {};'); | 348 writer.writeln('var ${type.jsname} = {};'); |
sra1
2012/01/23 20:40:30
This code is being emitted unnecessarily for all 5
| |
335 } | 349 } |
336 } else { | 350 } else { |
337 writer.writeln('${type.jsname} = ${nativeName};'); | 351 writer.writeln('${type.jsname} = ${nativeName};'); |
338 } | 352 } |
339 } | 353 } |
340 } | 354 } |
341 | 355 |
356 // TODO(jimhug): This comment below seems out-of-order now? | |
342 // We need the $inherits function to be declared before factory constructors | 357 // We need the $inherits function to be declared before factory constructors |
343 // so that inheritance ($inherits) will work correctly in IE. | 358 // so that inheritance ($inherits) will work correctly in IE. |
344 if (!type.isTop) { | 359 if (!type.isTop) { |
345 if (type is ConcreteType) { | 360 if (type.genericType !== type) { |
361 corejs.ensureInheritsHelper(); | |
362 writer.writeln('\$inherits(${type.jsname}, ${type.genericType.jsname});' ); | |
363 } | |
364 | |
365 // TODO(jimhug): Do we still need this code below? | |
366 /*if (type is ConcreteType) { | |
346 ConcreteType c = type; | 367 ConcreteType c = type; |
347 corejs.ensureInheritsHelper(); | 368 corejs.ensureInheritsHelper(); |
348 writer.writeln('\$inherits(${c.jsname}, ${c.genericType.jsname});'); | 369 writer.writeln('\$inherits(${c.jsname}, ${c.genericType.jsname});'); |
349 | 370 |
350 // Mixin members from concrete specializations of base types too. | 371 // Mixin members from concrete specializations of base types too. |
351 // TODO(jmesserly): emit this sooner instead of at the end. | 372 // TODO(jmesserly): emit this sooner instead of at the end. |
352 // But it needs to come after we've emitted both types. | 373 // But it needs to come after we've emitted both types. |
353 // TODO(jmesserly): HACK: using _parent instead of parent so we don't | 374 // TODO(jmesserly): HACK: using _parent instead of parent so we don't |
354 // try to inherit things that we didn't actually use. | 375 // try to inherit things that we didn't actually use. |
355 for (var p = c._parent; p is ConcreteType; p = p._parent) { | 376 for (var p = c._parent; p is ConcreteType; p = p._parent) { |
356 _ensureInheritMembersHelper(); | 377 _ensureInheritMembersHelper(); |
357 _mixins.writeln('\$inheritsMembers(${c.jsname}, ${p.jsname});'); | 378 _mixins.writeln('\$inheritsMembers(${c.jsname}, ${p.jsname});'); |
358 } | 379 } |
359 } else if (!type.isNative) { | 380 } else*/ |
381 else if (!type.isNative) { | |
360 if (type.parent != null && !type.parent.isObject) { | 382 if (type.parent != null && !type.parent.isObject) { |
361 corejs.ensureInheritsHelper(); | 383 corejs.ensureInheritsHelper(); |
362 writer.writeln('\$inherits(${type.jsname}, ${type.parent.jsname});'); | 384 writer.writeln('\$inherits(${type.jsname}, ${type.parent.jsname});'); |
363 } | 385 } |
364 } | 386 } |
365 } | 387 } |
366 | 388 |
367 if (type.isTop) { | 389 if (type.isTop) { |
368 // no preludes for top type | 390 // no preludes for top type |
369 } else if (type.constructors.length == 0) { | 391 } else if (type.constructors.length == 0) { |
370 if (!type.isNative) { | 392 if (!type.isNative || type.isConcreteGeneric) { |
371 // TODO(jimhug): More guards to guarantee staticness | 393 // TODO(jimhug): More guards to guarantee staticness |
372 writer.writeln('function ${type.jsname}() {}'); | 394 writer.writeln('function ${type.jsname}() {}'); |
373 } | 395 } |
374 } else { | 396 } else { |
375 Member standardConstructor = type.constructors['']; | 397 bool wroteStandard = false; |
376 if (standardConstructor == null || | 398 for (var c in type.constructors.getValues()) { |
377 standardConstructor.generator == null) { | 399 if (c.methodData.writeDefinition(c, writer)) { |
378 if (!type.isNative) { | 400 if (c.isConstructor && c.constructorName == '') wroteStandard = true; |
379 writer.writeln('function ${type.jsname}() {}'); | |
380 } | 401 } |
381 } else { | |
382 standardConstructor.generator.writeDefinition(writer, null); | |
383 } | 402 } |
384 | 403 |
385 for (var c in type.constructors.getValues()) { | 404 if (!wroteStandard && (!type.isNative || type.genericType !== type)) { |
386 if (c.generator != null && c != standardConstructor) { | 405 writer.writeln('function ${type.jsname}() {}'); |
387 c.generator.writeDefinition(writer, null); | |
388 } | |
389 } | 406 } |
390 } | 407 } |
391 | 408 |
392 // Concrete types (like List<String>) will have this already defined on | 409 // Concrete types (like List<String>) will have this already defined on |
393 // their prototype from the generic type (like List) | 410 // their prototype from the generic type (like List) |
394 if (type is! ConcreteType) { | 411 if (!type.isConcreteGeneric) { |
395 _maybeIsTest(type, type); | 412 _maybeIsTest(type, type); |
396 } | 413 } |
397 if (type.genericType._concreteTypes != null) { | 414 if (type.genericType._concreteTypes != null) { |
398 for (var ct in _orderValues(type.genericType._concreteTypes)) { | 415 for (var ct in _orderValues(type.genericType._concreteTypes)) { |
399 _maybeIsTest(type, ct); | 416 _maybeIsTest(type, ct); |
400 } | 417 } |
401 } | 418 } |
402 | 419 |
403 if (type.interfaces != null) { | 420 if (type.interfaces != null) { |
404 final seen = new Set(); | 421 final seen = new Set(); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
452 * } | 469 * } |
453 * | 470 * |
454 * The factory method and static member are generated something like this: | 471 * The factory method and static member are generated something like this: |
455 * var lib_Float32Array = {}; | 472 * var lib_Float32Array = {}; |
456 * lib_Float32Array.Float32Array$factory = ... ; | 473 * lib_Float32Array.Float32Array$factory = ... ; |
457 * lib_Float32Array._construct = ... ; | 474 * lib_Float32Array._construct = ... ; |
458 * | 475 * |
459 * This predicate determines when we need to define lib_Float32Array. | 476 * This predicate determines when we need to define lib_Float32Array. |
460 */ | 477 */ |
461 _typeNeedsHolderForStaticMethods(Type type) { | 478 _typeNeedsHolderForStaticMethods(Type type) { |
462 for (var member in type.members.getValues()) { | 479 return type.isUsed; |
sra1
2012/01/23 20:40:30
This new code causes 500 useless dummy declaration
| |
463 if (member.isMethod) { | |
464 if (member.isConstructor || member.isStatic) { | |
465 if (member.isGenerated) { | |
466 return true; | |
467 } | |
468 } | |
469 } | |
470 } | |
471 return false; | |
472 } | 480 } |
473 | 481 |
474 /** | 482 /** |
475 * Generates the $inheritsMembers function when it's first used. | 483 * Generates the $inheritsMembers function when it's first used. |
476 * This is used to mix in specialized generic members from the base class. | 484 * This is used to mix in specialized generic members from the base class. |
477 */ | 485 */ |
478 _ensureInheritMembersHelper() { | 486 _ensureInheritMembersHelper() { |
479 if (_mixins != null) return; | 487 if (_mixins != null) return; |
480 _mixins = new CodeWriter(); | 488 _mixins = new CodeWriter(); |
481 _mixins.comment('// ********** Generic Type Inheritance **************'); | 489 _mixins.comment('// ********** Generic Type Inheritance **************'); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
513 // No need to write code for a static class field with no initial value. | 521 // No need to write code for a static class field with no initial value. |
514 } | 522 } |
515 | 523 |
516 _writeField(FieldMember field) { | 524 _writeField(FieldMember field) { |
517 // Generate declarations for static top-level fields with no value. | 525 // Generate declarations for static top-level fields with no value. |
518 if (field.declaringType.isTop && !field.isNative && field.value == null) { | 526 if (field.declaringType.isTop && !field.isNative && field.value == null) { |
519 writer.writeln('var ${field.jsname};'); | 527 writer.writeln('var ${field.jsname};'); |
520 } | 528 } |
521 | 529 |
522 // generate code for instance fields | 530 // generate code for instance fields |
523 if (field._providePropertySyntax) { | 531 if (field._providePropertySyntax && |
532 !field.declaringType.isConcreteGeneric) { | |
524 _writePrototypePatch(field.declaringType, 'get\$${field.jsname}', | 533 _writePrototypePatch(field.declaringType, 'get\$${field.jsname}', |
525 'function() { return this.${field.jsname}; }', writer); | 534 'function() { return this.${field.jsname}; }', writer); |
526 if (!field.isFinal) { | 535 if (!field.isFinal) { |
527 _writePrototypePatch(field.declaringType, 'set\$${field.jsname}', | 536 _writePrototypePatch(field.declaringType, 'set\$${field.jsname}', |
528 'function(value) { return this.${field.jsname} = value; }', writer); | 537 'function(value) { return this.${field.jsname} = value; }', writer); |
529 } | 538 } |
530 } | 539 } |
531 | 540 |
532 // TODO(jimhug): Currently choose not to initialize fields on objects, but | 541 // TODO(jimhug): Currently choose not to initialize fields on objects, but |
533 // instead to rely on uninitialized === null in our generated code. | 542 // instead to rely on uninitialized === null in our generated code. |
534 // Investigate the perf pros and cons of this. | 543 // Investigate the perf pros and cons of this. |
535 } | 544 } |
536 | 545 |
537 _writeProperty(PropertyMember property) { | 546 _writeProperty(PropertyMember property) { |
538 if (property.getter != null) _writeMethod(property.getter); | 547 if (property.getter != null) _writeMethod(property.getter); |
539 if (property.setter != null) _writeMethod(property.setter); | 548 if (property.setter != null) _writeMethod(property.setter); |
540 | 549 |
541 // TODO(jmesserly): make sure we don't do this on hidden native types! | 550 // TODO(jmesserly): make sure we don't do this on hidden native types! |
542 if (property._provideFieldSyntax) { | 551 if (property.needsFieldSyntax) { |
543 writer.enterBlock('Object.defineProperty(' + | 552 writer.enterBlock('Object.defineProperty(' + |
544 '${property.declaringType.jsname}.prototype, "${property.jsname}", {'); | 553 '${property.declaringType.jsname}.prototype, "${property.jsname}", {'); |
545 if (property.getter != null) { | 554 if (property.getter != null) { |
546 writer.write( | 555 writer.write( |
547 'get: ${property.declaringType.jsname}.prototype.${property.getter.jsn ame}'); | 556 'get: ${property.declaringType.jsname}.prototype.${property.getter.jsn ame}'); |
548 // The shenanigan below is to make IE happy -- IE 9 doesn't like a | 557 // The shenanigan below is to make IE happy -- IE 9 doesn't like a |
549 // trailing comma on the last element in a list. | 558 // trailing comma on the last element in a list. |
550 writer.writeln(property.setter == null ? '' : ','); | 559 writer.writeln(property.setter == null ? '' : ','); |
551 } | 560 } |
552 if (property.setter != null) { | 561 if (property.setter != null) { |
553 writer.writeln( | 562 writer.writeln( |
554 'set: ${property.declaringType.jsname}.prototype.${property.setter.jsn ame}'); | 563 'set: ${property.declaringType.jsname}.prototype.${property.setter.jsn ame}'); |
555 } | 564 } |
556 writer.exitBlock('});'); | 565 writer.exitBlock('});'); |
557 } | 566 } |
558 } | 567 } |
559 | 568 |
560 _writeMethod(Member m) { | 569 _writeMethod(MethodMember m) { |
561 if (m.generator != null) { | 570 m.methodData.writeDefinition(m, writer); |
562 m.generator.writeDefinition(writer, null); | 571 |
563 } else if (m is MethodMember && m.isNative | 572 if (m.isNative && m._providePropertySyntax) { |
564 && m._providePropertySyntax && !m._provideFieldSyntax) { | |
565 MethodGenerator._maybeGenerateBoundGetter(m, writer); | 573 MethodGenerator._maybeGenerateBoundGetter(m, writer); |
566 } | 574 } |
567 } | 575 } |
568 | 576 |
569 writeGlobals() { | 577 writeGlobals() { |
570 if (globals.length > 0) { | 578 if (globals.length > 0) { |
571 writer.comment('// ********** Globals **************'); | 579 writer.comment('// ********** Globals **************'); |
572 var list = globals.getValues(); | 580 var list = globals.getValues(); |
573 list.sort((a, b) => a.compareTo(b)); | 581 list.sort((a, b) => a.compareTo(b)); |
574 | 582 |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
740 | 748 |
741 // If that fails, compare by name. | 749 // If that fails, compare by name. |
742 return x.name.compareTo(y.name); | 750 return x.name.compareTo(y.name); |
743 } | 751 } |
744 } | 752 } |
745 | 753 |
746 | 754 |
747 /** | 755 /** |
748 * A naive code generator for Dart. | 756 * A naive code generator for Dart. |
749 */ | 757 */ |
750 class MethodGenerator implements TreeVisitor { | 758 class MethodGenerator implements TreeVisitor, CallingContext { |
751 Member method; | 759 Member method; |
752 CodeWriter writer; | 760 CodeWriter writer; |
753 BlockScope _scope; | 761 BlockScope _scope; |
754 MethodGenerator enclosingMethod; | 762 MethodGenerator enclosingMethod; |
755 bool needsThis; | 763 bool needsThis; |
756 List<String> _paramCode; | 764 List<String> _paramCode; |
757 | 765 |
758 // TODO(jmesserly): if we knew temps were always used like a stack, we could | 766 // TODO(jmesserly): if we knew temps were always used like a stack, we could |
759 // reduce the overhead here. | 767 // reduce the overhead here. |
760 List<String> _freeTemps; | 768 List<String> _freeTemps; |
(...skipping 23 matching lines...) Expand all Loading... | |
784 counters = world.counters; | 792 counters = world.counters; |
785 } | 793 } |
786 | 794 |
787 Library get library() => method.library; | 795 Library get library() => method.library; |
788 | 796 |
789 // TODO(jimhug): Where does this really belong? | 797 // TODO(jimhug): Where does this really belong? |
790 MemberSet findMembers(String name) { | 798 MemberSet findMembers(String name) { |
791 return library._findMembers(name); | 799 return library._findMembers(name); |
792 } | 800 } |
793 | 801 |
802 bool get needsCode() => true; | |
803 bool get showWarnings() => false; | |
804 | |
794 bool get isClosure() => (enclosingMethod != null); | 805 bool get isClosure() => (enclosingMethod != null); |
795 | 806 |
796 bool get isStatic() => method.isStatic; | 807 bool get isStatic() => method.isStatic; |
797 | 808 |
798 Value getTemp(Value value) { | 809 Value getTemp(Value value) { |
799 return value.needsTemp ? forceTemp(value) : value; | 810 return value.needsTemp ? forceTemp(value) : value; |
800 } | 811 } |
801 | 812 |
802 VariableValue forceTemp(Value value) { | 813 VariableValue forceTemp(Value value) { |
803 String name; | 814 String name; |
(...skipping 23 matching lines...) Expand all Loading... | |
827 if (_usedTemps.remove(value.code)) { | 838 if (_usedTemps.remove(value.code)) { |
828 _freeTemps.add(value.code); | 839 _freeTemps.add(value.code); |
829 } else { | 840 } else { |
830 world.internalError( | 841 world.internalError( |
831 'tried to free unused value or non-temp "${value.code}"'); | 842 'tried to free unused value or non-temp "${value.code}"'); |
832 } | 843 } |
833 */ | 844 */ |
834 } | 845 } |
835 | 846 |
836 run() { | 847 run() { |
837 if (method.isGenerated) return; | |
838 | |
839 // This avoids any attempts to infer across recursion. | |
840 method.isGenerated = true; | |
841 method.generator = this; | |
842 | |
843 // Create most generic possible call for this method. | 848 // Create most generic possible call for this method. |
844 var thisObject; | 849 var thisObject; |
845 if (method.isConstructor) { | 850 if (method.isConstructor) { |
846 thisObject = new ObjectValue(false, method.declaringType, method.span); | 851 thisObject = new ObjectValue(false, method.declaringType, method.span); |
847 thisObject.initFields(); | 852 thisObject.initFields(); |
848 } else { | 853 } else { |
849 thisObject = new Value(method.declaringType, 'this', null); | 854 thisObject = new Value(method.declaringType, 'this', null); |
850 } | 855 } |
851 var values = []; | 856 var values = []; |
852 for (var p in method.parameters) { | 857 for (var p in method.parameters) { |
853 values.add(new Value(p.type, p.name, null)); | 858 values.add(new Value(p.type, p.name, null)); |
854 } | 859 } |
855 var args = new Arguments(null, values); | 860 var args = new Arguments(null, values); |
856 | 861 |
857 evalBody(thisObject, args); | 862 evalBody(thisObject, args); |
858 | |
859 if (method.definition.nativeBody != null) { | |
860 // Throw away the code--it was just used for tree shaking purposes. | |
861 writer = new CodeWriter(); | |
862 if (method.definition.nativeBody == '') { | |
863 method.generator = null; | |
864 } else { | |
865 _paramCode = map(method.parameters, (p) => p.name); | |
866 writer.write(method.definition.nativeBody); | |
867 } | |
868 } | |
869 } | 863 } |
870 | 864 |
871 | 865 |
872 writeDefinition(CodeWriter defWriter, LambdaExpression lambda/*=null*/) { | 866 writeDefinition(CodeWriter defWriter, LambdaExpression lambda/*=null*/) { |
873 // To implement block scope: capture any variables we need to. | 867 // To implement block scope: capture any variables we need to. |
874 var paramCode = _paramCode; | 868 var paramCode = _paramCode; |
875 var names = null; | 869 var names = null; |
876 if (captures != null && captures.length > 0) { | 870 if (captures != null && captures.length > 0) { |
877 names = new List.from(captures); | 871 names = new List.from(captures); |
878 names.sort((x, y) => x.compareTo(y)); | 872 names.sort((x, y) => x.compareTo(y)); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
949 } | 943 } |
950 } | 944 } |
951 | 945 |
952 static _maybeGenerateBoundGetter(MethodMember m, CodeWriter defWriter) { | 946 static _maybeGenerateBoundGetter(MethodMember m, CodeWriter defWriter) { |
953 if (m._providePropertySyntax) { | 947 if (m._providePropertySyntax) { |
954 String suffix = world.gen._writePrototypePatch(m.declaringType, | 948 String suffix = world.gen._writePrototypePatch(m.declaringType, |
955 'get\$' + m.jsname, 'function() {', defWriter, false); | 949 'get\$' + m.jsname, 'function() {', defWriter, false); |
956 // TODO(jimhug): Bind not available in older Safari, need fallback? | 950 // TODO(jimhug): Bind not available in older Safari, need fallback? |
957 defWriter.writeln('return this.${m.jsname}.bind(this);'); | 951 defWriter.writeln('return this.${m.jsname}.bind(this);'); |
958 defWriter.exitBlock(suffix); | 952 defWriter.exitBlock(suffix); |
959 | |
960 if (m._provideFieldSyntax) { | |
961 world.internalError('bound "${m.name}" accessed with field syntax', | |
962 m.definition.span); | |
963 } | |
964 } | 953 } |
965 } | 954 } |
966 | 955 |
967 /** | 956 /** |
968 * Generates information about the default/named arguments into the JS code. | 957 * Generates information about the default/named arguments into the JS code. |
969 * Only methods that are passed as bound methods to "var" need this. It is | 958 * Only methods that are passed as bound methods to "var" need this. It is |
970 * generated to support run time stub creation. | 959 * generated to support run time stub creation. |
971 */ | 960 */ |
972 _provideOptionalParamInfo(CodeWriter defWriter) { | 961 _provideOptionalParamInfo(CodeWriter defWriter) { |
973 if (method is MethodMember) { | 962 if (method is MethodMember) { |
974 MethodMember meth = method; | 963 MethodMember meth = method; |
975 if (meth._provideOptionalParamInfo) { | 964 if (meth._provideOptionalParamInfo) { |
976 var optNames = []; | 965 var optNames = []; |
977 var optValues = []; | 966 var optValues = []; |
978 meth.genParameterValues(); | 967 meth.genParameterValues(this); |
979 for (var param in meth.parameters) { | 968 for (var param in meth.parameters) { |
980 if (param.isOptional) { | 969 if (param.isOptional) { |
981 optNames.add(param.name); | 970 optNames.add(param.name); |
982 // TODO(jimhug): Remove this last usage of escapeString. | 971 // TODO(jimhug): Remove this last usage of escapeString. |
983 optValues.add(_escapeString(param.value.code)); | 972 optValues.add(_escapeString(param.value.code)); |
984 } | 973 } |
985 } | 974 } |
986 if (optNames.length > 0) { | 975 if (optNames.length > 0) { |
987 // TODO(jmesserly): the logic for how to refer to | 976 // TODO(jmesserly): the logic for how to refer to |
988 // static/instance/top-level members is duplicated all over the place. | 977 // static/instance/top-level members is duplicated all over the place. |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1026 var p = method.parameters[i]; | 1015 var p = method.parameters[i]; |
1027 Value currentArg = null; | 1016 Value currentArg = null; |
1028 // TODO(jimhug): bareCount is O(N) | 1017 // TODO(jimhug): bareCount is O(N) |
1029 if (i < args.bareCount) { | 1018 if (i < args.bareCount) { |
1030 currentArg = args.values[i]; | 1019 currentArg = args.values[i]; |
1031 } else { | 1020 } else { |
1032 // Handle named or missing arguments | 1021 // Handle named or missing arguments |
1033 currentArg = args.getValue(p.name); | 1022 currentArg = args.getValue(p.name); |
1034 if (currentArg === null) { | 1023 if (currentArg === null) { |
1035 // Ensure default value for param has been generated | 1024 // Ensure default value for param has been generated |
1036 p.genValue(method, method.generator); | 1025 p.genValue(method, this); |
1037 currentArg = p.value; | 1026 currentArg = p.value; |
1038 } | 1027 } |
1039 } | 1028 } |
1040 | 1029 |
1041 if (p.isInitializer) { | 1030 if (p.isInitializer) { |
1042 _paramCode.add(p.name); | 1031 _paramCode.add(p.name); |
1043 fieldsSet = true; | 1032 fieldsSet = true; |
1044 _initField(newObject, p.name, currentArg, p.definition.span); | 1033 _initField(newObject, p.name, currentArg, p.definition.span); |
1045 } else { | 1034 } else { |
1046 var paramValue = _scope.declareParameter(p); | 1035 var paramValue = _scope.declareParameter(p); |
1047 _paramCode.add(paramValue.code); | 1036 _paramCode.add(paramValue.code); |
1048 if (newObject != null && newObject.isConst) { | 1037 if (newObject != null && newObject.isConst) { |
1049 _scope.assign(p.name, currentArg.convertTo(this, p.type)); | 1038 _scope.assign(p.name, currentArg.convertTo(this, p.type)); |
1050 } | 1039 } |
1051 } | 1040 } |
1052 } | 1041 } |
1053 | 1042 |
1054 var initializerCall = null; | 1043 var initializerCall = null; |
1055 final declaredInitializers = method.definition.initializers; | 1044 final declaredInitializers = method.definition.dynamic.initializers; |
1056 if (declaredInitializers != null) { | 1045 if (declaredInitializers != null) { |
1057 for (var init in declaredInitializers) { | 1046 for (var init in declaredInitializers) { |
1058 if (init is CallExpression) { | 1047 if (init is CallExpression) { |
1059 if (initializerCall != null) { | 1048 if (initializerCall != null) { |
1060 world.error('only one initializer redirecting call is allowed', | 1049 world.error('only one initializer redirecting call is allowed', |
1061 init.span); | 1050 init.span); |
1062 } | 1051 } |
1063 initializerCall = init; | 1052 initializerCall = init; |
1064 } else if (init is BinaryExpression | 1053 } else if (init is BinaryExpression |
1065 && TokenKind.kindFromAssign(init.op.kind) == 0) { | 1054 && TokenKind.kindFromAssign(init.op.kind) == 0) { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1113 var value = fields[field]; | 1102 var value = fields[field]; |
1114 if (value === null && field.isFinal && | 1103 if (value === null && field.isFinal && |
1115 field.declaringType == method.declaringType && | 1104 field.declaringType == method.declaringType && |
1116 !newObject.dynamic.seenNativeInitializer) { | 1105 !newObject.dynamic.seenNativeInitializer) { |
1117 world.error('uninitialized final field "${field.name}"', | 1106 world.error('uninitialized final field "${field.name}"', |
1118 field.span, method.span); | 1107 field.span, method.span); |
1119 } | 1108 } |
1120 } | 1109 } |
1121 } | 1110 } |
1122 | 1111 |
1123 var body = method.definition.body; | 1112 var body = method.definition.dynamic.body; |
1124 | 1113 |
1125 if (body === null) { | 1114 if (body === null) { |
1126 // TODO(jimhug): Move check into resolve on method. | 1115 // TODO(jimhug): Move check into resolve on method. |
1127 if (!method.isConstructor && !method.isNative) { | 1116 if (!method.isConstructor && !method.isNative) { |
1128 world.error('unexpected empty body for ${method.name}', | 1117 world.error('unexpected empty body for ${method.name}', |
1129 method.definition.span); | 1118 method.definition.span); |
1130 } | 1119 } |
1131 } else { | 1120 } else { |
1132 visitStatementsInBlock(body); | 1121 visitStatementsInBlock(body); |
1133 } | 1122 } |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1309 savedWriter.write(writer.text); | 1298 savedWriter.write(writer.text); |
1310 writer = savedWriter; | 1299 writer = savedWriter; |
1311 savedCounters.add(counters); | 1300 savedCounters.add(counters); |
1312 counters = savedCounters; | 1301 counters = savedCounters; |
1313 } | 1302 } |
1314 | 1303 |
1315 MethodMember _makeLambdaMethod(String name, FunctionDefinition func) { | 1304 MethodMember _makeLambdaMethod(String name, FunctionDefinition func) { |
1316 var meth = new MethodMember(name, method.declaringType, func); | 1305 var meth = new MethodMember(name, method.declaringType, func); |
1317 meth.isLambda = true; | 1306 meth.isLambda = true; |
1318 meth.enclosingElement = method; | 1307 meth.enclosingElement = method; |
1308 meth._methodData = new MethodData(meth, this); | |
1319 meth.resolve(); | 1309 meth.resolve(); |
1320 return meth; | 1310 return meth; |
1321 } | 1311 } |
1322 | 1312 |
1323 visitBool(Expression node) { | 1313 visitBool(Expression node) { |
1324 // Boolean conversions in if/while/do/for/conditions require non-null bool. | 1314 // Boolean conversions in if/while/do/for/conditions require non-null bool. |
1325 | 1315 |
1326 // TODO(jmesserly): why do we have this rule? It seems inconsistent with | 1316 // TODO(jmesserly): why do we have this rule? It seems inconsistent with |
1327 // the rest of the type system, and just causes bogus asserts unless all | 1317 // the rest of the type system, and just causes bogus asserts unless all |
1328 // bools are initialized to false. | 1318 // bools are initialized to false. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1369 return false; | 1359 return false; |
1370 } | 1360 } |
1371 | 1361 |
1372 bool visitVariableDefinition(VariableDefinition node) { | 1362 bool visitVariableDefinition(VariableDefinition node) { |
1373 var isFinal = false; | 1363 var isFinal = false; |
1374 // TODO(jimhug): Clean this up and share modifier parsing somewhere. | 1364 // TODO(jimhug): Clean this up and share modifier parsing somewhere. |
1375 if (node.modifiers != null && node.modifiers[0].kind == TokenKind.FINAL) { | 1365 if (node.modifiers != null && node.modifiers[0].kind == TokenKind.FINAL) { |
1376 isFinal = true; | 1366 isFinal = true; |
1377 } | 1367 } |
1378 writer.write('var '); | 1368 writer.write('var '); |
1379 var type = method.resolveType(node.type, false); | 1369 var type = method.resolveType(node.type, false, true); |
1380 for (int i=0; i < node.names.length; i++) { | 1370 for (int i=0; i < node.names.length; i++) { |
1381 if (i > 0) { | 1371 if (i > 0) { |
1382 writer.write(', '); | 1372 writer.write(', '); |
1383 } | 1373 } |
1384 final name = node.names[i].name; | 1374 final name = node.names[i].name; |
1385 var value = visitValue(node.values[i]); | 1375 var value = visitValue(node.values[i]); |
1386 if (isFinal && value == null) { | 1376 if (isFinal && value == null) { |
1387 world.error('no value specified for final variable', node.span); | 1377 world.error('no value specified for final variable', node.span); |
1388 } | 1378 } |
1389 | 1379 |
(...skipping 16 matching lines...) Expand all Loading... | |
1406 writer.writeln(';'); | 1396 writer.writeln(';'); |
1407 return false; | 1397 return false; |
1408 | 1398 |
1409 } | 1399 } |
1410 | 1400 |
1411 bool visitFunctionDefinition(FunctionDefinition node) { | 1401 bool visitFunctionDefinition(FunctionDefinition node) { |
1412 var meth = _makeLambdaMethod(node.name.name, node); | 1402 var meth = _makeLambdaMethod(node.name.name, node); |
1413 var funcValue = _scope.create(meth.name, meth.functionType, | 1403 var funcValue = _scope.create(meth.name, meth.functionType, |
1414 method.definition.span, isFinal:true); | 1404 method.definition.span, isFinal:true); |
1415 | 1405 |
1416 world.gen.genMethod(meth, this); | 1406 meth.methodData.createFunction(writer); |
1417 meth.generator.writeDefinition(writer, null); | |
1418 return false; | 1407 return false; |
1419 } | 1408 } |
1420 | 1409 |
1421 /** | 1410 /** |
1422 * Returns true indicating that normal control-flow is interrupted by | 1411 * Returns true indicating that normal control-flow is interrupted by |
1423 * this statement. (This could be a return, break, throw, or continue.) | 1412 * this statement. (This could be a return, break, throw, or continue.) |
1424 */ | 1413 */ |
1425 bool visitReturnStatement(ReturnStatement node) { | 1414 bool visitReturnStatement(ReturnStatement node) { |
1426 if (node.value == null) { | 1415 if (node.value == null) { |
1427 // This is essentially "return null". | 1416 // This is essentially "return null". |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1567 node.body.visit(this); | 1556 node.body.visit(this); |
1568 _popBlock(node.body); | 1557 _popBlock(node.body); |
1569 }); | 1558 }); |
1570 _popBlock(node); | 1559 _popBlock(node); |
1571 return false; | 1560 return false; |
1572 } | 1561 } |
1573 | 1562 |
1574 bool _isFinal(typeRef) { | 1563 bool _isFinal(typeRef) { |
1575 if (typeRef is GenericTypeReference) { | 1564 if (typeRef is GenericTypeReference) { |
1576 typeRef = typeRef.baseType; | 1565 typeRef = typeRef.baseType; |
1566 } else if (typeRef is SimpleTypeReference) { | |
1567 return false; | |
1577 } | 1568 } |
1578 return typeRef != null && typeRef.isFinal; | 1569 return typeRef != null && typeRef.isFinal; |
1579 } | 1570 } |
1580 | 1571 |
1581 bool visitForInStatement(ForInStatement node) { | 1572 bool visitForInStatement(ForInStatement node) { |
1582 // TODO(jimhug): visitValue and other cleanups here. | 1573 // TODO(jimhug): visitValue and other cleanups here. |
1583 var itemType = method.resolveType(node.item.type, false); | 1574 var itemType = method.resolveType(node.item.type, false, true); |
1584 var list = node.list.visit(this); | 1575 var list = node.list.visit(this); |
1585 _visitLoop(node, () { | 1576 _visitLoop(node, () { |
1586 _visitForInBody(node, itemType, list); | 1577 _visitForInBody(node, itemType, list); |
1587 }); | 1578 }); |
1588 return false; | 1579 return false; |
1589 } | 1580 } |
1590 | 1581 |
1591 void _visitForInBody(ForInStatement node, Type itemType, Value list) { | 1582 void _visitForInBody(ForInStatement node, Type itemType, Value list) { |
1592 // TODO(jimhug): Check that itemType matches list members... | 1583 // TODO(jimhug): Check that itemType matches list members... |
1593 bool isFinal = _isFinal(node.item.type); | 1584 bool isFinal = _isFinal(node.item.type); |
1594 var itemName = node.item.name.name; | 1585 var itemName = node.item.name.name; |
1595 var item = _scope.create(itemName, itemType, node.item.name.span, isFinal); | 1586 var item = _scope.create(itemName, itemType, node.item.name.span, isFinal); |
1596 if (list.needsTemp) { | 1587 if (list.needsTemp) { |
1597 var listVar = _scope.create('\$list', list.type, null); | 1588 var listVar = _scope.create('\$list', list.type, null); |
1598 writer.writeln('var ${listVar.code} = ${list.code};'); | 1589 writer.writeln('var ${listVar.code} = ${list.code};'); |
1599 list = listVar; | 1590 list = listVar; |
1600 } | 1591 } |
1601 | 1592 |
1602 // Special path for list for readability and perf optimization. | 1593 // Special path for concrete Arrays for readability and perf optimization. |
1603 if (list.type.isList) { | 1594 if (list.type.genericType == world.listFactoryType) { |
1604 var tmpi = _scope.create('\$i', world.numType, null); | 1595 var tmpi = _scope.create('\$i', world.numType, null); |
1605 var listLength = list.get_(this, 'length', node.list); | 1596 var listLength = list.get_(this, 'length', node.list); |
1606 writer.enterBlock('for (var ${tmpi.code} = 0;' + | 1597 writer.enterBlock('for (var ${tmpi.code} = 0;' + |
1607 '${tmpi.code} < ${listLength.code}; ${tmpi.code}++) {'); | 1598 '${tmpi.code} < ${listLength.code}; ${tmpi.code}++) {'); |
1608 var value = list.invoke(this, ':index', node.list, | 1599 var value = list.invoke(this, ':index', node.list, |
1609 new Arguments(null, [tmpi])); | 1600 new Arguments(null, [tmpi])); |
1610 writer.writeln('var ${item.code} = ${value.code};'); | 1601 writer.writeln('var ${item.code} = ${value.code};'); |
1611 } else { | 1602 } else { |
1612 var iterator = list.invoke(this, 'iterator', node.list, Arguments.EMPTY); | 1603 var iterator = list.invoke(this, 'iterator', node.list, Arguments.EMPTY); |
1613 var tmpi = _scope.create('\$i', iterator.type, null); | 1604 var tmpi = _scope.create('\$i', iterator.type, null); |
(...skipping 24 matching lines...) Expand all Loading... | |
1638 writer.enterBlock('try {'); | 1629 writer.enterBlock('try {'); |
1639 _pushBlock(node.body); | 1630 _pushBlock(node.body); |
1640 visitStatementsInBlock(node.body); | 1631 visitStatementsInBlock(node.body); |
1641 _popBlock(node.body); | 1632 _popBlock(node.body); |
1642 | 1633 |
1643 if (node.catches.length == 1) { | 1634 if (node.catches.length == 1) { |
1644 // Handle a single catch. We can generate simple code here compared to the | 1635 // Handle a single catch. We can generate simple code here compared to the |
1645 // multiple catch, such as no extra temp or if-else-if chain. | 1636 // multiple catch, such as no extra temp or if-else-if chain. |
1646 var catch_ = node.catches[0]; | 1637 var catch_ = node.catches[0]; |
1647 _pushBlock(catch_); | 1638 _pushBlock(catch_); |
1648 var exType = method.resolveType(catch_.exception.type, false); | 1639 var exType = method.resolveType(catch_.exception.type, false, true); |
1649 var ex = _scope.declare(catch_.exception); | 1640 var ex = _scope.declare(catch_.exception); |
1650 _scope.rethrow = ex.code; | 1641 _scope.rethrow = ex.code; |
1651 writer.nextBlock('} catch (${ex.code}) {'); | 1642 writer.nextBlock('} catch (${ex.code}) {'); |
1652 if (catch_.trace != null) { | 1643 if (catch_.trace != null) { |
1653 var trace = _scope.declare(catch_.trace); | 1644 var trace = _scope.declare(catch_.trace); |
1654 _genStackTraceOf(trace, ex); | 1645 _genStackTraceOf(trace, ex); |
1655 } | 1646 } |
1656 _genToDartException(ex); | 1647 _genToDartException(ex); |
1657 | 1648 |
1658 if (!exType.isVarOrObject) { | 1649 if (!exType.isVarOrObject) { |
(...skipping 16 matching lines...) Expand all Loading... | |
1675 } | 1666 } |
1676 _genToDartException(ex); | 1667 _genToDartException(ex); |
1677 | 1668 |
1678 // We need a rethrow unless we encounter a "var" or "Object" catch | 1669 // We need a rethrow unless we encounter a "var" or "Object" catch |
1679 bool needsRethrow = true; | 1670 bool needsRethrow = true; |
1680 | 1671 |
1681 for (int i = 0; i < node.catches.length; i++) { | 1672 for (int i = 0; i < node.catches.length; i++) { |
1682 var catch_ = node.catches[i]; | 1673 var catch_ = node.catches[i]; |
1683 | 1674 |
1684 _pushBlock(catch_); | 1675 _pushBlock(catch_); |
1685 var tmpType = method.resolveType(catch_.exception.type, false); | 1676 var tmpType = method.resolveType(catch_.exception.type, false, true); |
1686 var tmp = _scope.declare(catch_.exception); | 1677 var tmp = _scope.declare(catch_.exception); |
1687 if (!tmpType.isVarOrObject) { | 1678 if (!tmpType.isVarOrObject) { |
1688 var test = ex.instanceOf(this, tmpType, catch_.exception.span, | 1679 var test = ex.instanceOf(this, tmpType, catch_.exception.span, |
1689 isTrue:true, forceCheck:true); | 1680 isTrue:true, forceCheck:true); |
1690 if (i == 0) { | 1681 if (i == 0) { |
1691 writer.enterBlock('if (${test.code}) {'); | 1682 writer.enterBlock('if (${test.code}) {'); |
1692 } else { | 1683 } else { |
1693 writer.nextBlock('} else if (${test.code}) {'); | 1684 writer.nextBlock('} else if (${test.code}) {'); |
1694 } | 1685 } |
1695 } else if (i > 0) { | 1686 } else if (i > 0) { |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1880 return new ThisValue(method.declaringType, 'this', | 1871 return new ThisValue(method.declaringType, 'this', |
1881 node != null ? node.span : null); | 1872 node != null ? node.span : null); |
1882 } | 1873 } |
1883 } | 1874 } |
1884 | 1875 |
1885 // ******************* Expressions ******************* | 1876 // ******************* Expressions ******************* |
1886 visitLambdaExpression(LambdaExpression node) { | 1877 visitLambdaExpression(LambdaExpression node) { |
1887 var name = (node.func.name != null) ? node.func.name.name : ''; | 1878 var name = (node.func.name != null) ? node.func.name.name : ''; |
1888 | 1879 |
1889 MethodMember meth = _makeLambdaMethod(name, node.func); | 1880 MethodMember meth = _makeLambdaMethod(name, node.func); |
1890 final lambdaGen = new MethodGenerator(meth, this); | 1881 return meth.methodData.createLambda(node, this); |
1891 if (name != '') { | |
1892 // Note: we don't want to put this in our enclosing scope because the | |
1893 // name shouldn't be visible except inside the lambda. We also don't want | |
1894 // to put the name directly in the lambda's scope because parameters are | |
1895 // allowed to shadow it. So we create an extra scope for it to go into. | |
1896 lambdaGen._scope.create(name, meth.functionType, meth.definition.span, | |
1897 isFinal:true); | |
1898 lambdaGen._pushBlock(node); | |
1899 } | |
1900 lambdaGen.run(); | |
1901 | |
1902 var w = new CodeWriter(); | |
1903 meth.generator.writeDefinition(w, node); | |
1904 return new Value(meth.functionType, w.text, node.span); | |
1905 } | 1882 } |
1906 | 1883 |
1907 visitCallExpression(CallExpression node) { | 1884 visitCallExpression(CallExpression node) { |
1908 var target; | 1885 var target; |
1909 var position = node.target; | 1886 var position = node.target; |
1910 var name = ':call'; | 1887 var name = ':call'; |
1911 if (node.target is DotExpression) { | 1888 if (node.target is DotExpression) { |
1912 DotExpression dot = node.target; | 1889 DotExpression dot = node.target; |
1913 target = dot.self.visit(this); | 1890 target = dot.self.visit(this); |
1914 name = dot.name.name; | 1891 name = dot.name.name; |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2060 | 2037 |
2061 return target.set_(this, xn.name.name, xn.name, y, kind: kind, | 2038 return target.set_(this, xn.name.name, xn.name, y, kind: kind, |
2062 returnKind: returnKind); | 2039 returnKind: returnKind); |
2063 } | 2040 } |
2064 | 2041 |
2065 visitUnaryExpression(UnaryExpression node) { | 2042 visitUnaryExpression(UnaryExpression node) { |
2066 var value = visitValue(node.self); | 2043 var value = visitValue(node.self); |
2067 switch (node.op.kind) { | 2044 switch (node.op.kind) { |
2068 case TokenKind.INCR: | 2045 case TokenKind.INCR: |
2069 case TokenKind.DECR: | 2046 case TokenKind.DECR: |
2070 // TODO(jimhug): Requires non-null num to be correct. | 2047 // TODO(jimhug): Hackish optimization not always correct |
2071 if (value.type.isNum) { | 2048 if (value.type.isNum && !value.isFinal && node.self is VarExpression) { |
2072 return new Value(value.type, '${node.op}${value.code}', node.span); | 2049 return new Value(value.type, '${node.op}${value.code}', node.span); |
2073 } else { | 2050 } else { |
2074 // ++x becomes x += 1 | 2051 // ++x becomes x += 1 |
2075 // --x becomes x -= 1 | 2052 // --x becomes x -= 1 |
2076 // TODO(jimhug): Confirm that --x becomes x -= 1 as it is in VM. | |
2077 var kind = (TokenKind.INCR == node.op.kind ? | 2053 var kind = (TokenKind.INCR == node.op.kind ? |
2078 TokenKind.ADD : TokenKind.SUB); | 2054 TokenKind.ADD : TokenKind.SUB); |
2079 // TODO(jimhug): Shouldn't need a full-expression here. | 2055 // TODO(jimhug): Shouldn't need a full-expression here. |
2080 var operand = new LiteralExpression(Value.fromInt(1, node.span), | 2056 var operand = new LiteralExpression(Value.fromInt(1, node.span), |
2081 node.span); | 2057 node.span); |
2082 | 2058 |
2083 var assignValue = _visitAssign(kind, node.self, operand, node, | 2059 var assignValue = _visitAssign(kind, node.self, operand, node, |
2084 ReturnKind.POST); | 2060 ReturnKind.POST); |
2085 return new Value(assignValue.type, '(${assignValue.code})', | 2061 return new Value(assignValue.type, '(${assignValue.code})', |
2086 node.span); | 2062 node.span); |
2087 } | 2063 } |
2088 } | 2064 } |
2089 return value.unop(node.op.kind, this, node); | 2065 return value.unop(node.op.kind, this, node); |
2090 } | 2066 } |
2091 | 2067 |
2092 visitDeclaredIdentifier(DeclaredIdentifier node) { | 2068 visitDeclaredIdentifier(DeclaredIdentifier node) { |
2093 world.error('Expected expression', node.span); | 2069 world.error('Expected expression', node.span); |
2094 } | 2070 } |
2095 | 2071 |
2096 visitAwaitExpression(AwaitExpression node) { | 2072 visitAwaitExpression(AwaitExpression node) { |
2097 world.internalError( | 2073 world.internalError( |
2098 'Await expressions should have been eliminated before code generation', | 2074 'Await expressions should have been eliminated before code generation', |
2099 node.span); | 2075 node.span); |
2100 } | 2076 } |
2101 | 2077 |
2102 visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { | 2078 visitPostfixExpression(PostfixExpression node, [bool isVoid = false]) { |
2079 // TODO(jimhug): Hackish optimization here to revisit in many ways... | |
2103 var value = visitValue(node.body); | 2080 var value = visitValue(node.body); |
2104 // TODO(jimhug): Move and validate this code with nullable ints. | 2081 if (value.type.isNum && !value.isFinal && node.body is VarExpression) { |
2105 if (value.type.isNum && !value.isFinal) { | 2082 // Would like to also do on "pure" fields - check to see if possible... |
2106 return new Value(value.type, '${value.code}${node.op}', node.span); | 2083 return new Value(value.type, '${value.code}${node.op}', node.span); |
2107 } | 2084 } |
2108 | 2085 |
2109 // x++ is equivalent to (t = x, x = t + 1, t), where we capture all temps | 2086 // x++ is equivalent to (t = x, x = t + 1, t), where we capture all temps |
2110 // needed to evaluate x so we're not evaluating multiple times. Likewise, | 2087 // needed to evaluate x so we're not evaluating multiple times. Likewise, |
2111 // x-- is equivalent to (t = x, x = t - 1, t). | 2088 // x-- is equivalent to (t = x, x = t - 1, t). |
2112 var kind = (TokenKind.INCR == node.op.kind) ? | 2089 var kind = (TokenKind.INCR == node.op.kind) ? |
2113 TokenKind.ADD : TokenKind.SUB; | 2090 TokenKind.ADD : TokenKind.SUB; |
2114 // TODO(jimhug): Shouldn't need a full-expression here. | 2091 // TODO(jimhug): Shouldn't need a full-expression here. |
2115 var operand = new LiteralExpression(Value.fromInt(1, node.span), | 2092 var operand = new LiteralExpression(Value.fromInt(1, node.span), |
2116 node.span); | 2093 node.span); |
2117 var ret = _visitAssign(kind, node.body, operand, node, | 2094 var ret = _visitAssign(kind, node.body, operand, node, |
2118 isVoid ? ReturnKind.IGNORE : ReturnKind.PRE); | 2095 isVoid ? ReturnKind.IGNORE : ReturnKind.PRE); |
2119 return ret; | 2096 return ret; |
2120 } | 2097 } |
2121 | 2098 |
2122 visitNewExpression(NewExpression node) { | 2099 visitNewExpression(NewExpression node) { |
2123 var typeRef = node.type; | 2100 var typeRef = node.type; |
2124 | 2101 |
2125 var constructorName = ''; | 2102 var constructorName = ''; |
2126 if (node.name != null) { | 2103 if (node.name != null) { |
2127 constructorName = node.name.name; | 2104 constructorName = node.name.name; |
2128 } | 2105 } |
2129 | 2106 |
2130 // Named constructors and library prefixes, oh my! | 2107 // Named constructors and library prefixes, oh my! |
2131 // At last, we can collapse the ambiguous wave function... | 2108 // At last, we can collapse the ambiguous wave function... |
2132 if (constructorName == '' && typeRef is !GenericTypeReference && | 2109 if (constructorName == '' && typeRef is NameTypeReference && |
2133 typeRef.names != null) { | 2110 typeRef.names != null) { |
2134 | 2111 |
2135 // Pull off the last name from the type, guess it's the constructor name. | 2112 // Pull off the last name from the type, guess it's the constructor name. |
2136 var names = new List.from(typeRef.names); | 2113 var names = new List.from(typeRef.names); |
2137 constructorName = names.removeLast().name; | 2114 constructorName = names.removeLast().name; |
2138 if (names.length == 0) names = null; | 2115 if (names.length == 0) names = null; |
2139 | 2116 |
2140 typeRef = new NameTypeReference( | 2117 typeRef = new NameTypeReference( |
2141 typeRef.isFinal, typeRef.name, names, typeRef.span); | 2118 typeRef.isFinal, typeRef.name, names, typeRef.span); |
2142 } | 2119 } |
2143 | 2120 |
2144 var type = method.resolveType(typeRef, true); | 2121 var type = method.resolveType(typeRef, true, true); |
2145 if (type.isTop) { | 2122 if (type.isTop) { |
2146 type = type.library.findTypeByName(constructorName); | 2123 type = type.library.findTypeByName(constructorName); |
2147 constructorName = ''; | 2124 constructorName = ''; |
2148 } | 2125 } |
2149 | 2126 |
2150 if (type is ParameterType) { | 2127 if (type is ParameterType) { |
2151 world.error('cannot instantiate a type parameter', node.span); | 2128 world.error('cannot instantiate a type parameter', node.span); |
2152 return _makeMissingValue(constructorName); | 2129 return _makeMissingValue(constructorName); |
2153 } | 2130 } |
2154 | 2131 |
(...skipping 24 matching lines...) Expand all Loading... | |
2179 // interface, not the class. | 2156 // interface, not the class. |
2180 var target = new TypeValue(type, typeRef.span); | 2157 var target = new TypeValue(type, typeRef.span); |
2181 return m.invoke(this, node, target, _makeArgs(node.arguments)); | 2158 return m.invoke(this, node, target, _makeArgs(node.arguments)); |
2182 } | 2159 } |
2183 | 2160 |
2184 visitListExpression(ListExpression node) { | 2161 visitListExpression(ListExpression node) { |
2185 var argValues = []; | 2162 var argValues = []; |
2186 var listType = world.listType; | 2163 var listType = world.listType; |
2187 var type = world.varType; | 2164 var type = world.varType; |
2188 if (node.itemType != null) { | 2165 if (node.itemType != null) { |
2189 type = method.resolveType(node.itemType, true); | 2166 type = method.resolveType(node.itemType, true, !node.isConst); |
2190 if (node.isConst && (type is ParameterType || type.hasTypeParams)) { | 2167 if (node.isConst && (type is ParameterType || type.hasTypeParams)) { |
2191 world.error('type parameter cannot be used in const list literals'); | 2168 world.error('type parameter cannot be used in const list literals'); |
2192 } | 2169 } |
2193 listType = listType.getOrMakeConcreteType([type]); | 2170 listType = listType.getOrMakeConcreteType([type]); |
2194 } | 2171 } |
2195 for (var item in node.values) { | 2172 for (var item in node.values) { |
2196 var arg = visitTypedValue(item, type); | 2173 var arg = visitTypedValue(item, type); |
2197 argValues.add(arg); | 2174 argValues.add(arg); |
2198 if (node.isConst && !arg.isConst) { | 2175 if (node.isConst && !arg.isConst) { |
2199 world.error('const list can only contain const values', arg.span); | 2176 world.error('const list can only contain const values', arg.span); |
(...skipping 13 matching lines...) Expand all Loading... | |
2213 if (node.items.length == 0 && !node.isConst) { | 2190 if (node.items.length == 0 && !node.isConst) { |
2214 return world.mapType.getConstructor('').invoke(this, node, | 2191 return world.mapType.getConstructor('').invoke(this, node, |
2215 new TypeValue(world.mapType, node.span), Arguments.EMPTY); | 2192 new TypeValue(world.mapType, node.span), Arguments.EMPTY); |
2216 } | 2193 } |
2217 | 2194 |
2218 var values = <Value>[]; | 2195 var values = <Value>[]; |
2219 var valueType = world.varType, keyType = world.stringType; | 2196 var valueType = world.varType, keyType = world.stringType; |
2220 var mapType = world.mapType; // TODO(jimhug): immutable type? | 2197 var mapType = world.mapType; // TODO(jimhug): immutable type? |
2221 if (node.valueType !== null) { | 2198 if (node.valueType !== null) { |
2222 if (node.keyType !== null) { | 2199 if (node.keyType !== null) { |
2223 keyType = method.resolveType(node.keyType, true); | 2200 keyType = method.resolveType(node.keyType, true, !node.isConst); |
2224 // TODO(jimhug): Would be nice to allow arbitrary keys here (this is | 2201 // TODO(jimhug): Would be nice to allow arbitrary keys here (this is |
2225 // currently not allowed by the spec). | 2202 // currently not allowed by the spec). |
2226 if (!keyType.isString) { | 2203 if (!keyType.isString) { |
2227 world.error('the key type of a map literal must be "String"', | 2204 world.error('the key type of a map literal must be "String"', |
2228 keyType.span); | 2205 keyType.span); |
2229 } | 2206 } |
2230 if (node.isConst && | 2207 if (node.isConst && |
2231 (keyType is ParameterType || keyType.hasTypeParams)) { | 2208 (keyType is ParameterType || keyType.hasTypeParams)) { |
2232 world.error('type parameter cannot be used in const map literals'); | 2209 world.error('type parameter cannot be used in const map literals'); |
2233 } | 2210 } |
2234 } | 2211 } |
2235 | 2212 |
2236 valueType = method.resolveType(node.valueType, true); | 2213 valueType = method.resolveType(node.valueType, true, !node.isConst); |
2237 if (node.isConst && | 2214 if (node.isConst && |
2238 (valueType is ParameterType || valueType.hasTypeParams)) { | 2215 (valueType is ParameterType || valueType.hasTypeParams)) { |
2239 world.error('type parameter cannot be used in const map literals'); | 2216 world.error('type parameter cannot be used in const map literals'); |
2240 } | 2217 } |
2241 | 2218 |
2242 mapType = mapType.getOrMakeConcreteType([keyType, valueType]); | 2219 mapType = mapType.getOrMakeConcreteType([keyType, valueType]); |
2243 } | 2220 } |
2244 | 2221 |
2245 for (int i = 0; i < node.items.length; i += 2) { | 2222 for (int i = 0; i < node.items.length; i += 2) { |
2246 var key = visitTypedValue(node.items[i], keyType); | 2223 var key = visitTypedValue(node.items[i], keyType); |
(...skipping 20 matching lines...) Expand all Loading... | |
2267 var falseBranch = visitValue(node.falseBranch); | 2244 var falseBranch = visitValue(node.falseBranch); |
2268 | 2245 |
2269 // TODO(jmesserly): is there a way to use Value.union here, even though | 2246 // TODO(jmesserly): is there a way to use Value.union here, even though |
2270 // we need different code? | 2247 // we need different code? |
2271 return new Value(Type.union(trueBranch.type, falseBranch.type), | 2248 return new Value(Type.union(trueBranch.type, falseBranch.type), |
2272 '${test.code} ? ${trueBranch.code} : ${falseBranch.code}', node.span); | 2249 '${test.code} ? ${trueBranch.code} : ${falseBranch.code}', node.span); |
2273 } | 2250 } |
2274 | 2251 |
2275 visitIsExpression(IsExpression node) { | 2252 visitIsExpression(IsExpression node) { |
2276 var value = visitValue(node.x); | 2253 var value = visitValue(node.x); |
2277 var type = method.resolveType(node.type, false); | 2254 var type = method.resolveType(node.type, true, true); |
2255 if (type.isVar) { | |
2256 return Value.comma(value, Value.fromBool(true, node.span)); | |
2257 } | |
2258 | |
2278 return value.instanceOf(this, type, node.span, node.isTrue); | 2259 return value.instanceOf(this, type, node.span, node.isTrue); |
2279 } | 2260 } |
2280 | 2261 |
2281 visitParenExpression(ParenExpression node) { | 2262 visitParenExpression(ParenExpression node) { |
2282 var body = visitValue(node.body); | 2263 var body = visitValue(node.body); |
2283 // Assumption implicit here that const values never need parens... | 2264 // Assumption implicit here that const values never need parens... |
2284 if (body.isConst) return body; | 2265 if (body.isConst) return body; |
2285 return new Value(body.type, '(${body.code})', node.span); | 2266 return new Value(body.type, '(${body.code})', node.span); |
2286 } | 2267 } |
2287 | 2268 |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2465 for (int i = 0; i < bareCount; i++) { | 2446 for (int i = 0; i < bareCount; i++) { |
2466 result.add(new VariableValue(world.varType, '\$$i', null)); | 2447 result.add(new VariableValue(world.varType, '\$$i', null)); |
2467 } | 2448 } |
2468 for (int i = bareCount; i < length; i++) { | 2449 for (int i = bareCount; i < length; i++) { |
2469 var name = getName(i); | 2450 var name = getName(i); |
2470 if (name == null) name = '\$$i'; | 2451 if (name == null) name = '\$$i'; |
2471 result.add(new VariableValue(world.varType, name, null)); | 2452 result.add(new VariableValue(world.varType, name, null)); |
2472 } | 2453 } |
2473 return new Arguments(nodes, result); | 2454 return new Arguments(nodes, result); |
2474 } | 2455 } |
2456 | |
2457 bool matches(Arguments other) { | |
2458 if (length != other.length) return false; | |
2459 if (bareCount != other.bareCount) return false; | |
2460 | |
2461 for (int i = 0; i < bareCount; i++) { | |
2462 if (values[i].type != other.values[i].type) return false; | |
2463 } | |
2464 // TODO(jimhug): Needs to check that named args also match! | |
2465 return true; | |
2466 } | |
2467 | |
2475 } | 2468 } |
2476 | 2469 |
2477 class ReturnKind { | 2470 class ReturnKind { |
2478 static final int IGNORE = 1; | 2471 static final int IGNORE = 1; |
2479 static final int POST = 2; | 2472 static final int POST = 2; |
2480 static final int PRE = 3; | 2473 static final int PRE = 3; |
2481 } | 2474 } |
OLD | NEW |