OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 | 2 |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 | 5 |
6 import 'dart:collection' show HashMap, HashSet; | 6 import 'dart:collection' show HashMap, HashSet; |
7 import 'dart:math' show min, max; | 7 import 'dart:math' show min, max; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
133 final InterfaceType _asyncStreamIterator; | 133 final InterfaceType _asyncStreamIterator; |
134 | 134 |
135 /// The dart:core `identical` element. | 135 /// The dart:core `identical` element. |
136 final FunctionElement _coreIdentical; | 136 final FunctionElement _coreIdentical; |
137 | 137 |
138 /// The dart:_interceptors JSArray element. | 138 /// The dart:_interceptors JSArray element. |
139 final ClassElement _jsArray; | 139 final ClassElement _jsArray; |
140 | 140 |
141 final ClassElement boolClass; | 141 final ClassElement boolClass; |
142 final ClassElement intClass; | 142 final ClassElement intClass; |
143 final ClassElement doubleClass; | |
143 final ClassElement interceptorClass; | 144 final ClassElement interceptorClass; |
144 final ClassElement nullClass; | 145 final ClassElement nullClass; |
145 final ClassElement numClass; | 146 final ClassElement numClass; |
146 final ClassElement objectClass; | 147 final ClassElement objectClass; |
147 final ClassElement stringClass; | 148 final ClassElement stringClass; |
148 final ClassElement functionClass; | 149 final ClassElement functionClass; |
149 final ClassElement privateSymbolClass; | 150 final ClassElement privateSymbolClass; |
150 | 151 |
151 ConstFieldVisitor _constants; | 152 ConstFieldVisitor _constants; |
152 | 153 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
203 _asyncStreamIterator = | 204 _asyncStreamIterator = |
204 _getLibrary(c, 'dart:async').getType('StreamIterator').type, | 205 _getLibrary(c, 'dart:async').getType('StreamIterator').type, |
205 _coreIdentical = | 206 _coreIdentical = |
206 _getLibrary(c, 'dart:core').publicNamespace.get('identical'), | 207 _getLibrary(c, 'dart:core').publicNamespace.get('identical'), |
207 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), | 208 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), |
208 interceptorClass = | 209 interceptorClass = |
209 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), | 210 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), |
210 dartCoreLibrary = _getLibrary(c, 'dart:core'), | 211 dartCoreLibrary = _getLibrary(c, 'dart:core'), |
211 boolClass = _getLibrary(c, 'dart:core').getType('bool'), | 212 boolClass = _getLibrary(c, 'dart:core').getType('bool'), |
212 intClass = _getLibrary(c, 'dart:core').getType('int'), | 213 intClass = _getLibrary(c, 'dart:core').getType('int'), |
214 doubleClass = _getLibrary(c, 'dart:core').getType('double'), | |
213 numClass = _getLibrary(c, 'dart:core').getType('num'), | 215 numClass = _getLibrary(c, 'dart:core').getType('num'), |
214 nullClass = _getLibrary(c, 'dart:core').getType('Null'), | 216 nullClass = _getLibrary(c, 'dart:core').getType('Null'), |
215 objectClass = _getLibrary(c, 'dart:core').getType('Object'), | 217 objectClass = _getLibrary(c, 'dart:core').getType('Object'), |
216 stringClass = _getLibrary(c, 'dart:core').getType('String'), | 218 stringClass = _getLibrary(c, 'dart:core').getType('String'), |
217 functionClass = _getLibrary(c, 'dart:core').getType('Function'), | 219 functionClass = _getLibrary(c, 'dart:core').getType('Function'), |
218 privateSymbolClass = | 220 privateSymbolClass = |
219 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), | 221 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), |
220 dartJSLibrary = _getLibrary(c, 'dart:js') { | 222 dartJSLibrary = _getLibrary(c, 'dart:js') { |
221 typeRep = new JSTypeRep(rules, types); | 223 typeRep = new JSTypeRep(rules, types); |
222 } | 224 } |
(...skipping 610 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
833 if (isGeneric) { | 835 if (isGeneric) { |
834 if (isMixinAlias) { | 836 if (isMixinAlias) { |
835 block.add(js.statement('const # = #;', [className, classExpr])); | 837 block.add(js.statement('const # = #;', [className, classExpr])); |
836 } else { | 838 } else { |
837 block.add(new JS.ClassDeclaration(classExpr)); | 839 block.add(new JS.ClassDeclaration(classExpr)); |
838 } | 840 } |
839 } else { | 841 } else { |
840 block.add(js.statement('# = #;', [className, classExpr])); | 842 block.add(js.statement('# = #;', [className, classExpr])); |
841 } | 843 } |
842 | 844 |
845 JS.Statement finishGenericTypeTest; | |
846 | |
843 if (!isMixinAlias) { | 847 if (!isMixinAlias) { |
844 block.addAll(_defineConstructors(classElem, className, [], [])); | 848 block.addAll(_defineConstructors(classElem, className, [], [])); |
849 finishGenericTypeTest = _emitClassTypeTests(classElem, className, block); | |
845 } | 850 } |
846 | 851 |
847 if (classElem.interfaces.isNotEmpty) { | 852 if (classElem.interfaces.isNotEmpty) { |
848 block.add(js.statement('#[#.implements] = () => #;', [ | 853 block.add(js.statement('#[#.implements] = () => #;', [ |
849 className, | 854 className, |
850 _runtimeModule, | 855 _runtimeModule, |
851 new JS.ArrayInitializer(classElem.interfaces.map(_emitType).toList()) | 856 new JS.ArrayInitializer(classElem.interfaces.map(_emitType).toList()) |
852 ])); | 857 ])); |
853 } | 858 } |
854 | 859 |
855 if (isGeneric) { | 860 if (isGeneric) { |
856 return _defineClassTypeArguments( | 861 var classDef = |
857 classElem, typeFormals, _statement(block)); | 862 _defineClassTypeArguments(classElem, typeFormals, _statement(block)); |
863 if (finishGenericTypeTest == null) return classDef; | |
864 block = [classDef, finishGenericTypeTest]; | |
858 } | 865 } |
859 return _statement(block); | 866 return _statement(block); |
860 } | 867 } |
861 | 868 |
862 JS.Statement _emitJSType(Element e) { | 869 JS.Statement _emitJSType(Element e) { |
863 var jsTypeName = getAnnotationName(e, isJSAnnotation); | 870 var jsTypeName = getAnnotationName(e, isJSAnnotation); |
864 if (jsTypeName == null || jsTypeName == e.name) return null; | 871 if (jsTypeName == null || jsTypeName == e.name) return null; |
865 | 872 |
866 // We export the JS type as if it was a Dart type. For example this allows | 873 // We export the JS type as if it was a Dart type. For example this allows |
867 // `dom.InputElement` to actually be HTMLInputElement. | 874 // `dom.InputElement` to actually be HTMLInputElement. |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
925 | 932 |
926 // Emit the class, e.g. `core.Object = class Object { ... }` | 933 // Emit the class, e.g. `core.Object = class Object { ... }` |
927 _defineClass(classElem, className, classExpr, body); | 934 _defineClass(classElem, className, classExpr, body); |
928 body.addAll(jsCtors); | 935 body.addAll(jsCtors); |
929 | 936 |
930 // Emit things that come after the ES6 `class ... { ... }`. | 937 // Emit things that come after the ES6 `class ... { ... }`. |
931 var jsPeerNames = _getJSPeerNames(classElem); | 938 var jsPeerNames = _getJSPeerNames(classElem); |
932 JS.Statement deferredBaseClass = | 939 JS.Statement deferredBaseClass = |
933 _setBaseClass(classElem, className, jsPeerNames, body); | 940 _setBaseClass(classElem, className, jsPeerNames, body); |
934 | 941 |
935 _emitClassTypeTests(classElem, className, body); | 942 var finishGenericTypeTest = _emitClassTypeTests(classElem, className, body); |
936 | 943 |
937 _emitVirtualFieldSymbols(classElem, body); | 944 _emitVirtualFieldSymbols(classElem, body); |
938 _emitClassSignature(methods, allFields, classElem, ctors, className, body); | 945 _emitClassSignature(methods, allFields, classElem, ctors, className, body); |
939 _defineExtensionMembers(className, body); | 946 _defineExtensionMembers(className, body); |
940 _emitClassMetadata(node.metadata, className, body); | 947 _emitClassMetadata(node.metadata, className, body); |
941 | 948 |
942 JS.Statement classDef = _statement(body); | 949 JS.Statement classDef = _statement(body); |
943 | 950 |
944 var typeFormals = classElem.typeParameters; | 951 var typeFormals = classElem.typeParameters; |
945 if (typeFormals.isNotEmpty) { | 952 if (typeFormals.isNotEmpty) { |
946 classDef = _defineClassTypeArguments( | 953 classDef = _defineClassTypeArguments( |
947 classElem, typeFormals, classDef, className, deferredBaseClass); | 954 classElem, typeFormals, classDef, className, deferredBaseClass); |
948 } | 955 } |
949 | 956 |
950 body = <JS.Statement>[classDef]; | 957 body = <JS.Statement>[classDef]; |
951 _emitStaticFields(staticFields, classElem, body); | 958 _emitStaticFields(staticFields, classElem, body); |
959 if (finishGenericTypeTest != null) body.add(finishGenericTypeTest); | |
952 for (var peer in jsPeerNames) { | 960 for (var peer in jsPeerNames) { |
953 _registerExtensionType(classElem, peer, body); | 961 _registerExtensionType(classElem, peer, body); |
954 } | 962 } |
955 | 963 |
956 _classProperties = savedClassProperties; | 964 _classProperties = savedClassProperties; |
957 return _statement(body); | 965 return _statement(body); |
958 } | 966 } |
959 | 967 |
960 void _emitClassTypeTests(ClassElement classElem, JS.Expression className, | 968 JS.Statement _emitClassTypeTests(ClassElement classElem, |
961 List<JS.Statement> body) { | 969 JS.Expression className, List<JS.Statement> body) { |
962 if (classElem == objectClass) { | 970 JS.Expression getInterfaceSymbol(ClassElement c) { |
963 // We rely on ES6 static inheritance. All types that are represented by | 971 var library = c.library; |
964 // class constructor functions will see these definitions, with [this] | 972 if (library.isDartCore || library.isDartAsync) { |
965 // being bound to the class constructor. | 973 switch (c.name) { |
966 | 974 case 'List': |
967 // The 'instanceof' checks don't work for primitive types (which have fast | 975 case 'Map': |
968 // definitions below) and don't work for native types. In those cases we | 976 case 'Iterable': |
969 // fall through to the general purpose checking code. | 977 case 'Future': |
970 body.add(js.statement( | 978 case 'Stream': |
971 '#.is = function is_Object(o) {' | 979 case 'StreamSubscription': |
972 ' if (o instanceof this) return true;' | 980 return _callHelper('is' + c.name); |
973 ' return #.is(o, this);' | |
974 '}', | |
975 [className, _runtimeModule])); | |
976 body.add(js.statement( | |
977 '#.as = function as_Object(o) {' | |
978 ' if (o == null || o instanceof this) return o;' | |
979 ' return #.as(o, this);' | |
980 '}', | |
981 [className, _runtimeModule])); | |
982 body.add(js.statement( | |
983 '#._check = function check_Object(o) {' | |
984 ' if (o == null || o instanceof this) return o;' | |
985 ' return #.check(o, this);' | |
986 '}', | |
987 [className, _runtimeModule])); | |
988 return; | |
989 } | |
990 if (classElem == stringClass) { | |
991 body.add(js.statement( | |
992 '#.is = function is_String(o) { return typeof o == "string"; }', | |
993 className)); | |
994 body.add(js.statement( | |
995 '#.as = function as_String(o) {' | |
996 ' if (typeof o == "string" || o == null) return o;' | |
997 ' return #.as(o, #);' | |
998 '}', | |
999 [className, _runtimeModule, className])); | |
1000 body.add(js.statement( | |
1001 '#._check = function check_String(o) {' | |
1002 ' if (typeof o == "string" || o == null) return o;' | |
1003 ' return #.check(o, #);' | |
1004 '}', | |
1005 [className, _runtimeModule, className])); | |
1006 return; | |
1007 } | |
1008 if (classElem == functionClass) { | |
1009 body.add(js.statement( | |
1010 '#.is = function is_Function(o) { return typeof o == "function"; }', | |
1011 className)); | |
1012 body.add(js.statement( | |
1013 '#.as = function as_Function(o) {' | |
1014 ' if (typeof o == "function" || o == null) return o;' | |
1015 ' return #.as(o, #);' | |
1016 '}', | |
1017 [className, _runtimeModule, className])); | |
1018 body.add(js.statement( | |
1019 '#._check = function check_String(o) {' | |
1020 ' if (typeof o == "function" || o == null) return o;' | |
1021 ' return #.check(o, #);' | |
1022 '}', | |
1023 [className, _runtimeModule, className])); | |
1024 return; | |
1025 } | |
1026 | |
1027 if (classElem == intClass) { | |
1028 body.add(js.statement( | |
1029 '#.is = function is_int(o) {' | |
1030 ' return typeof o == "number" && Math.floor(o) == o;' | |
1031 '}', | |
1032 className)); | |
1033 body.add(js.statement( | |
1034 '#.as = function as_int(o) {' | |
1035 ' if ((typeof o == "number" && Math.floor(o) == o) || o == null)' | |
1036 ' return o;' | |
1037 ' return #.as(o, #);' | |
1038 '}', | |
1039 [className, _runtimeModule, className])); | |
1040 body.add(js.statement( | |
1041 '#._check = function check_int(o) {' | |
1042 ' if ((typeof o == "number" && Math.floor(o) == o) || o == null)' | |
1043 ' return o;' | |
1044 ' return #.check(o, #);' | |
1045 '}', | |
1046 [className, _runtimeModule, className])); | |
1047 return; | |
1048 } | |
1049 if (classElem == nullClass) { | |
1050 body.add(js.statement( | |
1051 '#.is = function is_Null(o) { return o == null; }', className)); | |
1052 body.add(js.statement( | |
1053 '#.as = function as_Null(o) {' | |
1054 ' if (o == null) return o;' | |
1055 ' return #.as(o, #);' | |
1056 '}', | |
1057 [className, _runtimeModule, className])); | |
1058 body.add(js.statement( | |
1059 '#._check = function check_Null(o) {' | |
1060 ' if (o == null) return o;' | |
1061 ' return #.check(o, #);' | |
1062 '}', | |
1063 [className, _runtimeModule, className])); | |
1064 return; | |
1065 } | |
1066 if (classElem == numClass) { | |
1067 body.add(js.statement( | |
1068 '#.is = function is_num(o) { return typeof o == "number"; }', | |
1069 className)); | |
1070 body.add(js.statement( | |
1071 '#.as = function as_num(o) {' | |
1072 ' if (typeof o == "number" || o == null) return o;' | |
1073 ' return #.as(o, #);' | |
1074 '}', | |
1075 [className, _runtimeModule, className])); | |
1076 body.add(js.statement( | |
1077 '#._check = function check_num(o) {' | |
1078 ' if (typeof o == "number" || o == null) return o;' | |
1079 ' return #.check(o, #);' | |
1080 '}', | |
1081 [className, _runtimeModule, className])); | |
1082 return; | |
1083 } | |
1084 if (classElem == boolClass) { | |
1085 body.add(js.statement( | |
1086 '#.is = function is_bool(o) { return o === true || o === false; }', | |
1087 className)); | |
1088 body.add(js.statement( | |
1089 '#.as = function as_bool(o) {' | |
1090 ' if (o === true || o === false || o == null) return o;' | |
1091 ' return #.as(o, #);' | |
1092 '}', | |
1093 [className, _runtimeModule, className])); | |
1094 body.add(js.statement( | |
1095 '#._check = function check_bool(o) {' | |
1096 ' if (o === true || o === false || o == null) return o;' | |
1097 ' return #.check(o, #);' | |
1098 '}', | |
1099 [className, _runtimeModule, className])); | |
1100 return; | |
1101 } | |
1102 // TODO(sra): Add special cases for hot tests like `x is html.Element`. | |
1103 | |
1104 // `instanceof` check is futile for classes that are Interceptor classes. | |
1105 ClassElement parent = classElem; | |
1106 while (parent != objectClass) { | |
1107 if (parent == interceptorClass) { | |
1108 if (classElem == interceptorClass) { | |
1109 // Place non-instanceof version of checks on Interceptor. All | |
1110 // interceptor classes will inherit the methods via ES6 class static | |
1111 // inheritance. | |
1112 body.add(_callHelperStatement('addTypeTests(#);', className)); | |
1113 | |
1114 // TODO(sra): We could place on the extension type a pointer to the | |
1115 // peer constructor and use that for the `instanceof` check, e.g. | |
1116 // | |
1117 // if (o instanceof this[_peerConstructor]) return o; | |
1118 // | |
1119 } | 981 } |
1120 return; | 982 } |
1121 } | 983 return null; |
1122 parent = parent.type.superclass.element; | 984 } |
1123 } | 985 |
1124 | 986 void markSubtypeOf(JS.Expression testSymbol) { |
1125 // Choose between 'simple' checks, which are often accelerated by | 987 body.add(js.statement('#.prototype[#] = true', [className, testSymbol])); |
1126 // `instanceof`, and other checks, which are slowed down by taking time to | 988 } |
1127 // do an `instanceof` check that is futile or likely futile. | 989 |
1128 // | 990 for (var iface in classElem.interfaces) { |
1129 // The `instanceof` check is futile for (1) a class that is only used as a | 991 var prop = getInterfaceSymbol(iface.element); |
1130 // mixin, or (2) is only used as an interface in an `implements` clause, and | 992 if (prop != null) markSubtypeOf(prop); |
1131 // is likely futile (3) if the class has type parameters, since `Foo` aka | 993 } |
1132 // `Foo<dynamic>` is not a superclass of `Foo<int>`. The first two are | 994 |
1133 // whole-program properites, but we can check for the last case. | 995 if (classElem.library.isDartCore) { |
1134 | 996 if (classElem == objectClass) { |
Leaf
2017/08/24 17:19:39
Is there a reason we attach these here instead of
Jennifer Messerly
2017/08/24 18:26:37
Yeah I'm not sure why the existing code does that.
| |
1135 // Since ES6 classes have inheritance of static properties, we need only | 997 // Everything is an Object. |
1136 // install checks that differ from the parent. | 998 body.add(js.statement( |
1137 | 999 '#.is = function is_Object(o) { return true; }', [className])); |
1138 bool isSimple(ClassElement classElement) { | 1000 body.add(js.statement( |
1139 if (classElement.typeParameters.isNotEmpty) return false; | 1001 '#.as = function as_Object(o) { return o; }', [className])); |
1140 return true; | 1002 body.add(js.statement( |
1141 } | 1003 '#._check = function check_Object(o) { return o; }', [className])); |
1142 | 1004 return null; |
1143 assert(classElem != objectClass); | 1005 } |
1144 bool thisIsSimple = isSimple(classElem); | 1006 if (classElem == stringClass) { |
1145 bool superIsSimple = isSimple(classElem.type.superclass.element); | 1007 body.add(js.statement( |
1146 | 1008 '#.is = function is_String(o) { return typeof o == "string"; }', |
1147 if (thisIsSimple == superIsSimple) return; | 1009 className)); |
1148 | 1010 body.add(js.statement( |
1149 if (thisIsSimple) { | 1011 '#.as = function as_String(o) {' |
1150 body.add(_callHelperStatement('addSimpleTypeTests(#);', className)); | 1012 ' if (typeof o == "string" || o == null) return o;' |
1151 } else { | 1013 ' return #.as(o, #, false);' |
1152 body.add(_callHelperStatement('addTypeTests(#);', className)); | 1014 '}', |
1153 } | 1015 [className, _runtimeModule, className])); |
1016 body.add(js.statement( | |
1017 '#._check = function check_String(o) {' | |
1018 ' if (typeof o == "string" || o == null) return o;' | |
1019 ' return #.as(o, #, true);' | |
1020 '}', | |
1021 [className, _runtimeModule, className])); | |
1022 return null; | |
1023 } | |
1024 if (classElem == functionClass) { | |
1025 body.add(js.statement( | |
1026 '#.is = function is_Function(o) { return typeof o == "function"; }', | |
Leaf
2017/08/24 17:19:39
Does this handle call methods?
Jennifer Messerly
2017/08/24 18:26:37
yup. Callable classes are reified as JS functions
| |
1027 className)); | |
1028 body.add(js.statement( | |
1029 '#.as = function as_Function(o) {' | |
1030 ' if (typeof o == "function" || o == null) return o;' | |
1031 ' return #.as(o, #, false);' | |
1032 '}', | |
1033 [className, _runtimeModule, className])); | |
1034 body.add(js.statement( | |
1035 '#._check = function check_String(o) {' | |
1036 ' if (typeof o == "function" || o == null) return o;' | |
1037 ' return #.as(o, #, true);' | |
1038 '}', | |
1039 [className, _runtimeModule, className])); | |
1040 return null; | |
1041 } | |
1042 if (classElem == intClass) { | |
1043 body.add(js.statement( | |
1044 '#.is = function is_int(o) {' | |
1045 ' return typeof o == "number" && Math.floor(o) == o;' | |
1046 '}', | |
1047 className)); | |
1048 body.add(js.statement( | |
1049 '#.as = function as_int(o) {' | |
1050 ' if ((typeof o == "number" && Math.floor(o) == o) || o == null)' | |
1051 ' return o;' | |
1052 ' return #.as(o, #, false);' | |
1053 '}', | |
1054 [className, _runtimeModule, className])); | |
1055 body.add(js.statement( | |
1056 '#._check = function check_int(o) {' | |
1057 ' if ((typeof o == "number" && Math.floor(o) == o) || o == null)' | |
1058 ' return o;' | |
1059 ' return #.as(o, #, true);' | |
1060 '}', | |
1061 [className, _runtimeModule, className])); | |
1062 return null; | |
1063 } | |
1064 if (classElem == nullClass) { | |
1065 body.add(js.statement( | |
1066 '#.is = function is_Null(o) { return o == null; }', className)); | |
1067 body.add(js.statement( | |
1068 '#.as = function as_Null(o) {' | |
1069 ' if (o == null) return o;' | |
1070 ' return #.as(o, #, false);' | |
1071 '}', | |
1072 [className, _runtimeModule, className])); | |
1073 body.add(js.statement( | |
1074 '#._check = function check_Null(o) {' | |
1075 ' if (o == null) return o;' | |
1076 ' return #.as(o, #, true);' | |
1077 '}', | |
1078 [className, _runtimeModule, className])); | |
1079 return null; | |
1080 } | |
1081 if (classElem == numClass || classElem == doubleClass) { | |
1082 body.add(js.statement( | |
1083 '#.is = function is_num(o) { return typeof o == "number"; }', | |
1084 className)); | |
1085 body.add(js.statement( | |
1086 '#.as = function as_num(o) {' | |
1087 ' if (typeof o == "number" || o == null) return o;' | |
1088 ' return #.as(o, #, false);' | |
1089 '}', | |
1090 [className, _runtimeModule, className])); | |
1091 body.add(js.statement( | |
1092 '#._check = function check_num(o) {' | |
1093 ' if (typeof o == "number" || o == null) return o;' | |
1094 ' return #.as(o, #, true);' | |
1095 '}', | |
1096 [className, _runtimeModule, className])); | |
1097 return null; | |
1098 } | |
1099 if (classElem == boolClass) { | |
1100 body.add(js.statement( | |
1101 '#.is = function is_bool(o) { return o === true || o === false; }', | |
1102 className)); | |
1103 body.add(js.statement( | |
1104 '#.as = function as_bool(o) {' | |
1105 ' if (o === true || o === false || o == null) return o;' | |
1106 ' return #.as(o, #, false);' | |
1107 '}', | |
1108 [className, _runtimeModule, className])); | |
1109 body.add(js.statement( | |
1110 '#._check = function check_bool(o) {' | |
1111 ' if (o === true || o === false || o == null) return o;' | |
1112 ' return #.as(o, #, true);' | |
1113 '}', | |
1114 [className, _runtimeModule, className])); | |
1115 return null; | |
1116 } | |
1117 } | |
1118 if (classElem.library.isDartAsync) { | |
1119 if (classElem == types.futureOrType.element) { | |
1120 var typeParamT = classElem.typeParameters[0].type; | |
1121 var tOrFutureOfT = js.call('#.is(o) || #.is(o)', [ | |
1122 _emitType(typeParamT), | |
1123 _emitType(types.futureType.instantiate([typeParamT])) | |
1124 ]); | |
1125 body.add(js.statement(''' | |
1126 #.is = function is_FutureOr(o) { | |
1127 return #; | |
1128 } | |
1129 ''', [className, tOrFutureOfT])); | |
1130 body.add(js.statement(''' | |
1131 #.as = function as_FutureOr(o) { | |
1132 if (o == null || #) return o; | |
1133 return #.castError(o, this, false); | |
1134 } | |
1135 ''', [className, tOrFutureOfT, _runtimeModule])); | |
1136 body.add(js.statement(''' | |
1137 #._check = function check_FutureOr(o) { | |
1138 if (o == null || #) return o; | |
1139 return #.castError(o, this, true); | |
Leaf
2017/08/24 17:19:39
Is there a reason the other cases above go through
Jennifer Messerly
2017/08/24 18:26:37
yeah, I'm trying to simplify things and have the c
| |
1140 } | |
1141 ''', [className, tOrFutureOfT, _runtimeModule])); | |
1142 return null; | |
1143 } | |
1144 } | |
1145 | |
1146 body.add(_callHelperStatement('addTypeTests(#);', [className])); | |
1147 | |
1148 if (classElem.typeParameters.isEmpty) return null; | |
1149 | |
1150 // For generics, testing against the default instantiation is common, | |
1151 // so optimize that. | |
1152 var isClassSymbol = getInterfaceSymbol(classElem); | |
1153 if (isClassSymbol == null) { | |
1154 // TODO(jmesserly): we could export these symbols, if we want to mark | |
1155 // implemented interfaces for user-defined classes. | |
1156 var id = new JS.TemporaryId("_is_${classElem.name}_default"); | |
1157 _moduleItems.add( | |
1158 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | |
1159 isClassSymbol = id; | |
1160 } | |
1161 // Marking every generic type instantiation as a subtype of its default | |
1162 // instantiation. | |
1163 markSubtypeOf(isClassSymbol); | |
1164 | |
1165 // Define the type tests on the default instantiation to check for that | |
1166 // marker. | |
1167 var defaultInst = _emitTopLevelName(classElem); | |
1168 | |
1169 // Return this `addTypeTests` call so we can emit it outside of the generic | |
1170 // type parameter scope. | |
1171 return _callHelperStatement( | |
1172 'addTypeTests(#, #);', [defaultInst, isClassSymbol]); | |
1154 } | 1173 } |
1155 | 1174 |
1156 void _emitSuperHelperSymbols(List<JS.Statement> body) { | 1175 void _emitSuperHelperSymbols(List<JS.Statement> body) { |
1157 for (var id in _superHelpers.values.map((m) => m.name as JS.TemporaryId)) { | 1176 for (var id in _superHelpers.values.map((m) => m.name as JS.TemporaryId)) { |
1158 body.add(js.statement('const # = Symbol(#)', [id, js.string(id.name)])); | 1177 body.add(js.statement('const # = Symbol(#)', [id, js.string(id.name)])); |
1159 } | 1178 } |
1160 _superHelpers.clear(); | 1179 _superHelpers.clear(); |
1161 } | 1180 } |
1162 | 1181 |
1163 void _emitVirtualFieldSymbols( | 1182 void _emitVirtualFieldSymbols( |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1275 } | 1294 } |
1276 | 1295 |
1277 var genericCall = _callHelper('generic(#)', [genericArgs]); | 1296 var genericCall = _callHelper('generic(#)', [genericArgs]); |
1278 | 1297 |
1279 if (element.library.isDartAsync && | 1298 if (element.library.isDartAsync && |
1280 (element.name == "Future" || element.name == "_Future")) { | 1299 (element.name == "Future" || element.name == "_Future")) { |
1281 genericCall = _callHelper('flattenFutures(#)', [genericCall]); | 1300 genericCall = _callHelper('flattenFutures(#)', [genericCall]); |
1282 } | 1301 } |
1283 var genericDef = js.statement( | 1302 var genericDef = js.statement( |
1284 '# = #;', [_emitTopLevelName(element, suffix: r'$'), genericCall]); | 1303 '# = #;', [_emitTopLevelName(element, suffix: r'$'), genericCall]); |
1304 // TODO(jmesserly): this should be instantiate to bounds | |
1285 var dynType = fillDynamicTypeArgs(element.type); | 1305 var dynType = fillDynamicTypeArgs(element.type); |
1286 var genericInst = _emitType(dynType, lowerGeneric: true); | 1306 var genericInst = _emitType(dynType, lowerGeneric: true); |
1287 return js.statement( | 1307 return js.statement( |
1288 '{ #; # = #; }', [genericDef, _emitTopLevelName(element), genericInst]); | 1308 '{ #; # = #; }', [genericDef, _emitTopLevelName(element), genericInst]); |
1289 } | 1309 } |
1290 | 1310 |
1291 bool _deferIfNeeded(DartType type, ClassElement current) { | 1311 bool _deferIfNeeded(DartType type, ClassElement current) { |
1292 if (type is ParameterizedType) { | 1312 if (type is ParameterizedType) { |
1293 var typeArguments = type.typeArguments; | 1313 var typeArguments = type.typeArguments; |
1294 for (var typeArg in typeArguments) { | 1314 for (var typeArg in typeArguments) { |
(...skipping 538 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1833 // We can ignore `noSuchMethod` because: | 1853 // We can ignore `noSuchMethod` because: |
1834 // * `dynamic d; d();` without a declared `call` method is handled by dcall. | 1854 // * `dynamic d; d();` without a declared `call` method is handled by dcall. |
1835 // * for `class C implements Callable { noSuchMethod(i) { ... } }` we find | 1855 // * for `class C implements Callable { noSuchMethod(i) { ... } }` we find |
1836 // the `call` method on the `Callable` interface. | 1856 // the `call` method on the `Callable` interface. |
1837 var callMethod = classElem.type.lookUpInheritedGetterOrMethod('call'); | 1857 var callMethod = classElem.type.lookUpInheritedGetterOrMethod('call'); |
1838 bool isCallable = callMethod is PropertyAccessorElement | 1858 bool isCallable = callMethod is PropertyAccessorElement |
1839 ? callMethod.returnType is FunctionType | 1859 ? callMethod.returnType is FunctionType |
1840 : callMethod != null; | 1860 : callMethod != null; |
1841 | 1861 |
1842 var body = <JS.Statement>[]; | 1862 var body = <JS.Statement>[]; |
1863 if (isCallable) { | |
1864 // Our class instances will have JS `typeof this == "function"`, | |
1865 // so make sure to attach the runtime type information the same way | |
1866 // we would do it for function types. | |
1867 body.add(js.statement('#.prototype[#] = #;', | |
1868 [className, _callHelper('_runtimeType'), className])); | |
1869 } | |
1870 | |
1843 void addConstructor(ConstructorElement element, JS.Expression jsCtor) { | 1871 void addConstructor(ConstructorElement element, JS.Expression jsCtor) { |
1844 var ctorName = _constructorName(element); | 1872 var ctorName = _constructorName(element); |
1845 if (JS.invalidStaticFieldName(element.name)) { | 1873 if (JS.invalidStaticFieldName(element.name)) { |
1846 jsCtor = | 1874 jsCtor = |
1847 _callHelper('defineValue(#, #, #)', [className, ctorName, jsCtor]); | 1875 _callHelper('defineValue(#, #, #)', [className, ctorName, jsCtor]); |
1848 } else { | 1876 } else { |
1849 jsCtor = js.call('#.# = #', [className, ctorName, jsCtor]); | 1877 jsCtor = js.call('#.# = #', [className, ctorName, jsCtor]); |
1850 } | 1878 } |
1851 body.add(js.statement('#.prototype = #.prototype;', [jsCtor, className])); | 1879 body.add(js.statement('#.prototype = #.prototype;', [jsCtor, className])); |
1852 } | 1880 } |
(...skipping 4139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5992 if (targetIdentifier.staticElement is! PrefixElement) return false; | 6020 if (targetIdentifier.staticElement is! PrefixElement) return false; |
5993 var prefix = targetIdentifier.staticElement as PrefixElement; | 6021 var prefix = targetIdentifier.staticElement as PrefixElement; |
5994 | 6022 |
5995 // The library the prefix is referring to must come from a deferred import. | 6023 // The library the prefix is referring to must come from a deferred import. |
5996 var containingLibrary = resolutionMap | 6024 var containingLibrary = resolutionMap |
5997 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 6025 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
5998 .library; | 6026 .library; |
5999 var imports = containingLibrary.getImportsWithPrefix(prefix); | 6027 var imports = containingLibrary.getImportsWithPrefix(prefix); |
6000 return imports.length == 1 && imports[0].isDeferred; | 6028 return imports.length == 1 && imports[0].isDeferred; |
6001 } | 6029 } |
OLD | NEW |