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

Side by Side Diff: frog/gen.dart

Issue 9107067: Work in progress: changes to interpretation (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: work in progress Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/frog_options.dart ('k') | frog/lang.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Top level generator object for writing code and keeping track of 6 * Top level generator object for writing code and keeping track of
7 * dependencies. 7 * dependencies.
8 * 8 *
9 * Should have two compilation models, but only one implemented so far. 9 * Should have two compilation models, but only one implemented so far.
10 * 10 *
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « frog/frog_options.dart ('k') | frog/lang.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698