OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 2 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
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 """This module provides shared functionality for the systems to generate | 6 """This module provides shared functionality for the systems to generate |
7 native binding from the IDL database.""" | 7 native binding from the IDL database.""" |
8 | 8 |
9 import emitter | 9 import emitter |
10 import os | 10 import os |
(...skipping 725 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
736 dart_declaration = 'void operator[]=(int index, %s value)' % element_type | 736 dart_declaration = 'void operator[]=(int index, %s value)' % element_type |
737 self._GenerateNativeBinding('numericIndexSetter', 3, dart_declaration, | 737 self._GenerateNativeBinding('numericIndexSetter', 3, dart_declaration, |
738 'Callback', True) | 738 'Callback', True) |
739 | 739 |
740 def _AddOperation(self, info): | 740 def _AddOperation(self, info): |
741 """ | 741 """ |
742 Arguments: | 742 Arguments: |
743 info: An OperationInfo object. | 743 info: An OperationInfo object. |
744 """ | 744 """ |
745 | 745 |
746 if 'CheckSecurityForNode' in info.overloads[0].ext_attrs: | 746 operation = info.operations[0] |
| 747 |
| 748 if 'CheckSecurityForNode' in operation.ext_attrs: |
747 # FIXME: exclude from interface as well. | 749 # FIXME: exclude from interface as well. |
748 return | 750 return |
749 | 751 |
750 html_interface_name = self._HTMLInterfaceName(self._interface.id) | 752 html_interface_name = self._HTMLInterfaceName(self._interface.id) |
751 html_name = self._html_system.RenameInHtmlLibrary( | 753 html_name = self._html_system.RenameInHtmlLibrary( |
752 html_interface_name, info.name, implementation_class=True) | 754 html_interface_name, info.name, implementation_class=True) |
753 | 755 |
754 if not html_name and info.name == 'item': | 756 if not html_name and info.name == 'item': |
755 # FIXME: item should be renamed to operator[], not removed. | 757 # FIXME: item should be renamed to operator[], not removed. |
756 html_name = 'item' | 758 html_name = 'item' |
757 | 759 |
758 if not html_name: | 760 if not html_name: |
759 return | 761 return |
760 | 762 |
761 dart_declaration = '%s%s %s(%s)' % ( | 763 dart_declaration = '%s%s %s(%s)' % ( |
762 'static ' if info.IsStatic() else '', | 764 'static ' if info.IsStatic() else '', |
763 self._DartType(info.type_name), | 765 self._DartType(info.type_name), |
764 html_name or info.name, | 766 html_name, |
765 info.ParametersImplementationDeclaration(self._DartType)) | 767 info.ParametersImplementationDeclaration(self._DartType)) |
766 | 768 |
767 if 'Custom' in info.overloads[0].ext_attrs: | 769 is_custom = 'Custom' in operation.ext_attrs |
| 770 has_optional_arguments = any(IsOptional(argument) for argument in operation.
arguments) |
| 771 needs_dispatcher = not is_custom and (len(info.operations) > 1 or has_option
al_arguments) |
| 772 if not needs_dispatcher: |
| 773 # Bind directly to native implementation |
768 argument_count = (0 if info.IsStatic() else 1) + len(info.param_infos) | 774 argument_count = (0 if info.IsStatic() else 1) + len(info.param_infos) |
769 self._GenerateNativeBinding(info.name , argument_count, | 775 cpp_callback_name = self._GenerateNativeBinding( |
770 dart_declaration, 'Callback', True) | 776 info.name, argument_count, dart_declaration, 'Callback', is_custom) |
771 return | 777 if not is_custom: |
| 778 self._GenerateOperationNativeCallback(operation, operation.arguments, cp
p_callback_name) |
| 779 else: |
| 780 self._GenerateDispatcher(info.operations, dart_declaration, [info.name for
info in info.param_infos]) |
| 781 |
| 782 def _GenerateDispatcher(self, operations, dart_declaration, argument_names): |
772 | 783 |
773 body = self._members_emitter.Emit( | 784 body = self._members_emitter.Emit( |
774 '\n' | 785 '\n' |
775 ' $DECLARATION {\n' | 786 ' $DECLARATION {\n' |
776 '$!BODY' | 787 '$!BODY' |
777 ' }\n', | 788 ' }\n', |
778 DECLARATION=dart_declaration) | 789 DECLARATION=dart_declaration) |
779 | 790 |
780 if self._interface.id == 'IDBObjectStore' and info.name == 'openCursor': | 791 version = [1] |
781 # FIXME: implement v8-like overload resolver and remove this hack. | 792 def GenerateCall(operation, argument_count, checks): |
782 info.overloads = info.overloads[1:] | 793 if checks: |
| 794 if operation.type.id != 'void': |
| 795 template = ' if ($CHECKS) {\n return $CALL;\n }\n' |
| 796 else: |
| 797 template = ' if ($CHECKS) {\n $CALL;\n return;\n }\n' |
| 798 else: |
| 799 if operation.type.id != 'void': |
| 800 template = ' return $CALL;\n' |
| 801 else: |
| 802 template = ' $CALL;\n' |
783 | 803 |
784 self._native_version = 0 | 804 overload_name = '%s_%s' % (operation.id, version[0]) |
785 overloads = self.CombineOverloads(info.overloads) | 805 version[0] += 1 |
786 fallthrough = self.GenerateDispatch(body, info, ' ', overloads) | 806 argument_list = ', '.join(argument_names[:argument_count]) |
787 if fallthrough: | 807 call = '_%s(%s)' % (overload_name, argument_list) |
| 808 body.Emit(template, CHECKS=' && '.join(checks), CALL=call) |
| 809 |
| 810 dart_declaration = '%s%s _%s(%s)' % ( |
| 811 'static ' if operation.is_static else '', |
| 812 self._DartType(operation.type.id), overload_name, argument_list) |
| 813 cpp_callback_name = self._GenerateNativeBinding( |
| 814 overload_name, (0 if operation.is_static else 1) + argument_count, |
| 815 dart_declaration, 'Callback', False) |
| 816 self._GenerateOperationNativeCallback(operation, operation.arguments[:argu
ment_count], cpp_callback_name) |
| 817 |
| 818 def GenerateChecksAndCall(operation, argument_count): |
| 819 checks = ['%s === null' % name for name in argument_names] |
| 820 for i in range(0, argument_count): |
| 821 argument = operation.arguments[i] |
| 822 checks[i] = '%s is %s' % (argument_names[i], self._DartType(argument.typ
e.id)) |
| 823 if IsOptional(argument) and 'Callback' in argument.ext_attrs: |
| 824 checks[i] = '(%s or %s === null)' % (checks[position], argument_names[
i]) |
| 825 GenerateCall(operation, argument_count, checks) |
| 826 |
| 827 def IsOptionalInWebCore(argument): |
| 828 return IsOptional(argument) and not 'Callback' in argument.ext_attrs |
| 829 |
| 830 # TODO: Optimize the dispatch to avoid repeated checks. |
| 831 if len(operations) > 1: |
| 832 for operation in operations: |
| 833 for position, argument in enumerate(operation.arguments): |
| 834 if IsOptionalInWebCore(argument): |
| 835 GenerateChecksAndCall(operation, position) |
| 836 GenerateChecksAndCall(operation, len(operation.arguments)) |
788 body.Emit(' throw "Incorrect number or type of arguments";\n'); | 837 body.Emit(' throw "Incorrect number or type of arguments";\n'); |
789 | 838 else: |
790 def CombineOverloads(self, overloads): | 839 operation = operations[0] |
791 # Combine overloads that can be implemented by the same native method. This | 840 for position, argument in list(enumerate(operation.arguments))[::-1]: |
792 # undoes the expansion of optional arguments into multiple overloads unless | 841 if IsOptionalInWebCore(argument): |
793 # IDL merging has made the overloads necessary. Starting with overload with | 842 check = '%s === null' % argument_names[position] |
794 # no optional arguments and grow it by adding optional arguments, then the | 843 GenerateCall(operation, position, [check]) |
795 # longest overload can serve for all the shorter ones. | 844 GenerateCall(operation, len(operation.arguments), []) |
796 out = [] | |
797 seed_index = 0 | |
798 while seed_index < len(overloads): | |
799 seed = overloads[seed_index] | |
800 if len(seed.arguments) > 0 and IsOptional(seed.arguments[-1]): | |
801 # Must start with no optional arguments. | |
802 out.append(seed) | |
803 seed_index += 1 | |
804 continue | |
805 | |
806 prev = seed | |
807 probe_index = seed_index + 1 | |
808 while probe_index < len(overloads): | |
809 probe = overloads[probe_index] | |
810 # Check that 'probe' extends 'prev' by one optional argument. | |
811 if len(probe.arguments) != len(prev.arguments) + 1: | |
812 break | |
813 if probe.arguments[:-1] != prev.arguments: | |
814 break | |
815 if not IsOptional(probe.arguments[-1]): | |
816 break | |
817 # See Issue 3177. This test against known implemented types is to | |
818 # prevent combining a possibly unimplemented type. Combining with an | |
819 # unimplemented type will cause all set of combined overloads to become | |
820 # 'unimplemented', even if no argument is passed to the the | |
821 # unimplemented parameter. | |
822 if DartType(probe.arguments[-1].type.id) not in [ | |
823 'String', 'int', 'num', 'double', 'bool', | |
824 'IDBKeyRange']: | |
825 break | |
826 probe_index += 1 | |
827 prev = probe | |
828 out.append(prev) | |
829 seed_index = probe_index | |
830 | |
831 return out | |
832 | |
833 def PrintOverloadsComment(self, emitter, info, indent, note, overloads): | |
834 emitter.Emit('$(INDENT)//$NOTE\n', INDENT=indent, NOTE=note) | |
835 for operation in overloads: | |
836 params = ', '.join([ | |
837 ('[Optional] ' if IsOptional(arg) else '') + DartType(arg.type.id) + '
' | |
838 + arg.id for arg in operation.arguments]) | |
839 emitter.Emit('$(INDENT)// $NAME($PARAMS)\n', | |
840 INDENT=indent, | |
841 NAME=info.name, | |
842 PARAMS=params) | |
843 emitter.Emit('$(INDENT)//\n', INDENT=indent) | |
844 | |
845 def GenerateDispatch(self, emitter, info, indent, overloads): | |
846 """Generates a dispatch to one of the overloads. | |
847 | |
848 Arguments: | |
849 emitter: an Emitter for the body of a block of code. | |
850 info: the compound information about the operation and its overloads. | |
851 indent: an indentation string for generated code. | |
852 position: the index of the parameter to dispatch on. | |
853 overloads: a list of the IDLOperations to dispatch. | |
854 | |
855 Returns True if the dispatch can fall through on failure, False if the code | |
856 always dispatches. | |
857 """ | |
858 | |
859 def NullCheck(name): | |
860 return '%s === null' % name | |
861 | |
862 def TypeCheck(name, type): | |
863 return '%s is %s' % (name, type) | |
864 | |
865 def IsNullable(type): | |
866 #return type != 'int' and type != 'num' | |
867 return True | |
868 | |
869 def PickRequiredCppSingleOperation(): | |
870 # Returns a special case single operation, or None. Check if we dispatch | |
871 # on RequiredCppParameter arguments. In this case all trailing arguments | |
872 # must be RequiredCppParameter and there is no need in dispatch. | |
873 def IsRequiredCppParameter(arg): | |
874 return 'RequiredCppParameter' in arg.ext_attrs | |
875 def HasRequiredCppParameters(op): | |
876 matches = filter(IsRequiredCppParameter, op.arguments) | |
877 if matches: | |
878 # Validate all the RequiredCppParameter ones are at the end. | |
879 rematches = filter(IsRequiredCppParameter, | |
880 op.arguments[len(op.arguments) - len(matches):]) | |
881 if len(matches) != len(rematches): | |
882 raise Exception('Invalid RequiredCppParameter - all subsequent ' | |
883 'parameters must also be RequiredCppParameter.') | |
884 return True | |
885 return False | |
886 if any(HasRequiredCppParameters(op) for op in overloads): | |
887 longest = max(overloads, key=lambda op: len(op.arguments)) | |
888 # Validate all other overloads are prefixes. | |
889 for op in overloads: | |
890 for (index, arg) in enumerate(op.arguments): | |
891 type1 = arg.type.id | |
892 type2 = longest.arguments[index].type.id | |
893 if type1 != type2: | |
894 raise Exception( | |
895 'Overloads for method %s with RequiredCppParameter have ' | |
896 'inconsistent types %s and %s for parameter #%s' % | |
897 (info.name, type1, type2, index)) | |
898 return longest | |
899 return None | |
900 | |
901 single_operation = PickRequiredCppSingleOperation() | |
902 if single_operation: | |
903 self.GenerateSingleOperation(emitter, info, indent, single_operation) | |
904 return False | |
905 | |
906 # Print just the interesting sets of overloads. | |
907 if len(overloads) > 1 or len(info.overloads) > 1: | |
908 self.PrintOverloadsComment(emitter, info, indent, '', info.overloads) | |
909 if overloads != info.overloads: | |
910 self.PrintOverloadsComment(emitter, info, indent, ' -- reduced:', | |
911 overloads) | |
912 | |
913 # Match each operation in turn. | |
914 # TODO: Optimize the dispatch to avoid repeated tests. | |
915 fallthrough = True | |
916 for operation in overloads: | |
917 tests = [] | |
918 for (position, param) in enumerate(info.param_infos): | |
919 if position < len(operation.arguments): | |
920 arg = operation.arguments[position] | |
921 dart_type = self._DartType(arg.type.id) | |
922 if dart_type == param.dart_type: | |
923 # The overload type matches the method parameter type exactly. We | |
924 # will have already tested this type in checked mode, and the target | |
925 # will expect (i.e. check) this type. This case happens when all | |
926 # the overloads have the same type in this position, including the | |
927 # trivial case of one overload. | |
928 test = None | |
929 else: | |
930 test = TypeCheck(param.name, dart_type) | |
931 if IsNullable(dart_type) or IsOptional(arg): | |
932 test = '(%s || %s)' % (NullCheck(param.name), test) | |
933 else: | |
934 test = NullCheck(param.name) | |
935 if test: | |
936 tests.append(test) | |
937 if tests: | |
938 cond = ' && '.join(tests) | |
939 if len(cond) + len(indent) + 7 > 80: | |
940 cond = (' &&\n' + indent + ' ').join(tests) | |
941 call = emitter.Emit( | |
942 '$(INDENT)if ($COND) {\n' | |
943 '$!CALL' | |
944 '$(INDENT)}\n', | |
945 COND=cond, | |
946 INDENT=indent) | |
947 self.GenerateSingleOperation(call, info, indent + ' ', operation) | |
948 else: | |
949 self.GenerateSingleOperation(emitter, info, indent, operation) | |
950 fallthrough = False | |
951 return fallthrough | |
952 | 845 |
953 def AddOperation(self, info): | 846 def AddOperation(self, info): |
954 self._AddOperation(info) | 847 self._AddOperation(info) |
955 | 848 |
956 def AddStaticOperation(self, info): | 849 def AddStaticOperation(self, info): |
957 self._AddOperation(info) | 850 self._AddOperation(info) |
958 | 851 |
959 def AddSecondaryOperation(self, interface, info): | 852 def AddSecondaryOperation(self, interface, info): |
960 self.AddOperation(info) | 853 self.AddOperation(info) |
961 | 854 |
962 def GenerateSingleOperation(self, dispatch_emitter, info, indent, operation): | 855 def _GenerateOperationNativeCallback(self, operation, arguments, cpp_callback_
name): |
963 """Generates a call to a single operation. | |
964 | |
965 Arguments: | |
966 dispatch_emitter: an dispatch_emitter for the body of a block of code. | |
967 info: the compound information about the operation and its overloads. | |
968 indent: an indentation string for generated code. | |
969 operation: the IDLOperation to call. | |
970 """ | |
971 | |
972 self._native_version += 1 | |
973 native_name = info.name | |
974 if self._native_version > 1: | |
975 native_name = '%s_%s' % (native_name, self._native_version) | |
976 argument_list = ', '.join([info.param_infos[i].name | |
977 for (i, arg) in enumerate(operation.arguments)]) | |
978 | |
979 # Generate dispatcher. | |
980 if info.type_name != 'void': | |
981 dispatch_emitter.Emit('$(INDENT)return _$NATIVENAME($ARGS);\n', | |
982 INDENT=indent, | |
983 NATIVENAME=native_name, | |
984 ARGS=argument_list) | |
985 else: | |
986 dispatch_emitter.Emit('$(INDENT)_$NATIVENAME($ARGS);\n' | |
987 '$(INDENT)return;\n', | |
988 INDENT=indent, | |
989 NATIVENAME=native_name, | |
990 ARGS=argument_list) | |
991 # Generate binding. | |
992 modifier = '' | |
993 if operation.is_static: | |
994 modifier = 'static ' | |
995 dart_declaration = '%s%s _%s(%s)' % (modifier, self._DartType(info.type_name
), native_name, | |
996 argument_list) | |
997 is_custom = 'Custom' in operation.ext_attrs | |
998 cpp_callback_name = self._GenerateNativeBinding( | |
999 native_name, (0 if operation.is_static else 1) + len(operation.arguments
), dart_declaration, 'Callback', | |
1000 is_custom) | |
1001 if is_custom: | |
1002 return | |
1003 | |
1004 # Generate callback. | |
1005 webcore_function_name = operation.ext_attrs.get('ImplementedAs', operation.i
d) | 856 webcore_function_name = operation.ext_attrs.get('ImplementedAs', operation.i
d) |
1006 | 857 |
1007 parameter_definitions_emitter = emitter.Emitter() | 858 parameter_definitions_emitter = emitter.Emitter() |
1008 arguments = [] | 859 cpp_arguments = [] |
1009 raises_exceptions = self._GenerateCallWithHandling( | 860 raises_exceptions = self._GenerateCallWithHandling( |
1010 operation, parameter_definitions_emitter, arguments) | 861 operation, parameter_definitions_emitter, cpp_arguments) |
1011 raises_exceptions = raises_exceptions or len(operation.arguments) > 0 or ope
ration.raises | 862 raises_exceptions = raises_exceptions or len(arguments) > 0 or operation.rai
ses |
1012 | 863 |
1013 # Process Dart arguments. | 864 # Process Dart cpp_arguments. |
1014 start_index = 1 | 865 start_index = 1 |
1015 if operation.is_static: | 866 if operation.is_static: |
1016 start_index = 0 | 867 start_index = 0 |
1017 for (i, argument) in enumerate(operation.arguments): | 868 for (i, argument) in enumerate(arguments): |
1018 if (i == len(operation.arguments) - 1 and | 869 if (i == len(arguments) - 1 and |
1019 self._interface.id == 'Console' and | 870 self._interface.id == 'Console' and |
1020 argument.id == 'arg'): | 871 argument.id == 'arg'): |
1021 # FIXME: we are skipping last argument here because it was added in | 872 # FIXME: we are skipping last argument here because it was added in |
1022 # supplemental dart.idl. Cleanup dart.idl and remove this check. | 873 # supplemental dart.idl. Cleanup dart.idl and remove this check. |
1023 break | 874 break |
1024 argument_expression = self._GenerateToNative( | 875 argument_expression = self._GenerateToNative( |
1025 parameter_definitions_emitter, argument, start_index + i) | 876 parameter_definitions_emitter, argument, start_index + i) |
1026 arguments.append(argument_expression) | 877 cpp_arguments.append(argument_expression) |
1027 | 878 |
1028 if operation.id in ['addEventListener', 'removeEventListener']: | 879 if operation.id in ['addEventListener', 'removeEventListener']: |
1029 # addEventListener's and removeEventListener's last argument is marked | 880 # addEventListener's and removeEventListener's last argument is marked |
1030 # as optional in idl, but is not optional in webcore implementation. | 881 # as optional in idl, but is not optional in webcore implementation. |
1031 if len(operation.arguments) == 2: | 882 if len(arguments) == 2: |
1032 arguments.append('false') | 883 cpp_arguments.append('false') |
1033 | 884 |
1034 if self._interface.id == 'CSSStyleDeclaration' and operation.id == 'setPrope
rty': | 885 if self._interface.id == 'CSSStyleDeclaration' and operation.id == 'setPrope
rty': |
1035 # CSSStyleDeclaration.setProperty priority parameter is optional in Dart | 886 # CSSStyleDeclaration.setProperty priority parameter is optional in Dart |
1036 # idl, but is not optional in webcore implementation. | 887 # idl, but is not optional in webcore implementation. |
1037 if len(operation.arguments) == 2: | 888 if len(arguments) == 2: |
1038 arguments.append('String()') | 889 cpp_arguments.append('String()') |
1039 | 890 |
1040 if 'NeedsUserGestureCheck' in operation.ext_attrs: | 891 if 'NeedsUserGestureCheck' in operation.ext_attrs: |
1041 arguments.append('DartUtilities::processingUserGesture') | 892 cpp_arguments.append('DartUtilities::processingUserGesture') |
1042 | 893 |
1043 function_expression = self._GenerateWebCoreFunctionExpression(webcore_functi
on_name, operation) | 894 function_expression = self._GenerateWebCoreFunctionExpression(webcore_functi
on_name, operation) |
1044 invocation = self._GenerateWebCoreInvocation(function_expression, arguments, | 895 invocation = self._GenerateWebCoreInvocation(function_expression, cpp_argume
nts, |
1045 operation.type.id, operation.ext_attrs, operation.raises) | 896 operation.type.id, operation.ext_attrs, operation.raises) |
1046 self._GenerateNativeCallback(cpp_callback_name, | 897 self._GenerateNativeCallback(cpp_callback_name, |
1047 parameter_definitions=parameter_definitions_emitter.Fragments(), | 898 parameter_definitions=parameter_definitions_emitter.Fragments(), |
1048 needs_receiver=not operation.is_static, invocation=invocation, | 899 needs_receiver=not operation.is_static, invocation=invocation, |
1049 raises_exceptions=raises_exceptions) | 900 raises_exceptions=raises_exceptions) |
1050 | 901 |
1051 def _GenerateNativeCallback(self, callback_name, parameter_definitions, | 902 def _GenerateNativeCallback(self, callback_name, parameter_definitions, |
1052 needs_receiver, invocation, raises_exceptions): | 903 needs_receiver, invocation, raises_exceptions): |
1053 | 904 |
1054 if needs_receiver: | 905 if needs_receiver: |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1196 for parent in interface.parents: | 1047 for parent in interface.parents: |
1197 parent_name = parent.type.id | 1048 parent_name = parent.type.id |
1198 if not database.HasInterface(parent.type.id): | 1049 if not database.HasInterface(parent.type.id): |
1199 continue | 1050 continue |
1200 parent_interface = database.GetInterface(parent.type.id) | 1051 parent_interface = database.GetInterface(parent.type.id) |
1201 if callback(parent_interface): | 1052 if callback(parent_interface): |
1202 return parent_interface | 1053 return parent_interface |
1203 parent_interface = _FindParent(parent_interface, database, callback) | 1054 parent_interface = _FindParent(parent_interface, database, callback) |
1204 if parent_interface: | 1055 if parent_interface: |
1205 return parent_interface | 1056 return parent_interface |
OLD | NEW |