Chromium Code Reviews| 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 |