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