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

Side by Side Diff: frog/gen.dart

Issue 9270048: Lots of frog cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/element.dart ('k') | frog/lang.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « frog/element.dart ('k') | frog/lang.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698