| 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 * |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 // These are essentially always used through literals - just include them | 49 // These are essentially always used through literals - just include them |
| 50 world.numImplType.markUsed(); | 50 world.numImplType.markUsed(); |
| 51 world.stringImplType.markUsed(); | 51 world.stringImplType.markUsed(); |
| 52 | 52 |
| 53 // Only include isolate-specific code if isolates are used. | 53 // Only include isolate-specific code if isolates are used. |
| 54 if (world.corelib.types['Isolate'].isUsed | 54 if (world.corelib.types['Isolate'].isUsed |
| 55 || world.coreimpl.types['ReceivePortImpl'].isUsed) { | 55 || world.coreimpl.types['ReceivePortImpl'].isUsed) { |
| 56 | 56 |
| 57 // Generate callbacks from JS to isolate code if needed | 57 // Generate callbacks from JS to isolate code if needed |
| 58 if (corejs.useWrap0 || corejs.useWrap1) { | 58 if (corejs.useWrap0 || corejs.useWrap1) { |
| 59 genMethod(world.coreimpl.types['IsolateContext'].getMember('eval')); | 59 invokeMethod(world.coreimpl.types['IsolateContext'].getMember('eval')); |
| 60 genMethod(world.coreimpl.types['EventLoop'].getMember('run')); | 60 invokeMethod(world.coreimpl.types['EventLoop'].getMember('run')); |
| 61 } | 61 } |
| 62 | 62 |
| 63 corejs.useIsolates = true; | 63 corejs.useIsolates = true; |
| 64 MethodMember isolateMain = | 64 MethodMember isolateMain = |
| 65 world.coreimpl.lookup('startRootIsolate', main.span); | 65 world.coreimpl.lookup('startRootIsolate', main.span); |
| 66 var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); | 66 var isolateMainTarget = new TypeValue(world.coreimpl.topType, main.span); |
| 67 mainCall = isolateMain.invoke(metaGen, null, isolateMainTarget, | 67 mainCall = isolateMain.invoke(metaGen, null, isolateMainTarget, |
| 68 new Arguments(null, [main._get(metaGen, main.definition, null)])); | 68 new Arguments(null, [main._get(metaGen, main.definition, null)])); |
| 69 } | 69 } |
| 70 | 70 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 95 type.markUsed(); | 95 type.markUsed(); |
| 96 type.isTested = true; | 96 type.isTested = true; |
| 97 // (e.g. Math, console, process) | 97 // (e.g. Math, console, process) |
| 98 type.isTested = !type.isTop && !(type.isNative && | 98 type.isTested = !type.isTop && !(type.isNative && |
| 99 type.members.getValues().every((m) => m.isStatic && !m.isFactory)); | 99 type.members.getValues().every((m) => m.isStatic && !m.isFactory)); |
| 100 final members = new List.from(type.members.getValues()); | 100 final members = new List.from(type.members.getValues()); |
| 101 members.addAll(type.constructors.getValues()); | 101 members.addAll(type.constructors.getValues()); |
| 102 type.factories.forEach((f) => members.add(f)); | 102 type.factories.forEach((f) => members.add(f)); |
| 103 for (var member in members) { | 103 for (var member in members) { |
| 104 if (member is PropertyMember) { | 104 if (member is PropertyMember) { |
| 105 if (member.getter != null) genMethod(member.getter); | 105 if (member.getter != null) invokeMethod(member.getter); |
| 106 if (member.setter != null) genMethod(member.setter); | 106 if (member.setter != null) invokeMethod(member.setter); |
| 107 } | 107 } |
| 108 | 108 |
| 109 if (member is MethodMember) genMethod(member); | 109 if (member is MethodMember) invokeMethod(member); |
| 110 } | 110 } |
| 111 } | 111 } |
| 112 | 112 |
| 113 void writeAllDynamicStubs(List<Library> libs) => | 113 void writeAllDynamicStubs(List<Library> libs) => |
| 114 getAllTypes(libs).forEach((Type type) { | 114 getAllTypes(libs).forEach((Type type) { |
| 115 if (type.isClass || type.isFunction) _writeDynamicStubs(type); | 115 if (type.isClass || type.isFunction) _writeDynamicStubs(type); |
| 116 }); | 116 }); |
| 117 | 117 |
| 118 List<Type> getAllTypes(List<Library> libs) { | 118 List<Type> getAllTypes(List<Library> libs) { |
| 119 List<Type> types = <Type>[]; | 119 List<Type> types = <Type>[]; |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 writer.comment('// ********** Code for ${type.jsname} **************'); | 219 writer.comment('// ********** Code for ${type.jsname} **************'); |
| 220 _writeDynamicStubs(type); | 220 _writeDynamicStubs(type); |
| 221 } | 221 } |
| 222 // Type check functions for builtin JS types | 222 // Type check functions for builtin JS types |
| 223 if (type.typeCheckCode != null) { | 223 if (type.typeCheckCode != null) { |
| 224 writer.writeln(type.typeCheckCode); | 224 writer.writeln(type.typeCheckCode); |
| 225 } | 225 } |
| 226 } | 226 } |
| 227 } | 227 } |
| 228 | 228 |
| 229 genMethod(Member meth, [MethodGenerator enclosingMethod=null]) { | 229 Type invokeMethod(Member method, [Value target, Arguments args]) { |
| 230 if (!meth.isGenerated && !meth.isAbstract && meth.definition != null) { | 230 if (method.isAbstract || method.definition == null) { |
| 231 new MethodGenerator(meth, enclosingMethod).run(); | 231 return method.returnType; |
| 232 } | 232 } |
| 233 |
| 234 var s = method.specializer; |
| 235 if (s == null) { |
| 236 s = new MethodSpecializer(method); |
| 237 } |
| 238 return s.run(target, args); |
| 233 } | 239 } |
| 234 | 240 |
| 235 String _prototypeOf(Type type, String name) { | 241 String _prototypeOf(Type type, String name) { |
| 236 if (type.isSingletonNative) { | 242 if (type.isSingletonNative) { |
| 237 // e.g. window.console.log$1 | 243 // e.g. window.console.log$1 |
| 238 return '${type.jsname}.$name'; | 244 return '${type.jsname}.$name'; |
| 239 } else if (type.isHiddenNativeType) { | 245 } else if (type.isHiddenNativeType) { |
| 240 corejs.ensureDynamicProto(); | 246 corejs.ensureDynamicProto(); |
| 241 _usedDynamicDispatchOnType(type); | 247 _usedDynamicDispatchOnType(type); |
| 242 return '\$dynamic("$name").${type.definition.nativeType.name}'; | 248 return '\$dynamic("$name").${type.definition.nativeType.name}'; |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 366 | 372 |
| 367 if (type.isTop) { | 373 if (type.isTop) { |
| 368 // no preludes for top type | 374 // no preludes for top type |
| 369 } else if (type.constructors.length == 0) { | 375 } else if (type.constructors.length == 0) { |
| 370 if (!type.isNative) { | 376 if (!type.isNative) { |
| 371 // TODO(jimhug): More guards to guarantee staticness | 377 // TODO(jimhug): More guards to guarantee staticness |
| 372 writer.writeln('function ${type.jsname}() {}'); | 378 writer.writeln('function ${type.jsname}() {}'); |
| 373 } | 379 } |
| 374 } else { | 380 } else { |
| 375 Member standardConstructor = type.constructors['']; | 381 Member standardConstructor = type.constructors['']; |
| 376 if (standardConstructor == null || | 382 if (standardConstructor == null || !standardConstructor.isUsed) { |
| 377 standardConstructor.generator == null) { | |
| 378 if (!type.isNative) { | 383 if (!type.isNative) { |
| 379 writer.writeln('function ${type.jsname}() {}'); | 384 writer.writeln('function ${type.jsname}() {}'); |
| 380 } | 385 } |
| 381 } else { | 386 } else { |
| 382 standardConstructor.generator.writeDefinition(writer, null); | 387 standardConstructor.generator.writeDefinition(writer, null); |
| 383 } | 388 } |
| 384 | 389 |
| 385 for (var c in type.constructors.getValues()) { | 390 for (var c in type.constructors.getValues()) { |
| 386 if (c.generator != null && c != standardConstructor) { | 391 if (c.isUsed && c != standardConstructor) { |
| 387 c.generator.writeDefinition(writer, null); | 392 c.generator.writeDefinition(writer, null); |
| 388 } | 393 } |
| 389 } | 394 } |
| 390 } | 395 } |
| 391 | 396 |
| 392 // Concrete types (like List<String>) will have this already defined on | 397 // Concrete types (like List<String>) will have this already defined on |
| 393 // their prototype from the generic type (like List) | 398 // their prototype from the generic type (like List) |
| 394 if (type is! ConcreteType) { | 399 if (type is! ConcreteType) { |
| 395 _maybeIsTest(type, type); | 400 _maybeIsTest(type, type); |
| 396 } | 401 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 * } | 457 * } |
| 453 * | 458 * |
| 454 * The factory method and static member are generated something like this: | 459 * The factory method and static member are generated something like this: |
| 455 * var lib_Float32Array = {}; | 460 * var lib_Float32Array = {}; |
| 456 * lib_Float32Array.Float32Array$factory = ... ; | 461 * lib_Float32Array.Float32Array$factory = ... ; |
| 457 * lib_Float32Array._construct = ... ; | 462 * lib_Float32Array._construct = ... ; |
| 458 * | 463 * |
| 459 * This predicate determines when we need to define lib_Float32Array. | 464 * This predicate determines when we need to define lib_Float32Array. |
| 460 */ | 465 */ |
| 461 _typeNeedsHolderForStaticMethods(Type type) { | 466 _typeNeedsHolderForStaticMethods(Type type) { |
| 462 for (var member in type.members.getValues()) { | 467 return type.members.getValues().some( |
| 463 if (member.isMethod) { | 468 (m) => m.isUsed && m.isMethod && (m.isConstructor || m.isStatic)); |
| 464 if (member.isConstructor || member.isStatic) { | |
| 465 if (member.isGenerated) { | |
| 466 return true; | |
| 467 } | |
| 468 } | |
| 469 } | |
| 470 } | |
| 471 return false; | |
| 472 } | 469 } |
| 473 | 470 |
| 474 /** | 471 /** |
| 475 * Generates the $inheritsMembers function when it's first used. | 472 * Generates the $inheritsMembers function when it's first used. |
| 476 * This is used to mix in specialized generic members from the base class. | 473 * This is used to mix in specialized generic members from the base class. |
| 477 */ | 474 */ |
| 478 _ensureInheritMembersHelper() { | 475 _ensureInheritMembersHelper() { |
| 479 if (_mixins != null) return; | 476 if (_mixins != null) return; |
| 480 _mixins = new CodeWriter(); | 477 _mixins = new CodeWriter(); |
| 481 _mixins.comment('// ********** Generic Type Inheritance **************'); | 478 _mixins.comment('// ********** Generic Type Inheritance **************'); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 551 } | 548 } |
| 552 if (property.setter != null) { | 549 if (property.setter != null) { |
| 553 writer.writeln( | 550 writer.writeln( |
| 554 'set: ${property.declaringType.jsname}.prototype.${property.setter.jsn
ame}'); | 551 'set: ${property.declaringType.jsname}.prototype.${property.setter.jsn
ame}'); |
| 555 } | 552 } |
| 556 writer.exitBlock('});'); | 553 writer.exitBlock('});'); |
| 557 } | 554 } |
| 558 } | 555 } |
| 559 | 556 |
| 560 _writeMethod(Member m) { | 557 _writeMethod(Member m) { |
| 561 if (m.generator != null) { | 558 if (m.isUsed) { |
| 562 m.generator.writeDefinition(writer, null); | 559 m.generator.writeDefinition(writer, null); |
| 563 } else if (m is MethodMember && m.isNative | 560 } else if (m is MethodMember && m.isNative |
| 564 && m._providePropertySyntax && !m._provideFieldSyntax) { | 561 && m._providePropertySyntax && !m._provideFieldSyntax) { |
| 565 MethodGenerator._maybeGenerateBoundGetter(m, writer); | 562 MethodGenerator._maybeGenerateBoundGetter(m, writer); |
| 566 } | 563 } |
| 567 } | 564 } |
| 568 | 565 |
| 569 writeGlobals() { | 566 writeGlobals() { |
| 570 if (globals.length > 0) { | 567 if (globals.length > 0) { |
| 571 writer.comment('// ********** Globals **************'); | 568 writer.comment('// ********** Globals **************'); |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 738 if (x.span == null) return 1; | 735 if (x.span == null) return 1; |
| 739 if (y.span == null) return -1; | 736 if (y.span == null) return -1; |
| 740 | 737 |
| 741 // If that fails, compare by name. | 738 // If that fails, compare by name. |
| 742 return x.name.compareTo(y.name); | 739 return x.name.compareTo(y.name); |
| 743 } | 740 } |
| 744 } | 741 } |
| 745 | 742 |
| 746 | 743 |
| 747 /** | 744 /** |
| 748 * A naive code generator for Dart. | 745 * When inferring types, this will support potentially creating multiple |
| 746 * copies of a method with different signatures. We don't implement that yet, |
| 747 * though. Right now it's just a way of guarding against recursive invokes |
| 748 * and tracking [option.inferTypes] specific state for a method. |
| 749 */ | 749 */ |
| 750 // TODO(jmesserly): better name for this and MethodGenerator? (Perhaps |
| 751 // this should be named MethodGenerator and the other one should be |
| 752 // MethodInterpreter or something like that.) |
| 753 class MethodSpecializer { |
| 754 final Member method; |
| 755 final MethodGenerator enclosingMethod; |
| 756 |
| 757 // These are used to track what inferred types are assumed by the generated |
| 758 // code. Eventually we may want to use this as a key and generate multiple |
| 759 // typed copies of a method. However that needs good heuristics to avoid |
| 760 // making the code too big. |
| 761 Value _thisObject; |
| 762 List<VariableValue> _arguments; |
| 763 int _evalCount = 0; |
| 764 |
| 765 MethodGenerator generator; |
| 766 |
| 767 MethodSpecializer(this.method, [this.enclosingMethod]) { |
| 768 assert(method.specializer === null); |
| 769 // TODO(jmesserly): I don't like side effects from constructors, but |
| 770 // otherwise we rely on our callers to maintain this invariant which isn't |
| 771 // great either. |
| 772 method.specializer = this; |
| 773 reset(); |
| 774 } |
| 775 |
| 776 reset() { |
| 777 _thisObject = new VariableValue(method.declaringType, 'this', null); |
| 778 _arguments = method.parameters.map( |
| 779 (p) => new VariableValue(p.type, p.name, p.span)); |
| 780 } |
| 781 |
| 782 // TODO(jmesserly): Should this be renamed invoke? |
| 783 // Also our caller has already taken care of named/missing args, but that's |
| 784 // probably not what we want. Instead that could be handled here, and we |
| 785 // could return a Value that performs the method call. (In other words, |
| 786 // absorb some of MethodMember.invoke's current implementation.) |
| 787 Type run([Value target, Arguments args]) { |
| 788 method.markUsed(); |
| 789 |
| 790 // If this is the first time we've seen this call, or the argument types are |
| 791 // now different, we'll need to interpret the method body. |
| 792 bool shouldEval = _evalCount == 0; |
| 793 bool infer = options.inferTypes && _evalCount <= options.maxInferenceCalls; |
| 794 |
| 795 // Unless type inference is on, ignore passed in target & arg types. |
| 796 if (infer) { |
| 797 // TODO(jmesserly): should unify the target type too! |
| 798 List<Value> argValues; |
| 799 if (args == null) { |
| 800 // TODO(jmesserly): we should always be passing in arguments |
| 801 argValues = method.parameters.map( |
| 802 (p) => new Value(p.type, p.name, p.span)); |
| 803 } else { |
| 804 argValues = args.values; |
| 805 } |
| 806 |
| 807 for (int i = 0; i < _arguments.length; i++) { |
| 808 var orig = _arguments[i].value; |
| 809 var updated = Value.union(orig, argValues[i]); |
| 810 if (updated !== orig) { |
| 811 _arguments[i] = _arguments[i].replaceValue(updated); |
| 812 shouldEval = true; |
| 813 } |
| 814 } |
| 815 } |
| 816 |
| 817 if (shouldEval) { |
| 818 _evalCount++; |
| 819 |
| 820 if (infer && _evalCount > options.maxInferenceCalls) { |
| 821 // If we've tried too many times and haven't converged, rerun without |
| 822 // inference. |
| 823 world.info('inference failed to converge for method "${method.name}"', |
| 824 method.span); |
| 825 |
| 826 // Discard inferred things so we do a fresh run. |
| 827 reset(); |
| 828 } |
| 829 |
| 830 // Note: this might replace a generator from higher up on our call stack, |
| 831 // if we're calling this method recursively (which is rather common, e.g. |
| 832 // toString methods are potentially recursive). That's okay: the later |
| 833 // generator will have collected more type info, so let it win. |
| 834 // (Eventually we'll have some specialization, and key these off of the |
| 835 // input types.) |
| 836 generator = new MethodGenerator(method, enclosingMethod); |
| 837 generator.evalBody(_thisObject, new Arguments(null, _arguments)); |
| 838 } |
| 839 |
| 840 return generator._inferResult(); |
| 841 } |
| 842 } |
| 843 |
| 844 |
| 845 /** A code generator and abstract interpreter for Dart function/method. */ |
| 750 class MethodGenerator implements TreeVisitor { | 846 class MethodGenerator implements TreeVisitor { |
| 751 Member method; | 847 final Member method; |
| 848 final MethodGenerator enclosingMethod; |
| 752 CodeWriter writer; | 849 CodeWriter writer; |
| 753 BlockScope _scope; | 850 BlockScope _scope; |
| 754 MethodGenerator enclosingMethod; | |
| 755 bool needsThis; | 851 bool needsThis; |
| 756 List<String> _paramCode; | 852 List<String> _paramCode; |
| 757 | 853 |
| 758 // TODO(jmesserly): if we knew temps were always used like a stack, we could | 854 // TODO(jmesserly): if we knew temps were always used like a stack, we could |
| 759 // reduce the overhead here. | 855 // reduce the overhead here. |
| 760 List<String> _freeTemps; | 856 List<String> _freeTemps; |
| 761 Set<String> _usedTemps; | 857 Set<String> _usedTemps; |
| 762 | 858 |
| 763 /** | 859 /** |
| 764 * The set of variables that this lambda closes that need to capture | 860 * The set of variables that this lambda closes that need to capture |
| 765 * with Function.prototype.bind. This is any variable that lives inside a | 861 * with Function.prototype.bind. This is any variable that lives inside a |
| 766 * reentrant block scope (e.g. loop bodies). | 862 * reentrant block scope (e.g. loop bodies). |
| 767 * | 863 * |
| 768 * This field is null if we don't need to track this. | 864 * This field is null if we don't need to track this. |
| 769 */ | 865 */ |
| 770 Set<String> captures; | 866 Set<String> captures; |
| 771 | 867 |
| 868 /** The inferred result type. */ |
| 869 Value _inferredResult; |
| 772 CounterLog counters; | 870 CounterLog counters; |
| 773 | 871 |
| 774 MethodGenerator(this.method, this.enclosingMethod) | 872 MethodGenerator(this.method, [this.enclosingMethod]) |
| 775 : writer = new CodeWriter(), needsThis = false { | 873 : writer = new CodeWriter(), needsThis = false { |
| 776 if (enclosingMethod != null) { | 874 if (enclosingMethod != null) { |
| 777 _scope = new BlockScope(this, enclosingMethod._scope, method.definition); | 875 _scope = new BlockScope(this, enclosingMethod._scope, method.definition); |
| 778 captures = new Set(); | 876 captures = new Set(); |
| 779 } else { | 877 } else { |
| 780 _scope = new BlockScope(this, null, method.definition); | 878 _scope = new BlockScope(this, null, method.definition); |
| 781 } | 879 } |
| 782 _usedTemps = new Set(); | 880 _usedTemps = new Set(); |
| 783 _freeTemps = []; | 881 _freeTemps = []; |
| 784 counters = world.counters; | 882 counters = world.counters; |
| 785 } | 883 } |
| 786 | 884 |
| 885 // TODO(jmesserly): do we need anything else here? (e.g. do we need to replace |
| 886 // the scope with an empty scope so variables don't leak?) |
| 887 Value evalExpression(Expression expr) => expr.visit(this); |
| 888 |
| 787 Library get library() => method.library; | 889 Library get library() => method.library; |
| 788 | 890 |
| 789 // TODO(jimhug): Where does this really belong? | 891 // TODO(jimhug): Where does this really belong? |
| 790 MemberSet findMembers(String name) { | 892 MemberSet findMembers(String name) { |
| 791 return library._findMembers(name); | 893 return library._findMembers(name); |
| 792 } | 894 } |
| 793 | 895 |
| 794 bool get isClosure() => (enclosingMethod != null); | 896 bool get isClosure() => (enclosingMethod != null); |
| 795 | 897 |
| 796 bool get isStatic() => method.isStatic; | 898 bool get isStatic() => method.isStatic; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 826 /* | 928 /* |
| 827 if (_usedTemps.remove(value.code)) { | 929 if (_usedTemps.remove(value.code)) { |
| 828 _freeTemps.add(value.code); | 930 _freeTemps.add(value.code); |
| 829 } else { | 931 } else { |
| 830 world.internalError( | 932 world.internalError( |
| 831 'tried to free unused value or non-temp "${value.code}"'); | 933 'tried to free unused value or non-temp "${value.code}"'); |
| 832 } | 934 } |
| 833 */ | 935 */ |
| 834 } | 936 } |
| 835 | 937 |
| 836 run() { | 938 Type _inferResult() { |
| 837 if (method.isGenerated) return; | 939 if (method.isNative || !options.inferTypes) { |
| 940 // If it's a native, use the return type. |
| 941 var t = method.returnType; |
| 942 if (t.isBool && (method.library.isCore || method.library.isCoreImpl)) { |
| 943 // We trust our core libraries not to return null from bools. |
| 944 // I hope this trust is well placed! |
| 945 t = world.nonNullBool; |
| 946 } |
| 947 return t; |
| 948 } |
| 838 | 949 |
| 839 // This avoids any attempts to infer across recursion. | 950 if (_inferredResult == null) { |
| 840 method.isGenerated = true; | 951 // Handle the implicit 'return null;' |
| 841 method.generator = this; | 952 // TODO(jmesserly): this needs to be added in more cases, e.g. if there |
| 953 // is a "return" statement in one control flow path but not another |
| 954 // path. |
| 955 _inferredResult = Value.fromNull(null); |
| 956 } |
| 957 return _inferredResult.type; |
| 958 } |
| 842 | 959 |
| 843 // Create most generic possible call for this method. | 960 _evalNativeBody() { |
| 844 var thisObject; | 961 // TODO: the .dynamic here is annoying |
| 845 if (method.isConstructor) { | 962 var nativeBody = method.definition.dynamic.nativeBody; |
| 846 thisObject = new ObjectValue(false, method.declaringType, method.span); | 963 if (nativeBody != null) { |
| 847 thisObject.initFields(); | |
| 848 } else { | |
| 849 thisObject = new Value(method.declaringType, 'this', null); | |
| 850 } | |
| 851 var values = []; | |
| 852 for (var p in method.parameters) { | |
| 853 values.add(new Value(p.type, p.name, null)); | |
| 854 } | |
| 855 var args = new Arguments(null, values); | |
| 856 | |
| 857 evalBody(thisObject, args); | |
| 858 | |
| 859 if (method.definition.nativeBody != null) { | |
| 860 // Throw away the code--it was just used for tree shaking purposes. | 964 // Throw away the code--it was just used for tree shaking purposes. |
| 861 writer = new CodeWriter(); | 965 if (nativeBody == '') { |
| 862 if (method.definition.nativeBody == '') { | 966 // Set this to null so we don't try to write code. |
| 863 method.generator = null; | 967 writer = null; |
| 864 } else { | 968 } else { |
| 865 _paramCode = map(method.parameters, (p) => p.name); | 969 _paramCode = map(method.parameters, (p) => p.name); |
| 866 writer.write(method.definition.nativeBody); | 970 writer = new CodeWriter(); |
| 971 writer.write(nativeBody); |
| 867 } | 972 } |
| 868 } | 973 } |
| 869 } | 974 } |
| 870 | 975 |
| 976 writeDefinition(CodeWriter defWriter, LambdaExpression lambda/*=null*/) { |
| 977 // No need to write anything for natives with no native body. |
| 978 if (writer == null) return; |
| 871 | 979 |
| 872 writeDefinition(CodeWriter defWriter, LambdaExpression lambda/*=null*/) { | |
| 873 // To implement block scope: capture any variables we need to. | 980 // To implement block scope: capture any variables we need to. |
| 874 var paramCode = _paramCode; | 981 var paramCode = _paramCode; |
| 875 var names = null; | 982 var names = null; |
| 876 if (captures != null && captures.length > 0) { | 983 if (captures != null && captures.length > 0) { |
| 877 names = new List.from(captures); | 984 names = new List.from(captures); |
| 878 names.sort((x, y) => x.compareTo(y)); | 985 names.sort((x, y) => x.compareTo(y)); |
| 879 // Prepend these as extra parameters. We'll bind them below. | 986 // Prepend these as extra parameters. We'll bind them below. |
| 880 paramCode = new List.from(names); | 987 paramCode = new List.from(names); |
| 881 paramCode.addAll(_paramCode); | 988 paramCode.addAll(_paramCode); |
| 882 } | 989 } |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1008 var field = method.declaringType.getMember(name); | 1115 var field = method.declaringType.getMember(name); |
| 1009 if (field == null) { | 1116 if (field == null) { |
| 1010 world.error('bad initializer - no matching field', span); | 1117 world.error('bad initializer - no matching field', span); |
| 1011 } | 1118 } |
| 1012 if (!field.isField) { | 1119 if (!field.isField) { |
| 1013 world.error('"this.${name}" does not refer to a field', span); | 1120 world.error('"this.${name}" does not refer to a field', span); |
| 1014 } | 1121 } |
| 1015 return newObject.setField(field, value, duringInit: true); | 1122 return newObject.setField(field, value, duringInit: true); |
| 1016 } | 1123 } |
| 1017 | 1124 |
| 1018 evalBody(Value newObject, Arguments args) { | 1125 void evalBody(thisObject, Arguments args) { |
| 1126 if (method.isConstructor && thisObject is! ObjectValue) { |
| 1127 // TODO(jmesserly): fix how contructors interact... |
| 1128 thisObject = new ObjectValue(false, method.declaringType, method.span); |
| 1129 thisObject.initFields(); |
| 1130 } |
| 1131 |
| 1019 bool fieldsSet = false; | 1132 bool fieldsSet = false; |
| 1020 if (method.isNative && method.isConstructor && newObject is ObjectValue) { | 1133 if (method.isNative && method.isConstructor) { |
| 1021 newObject.dynamic.seenNativeInitializer = true; | 1134 thisObject.dynamic.seenNativeInitializer = true; |
| 1022 } | 1135 } |
| 1023 // Collects parameters for writing signature in the future. | 1136 // Collects parameters for writing signature in the future. |
| 1024 _paramCode = []; | 1137 _paramCode = []; |
| 1025 for (int i = 0; i < method.parameters.length; i++) { | 1138 for (int i = 0; i < method.parameters.length; i++) { |
| 1026 var p = method.parameters[i]; | 1139 var p = method.parameters[i]; |
| 1027 Value currentArg = null; | 1140 Value currentArg = null; |
| 1028 // TODO(jimhug): bareCount is O(N) | |
| 1029 if (i < args.bareCount) { | 1141 if (i < args.bareCount) { |
| 1030 currentArg = args.values[i]; | 1142 currentArg = args.values[i]; |
| 1031 } else { | 1143 } else { |
| 1032 // Handle named or missing arguments | 1144 // TODO(jmesserly): right now we're evaluating named/missing args on |
| 1145 // the call site side. That should probably move into here. |
| 1033 currentArg = args.getValue(p.name); | 1146 currentArg = args.getValue(p.name); |
| 1034 if (currentArg === null) { | 1147 if (currentArg === null) { |
| 1035 // Ensure default value for param has been generated | 1148 // Ensure default value for param has been generated |
| 1036 p.genValue(method, method.generator); | 1149 p.genValue(method, this); |
| 1037 currentArg = p.value; | 1150 currentArg = p.value; |
| 1038 } | 1151 } |
| 1039 } | 1152 } |
| 1040 | 1153 |
| 1041 if (p.isInitializer) { | 1154 if (p.isInitializer) { |
| 1042 _paramCode.add(p.name); | 1155 _paramCode.add(p.name); |
| 1043 fieldsSet = true; | 1156 fieldsSet = true; |
| 1044 _initField(newObject, p.name, currentArg, p.definition.span); | 1157 _initField(thisObject, p.name, currentArg, p.definition.span); |
| 1045 } else { | 1158 } else { |
| 1046 var paramValue = _scope.declareParameter(p); | 1159 var paramValue = _scope.declareParameter(p); |
| 1047 _paramCode.add(paramValue.code); | 1160 _paramCode.add(paramValue.code); |
| 1048 if (newObject != null && newObject.isConst) { | 1161 if (thisObject != null && thisObject.isConst) { |
| 1049 _scope.assign(p.name, currentArg.convertTo(this, p.type)); | 1162 _scope.assign(p.name, currentArg.convertTo(this, p.type)); |
| 1050 } | 1163 } |
| 1051 } | 1164 } |
| 1052 } | 1165 } |
| 1053 | 1166 |
| 1054 var initializerCall = null; | 1167 var initializerCall = null; |
| 1055 final declaredInitializers = method.definition.initializers; | 1168 var methodDef = method.definition; |
| 1169 final declaredInitializers = methodDef.initializers; |
| 1056 if (declaredInitializers != null) { | 1170 if (declaredInitializers != null) { |
| 1057 for (var init in declaredInitializers) { | 1171 for (var init in declaredInitializers) { |
| 1058 if (init is CallExpression) { | 1172 if (init is CallExpression) { |
| 1059 if (initializerCall != null) { | 1173 if (initializerCall != null) { |
| 1060 world.error('only one initializer redirecting call is allowed', | 1174 world.error('only one initializer redirecting call is allowed', |
| 1061 init.span); | 1175 init.span); |
| 1062 } | 1176 } |
| 1063 initializerCall = init; | 1177 initializerCall = init; |
| 1064 } else if (init is BinaryExpression | 1178 } else if (init is BinaryExpression |
| 1065 && TokenKind.kindFromAssign(init.op.kind) == 0) { | 1179 && TokenKind.kindFromAssign(init.op.kind) == 0) { |
| 1066 var left = init.x; | 1180 var left = init.x; |
| 1067 if (!(left is DotExpression && left.self is ThisExpression | 1181 if (!(left is DotExpression && left.self is ThisExpression |
| 1068 || left is VarExpression)) { | 1182 || left is VarExpression)) { |
| 1069 world.error('invalid left side of initializer', left.span); | 1183 world.error('invalid left side of initializer', left.span); |
| 1070 continue; | 1184 continue; |
| 1071 } | 1185 } |
| 1072 // TODO(jmesserly): eval right side of initializers in static | 1186 // TODO(jmesserly): eval right side of initializers in static |
| 1073 // context, so "this." is not in scope | 1187 // context, so "this." is not in scope |
| 1074 var initValue = visitValue(init.y); | 1188 var initValue = visitValue(init.y); |
| 1075 fieldsSet = true; | 1189 fieldsSet = true; |
| 1076 _initField(newObject, left.name.name, initValue, left.span); | 1190 _initField(thisObject, left.name.name, initValue, left.span); |
| 1077 } else { | 1191 } else { |
| 1078 world.error('invalid initializer', init.span); | 1192 world.error('invalid initializer', init.span); |
| 1079 } | 1193 } |
| 1080 } | 1194 } |
| 1081 } | 1195 } |
| 1082 | 1196 |
| 1083 if (method.isConstructor && initializerCall == null && !method.isNative) { | 1197 if (method.isConstructor && initializerCall == null && !method.isNative) { |
| 1084 var parentType = method.declaringType.parent; | 1198 var parentType = method.declaringType.parent; |
| 1085 if (parentType != null && !parentType.isObject) { | 1199 if (parentType != null && !parentType.isObject) { |
| 1086 // TODO(jmesserly): we could omit this if all supertypes are using | 1200 // TODO(jmesserly): we could omit this if all supertypes are using |
| 1087 // default constructors. | 1201 // default constructors. |
| 1088 initializerCall = new CallExpression( | 1202 initializerCall = new CallExpression( |
| 1089 new SuperExpression(method.span), [], method.span); | 1203 new SuperExpression(method.span), [], method.span); |
| 1090 } | 1204 } |
| 1091 } | 1205 } |
| 1092 | 1206 |
| 1093 if (method.isConstructor && newObject is ObjectValue) { | 1207 if (method.isConstructor && thisObject is ObjectValue) { |
| 1094 var fields = newObject.dynamic.fields; | 1208 var fields = thisObject.dynamic.fields; |
| 1095 for (var field in fields.getKeys()) { | 1209 for (var field in fields.getKeys()) { |
| 1096 var value = fields[field]; | 1210 var value = fields[field]; |
| 1097 if (value !== null) { | 1211 if (value !== null) { |
| 1098 writer.writeln('this.${field.jsname} = ${value.code};'); | 1212 writer.writeln('this.${field.jsname} = ${value.code};'); |
| 1099 } | 1213 } |
| 1100 } | 1214 } |
| 1101 } | 1215 } |
| 1102 | 1216 |
| 1103 // TODO(jimhug): Doing this call last does not match spec. | 1217 // TODO(jimhug): Doing this call last does not match spec. |
| 1104 if (initializerCall != null) { | 1218 if (initializerCall != null) { |
| 1105 evalInitializerCall(newObject, initializerCall, fieldsSet); | 1219 evalInitializerCall(thisObject, initializerCall, fieldsSet); |
| 1106 } | 1220 } |
| 1107 | 1221 |
| 1108 if (method.isConstructor && newObject !== null && newObject.isConst) { | 1222 if (method.isConstructor && thisObject !== null && thisObject.isConst) { |
| 1109 newObject.validateInitialized(method.span); | 1223 thisObject.validateInitialized(method.span); |
| 1110 } else if (method.isConstructor) { | 1224 } else if (method.isConstructor) { |
| 1111 var fields = newObject.dynamic.fields; | 1225 var fields = thisObject.dynamic.fields; |
| 1112 for (var field in fields.getKeys()) { | 1226 for (var field in fields.getKeys()) { |
| 1113 var value = fields[field]; | 1227 var value = fields[field]; |
| 1114 if (value === null && field.isFinal && | 1228 if (value === null && field.isFinal && |
| 1115 field.declaringType == method.declaringType && | 1229 field.declaringType == method.declaringType && |
| 1116 !newObject.dynamic.seenNativeInitializer) { | 1230 !thisObject.dynamic.seenNativeInitializer) { |
| 1117 world.error('uninitialized final field "${field.name}"', | 1231 world.error('uninitialized final field "${field.name}"', |
| 1118 field.span, method.span); | 1232 field.span, method.span); |
| 1119 } | 1233 } |
| 1120 } | 1234 } |
| 1121 } | 1235 } |
| 1122 | 1236 |
| 1123 var body = method.definition.body; | 1237 var body = methodDef.body; |
| 1124 | 1238 |
| 1125 if (body === null) { | 1239 if (body === null) { |
| 1126 // TODO(jimhug): Move check into resolve on method. | 1240 // TODO(jimhug): Move check into resolve on method. |
| 1127 if (!method.isConstructor && !method.isNative) { | 1241 if (!method.isConstructor && !method.isNative) { |
| 1128 world.error('unexpected empty body for ${method.name}', | 1242 world.error('unexpected empty body for ${method.name}', |
| 1129 method.definition.span); | 1243 method.definition.span); |
| 1130 } | 1244 } |
| 1131 } else { | 1245 } else { |
| 1132 visitStatementsInBlock(body); | 1246 visitStatementsInBlock(body); |
| 1133 } | 1247 } |
| 1248 |
| 1249 _evalNativeBody(); |
| 1134 } | 1250 } |
| 1135 | 1251 |
| 1136 evalInitializerCall(ObjectValue newObject, CallExpression node, | 1252 evalInitializerCall(ObjectValue newObject, CallExpression node, |
| 1137 [bool fieldsSet = false]) { | 1253 [bool fieldsSet = false]) { |
| 1138 String contructorName = ''; | 1254 String contructorName = ''; |
| 1139 var targetExp = node.target; | 1255 var targetExp = node.target; |
| 1140 if (targetExp is DotExpression) { | 1256 if (targetExp is DotExpression) { |
| 1141 DotExpression dot = targetExp; | 1257 DotExpression dot = targetExp; |
| 1142 targetExp = dot.self; | 1258 targetExp = dot.self; |
| 1143 contructorName = dot.name.name; | 1259 contructorName = dot.name.name; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1171 while (other != null) { | 1287 while (other != null) { |
| 1172 if (other == method) { | 1288 if (other == method) { |
| 1173 world.error('initialization cycle', node.span); | 1289 world.error('initialization cycle', node.span); |
| 1174 break; | 1290 break; |
| 1175 } | 1291 } |
| 1176 other = other.initDelegate; | 1292 other = other.initDelegate; |
| 1177 } | 1293 } |
| 1178 | 1294 |
| 1179 var newArgs = _makeArgs(node.arguments); | 1295 var newArgs = _makeArgs(node.arguments); |
| 1180 // ???? wacky stuff ???? | 1296 // ???? wacky stuff ???? |
| 1181 world.gen.genMethod(m); | 1297 world.gen.invokeMethod(m); |
| 1182 | 1298 |
| 1183 m._evalConstConstructor(newObject, newArgs); | 1299 m._evalConstConstructor(newObject, newArgs); |
| 1184 | 1300 |
| 1185 if (!newObject.isConst) { | 1301 if (!newObject.isConst) { |
| 1186 var value = m.invoke(this, node, target, newArgs); | 1302 var value = m.invoke(this, node, target, newArgs); |
| 1187 if (target.type != world.objectType) { | 1303 if (target.type != world.objectType) { |
| 1188 // No need to actually call Object's empty super constructor. | 1304 // No need to actually call Object's empty super constructor. |
| 1189 writer.writeln('${value.code};'); | 1305 writer.writeln('${value.code};'); |
| 1190 } | 1306 } |
| 1191 } | 1307 } |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1406 writer.writeln(';'); | 1522 writer.writeln(';'); |
| 1407 return false; | 1523 return false; |
| 1408 | 1524 |
| 1409 } | 1525 } |
| 1410 | 1526 |
| 1411 bool visitFunctionDefinition(FunctionDefinition node) { | 1527 bool visitFunctionDefinition(FunctionDefinition node) { |
| 1412 var meth = _makeLambdaMethod(node.name.name, node); | 1528 var meth = _makeLambdaMethod(node.name.name, node); |
| 1413 var funcValue = _scope.create(meth.name, meth.functionType, | 1529 var funcValue = _scope.create(meth.name, meth.functionType, |
| 1414 method.definition.span, isFinal:true); | 1530 method.definition.span, isFinal:true); |
| 1415 | 1531 |
| 1416 world.gen.genMethod(meth, this); | 1532 new MethodSpecializer(meth, this).run(); |
| 1417 meth.generator.writeDefinition(writer, null); | 1533 meth.generator.writeDefinition(writer, null); |
| 1418 return false; | 1534 return false; |
| 1419 } | 1535 } |
| 1420 | 1536 |
| 1421 /** | 1537 /** |
| 1422 * Returns true indicating that normal control-flow is interrupted by | 1538 * Returns true indicating that normal control-flow is interrupted by |
| 1423 * this statement. (This could be a return, break, throw, or continue.) | 1539 * this statement. (This could be a return, break, throw, or continue.) |
| 1424 */ | 1540 */ |
| 1425 bool visitReturnStatement(ReturnStatement node) { | 1541 bool visitReturnStatement(ReturnStatement node) { |
| 1426 if (node.value == null) { | 1542 if (node.value == null) { |
| 1427 // This is essentially "return null". | 1543 // This is essentially "return null". |
| 1428 // It can't issue a warning because every type is nullable. | 1544 // TODO(jmesserly): this should be a warning in non-void methods |
| 1429 writer.writeln('return;'); | 1545 writer.writeln('return;'); |
| 1546 _inferredResult = Value.union(_inferredResult, Value.fromNull(null)); |
| 1430 } else { | 1547 } else { |
| 1431 if (method.isConstructor) { | 1548 if (method.isConstructor) { |
| 1432 world.error('return of value not allowed from constructor', node.span); | 1549 world.error('return of value not allowed from constructor', node.span); |
| 1433 } | 1550 } |
| 1434 var value = visitTypedValue(node.value, method.returnType); | 1551 var value = visitTypedValue(node.value, method.returnType); |
| 1552 _inferredResult = Value.union(_inferredResult, value); |
| 1435 writer.writeln('return ${value.code};'); | 1553 writer.writeln('return ${value.code};'); |
| 1436 } | 1554 } |
| 1437 return true; | 1555 return true; |
| 1438 } | 1556 } |
| 1439 | 1557 |
| 1440 bool visitThrowStatement(ThrowStatement node) { | 1558 bool visitThrowStatement(ThrowStatement node) { |
| 1441 // Dart allows throwing anything, just like JS | 1559 // Dart allows throwing anything, just like JS |
| 1442 if (node.value != null) { | 1560 if (node.value != null) { |
| 1443 var value = visitValue(node.value); | 1561 var value = visitValue(node.value); |
| 1444 // Ensure that we generate a toString() method for things that we throw | 1562 // Ensure that we generate a toString() method for things that we throw |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1504 } | 1622 } |
| 1505 return true; | 1623 return true; |
| 1506 } | 1624 } |
| 1507 | 1625 |
| 1508 bool visitIfStatement(IfStatement node) { | 1626 bool visitIfStatement(IfStatement node) { |
| 1509 var test = visitBool(node.test); | 1627 var test = visitBool(node.test); |
| 1510 writer.write('if (${test.code}) '); | 1628 writer.write('if (${test.code}) '); |
| 1511 var exit1 = node.trueBranch.visit(this); | 1629 var exit1 = node.trueBranch.visit(this); |
| 1512 if (node.falseBranch != null) { | 1630 if (node.falseBranch != null) { |
| 1513 writer.write('else '); | 1631 writer.write('else '); |
| 1514 if (node.falseBranch.visit(this) && exit1) { | 1632 var exit2 = node.falseBranch.visit(this); |
| 1515 return true; | 1633 return exit1 && exit2; |
| 1516 } | |
| 1517 } | 1634 } |
| 1518 return false; | 1635 return false; |
| 1519 } | 1636 } |
| 1520 | 1637 |
| 1521 bool visitWhileStatement(WhileStatement node) { | 1638 bool visitWhileStatement(WhileStatement node) { |
| 1522 var test = visitBool(node.test); | 1639 var test = visitBool(node.test); |
| 1523 writer.write('while (${test.code}) '); | 1640 writer.write('while (${test.code}) '); |
| 1524 _visitLoop(node, () { | 1641 _visitLoop(node, () { |
| 1525 node.body.visit(this); | 1642 node.body.visit(this); |
| 1526 }); | 1643 }); |
| (...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1880 return new ThisValue(method.declaringType, 'this', | 1997 return new ThisValue(method.declaringType, 'this', |
| 1881 node != null ? node.span : null); | 1998 node != null ? node.span : null); |
| 1882 } | 1999 } |
| 1883 } | 2000 } |
| 1884 | 2001 |
| 1885 // ******************* Expressions ******************* | 2002 // ******************* Expressions ******************* |
| 1886 visitLambdaExpression(LambdaExpression node) { | 2003 visitLambdaExpression(LambdaExpression node) { |
| 1887 var name = (node.func.name != null) ? node.func.name.name : ''; | 2004 var name = (node.func.name != null) ? node.func.name.name : ''; |
| 1888 | 2005 |
| 1889 MethodMember meth = _makeLambdaMethod(name, node.func); | 2006 MethodMember meth = _makeLambdaMethod(name, node.func); |
| 1890 final lambdaGen = new MethodGenerator(meth, this); | |
| 1891 if (name != '') { | 2007 if (name != '') { |
| 1892 // Note: we don't want to put this in our enclosing scope because the | 2008 // Note: create a block scope for the lambda's name to go into. This keeps |
| 1893 // name shouldn't be visible except inside the lambda. We also don't want | 2009 // it from leaking into our current scope, and also allows the lambda's |
| 1894 // to put the name directly in the lambda's scope because parameters are | 2010 // parameters to shadow it. |
| 1895 // allowed to shadow it. So we create an extra scope for it to go into. | 2011 _pushBlock(node); |
| 1896 lambdaGen._scope.create(name, meth.functionType, meth.definition.span, | 2012 _scope.create(name, meth.functionType, meth.definition.span, |
| 1897 isFinal:true); | 2013 isFinal:true); |
| 1898 lambdaGen._pushBlock(node); | |
| 1899 } | 2014 } |
| 1900 lambdaGen.run(); | 2015 |
| 2016 // TODO(jmesserly): if we wanted to, we could track this as a function |
| 2017 // value, and let it be invoked (or not) by whoever uses it. |
| 2018 // (Since the number of lambdas is bounded in the program it may be |
| 2019 // viable to track them all individually as Values.) |
| 2020 new MethodSpecializer(meth, this).run(); |
| 2021 |
| 2022 if (name != '') { |
| 2023 _popBlock(node); |
| 2024 } |
| 1901 | 2025 |
| 1902 var w = new CodeWriter(); | 2026 var w = new CodeWriter(); |
| 1903 meth.generator.writeDefinition(w, node); | 2027 meth.generator.writeDefinition(w, node); |
| 1904 return new Value(meth.functionType, w.text, node.span); | 2028 return new Value(meth.functionType, w.text, node.span); |
| 1905 } | 2029 } |
| 1906 | 2030 |
| 1907 visitCallExpression(CallExpression node) { | 2031 visitCallExpression(CallExpression node) { |
| 1908 var target; | 2032 var target; |
| 1909 var position = node.target; | 2033 var position = node.target; |
| 1910 var name = ':call'; | 2034 var name = ':call'; |
| (...skipping 561 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2472 } | 2596 } |
| 2473 return new Arguments(nodes, result); | 2597 return new Arguments(nodes, result); |
| 2474 } | 2598 } |
| 2475 } | 2599 } |
| 2476 | 2600 |
| 2477 class ReturnKind { | 2601 class ReturnKind { |
| 2478 static final int IGNORE = 1; | 2602 static final int IGNORE = 1; |
| 2479 static final int POST = 2; | 2603 static final int POST = 2; |
| 2480 static final int PRE = 3; | 2604 static final int PRE = 3; |
| 2481 } | 2605 } |
| OLD | NEW |