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

Side by Side Diff: tools/dom/scripts/systemnative.py

Issue 1832713002: Optimize dartium dart:html bindings so real world application performance is acceptable. Improves d… (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 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 #!/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 logging 10 import logging
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 243
244 # Then we emit the impedance matching wrapper to call out to the 244 # Then we emit the impedance matching wrapper to call out to the
245 # toplevel wrapper 245 # toplevel wrapper
246 if not emit_to_native: 246 if not emit_to_native:
247 toplevel_name = \ 247 toplevel_name = \
248 self.DeriveQualifiedBlinkName(self._interface.id, 248 self.DeriveQualifiedBlinkName(self._interface.id,
249 dart_native_name) 249 dart_native_name)
250 self._members_emitter.Emit( 250 self._members_emitter.Emit(
251 '\n @DocsEditable()\n' 251 '\n @DocsEditable()\n'
252 ' static $INTERFACE_NAME $FACTORY_METHOD_NAME($PARAMETERS) => ' 252 ' static $INTERFACE_NAME $FACTORY_METHOD_NAME($PARAMETERS) => '
253 'wrap_jso($TOPLEVEL_NAME($OUTPARAMETERS));\n', 253 '$TOPLEVEL_NAME($OUTPARAMETERS);\n',
254 INTERFACE_NAME=self._interface_type_info.interface_name(), 254 INTERFACE_NAME=self._interface_type_info.interface_name(),
255 FACTORY_METHOD_NAME=factory_method_name, 255 FACTORY_METHOD_NAME=factory_method_name,
256 PARAMETERS=typed_formals, 256 PARAMETERS=typed_formals,
257 TOPLEVEL_NAME=toplevel_name, 257 TOPLEVEL_NAME=toplevel_name,
258 OUTPARAMETERS=parameters) 258 OUTPARAMETERS=parameters)
259 259
260 self._cpp_resolver_emitter.Emit( 260 self._cpp_resolver_emitter.Emit(
261 ' if (name == "$ID")\n' 261 ' if (name == "$ID")\n'
262 ' return Dart$(WEBKIT_INTERFACE_NAME)Internal::$CPP_CALLBACK;\n', 262 ' return Dart$(WEBKIT_INTERFACE_NAME)Internal::$CPP_CALLBACK;\n',
263 ID=constructor_callback_id, 263 ID=constructor_callback_id,
264 WEBKIT_INTERFACE_NAME=self._interface.id, 264 WEBKIT_INTERFACE_NAME=self._interface.id,
265 CPP_CALLBACK=constructor_callback_cpp_name) 265 CPP_CALLBACK=constructor_callback_cpp_name)
266 266
267 def GenerateCustomFactory(self, constructor_info): 267 def GenerateCustomFactory(self, constructor_info):
268 if 'CustomConstructor' not in self._interface.ext_attrs: 268 if 'CustomConstructor' not in self._interface.ext_attrs:
269 return False 269 return False
270 270
271 annotations = self._metadata.GetFormattedMetadata(self._library_name, 271 annotations = self._metadata.GetFormattedMetadata(self._library_name,
272 self._interface, self._interface.id, ' ') 272 self._interface, self._interface.id, ' ')
273 273
274 self._members_emitter.Emit( 274 self._members_emitter.Emit(
275 '\n $(ANNOTATIONS)factory $CTOR($PARAMS) => wrap_jso(_create($FACTORY_P ARAMS));\n', 275 '\n $(ANNOTATIONS)factory $CTOR($PARAMS) => _create($FACTORY_PARAMS);\n ',
276 ANNOTATIONS=annotations, 276 ANNOTATIONS=annotations,
277 CTOR=constructor_info._ConstructorFullName(self._DartType), 277 CTOR=constructor_info._ConstructorFullName(self._DartType),
278 PARAMS=constructor_info.ParametersAsDeclaration(self._DartType), 278 PARAMS=constructor_info.ParametersAsDeclaration(self._DartType),
279 FACTORY_PARAMS= \ 279 FACTORY_PARAMS= \
280 constructor_info.ParametersAsArgumentList()) 280 constructor_info.ParametersAsArgumentList())
281 281
282 # MutationObserver has custom _create. TODO(terry): Consider table but this is only one. 282 # MutationObserver has custom _create. TODO(terry): Consider table but this is only one.
283 if self._interface.id != 'MutationObserver': 283 if self._interface.id != 'MutationObserver':
284 constructor_callback_cpp_name = 'constructorCallback' 284 constructor_callback_cpp_name = 'constructorCallback'
285 self._EmitConstructorInfrastructure( 285 self._EmitConstructorInfrastructure(
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 # Unwrap the type to get the JsObject if Type is: 529 # Unwrap the type to get the JsObject if Type is:
530 # 530 #
531 # - known IDL type 531 # - known IDL type
532 # - type_id is None then it's probably a union type or overloaded 532 # - type_id is None then it's probably a union type or overloaded
533 # it's a dynamic/any type 533 # it's a dynamic/any type
534 # - type is Object 534 # - type is Object
535 # 535 #
536 # JsObject maybe stored in the Dart class. 536 # JsObject maybe stored in the Dart class.
537 return_wrap_jso = wrap_return_type_blink(return_type, attr.type.id, self ._type_registry) 537 return_wrap_jso = wrap_return_type_blink(return_type, attr.type.id, self ._type_registry)
538 wrap_unwrap_list.append(return_wrap_jso) # wrap_jso the returned objec t 538 wrap_unwrap_list.append(return_wrap_jso) # wrap_jso the returned objec t
539 wrap_unwrap_list.append(self._dart_use_blink) # this must be unwrap_jso 539 wrap_unwrap_list.append(self._dart_use_blink)
540 540
541 # This seems to have been replaced with Custom=Getter (see above), but 541 # This seems to have been replaced with Custom=Getter (see above), but
542 # check to be sure we don't see the old syntax 542 # check to be sure we don't see the old syntax
543 assert(not ('CustomGetter' in attr.ext_attrs)) 543 assert(not ('CustomGetter' in attr.ext_attrs))
544 native_suffix = 'Getter' 544 native_suffix = 'Getter'
545 auto_scope_setup = self._GenerateAutoSetupScope(attr.id, native_suffix) 545 auto_scope_setup = self._GenerateAutoSetupScope(attr.id, native_suffix)
546 native_entry = \ 546 native_entry = \
547 self.DeriveNativeEntry(attr.id, 'Getter', None) 547 self.DeriveNativeEntry(attr.id, 'Getter', None)
548 cpp_callback_name = self._GenerateNativeBinding(attr.id, 1, 548 cpp_callback_name = self._GenerateNativeBinding(attr.id, 1,
549 dart_declaration, attr.is_static, return_type, parameters, 549 dart_declaration, attr.is_static, return_type, parameters,
(...skipping 26 matching lines...) Expand all
576 attr.ext_attrs['RaisesException'] != 'Setter') 576 attr.ext_attrs['RaisesException'] != 'Setter')
577 577
578 def _AddSetter(self, attr, html_name): 578 def _AddSetter(self, attr, html_name):
579 return_type = 'void' 579 return_type = 'void'
580 ptype = self._DartType(attr.type.id) 580 ptype = self._DartType(attr.type.id)
581 581
582 type_info = self._TypeInfo(attr.type.id) 582 type_info = self._TypeInfo(attr.type.id)
583 583
584 # Is the setter value a DartClass (that has a JsObject) or the type is 584 # Is the setter value a DartClass (that has a JsObject) or the type is
585 # None (it's a dynamic/any type) then unwrap_jso before passing to blink. 585 # None (it's a dynamic/any type) then unwrap_jso before passing to blink.
586 parameters = ['unwrap_jso(value)' if (isinstance(type_info, InterfaceIDLType Info) or 586 parameters = ['value']
587 not(attr.type.id) or ptype == 'Object' )
588 else 'value']
589 587
590 dart_declaration = 'set %s(%s value)' % (html_name, ptype) 588 dart_declaration = 'set %s(%s value)' % (html_name, ptype)
591 is_custom = _IsCustom(attr) and (_IsCustomValue(attr, None) or 589 is_custom = _IsCustom(attr) and (_IsCustomValue(attr, None) or
592 _IsCustomValue(attr, 'Setter')) 590 _IsCustomValue(attr, 'Setter'))
593 # This seems to have been replaced with Custom=Setter (see above), but 591 # This seems to have been replaced with Custom=Setter (see above), but
594 # check to be sure we don't see the old syntax 592 # check to be sure we don't see the old syntax
595 assert(not ('CustomSetter' in attr.ext_attrs)) 593 assert(not ('CustomSetter' in attr.ext_attrs))
596 assert(not ('V8CustomSetter' in attr.ext_attrs)) 594 assert(not ('V8CustomSetter' in attr.ext_attrs))
597 native_suffix = 'Setter' 595 native_suffix = 'Setter'
598 auto_scope_setup = self._GenerateAutoSetupScope(attr.id, native_suffix) 596 auto_scope_setup = self._GenerateAutoSetupScope(attr.id, native_suffix)
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 dart_native_name, resolver_string = \ 654 dart_native_name, resolver_string = \
657 self.DeriveNativeEntry("item", 'Method', 1) 655 self.DeriveNativeEntry("item", 'Method', 1)
658 656
659 # Emit the method which calls the toplevel function, along with 657 # Emit the method which calls the toplevel function, along with
660 # the [] operator. 658 # the [] operator.
661 dart_qualified_name = \ 659 dart_qualified_name = \
662 self.DeriveQualifiedBlinkName(self._interface.id, 660 self.DeriveQualifiedBlinkName(self._interface.id,
663 dart_native_name) 661 dart_native_name)
664 662
665 type_info = self._TypeInfo(element_type) 663 type_info = self._TypeInfo(element_type)
666 # Does nativeIndexGetter return a DartClass (JsObject) if so wrap_jso.
667 wrap_jso_start = ''
668 wrap_jso_end = ''
669 if (isinstance(type_info, InterfaceIDLTypeInfo) or
670 wrap_type_blink(type_info.narrow_dart_type(), self._type_registry)):
671 wrap_jso_start = 'wrap_jso('
672 wrap_jso_end = ')'
673 blinkNativeIndexed = """ 664 blinkNativeIndexed = """
674 $TYPE operator[](int index) { 665 $TYPE operator[](int index) {
675 if (index < 0 || index >= length) 666 if (index < 0 || index >= length)
676 throw new RangeError.index(index, this); 667 throw new RangeError.index(index, this);
677 return %s$(DART_NATIVE_NAME)(unwrap_jso(this), index)%s; 668 return $(DART_NATIVE_NAME)(this, index);
678 } 669 }
679 670
680 $TYPE _nativeIndexedGetter(int index) => %s$(DART_NATIVE_NAME)(unwrap_jso(this ), index)%s; 671 $TYPE _nativeIndexedGetter(int index) => $(DART_NATIVE_NAME)(this, index);
681 """ % (wrap_jso_start, wrap_jso_end, wrap_jso_start, wrap_jso_end) 672 """
682 # Wrap the type to store the JsObject if Type is: 673 blinkNativeIndexedGetter = \
683 # 674 ' $(DART_NATIVE_NAME)(this, index);\n'
684 # - known IDL type
685 # - type_id is None then it's probably a union type or overloaded
686 # it's a dynamic/any type
687 # - type is Object
688 #
689 # JsObject maybe stored in the Dart class.
690 if isinstance(type_info, InterfaceIDLTypeInfo) or not(type_info) or dart_e lement_type == 'Object':
691 blinkNativeIndexedGetter = \
692 ' {0}$(DART_NATIVE_NAME)(unwrap_jso(this), index){1};\n'.format('w rap_jso(', ')')
693 else:
694 blinkNativeIndexedGetter = \
695 ' $(DART_NATIVE_NAME)(unwrap_jso(this), index);\n'
696 self._members_emitter.Emit(blinkNativeIndexed, 675 self._members_emitter.Emit(blinkNativeIndexed,
697 DART_NATIVE_NAME=dart_qualified_name, 676 DART_NATIVE_NAME=dart_qualified_name,
698 TYPE=self.SecureOutputType(element_type), 677 TYPE=self.SecureOutputType(element_type),
699 INTERFACE=self._interface.id) 678 INTERFACE=self._interface.id)
700 679
701 if self._HasNativeIndexSetter(): 680 if self._HasNativeIndexSetter():
702 self._EmitNativeIndexSetter(dart_element_type) 681 self._EmitNativeIndexSetter(dart_element_type)
703 else: 682 else:
704 self._members_emitter.Emit( 683 self._members_emitter.Emit(
705 '\n' 684 '\n'
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
814 has_optional_arguments = any(IsOptional(argument) for argument in operation. arguments) 793 has_optional_arguments = any(IsOptional(argument) for argument in operation. arguments)
815 needs_dispatcher = not is_custom and (len(info.operations) > 1 or has_option al_arguments) 794 needs_dispatcher = not is_custom and (len(info.operations) > 1 or has_option al_arguments)
816 795
817 # Operation uses blink? 796 # Operation uses blink?
818 wrap_unwrap_list = []; 797 wrap_unwrap_list = [];
819 return_wrap_jso = False 798 return_wrap_jso = False
820 # return type wrapped? 799 # return type wrapped?
821 if self._dart_use_blink: 800 if self._dart_use_blink:
822 # Wrap the type to store the JsObject if Type is: 801 # Wrap the type to store the JsObject if Type is:
823 # 802 #
824 # - known IDL type
825 # - type_id is None then it's probably a union type or overloaded
826 # it's a dynamic/any type 803 # it's a dynamic/any type
827 # - type is Object 804 # - type is Object
828 # 805 #
829 # JsObject maybe stored in the Dart class. 806 # JsObject maybe stored in the Dart class.
830 return_wrap_jso = wrap_return_type_blink(return_type, info.type_name, se lf._type_registry) 807 return_wrap_jso = wrap_return_type_blink(return_type, info.type_name, se lf._type_registry)
831 return_type_info = self._type_registry.TypeInfo(info.type_name) 808 return_type_info = self._type_registry.TypeInfo(info.type_name)
832 if (isinstance(return_type_info, SequenceIDLTypeInfo) and
833 not isinstance(return_type_info._item_info, PrimitiveIDLTypeInfo)):
834 return_wrap_jso = True
835 # wrap_jso the returned object 809 # wrap_jso the returned object
836 wrap_unwrap_list.append(return_wrap_jso) 810 wrap_unwrap_list.append(return_wrap_jso)
837 # The 'this' parameter must be unwrap_jso
838 wrap_unwrap_list.append(self._dart_use_blink) 811 wrap_unwrap_list.append(self._dart_use_blink)
839 812
840 if info.callback_args: 813 if info.callback_args:
841 self._AddFutureifiedOperation(info, html_name) 814 self._AddFutureifiedOperation(info, html_name)
842 elif not needs_dispatcher: 815 elif not needs_dispatcher:
843 # Bind directly to native implementation 816 # Bind directly to native implementation
844 argument_count = (0 if info.IsStatic() else 1) + len(info.param_infos) 817 argument_count = (0 if info.IsStatic() else 1) + len(info.param_infos)
845 native_suffix = 'Callback' 818 native_suffix = 'Callback'
846 auto_scope_setup = self._GenerateAutoSetupScope(info.name, native_suffix) 819 auto_scope_setup = self._GenerateAutoSetupScope(info.name, native_suffix)
847 native_entry = \ 820 native_entry = \
(...skipping 25 matching lines...) Expand all
873 846
874 return_wrap_jso = False 847 return_wrap_jso = False
875 if self._dart_use_blink: 848 if self._dart_use_blink:
876 return_wrap_jso = wrap_return_type_blink(return_type, info.type_name, self._type_registry) 849 return_wrap_jso = wrap_return_type_blink(return_type, info.type_name, self._type_registry)
877 850
878 native_suffix = 'Callback' 851 native_suffix = 'Callback'
879 is_custom = _IsCustom(operation) 852 is_custom = _IsCustom(operation)
880 base_name = '_%s_%s' % (operation.id, version) 853 base_name = '_%s_%s' % (operation.id, version)
881 static = True 854 static = True
882 if not operation.is_static: 855 if not operation.is_static:
883 actuals = ['unwrap_jso(this)' if self._dart_use_blink else 'this'] + act uals 856 actuals = ['this'] + actuals
884 formals = ['mthis'] + formals 857 formals = ['mthis'] + formals
885 actuals_s = ", ".join(actuals) 858 actuals_s = ", ".join(actuals)
886 formals_s = ", ".join(formals) 859 formals_s = ", ".join(formals)
887 dart_declaration = '%s(%s)' % ( 860 dart_declaration = '%s(%s)' % (
888 base_name, formals_s) 861 base_name, formals_s)
889 native_entry = \ 862 native_entry = \
890 self.DeriveNativeEntry(operation.id, 'Method', argument_count) 863 self.DeriveNativeEntry(operation.id, 'Method', argument_count)
891 overload_base_name = native_entry[0] 864 overload_base_name = native_entry[0]
892 overload_name = \ 865 overload_name = \
893 self.DeriveQualifiedBlinkName(self._interface.id, 866 self.DeriveQualifiedBlinkName(self._interface.id,
894 overload_base_name) 867 overload_base_name)
895 if return_wrap_jso: 868 call_emitter.Emit('$NAME($ARGS)', NAME=overload_name, ARGS=actuals_s)
896 call_emitter.Emit('wrap_jso($NAME($ARGS))', NAME=overload_name, ARGS=a ctuals_s)
897 else:
898 call_emitter.Emit('$NAME($ARGS)', NAME=overload_name, ARGS=actuals_s)
899 auto_scope_setup = \ 869 auto_scope_setup = \
900 self._GenerateAutoSetupScope(base_name, native_suffix) 870 self._GenerateAutoSetupScope(base_name, native_suffix)
901 cpp_callback_name = self._GenerateNativeBinding( 871 cpp_callback_name = self._GenerateNativeBinding(
902 base_name, (0 if static else 1) + argument_count, 872 base_name, (0 if static else 1) + argument_count,
903 dart_declaration, static, return_type, formals, 873 dart_declaration, static, return_type, formals,
904 native_suffix, is_custom, auto_scope_setup, emit_metadata=False, 874 native_suffix, is_custom, auto_scope_setup, emit_metadata=False,
905 emit_to_native=True, native_entry=native_entry) 875 emit_to_native=True, native_entry=native_entry)
906 if not is_custom: 876 if not is_custom:
907 self._GenerateOperationNativeCallback(operation, 877 self._GenerateOperationNativeCallback(operation,
908 operation.arguments[:argument_count], cpp_callback_name, 878 operation.arguments[:argument_count], cpp_callback_name,
(...skipping 30 matching lines...) Expand all
939 else: 909 else:
940 dart_native_name = \ 910 dart_native_name = \
941 self.DeriveNativeName(idl_name, native_suffix) 911 self.DeriveNativeName(idl_name, native_suffix)
942 native_binding_id = self._interface.id 912 native_binding_id = self._interface.id
943 native_binding_id = TypeIdToBlinkName(native_binding_id, self._database) 913 native_binding_id = TypeIdToBlinkName(native_binding_id, self._database)
944 native_binding = \ 914 native_binding = \
945 '%s_%s_%s' % (native_binding_id, idl_name, native_suffix) 915 '%s_%s_%s' % (native_binding_id, idl_name, native_suffix)
946 916
947 if not static: 917 if not static:
948 formals = ", ".join(['mthis'] + parameters) 918 formals = ", ".join(['mthis'] + parameters)
949 if wrap_unwrap_list and wrap_unwrap_list[1]: 919 actuals = ", ".join(['this'] + parameters)
950 actuals = ", ".join(['unwrap_jso(this)'] + parameters)
951 else:
952 actuals = ", ".join(['this'] + parameters)
953 else: 920 else:
954 formals = ", ".join(parameters) 921 formals = ", ".join(parameters)
955 actuals = ", ".join(parameters) 922 actuals = ", ".join(parameters)
956 923
957 if not emit_to_native: 924 if not emit_to_native:
958 caller_emitter = self._members_emitter 925 caller_emitter = self._members_emitter
959 full_dart_name = \ 926 full_dart_name = \
960 self.DeriveQualifiedBlinkName(self._interface.id, 927 self.DeriveQualifiedBlinkName(self._interface.id,
961 dart_native_name) 928 dart_native_name)
962 if IsPureInterface(self._interface.id): 929 if IsPureInterface(self._interface.id):
963 caller_emitter.Emit( 930 caller_emitter.Emit(
964 '\n' 931 '\n'
965 ' $METADATA$DART_DECLARATION;\n', 932 ' $METADATA$DART_DECLARATION;\n',
966 METADATA=metadata, 933 METADATA=metadata,
967 DART_DECLARATION=dart_declaration) 934 DART_DECLARATION=dart_declaration)
968 else: 935 else:
969 emit_template = ''' 936 emit_template = '''
970 $METADATA$DART_DECLARATION => $DART_NAME($ACTUALS); 937 $METADATA$DART_DECLARATION => $DART_NAME($ACTUALS);
971 ''' 938 '''
972 if wrap_unwrap_list and wrap_unwrap_list[0]: 939 if wrap_unwrap_list and wrap_unwrap_list[0]:
973 if return_type == 'Rectangle': 940 if return_type == 'Rectangle':
974 jso_util_method = 'make_dart_rectangle' 941 jso_util_method = 'make_dart_rectangle'
975 elif wrap_unwrap_list[0]: 942 elif wrap_unwrap_list[0]:
976 jso_util_method = 'wrap_jso' 943 jso_util_method = ''
977 944
978 if dictionary_return: 945 if dictionary_return:
979 emit_jso_template = ''' 946 emit_jso_template = '''
980 $METADATA$DART_DECLARATION => convertNativeDictionaryToDartDictionary(%s($DART _NAME($ACTUALS))); 947 $METADATA$DART_DECLARATION => convertNativeDictionaryToDartDictionary(%s($DART _NAME($ACTUALS)));
981 ''' 948 '''
982 else: 949 else:
983 emit_jso_template = ''' 950 emit_jso_template = '''
984 $METADATA$DART_DECLARATION => %s($DART_NAME($ACTUALS)); 951 $METADATA$DART_DECLARATION => %s($DART_NAME($ACTUALS));
985 ''' 952 '''
986 emit_template = emit_jso_template % jso_util_method 953 emit_template = emit_jso_template % jso_util_method
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
1250 1217
1251 def _IsCustom(op_or_attr): 1218 def _IsCustom(op_or_attr):
1252 assert(isinstance(op_or_attr, IDLMember)) 1219 assert(isinstance(op_or_attr, IDLMember))
1253 return 'Custom' in op_or_attr.ext_attrs or 'DartCustom' in op_or_attr.ext_attr s 1220 return 'Custom' in op_or_attr.ext_attrs or 'DartCustom' in op_or_attr.ext_attr s
1254 1221
1255 def _IsCustomValue(op_or_attr, value): 1222 def _IsCustomValue(op_or_attr, value):
1256 if _IsCustom(op_or_attr): 1223 if _IsCustom(op_or_attr):
1257 return op_or_attr.ext_attrs.get('Custom') == value \ 1224 return op_or_attr.ext_attrs.get('Custom') == value \
1258 or op_or_attr.ext_attrs.get('DartCustom') == value 1225 or op_or_attr.ext_attrs.get('DartCustom') == value
1259 return False 1226 return False
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698