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

Side by Side Diff: pkg/compiler/lib/src/native/behavior.dart

Issue 2686533002: Refactor computation of NativeBehavior. (Closed)
Patch Set: Updated cf. comments. Created 3 years, 10 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
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 import '../common.dart'; 5 import '../common.dart';
6 import '../common/backend_api.dart' show ForeignResolver; 6 import '../common/backend_api.dart' show BackendClasses, ForeignResolver;
7 import '../common/resolution.dart' show ParsingContext, Resolution; 7 import '../common/resolution.dart' show ParsingContext, Resolution;
8 import '../compiler.dart' show Compiler; 8 import '../compiler.dart' show Compiler;
9 import '../compile_time_constants.dart' show ConstantEnvironment;
9 import '../constants/expressions.dart'; 10 import '../constants/expressions.dart';
10 import '../constants/values.dart'; 11 import '../constants/values.dart';
11 import '../core_types.dart' show CommonElements; 12 import '../core_types.dart' show CommonElements;
13 import '../elements/elements.dart';
14 import '../elements/entities.dart';
12 import '../elements/resolution_types.dart'; 15 import '../elements/resolution_types.dart';
13 import '../elements/elements.dart'; 16 import '../elements/types.dart';
14 import '../js/js.dart' as js; 17 import '../js/js.dart' as js;
15 import '../js_backend/js_backend.dart'; 18 import '../js_backend/js_backend.dart';
19 import '../js_backend/backend_helpers.dart';
20 import '../options.dart';
16 import '../tree/tree.dart'; 21 import '../tree/tree.dart';
17 import '../universe/side_effects.dart' show SideEffects; 22 import '../universe/side_effects.dart' show SideEffects;
18 import '../util/util.dart'; 23 import '../util/util.dart';
19 import 'enqueue.dart';
20 import 'js.dart'; 24 import 'js.dart';
21 25
22 typedef dynamic /*DartType|SpecialType*/ TypeLookup(String typeString); 26 typedef dynamic /*DartType|SpecialType*/ TypeLookup(String typeString);
23 27
24 /// This class is a temporary work-around until we get a more powerful DartType. 28 /// This class is a temporary work-around until we get a more powerful DartType.
25 class SpecialType { 29 class SpecialType {
26 final String name; 30 final String name;
27 const SpecialType._(this.name); 31 const SpecialType._(this.name);
28 32
29 /// The type Object, but no subtypes: 33 /// The type Object, but no subtypes:
(...skipping 725 matching lines...) Expand 10 before | Expand all | Expand 10 after
755 759
756 ResolutionDartType lookup(String name) { 760 ResolutionDartType lookup(String name) {
757 Element e = element.buildScope().lookup(name); 761 Element e = element.buildScope().lookup(name);
758 if (e == null) return null; 762 if (e == null) return null;
759 if (e is! ClassElement) return null; 763 if (e is! ClassElement) return null;
760 ClassElement cls = e; 764 ClassElement cls = e;
761 cls.ensureResolved(compiler.resolution); 765 cls.ensureResolved(compiler.resolution);
762 return cls.thisType; 766 return cls.thisType;
763 } 767 }
764 768
765 return ofMethod(element, type, metadata, lookup, compiler, 769 BehaviorBuilder builder = new ResolverBehaviorBuilder(compiler);
770 return builder.buildMethodBehavior(type, metadata, lookup,
766 isJsInterop: compiler.backend.isJsInterop(element)); 771 isJsInterop: compiler.backend.isJsInterop(element));
767 } 772 }
768 773
769 static NativeBehavior ofMethod(
770 Spannable spannable,
771 ResolutionFunctionType type,
772 List<ConstantExpression> metadata,
773 TypeLookup lookupType,
774 Compiler compiler,
775 {bool isJsInterop}) {
776 var behavior = new NativeBehavior();
777 var returnType = type.returnType;
778 // Note: For dart:html and other internal libraries we maintain, we can
779 // trust the return type and use it to limit what we enqueue. We have to
780 // be more conservative about JS interop types and assume they can return
781 // anything (unless the user provides the experimental flag to trust the
782 // type of js-interop APIs). We do restrict the allocation effects and say
783 // that interop calls create only interop types (which may be unsound if
784 // an interop call returns a DOM type and declares a dynamic return type,
785 // but otherwise we would include a lot of code by default).
786 // TODO(sigmund,sra): consider doing something better for numeric types.
787 behavior.typesReturned.add(
788 !isJsInterop || compiler.options.trustJSInteropTypeAnnotations
789 ? returnType
790 : const ResolutionDynamicType());
791 if (!type.returnType.isVoid) {
792 // Declared types are nullable.
793 behavior.typesReturned.add(compiler.commonElements.nullType);
794 }
795 behavior._capture(type, compiler.resolution,
796 isInterop: isJsInterop, compiler: compiler);
797
798 for (ResolutionDartType type in type.optionalParameterTypes) {
799 behavior._escape(type, compiler.resolution);
800 }
801 for (ResolutionDartType type in type.namedParameterTypes) {
802 behavior._escape(type, compiler.resolution);
803 }
804
805 behavior._overrideWithAnnotations(
806 spannable, metadata, lookupType, compiler);
807 return behavior;
808 }
809
810 static NativeBehavior ofFieldElementLoad( 774 static NativeBehavior ofFieldElementLoad(
811 MemberElement element, Compiler compiler) { 775 MemberElement element, Compiler compiler) {
812 Resolution resolution = compiler.resolution; 776 Resolution resolution = compiler.resolution;
813 ResolutionDartType type = element.computeType(resolution); 777 ResolutionDartType type = element.computeType(resolution);
814 List<ConstantExpression> metadata = <ConstantExpression>[]; 778 List<ConstantExpression> metadata = <ConstantExpression>[];
815 for (MetadataAnnotation annotation in element.implementation.metadata) { 779 for (MetadataAnnotation annotation in element.implementation.metadata) {
816 annotation.ensureResolved(compiler.resolution); 780 annotation.ensureResolved(compiler.resolution);
817 metadata.add(annotation.constant); 781 metadata.add(annotation.constant);
818 } 782 }
819 783
820 ResolutionDartType lookup(String name) { 784 ResolutionDartType lookup(String name) {
821 Element e = element.buildScope().lookup(name); 785 Element e = element.buildScope().lookup(name);
822 if (e == null) return null; 786 if (e == null) return null;
823 if (e is! ClassElement) return null; 787 if (e is! ClassElement) return null;
824 ClassElement cls = e; 788 ClassElement cls = e;
825 cls.ensureResolved(compiler.resolution); 789 cls.ensureResolved(compiler.resolution);
826 return cls.thisType; 790 return cls.thisType;
827 } 791 }
828 792
829 return ofFieldLoad(element, type, metadata, lookup, compiler, 793 BehaviorBuilder builder = new ResolverBehaviorBuilder(compiler);
794 return builder.buildFieldLoadBehavior(type, metadata, lookup,
830 isJsInterop: compiler.backend.isJsInterop(element)); 795 isJsInterop: compiler.backend.isJsInterop(element));
831 } 796 }
832 797
833 static NativeBehavior ofFieldLoad( 798 static NativeBehavior ofFieldElementStore(
834 Spannable spannable, 799 MemberElement field, Compiler compiler) {
835 ResolutionDartType type, 800 BehaviorBuilder builder = new ResolverBehaviorBuilder(compiler);
836 List<ConstantExpression> metadata, 801 ResolutionDartType type = field.computeType(compiler.resolution);
837 TypeLookup lookupType, 802 return builder.buildFieldStoreBehavior(type);
838 Compiler compiler,
839 {bool isJsInterop}) {
840 Resolution resolution = compiler.resolution;
841 var behavior = new NativeBehavior();
842 // TODO(sigmund,sra): consider doing something better for numeric types.
843 behavior.typesReturned.add(
844 !isJsInterop || compiler.options.trustJSInteropTypeAnnotations
845 ? type
846 : const ResolutionDynamicType());
847 // Declared types are nullable.
848 behavior.typesReturned.add(resolution.commonElements.nullType);
849 behavior._capture(type, resolution,
850 isInterop: isJsInterop, compiler: compiler);
851 behavior._overrideWithAnnotations(
852 spannable, metadata, lookupType, compiler);
853 return behavior;
854 } 803 }
855 804
856 static NativeBehavior ofFieldElementStore( 805 static dynamic /*DartType|SpecialType*/ _parseType(String typeString,
857 MemberElement field, Resolution resolution) { 806 Spannable spannable, DiagnosticReporter reporter, TypeLookup lookupType) {
858 ResolutionDartType type = field.computeType(resolution); 807 if (typeString == '=Object') return SpecialType.JsObject;
859 return ofFieldStore(type, resolution); 808 if (typeString == 'dynamic') {
809 return const ResolutionDynamicType();
810 }
811 var type = lookupType(typeString);
812 if (type != null) return type;
813
814 int index = typeString.indexOf('<');
815 if (index < 1) {
816 reporter.reportErrorMessage(spannable, MessageKind.GENERIC,
817 {'text': "Type '$typeString' not found."});
818 return const ResolutionDynamicType();
819 }
820 type = lookupType(typeString.substring(0, index));
821 if (type != null) {
822 // TODO(sra): Parse type parameters.
823 return type;
824 }
825 reporter.reportErrorMessage(spannable, MessageKind.GENERIC,
826 {'text': "Type '$typeString' not found."});
827 return const ResolutionDynamicType();
860 } 828 }
829 }
861 830
862 static NativeBehavior ofFieldStore( 831 abstract class BehaviorBuilder {
863 ResolutionDartType type, Resolution resolution) { 832 CommonElements get commonElements;
864 var behavior = new NativeBehavior(); 833 BackendClasses get backendClasses;
865 behavior._escape(type, resolution); 834 BackendHelpers get helpers;
866 // We don't override the default behaviour - the annotations apply to 835 DiagnosticReporter get reporter;
867 // loading the field. 836 ConstantEnvironment get constants;
868 return behavior; 837 bool get trustJSInteropTypeAnnotations;
869 } 838
839 Resolution get resolution => null;
840
841 NativeBehavior _behavior;
870 842
871 void _overrideWithAnnotations( 843 void _overrideWithAnnotations(
872 Spannable spannable, 844 Iterable<ConstantExpression> metadata, TypeLookup lookupType) {
873 Iterable<ConstantExpression> metadata,
874 TypeLookup lookupType,
875 Compiler compiler) {
876 if (metadata.isEmpty) return; 845 if (metadata.isEmpty) return;
877 846
878 NativeEnqueuer enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; 847 List creates =
879 var creates = _collect(spannable, metadata, compiler, 848 _collect(metadata, helpers.annotationCreatesClass, lookupType);
880 enqueuer.annotationCreatesClass, lookupType); 849 List returns =
881 var returns = _collect(spannable, metadata, compiler, 850 _collect(metadata, helpers.annotationReturnsClass, lookupType);
882 enqueuer.annotationReturnsClass, lookupType);
883 851
884 if (creates != null) { 852 if (creates != null) {
885 typesInstantiated 853 _behavior.typesInstantiated
886 ..clear() 854 ..clear()
887 ..addAll(creates); 855 ..addAll(creates);
888 } 856 }
889 if (returns != null) { 857 if (returns != null) {
890 typesReturned 858 _behavior.typesReturned
891 ..clear() 859 ..clear()
892 ..addAll(returns); 860 ..addAll(returns);
893 } 861 }
894 } 862 }
895 863
896 /** 864 /**
897 * Returns a list of type constraints from the annotations of 865 * Returns a list of type constraints from the annotations of
898 * [annotationClass]. 866 * [annotationClass].
899 * Returns `null` if no constraints. 867 * Returns `null` if no constraints.
900 */ 868 */
901 static _collect(Spannable spannable, Iterable<ConstantExpression> metadata, 869 List _collect(Iterable<ConstantExpression> metadata,
902 Compiler compiler, Element annotationClass, TypeLookup lookupType) { 870 ClassEntity annotationClass, TypeLookup lookupType) {
903 DiagnosticReporter reporter = compiler.reporter;
904 var types = null; 871 var types = null;
905 for (ConstantExpression constant in metadata) { 872 for (ConstantExpression constant in metadata) {
906 ConstantValue value = compiler.constants.getConstantValue(constant); 873 ConstantValue value = constants.getConstantValue(constant);
907 if (!value.isConstructedObject) continue; 874 if (!value.isConstructedObject) continue;
908 ConstructedConstantValue constructedObject = value; 875 ConstructedConstantValue constructedObject = value;
909 if (constructedObject.type.element != annotationClass) continue; 876 if (constructedObject.type.element != annotationClass) continue;
910 877
911 Iterable<ConstantValue> fields = constructedObject.fields.values; 878 Iterable<ConstantValue> fields = constructedObject.fields.values;
912 // TODO(sra): Better validation of the constant. 879 // TODO(sra): Better validation of the constant.
913 if (fields.length != 1 || !fields.single.isString) { 880 if (fields.length != 1 || !fields.single.isString) {
914 reporter.internalError(spannable, 881 reporter.internalError(CURRENT_ELEMENT_SPANNABLE,
915 'Annotations needs one string: ${constant.toStructuredText()}'); 882 'Annotations needs one string: ${constant.toStructuredText()}');
916 } 883 }
917 StringConstantValue specStringConstant = fields.single; 884 StringConstantValue specStringConstant = fields.single;
918 String specString = specStringConstant.toDartString().slowToString(); 885 String specString = specStringConstant.toDartString().slowToString();
919 for (final typeString in specString.split('|')) { 886 for (final typeString in specString.split('|')) {
920 var type = _parseType(typeString, spannable, reporter, lookupType); 887 var type = NativeBehavior._parseType(
888 typeString, CURRENT_ELEMENT_SPANNABLE, reporter, lookupType);
921 if (types == null) types = []; 889 if (types == null) types = [];
922 types.add(type); 890 types.add(type);
923 } 891 }
924 } 892 }
925 return types; 893 return types;
926 } 894 }
927 895
928 /// Models the behavior of having intances of [type] escape from Dart code 896 /// Models the behavior of having intances of [type] escape from Dart code
929 /// into native code. 897 /// into native code.
930 void _escape(ResolutionDartType type, Resolution resolution) { 898 void _escape(DartType type) {
931 type.computeUnaliased(resolution); 899 if (type is ResolutionDartType) {
900 type.computeUnaliased(resolution);
901 }
932 type = type.unaliased; 902 type = type.unaliased;
933 if (type is ResolutionFunctionType) { 903 if (type is FunctionType) {
934 ResolutionFunctionType functionType = type; 904 FunctionType functionType = type;
935 // A function might be called from native code, passing us novel 905 // A function might be called from native code, passing us novel
936 // parameters. 906 // parameters.
937 _escape(functionType.returnType, resolution); 907 _escape(functionType.returnType);
938 for (ResolutionDartType parameter in functionType.parameterTypes) { 908 for (DartType parameter in functionType.parameterTypes) {
939 _capture(parameter, resolution); 909 _capture(parameter);
940 } 910 }
941 } 911 }
942 } 912 }
943 913
944 /// Models the behavior of Dart code receiving instances and methods of [type] 914 /// Models the behavior of Dart code receiving instances and methods of [type]
945 /// from native code. We usually start the analysis by capturing a native 915 /// from native code. We usually start the analysis by capturing a native
946 /// method that has been used. 916 /// method that has been used.
947 /// 917 ///
948 /// We assume that JS-interop APIs cannot instantiate Dart types or 918 /// We assume that JS-interop APIs cannot instantiate Dart types or
949 /// non-JSInterop native types. 919 /// non-JSInterop native types.
950 void _capture(ResolutionDartType type, Resolution resolution, 920 void _capture(DartType type, {bool isInterop: false}) {
951 {bool isInterop: false, Compiler compiler}) { 921 if (type is ResolutionDartType) {
952 type.computeUnaliased(resolution); 922 type.computeUnaliased(resolution);
923 }
953 type = type.unaliased; 924 type = type.unaliased;
954 if (type is ResolutionFunctionType) { 925 if (type is FunctionType) {
955 ResolutionFunctionType functionType = type; 926 FunctionType functionType = type;
956 _capture(functionType.returnType, resolution, 927 _capture(functionType.returnType, isInterop: isInterop);
957 isInterop: isInterop, compiler: compiler); 928 for (DartType parameter in functionType.parameterTypes) {
958 for (ResolutionDartType parameter in functionType.parameterTypes) { 929 _escape(parameter);
959 _escape(parameter, resolution);
960 } 930 }
961 } else { 931 } else {
962 JavaScriptBackend backend = compiler?.backend;
963 if (!isInterop) { 932 if (!isInterop) {
964 typesInstantiated.add(type); 933 _behavior.typesInstantiated.add(type);
965 } else { 934 } else {
966 if (type.element != null && backend.isNative(type.element)) { 935 if (type is InterfaceType &&
936 backendClasses.isNativeClass(type.element)) {
967 // Any declared native or interop type (isNative implies isJsInterop) 937 // Any declared native or interop type (isNative implies isJsInterop)
968 // is assumed to be allocated. 938 // is assumed to be allocated.
969 typesInstantiated.add(type); 939 _behavior.typesInstantiated.add(type);
970 } 940 }
971 941
972 if (!compiler.options.trustJSInteropTypeAnnotations || 942 if (!trustJSInteropTypeAnnotations ||
973 type.isDynamic || 943 type.isDynamic ||
974 type.isObject) { 944 type == commonElements.objectType) {
975 // By saying that only JS-interop types can be created, we prevent 945 // By saying that only JS-interop types can be created, we prevent
976 // pulling in every other native type (e.g. all of dart:html) when a 946 // pulling in every other native type (e.g. all of dart:html) when a
977 // JS interop API returns dynamic or when we don't trust the type 947 // JS interop API returns dynamic or when we don't trust the type
978 // annotations. This means that to some degree we still use the return 948 // annotations. This means that to some degree we still use the return
979 // type to decide whether to include native types, even if we don't 949 // type to decide whether to include native types, even if we don't
980 // trust the type annotation. 950 // trust the type annotation.
981 ClassElement cls = backend.helpers.jsJavaScriptObjectClass; 951 ClassElement cls = helpers.jsJavaScriptObjectClass;
982 cls.ensureResolved(resolution); 952 cls.ensureResolved(resolution);
983 typesInstantiated.add(cls.thisType); 953 _behavior.typesInstantiated.add(cls.thisType);
984 } else { 954 } else {
985 // Otherwise, when the declared type is a Dart type, we do not 955 // Otherwise, when the declared type is a Dart type, we do not
986 // register an allocation because we assume it cannot be instantiated 956 // register an allocation because we assume it cannot be instantiated
987 // from within the JS-interop code. It must have escaped from another 957 // from within the JS-interop code. It must have escaped from another
988 // API. 958 // API.
989 } 959 }
990 } 960 }
991 } 961 }
992 } 962 }
993 963
994 static dynamic /*DartType|SpecialType*/ _parseType(String typeString, 964 NativeBehavior buildFieldLoadBehavior(DartType type,
995 Spannable spannable, DiagnosticReporter reporter, TypeLookup lookupType) { 965 Iterable<ConstantExpression> metadata, TypeLookup lookupType,
996 if (typeString == '=Object') return SpecialType.JsObject; 966 {bool isJsInterop}) {
997 if (typeString == 'dynamic') { 967 _behavior = new NativeBehavior();
998 return const ResolutionDynamicType(); 968 // TODO(sigmund,sra): consider doing something better for numeric types.
969 _behavior.typesReturned.add(!isJsInterop || trustJSInteropTypeAnnotations
970 ? type
971 : commonElements.dynamicType);
972 // Declared types are nullable.
973 _behavior.typesReturned.add(commonElements.nullType);
974 _capture(type, isInterop: isJsInterop);
975 _overrideWithAnnotations(metadata, lookupType);
976 return _behavior;
977 }
978
979 NativeBehavior buildFieldStoreBehavior(DartType type) {
980 _behavior = new NativeBehavior();
981 _escape(type);
982 // We don't override the default behaviour - the annotations apply to
983 // loading the field.
984 return _behavior;
985 }
986
987 NativeBehavior buildMethodBehavior(FunctionType type,
988 List<ConstantExpression> metadata, TypeLookup lookupType,
989 {bool isJsInterop}) {
990 _behavior = new NativeBehavior();
991 DartType returnType = type.returnType;
992 // Note: For dart:html and other internal libraries we maintain, we can
993 // trust the return type and use it to limit what we enqueue. We have to
994 // be more conservative about JS interop types and assume they can return
995 // anything (unless the user provides the experimental flag to trust the
996 // type of js-interop APIs). We do restrict the allocation effects and say
997 // that interop calls create only interop types (which may be unsound if
998 // an interop call returns a DOM type and declares a dynamic return type,
999 // but otherwise we would include a lot of code by default).
1000 // TODO(sigmund,sra): consider doing something better for numeric types.
1001 _behavior.typesReturned.add(!isJsInterop || trustJSInteropTypeAnnotations
1002 ? returnType
1003 : commonElements.dynamicType);
1004 if (!type.returnType.isVoid) {
1005 // Declared types are nullable.
1006 _behavior.typesReturned.add(commonElements.nullType);
999 } 1007 }
1000 var type = lookupType(typeString); 1008 _capture(type, isInterop: isJsInterop);
1001 if (type != null) return type;
1002 1009
1003 int index = typeString.indexOf('<'); 1010 for (DartType type in type.optionalParameterTypes) {
1004 if (index < 1) { 1011 _escape(type);
1005 reporter.reportErrorMessage(spannable, MessageKind.GENERIC,
1006 {'text': "Type '$typeString' not found."});
1007 return const ResolutionDynamicType();
1008 } 1012 }
1009 type = lookupType(typeString.substring(0, index)); 1013 for (DartType type in type.namedParameterTypes) {
1010 if (type != null) { 1014 _escape(type);
1011 // TODO(sra): Parse type parameters.
1012 return type;
1013 } 1015 }
1014 reporter.reportErrorMessage(spannable, MessageKind.GENERIC, 1016
1015 {'text': "Type '$typeString' not found."}); 1017 _overrideWithAnnotations(metadata, lookupType);
1016 return const ResolutionDynamicType(); 1018 return _behavior;
1017 } 1019 }
1018 } 1020 }
1021
1022 class ResolverBehaviorBuilder extends BehaviorBuilder {
1023 final Compiler compiler;
1024
1025 ResolverBehaviorBuilder(this.compiler);
1026
1027 @override
1028 CommonElements get commonElements => compiler.commonElements;
1029
1030 @override
1031 bool get trustJSInteropTypeAnnotations =>
1032 compiler.options.trustJSInteropTypeAnnotations;
1033
1034 @override
1035 ConstantEnvironment get constants => compiler.constants;
1036
1037 @override
1038 DiagnosticReporter get reporter => compiler.reporter;
1039
1040 @override
1041 BackendHelpers get helpers {
1042 JavaScriptBackend backend = compiler.backend;
1043 return backend.helpers;
1044 }
1045
1046 @override
1047 BackendClasses get backendClasses => compiler.backend.backendClasses;
1048
1049 @override
1050 Resolution get resolution => compiler.resolution;
1051 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/kernel/world_builder.dart ('k') | pkg/compiler/lib/src/native/enqueue.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698