def _process_interface(self, interface, component, interfaces): def has_impl(interface): # Non legacy callback interface does not provide V8 callbacks. if interface.is_callback: return len(interface.constants) > 0 if 'RuntimeEnabled' in interface.extended_attributes: return False if 'Exposed' not in interface.extended_attributes: return True return any( exposure.exposed == 'Window' and exposure.runtime_enabled is None for exposure in interface.extended_attributes['Exposed']) if not has_impl(interface): return context_builder = InterfaceTemplateContextBuilder( self._opts, self._info_provider) context = context_builder.create_interface_context( interface, component, interfaces) name = '%s%s' % (interface.name, 'Partial' if interface.is_partial else '') self._interface_contexts[name] = context # Do not include unnecessary header files. if not context['attributes'] and not context['named_property_setter']: return include_file = 'third_party/blink/renderer/bindings/%s/v8/%s.h' % ( component, utilities.to_snake_case(context['v8_class'])) self._include_files.add(include_file)
def _process_interface(self, interface, component, interfaces): def has_impl(interface): if interface.name in WHITE_LIST_INTERFACES: return True # Non legacy callback interface does not provide V8 callbacks. if interface.is_callback: return len(interface.constants) > 0 if 'RuntimeEnabled' in interface.extended_attributes: return False return True if not has_impl(interface): return context_builder = InterfaceTemplateContextBuilder( self._opts, self._info_provider) context = context_builder.create_interface_context( interface, interfaces) name = '%s%s' % (interface.name, 'Partial' if interface.is_partial else '') self._interface_contexts[name] = context if self._opts.snake_case_generated_files: include_file = 'bindings/%s/v8/%s.h' % ( component, utilities.to_snake_case(context['v8_name'])) else: include_file = 'bindings/%s/v8/%s.h' % (component, context['v8_name']) self._include_files.add(include_file)
def _create_template_context(self): interfaces = [] for name in sorted(self._interface_contexts): interfaces.append(self._interface_contexts[name]) header_name = 'V8ContextSnapshotExternalReferences.h' if self._opts.snake_case_generated_files: header_name = 'v8_context_snapshot_external_references.h' include_files = list(self._include_files) # TODO(tkent): Update INCLUDES after the great mv, and remove the # following block. crbug.com/760462 if self._opts.snake_case_generated_files: include_files = [] for include in self._include_files: dirname, basename = posixpath.split(include) name, ext = posixpath.splitext(basename) include_files.append( posixpath.join(dirname, utilities.to_snake_case(name) + ext)) return { 'class': 'V8ContextSnapshotExternalReferences', 'interfaces': interfaces, 'include_files': sorted(include_files), 'this_include_header_name': header_name, 'code_generator': os.path.basename(__file__), 'jinja_template_filename': TEMPLATE_FILE }
def member_impl_context(member, interfaces_info, header_includes, header_forward_decls): idl_type = unwrap_nullable_if_needed(member.idl_type) cpp_name = to_snake_case(v8_utilities.cpp_name(member)) nullable_indicator_name = None if not idl_type.cpp_type_has_null_value: nullable_indicator_name = 'has_' + cpp_name + '_' def has_method_expression(): if nullable_indicator_name: return nullable_indicator_name if idl_type.is_union_type or idl_type.is_enum or idl_type.is_string_type: return '!%s_.IsNull()' % cpp_name if idl_type.name == 'Any': return '!({0}_.IsEmpty() || {0}_.IsUndefined())'.format(cpp_name) if idl_type.name == 'Object': return '!({0}_.IsEmpty() || {0}_.IsNull() || {0}_.IsUndefined())'.format(cpp_name) if idl_type.name == 'Dictionary': return '!%s_.IsUndefinedOrNull()' % cpp_name return '%s_' % cpp_name cpp_default_value = None if member.default_value and not member.default_value.is_null: cpp_default_value = idl_type.literal_cpp_value(member.default_value) forward_decl_name = idl_type.impl_forward_declaration_name if forward_decl_name: includes.update(idl_type.impl_includes_for_type(interfaces_info)) header_forward_decls.add(forward_decl_name) else: header_includes.update(idl_type.impl_includes_for_type(interfaces_info)) setter_value = 'value' if idl_type.is_array_buffer_view_or_typed_array: setter_value += '.View()' non_null_type = idl_type.inner_type if idl_type.is_nullable else idl_type setter_inline = 'inline ' if ( non_null_type.is_basic_type or non_null_type.is_enum or non_null_type.is_wrapper_type) else '' return { 'cpp_default_value': cpp_default_value, 'cpp_name': cpp_name, 'getter_expression': cpp_name + '_', 'getter_name': getter_name_for_dictionary_member(member), 'has_method_expression': has_method_expression(), 'has_method_name': has_method_name_for_dictionary_member(member), 'is_nullable': idl_type.is_nullable, 'is_traceable': idl_type.is_traceable, 'member_cpp_type': idl_type.cpp_type_args(used_in_cpp_sequence=True), 'null_setter_name': null_setter_name_for_dictionary_member(member), 'nullable_indicator_name': nullable_indicator_name, 'rvalue_cpp_type': idl_type.cpp_type_args(used_as_rvalue_type=True), 'setter_inline': setter_inline, 'setter_name': setter_name_for_dictionary_member(member), 'setter_value': setter_value, }
def validate_blink_idl_definitions(idl_filename, idl_file_basename, definitions): """Validate file contents with filename convention. The Blink IDL conventions are: - If an IDL file defines an interface or a dictionary, the IDL file must contain exactly one definition. The definition name must agree with the file's basename, unless it is a partial definition. (e.g., 'partial interface Foo' can be in FooBar.idl). - An IDL file can contain typedefs and enums without having other definitions. There is no filename convention in this case. - Otherwise, an IDL file is invalid. """ targets = (definitions.interfaces.values() + definitions.dictionaries.values()) number_of_targets = len(targets) if number_of_targets > 1: raise Exception( 'Expected exactly 1 definition in file {0}, but found {1}' .format(idl_filename, number_of_targets)) if number_of_targets == 0: number_of_definitions = ( len(definitions.enumerations) + len(definitions.typedefs) + len(definitions.callback_functions)) if number_of_definitions == 0: raise Exception('No definition found in %s. (Missing semicolon?)' % idl_filename) return target = targets[0] if target.is_partial: return if target.name != idl_file_basename and to_snake_case(target.name) != idl_file_basename: raise Exception( 'Definition name "{0}" disagrees with IDL file basename "{1}".' .format(target.name, idl_file_basename))
def includes_for_type(idl_type, extended_attributes=None): idl_type = idl_type.preprocessed_type extended_attributes = extended_attributes or {} # Simple types base_idl_type = idl_type.base_type if base_idl_type in INCLUDES_FOR_TYPE: return INCLUDES_FOR_TYPE[base_idl_type] if base_idl_type in TYPED_ARRAY_TYPES: return INCLUDES_FOR_TYPE['ArrayBufferView'].union( set(['bindings/%s/v8/V8%s.h' % (component_dir[base_idl_type], base_idl_type)]) ) if idl_type.is_basic_type: return set(['bindings/core/v8/IDLTypes.h', 'bindings/core/v8/NativeValueTraitsImpl.h']) if base_idl_type.endswith('ConstructorConstructor'): # FIXME: rename to NamedConstructor # FIXME: replace with a [NamedConstructorAttribute] extended attribute # Ending with 'ConstructorConstructor' indicates a named constructor, # and these do not have header files, as they are part of the generated # bindings for the interface return set() if base_idl_type.endswith('Constructor'): # FIXME: replace with a [ConstructorAttribute] extended attribute base_idl_type = idl_type.constructor_type_name if idl_type.is_custom_callback_function: return set() if idl_type.is_callback_function: component = IdlType.callback_functions[base_idl_type]['component_dir'] return set(['bindings/%s/v8/%s.h' % (component, to_snake_case('V8%s' % base_idl_type))]) if base_idl_type not in component_dir: return set() return set(['bindings/%s/v8/V8%s.h' % (component_dir[base_idl_type], base_idl_type)])
def generate_code_internal(self, callback_function, path): self.typedef_resolver.resolve(callback_function, callback_function.name) header_template = self.jinja_env.get_template('callback_function.h.tmpl') cpp_template = self.jinja_env.get_template('callback_function.cpp.tmpl') template_context = v8_callback_function.callback_function_context( callback_function) if not is_testing_target(path): template_context['exported'] = self.info_provider.specifier_for_export template_context['header_includes'].append( self.info_provider.include_path_for_export) # TODO(bashi): Dependency resolution shouldn't happen here. # Move this into includes_for_type() families. for argument in callback_function.arguments: if argument.idl_type.is_union_type: template_context['header_includes'].append( self.info_provider.include_path_for_union_types(argument.idl_type)) template_context['header_includes'] = normalize_and_sort_includes( template_context['header_includes']) template_context['cpp_includes'] = normalize_and_sort_includes( template_context['cpp_includes']) template_context['code_generator'] = MODULE_PYNAME header_text = render_template(header_template, template_context) cpp_text = render_template(cpp_template, template_context) snake_base_name = to_snake_case('V8%s' % callback_function.name) header_path = posixpath.join(self.output_dir, '%s.h' % snake_base_name) cpp_path = posixpath.join(self.output_dir, '%s.cc' % snake_base_name) return ( (header_path, header_text), (cpp_path, cpp_text), )
def callback_function_context(callback_function): includes.clear() includes.update(CALLBACK_FUNCTION_CPP_INCLUDES) idl_type = callback_function.idl_type idl_type_str = str(idl_type) for argument in callback_function.arguments: argument.idl_type.add_includes_for_type( callback_function.extended_attributes) context = { # While both |callback_function_name| and |cpp_class| are identical at # the moment, the two are being defined because their values may change # in the future (e.g. if we support [ImplementedAs=] in callback # functions). 'callback_function_name': callback_function.name, 'cpp_class': 'V8%s' % callback_function.name, 'cpp_includes': sorted(includes), 'forward_declarations': sorted(forward_declarations(callback_function)), 'header_includes': sorted(CALLBACK_FUNCTION_H_INCLUDES), 'idl_type': idl_type_str, 'return_cpp_type': idl_type.cpp_type, 'this_include_header_name': to_snake_case('V8%s' % callback_function.name), } if idl_type_str != 'void': context.update({ 'return_value_conversion': idl_type.v8_value_to_local_cpp_value( callback_function.extended_attributes, 'call_result', 'native_result', isolate='GetIsolate()', bailout_return_value='v8::Nothing<%s>()' % context['return_cpp_type']), }) context.update(arguments_context(callback_function.arguments)) return context
def build_basename(name, prefix=None, ext=None): basename = to_snake_case(name) if prefix: basename = prefix + basename if not ext: return basename return basename + ext
def _generate_container_code(self, union_type): union_type = union_type.resolve_typedefs(self.typedefs) header_template = self.jinja_env.get_template('union_container.h.tmpl') cpp_template = self.jinja_env.get_template('union_container.cpp.tmpl') template_context = v8_union.container_context(union_type, self.info_provider) template_context['header_includes'].append( self.info_provider.include_path_for_export) template_context['header_includes'] = normalize_and_sort_includes( template_context['header_includes'], self.snake_case_generated_files) template_context['cpp_includes'] = normalize_and_sort_includes( template_context['cpp_includes'], self.snake_case_generated_files) template_context['code_generator'] = self.generator_name template_context['exported'] = self.info_provider.specifier_for_export snake_base_name = to_snake_case(shorten_union_name(union_type)) template_context['this_include_header_name'] = snake_base_name header_text = render_template(header_template, template_context) cpp_text = render_template(cpp_template, template_context) header_path = posixpath.join(self.output_dir, '%s.h' % snake_base_name) cpp_path = posixpath.join(self.output_dir, '%s.cc' % snake_base_name) return ( (header_path, header_text), (cpp_path, cpp_text), )
def generate_content(component, basenames): # Add fixed content. output = [COPYRIGHT_TEMPLATE, '#define NO_IMPLICIT_ATOMICSTRING\n\n'] basenames.sort() output.extend('#include "bindings/%s/v8/v8_%s.cc"\n' % (component, to_snake_case(basename)) for basename in basenames) return ''.join(output)
def member_context(dictionary, member): extended_attributes = member.extended_attributes idl_type = member.idl_type idl_type.add_includes_for_type(extended_attributes) unwrapped_idl_type = unwrap_nullable_if_needed(idl_type) if member.is_required and member.default_value: raise Exception( 'Required member %s must not have a default value.' % member.name) def default_values(): if not member.default_value: return None, None if member.default_value.is_null: return None, 'v8::Null(isolate)' cpp_default_value = unwrapped_idl_type.literal_cpp_value( member.default_value) v8_default_value = unwrapped_idl_type.cpp_value_to_v8_value( cpp_value=cpp_default_value, isolate='isolate', creation_context='creationContext') return cpp_default_value, v8_default_value cpp_default_value, v8_default_value = default_values() cpp_name = to_snake_case(v8_utilities.cpp_name(member)) getter_name = getter_name_for_dictionary_member(member) is_deprecated_dictionary = unwrapped_idl_type.name == 'Dictionary' return { 'cpp_default_value': cpp_default_value, 'cpp_name': cpp_name, 'cpp_type': unwrapped_idl_type.cpp_type, 'cpp_value_to_v8_value': unwrapped_idl_type.cpp_value_to_v8_value( cpp_value='impl.%s()' % getter_name, isolate='isolate', creation_context='creationContext', extended_attributes=extended_attributes), 'deprecate_as': v8_utilities.deprecate_as(member), 'enum_type': idl_type.enum_type, 'enum_values': idl_type.enum_values, 'getter_name': getter_name, 'has_method_name': has_method_name_for_dictionary_member(member), 'idl_type': idl_type.base_type, 'is_interface_type': idl_type.is_interface_type and not is_deprecated_dictionary, 'is_nullable': idl_type.is_nullable, 'is_object': unwrapped_idl_type.name == 'Object' or is_deprecated_dictionary, 'is_string_type': idl_type.preprocessed_type.is_string_type, 'is_required': member.is_required, 'name': member.name, 'origin_trial_feature_name': v8_utilities.origin_trial_feature_name(member), # [OriginTrialEnabled] 'runtime_enabled_feature_name': v8_utilities.runtime_enabled_feature_name(member), # [RuntimeEnabled] 'setter_name': setter_name_for_dictionary_member(member), 'null_setter_name': null_setter_name_for_dictionary_member(member), 'v8_default_value': v8_default_value, 'v8_value_to_local_cpp_value': idl_type.v8_value_to_local_cpp_value( extended_attributes, member.name + 'Value', member.name + 'CppValue', isolate='isolate', use_exception_state=True), }
def normalize_and_sort_includes(include_paths, snake_case): normalized_include_paths = [] for include_path in include_paths: match = re.search(r'/gen/blink/(.*)$', posixpath.abspath(include_path)) if match: include_path = match.group(1) if snake_case: match = re.search(r'/([^/]+)\.h$', include_path) if match: name = match.group(1) if name.lower() != name: include_path = include_path[0:match.start(1)] + to_snake_case(name) + '.h' normalized_include_paths.append(include_path) return sorted(normalized_include_paths)
def build_basename(name, snake_case, prefix=None, ext=None): basename = name if prefix: basename = prefix + name if snake_case: basename = to_snake_case(basename) if not ext: return basename if ext == '.cpp': return basename + '.cc' return basename + ext if ext: return basename + ext return basename
def member_context(member, info_provider): _update_includes_and_forward_decls(member, info_provider) if member.is_nullable: member = member.inner_type type_name = (member.inner_type if member.is_annotated_type else member).name # When converting a sequence or frozen array, we need to call the GetMethod(V, @@iterator) # ES abstract operation and then use the result of that call to create a sequence from an # iterable. For the purposes of this method, it means we need to pass |script_iterator| # rather than |v8_value| in v8_value_to_local_cpp_value(). if member.is_array_or_sequence_type: v8_value_name = 'std::move(script_iterator)' else: v8_value_name = 'v8_value' return { 'cpp_name': to_snake_case(v8_utilities.cpp_name(member)), 'cpp_type': member.cpp_type_args(used_in_cpp_sequence=True), 'cpp_local_type': member.cpp_type, 'cpp_value_to_v8_value': member.cpp_value_to_v8_value( cpp_value='impl.GetAs%s()' % type_name, isolate='isolate', creation_context='creationContext'), 'enum_type': member.enum_type, 'enum_values': member.enum_values, 'is_array_buffer_or_view_type': member.is_array_buffer_or_view, 'is_array_buffer_view_or_typed_array': member.is_array_buffer_view_or_typed_array, 'is_traceable': member.is_traceable, 'rvalue_cpp_type': member.cpp_type_args(used_as_rvalue_type=True), 'specific_type_enum': 'k' + member.name, 'type_name': type_name, 'v8_value_to_local_cpp_value': member.v8_value_to_local_cpp_value({}, v8_value_name, 'cpp_value', isolate='isolate', use_exception_state=True) }
def include_path(idl_filename, implemented_as=None): """Returns relative path to header file in POSIX format; used in includes. POSIX format is used for consistency of output, so reference tests are platform-independent. """ if idl_filename.startswith(gen_path): relative_dir = relative_dir_posix(idl_filename, gen_path) else: relative_dir = relative_dir_posix(idl_filename, source_path) # IDL file basename is used even if only a partial interface file output_file_basename = implemented_as or idl_filename_to_basename( idl_filename) output_file_basename = to_snake_case(output_file_basename) return posixpath.join(relative_dir, output_file_basename + '.h')
def _generate_container_code(self, union_type): includes.clear() union_type = union_type.resolve_typedefs(self.typedefs) header_template = self.jinja_env.get_template('union_container.h.tmpl') cpp_template = self.jinja_env.get_template('union_container.cc.tmpl') template_context = v8_union.container_context(union_type, self.info_provider) template_context['header_includes'].append( self.info_provider.include_path_for_export) template_context['exported'] = self.info_provider.specifier_for_export snake_base_name = to_snake_case(shorten_union_name(union_type)) header_path = posixpath.join(self.output_dir, '%s.h' % snake_base_name) cpp_path = posixpath.join(self.output_dir, '%s.cc' % snake_base_name) this_include_header_path = self.normalize_this_header_path(header_path) template_context['this_include_header_path'] = this_include_header_path template_context['header_guard'] = to_header_guard(this_include_header_path) header_text, cpp_text = self.render_templates( [], header_template, cpp_template, template_context) return ( (header_path, header_text), (cpp_path, cpp_text), )
def member_context(member, info_provider): _update_includes_and_forward_decls(member, info_provider) if member.is_nullable: member = member.inner_type type_name = (member.inner_type if member.is_annotated_type else member).name return { 'cpp_name': to_snake_case(v8_utilities.cpp_name(member)), 'cpp_type': member.cpp_type_args(used_in_cpp_sequence=True), 'cpp_local_type': member.cpp_type, 'cpp_value_to_v8_value': member.cpp_value_to_v8_value(cpp_value='impl.GetAs%s()' % type_name, isolate='isolate', creation_context='creationContext'), 'enum_type': member.enum_type, 'enum_values': member.enum_values, 'is_array_buffer_or_view_type': member.is_array_buffer_or_view, 'is_array_buffer_view_or_typed_array': member.is_array_buffer_view_or_typed_array, 'is_traceable': member.is_traceable, 'rvalue_cpp_type': member.cpp_type_args(used_as_rvalue_type=True), 'specific_type_enum': 'k' + member.name, 'type_name': type_name, 'v8_value_to_local_cpp_value': member.v8_value_to_local_cpp_value({}, 'v8_value', 'cpp_value', isolate='isolate', use_exception_state=True) }
def include_path(idl_filename, implemented_as=None): """Returns relative path to header file in POSIX format; used in includes. POSIX format is used for consistency of output, so reference tests are platform-independent. """ if idl_filename.startswith(gen_path): relative_dir = relative_dir_posix(idl_filename, gen_path) else: relative_dir = relative_dir_posix(idl_filename, source_path) # The generated relative include path might be wrong if the relative path # points to a parent directory in case of shadow build. To avoid jumbled # relative paths use absolute path instead. if relative_dir.startswith(".."): relative_dir = abs(os.path.dirname(idl_filename)) relative_dir = relative_dir.replace(os.path.sep, posixpath.sep) # IDL file basename is used even if only a partial interface file output_file_basename = implemented_as or idl_filename_to_basename( idl_filename) output_file_basename = to_snake_case(output_file_basename) return posixpath.join(relative_dir, output_file_basename + '.h')
def generate_code_internal(self, callback_function, path): self.typedef_resolver.resolve(callback_function, callback_function.name) header_template = self.jinja_env.get_template('callback_function.h.tmpl') cpp_template = self.jinja_env.get_template('callback_function.cpp.tmpl') template_context = v8_callback_function.callback_function_context( callback_function) if not is_testing_target(path): template_context['exported'] = self.info_provider.specifier_for_export template_context['header_includes'].append( self.info_provider.include_path_for_export) template_context['header_includes'] = normalize_and_sort_includes( template_context['header_includes'], self.snake_case_generated_files) template_context['cpp_includes'] = normalize_and_sort_includes( template_context['cpp_includes'], self.snake_case_generated_files) template_context['code_generator'] = MODULE_PYNAME header_text = render_template(header_template, template_context) cpp_text = render_template(cpp_template, template_context) snake_base_name = to_snake_case('V8%s' % callback_function.name) header_path = posixpath.join(self.output_dir, '%s.h' % snake_base_name) cpp_path = posixpath.join(self.output_dir, '%s.cc' % snake_base_name) return ( (header_path, header_text), (cpp_path, cpp_text), )
def bindings_tests(output_directory, verbose, suppress_diff): executive = Executive() def list_files(directory): if not os.path.isdir(directory): return [] files = [] for component in os.listdir(directory): if component not in COMPONENT_DIRECTORY: continue directory_with_component = os.path.join(directory, component) for filename in os.listdir(directory_with_component): files.append(os.path.join(directory_with_component, filename)) return files def diff(filename1, filename2): with open(filename1) as file1: file1_lines = file1.readlines() with open(filename2) as file2: file2_lines = file2.readlines() # Use Python's difflib module so that diffing works across platforms return ''.join(difflib.context_diff(file1_lines, file2_lines)) def is_cache_file(filename): return filename.endswith('.cache') def delete_cache_files(): # FIXME: Instead of deleting cache files, don't generate them. cache_files = [ path for path in list_files(output_directory) if is_cache_file(os.path.basename(path)) ] for cache_file in cache_files: os.remove(cache_file) def identical_file(reference_filename, output_filename): reference_basename = os.path.basename(reference_filename) if not os.path.isfile(reference_filename): print 'Missing reference file!' print '(if adding new test, update reference files)' print reference_basename print return False if not filecmp.cmp(reference_filename, output_filename): # cmp is much faster than diff, and usual case is "no difference", # so only run diff if cmp detects a difference print 'FAIL: %s' % reference_basename if not suppress_diff: print diff(reference_filename, output_filename) return False if verbose: print 'PASS: %s' % reference_basename return True def identical_output_files(output_files): reference_files = [ os.path.join(REFERENCE_DIRECTORY, os.path.relpath(path, output_directory)) for path in output_files ] return all([ identical_file(reference_filename, output_filename) for (reference_filename, output_filename) in zip(reference_files, output_files) ]) def no_excess_files(output_files): generated_files = set( [os.path.relpath(path, output_directory) for path in output_files]) excess_files = [] for path in list_files(REFERENCE_DIRECTORY): relpath = os.path.relpath(path, REFERENCE_DIRECTORY) # Ignore backup files made by a VCS. if os.path.splitext(relpath)[1] == '.orig': continue if relpath not in generated_files: excess_files.append(relpath) if excess_files: print( 'Excess reference files! ' '(probably cruft from renaming or deleting):\n' + '\n'.join(excess_files)) return False return True def make_runtime_features_dict(): input_filename = os.path.join(TEST_INPUT_DIRECTORY, 'runtime_enabled_features.json5') json5_file = Json5File.load_from_files([input_filename]) features_map = {} for feature in json5_file.name_dictionaries: features_map[str(feature['name'])] = { 'in_origin_trial': feature['in_origin_trial'] } return features_map try: generate_interface_dependencies(make_runtime_features_dict()) for component in COMPONENT_DIRECTORY: output_dir = os.path.join(output_directory, component) if not os.path.exists(output_dir): os.makedirs(output_dir) options = IdlCompilerOptions(output_directory=output_dir, impl_output_directory=output_dir, cache_directory=None, target_component=component) if component == 'core': partial_interface_output_dir = os.path.join( output_directory, 'modules') if not os.path.exists(partial_interface_output_dir): os.makedirs(partial_interface_output_dir) partial_interface_options = IdlCompilerOptions( output_directory=partial_interface_output_dir, impl_output_directory=None, cache_directory=None, target_component='modules') idl_filenames = [] dictionary_impl_filenames = [] partial_interface_filenames = [] input_directory = os.path.join(TEST_INPUT_DIRECTORY, component) for filename in os.listdir(input_directory): if (filename.endswith('.idl') and # Dependencies aren't built # (they are used by the dependent) filename not in DEPENDENCY_IDL_FILES): idl_path = os.path.realpath( os.path.join(input_directory, filename)) idl_filenames.append(idl_path) idl_basename = os.path.basename(idl_path) name_from_basename, _ = os.path.splitext(idl_basename) definition_name = get_first_interface_name_from_idl( get_file_contents(idl_path)) is_partial_interface_idl = to_snake_case( definition_name) != name_from_basename if not is_partial_interface_idl: interface_info = interfaces_info[definition_name] if interface_info['is_dictionary']: dictionary_impl_filenames.append(idl_path) if component == 'core' and interface_info[ 'dependencies_other_component_full_paths']: partial_interface_filenames.append(idl_path) info_provider = component_info_providers[component] partial_interface_info_provider = component_info_providers[ 'modules'] generate_union_type_containers(CodeGeneratorUnionType, info_provider, options) generate_callback_function_impl(CodeGeneratorCallbackFunction, info_provider, options) generate_bindings(CodeGeneratorV8, info_provider, options, idl_filenames) generate_bindings(CodeGeneratorV8, partial_interface_info_provider, partial_interface_options, partial_interface_filenames) generate_dictionary_impl(CodeGeneratorDictionaryImpl, info_provider, options, dictionary_impl_filenames) generate_origin_trial_features(info_provider, options, [ filename for filename in idl_filenames if filename not in dictionary_impl_filenames ]) finally: delete_cache_files() # Detect all changes output_files = list_files(output_directory) passed = identical_output_files(output_files) passed &= no_excess_files(output_files) if passed: if verbose: print print PASS_MESSAGE return 0 print print FAIL_MESSAGE return 1
def build_basename(name, prefix=None): basename = to_snake_case(name) if prefix: basename = prefix + basename return basename
def member_impl_context(member, interfaces_info, header_includes, header_forward_decls): idl_type = unwrap_nullable_if_needed(member.idl_type) cpp_name = to_snake_case(v8_utilities.cpp_name(member)) # In most cases, we don't have to distinguish `null` and `not present`, # and use null-states (e.g. nullptr, foo.IsUndefinedOrNull()) to show such # states for some types for memory usage and performance. # For types whose |has_explicit_presence| is True, we provide explicit # states of presence. has_explicit_presence = (member.idl_type.is_nullable and member.idl_type.inner_type.is_interface_type) nullable_indicator_name = None if not idl_type.cpp_type_has_null_value or has_explicit_presence: nullable_indicator_name = 'has_' + cpp_name + '_' def has_method_expression(): if nullable_indicator_name: return nullable_indicator_name if idl_type.is_union_type or idl_type.is_enum or idl_type.is_string_type: return '!%s_.IsNull()' % cpp_name if idl_type.name == 'Any': return '!({0}_.IsEmpty() || {0}_.IsUndefined())'.format(cpp_name) if idl_type.name == 'Object': return '!({0}_.IsEmpty() || {0}_.IsNull() || {0}_.IsUndefined())'.format( cpp_name) if idl_type.name == 'Dictionary': return '!%s_.IsUndefinedOrNull()' % cpp_name return '%s_' % cpp_name cpp_default_value = None if member.default_value: if not member.default_value.is_null or has_explicit_presence: cpp_default_value = idl_type.literal_cpp_value( member.default_value) forward_decl_name = idl_type.impl_forward_declaration_name if forward_decl_name: includes.update(idl_type.impl_includes_for_type(interfaces_info)) header_forward_decls.add(forward_decl_name) else: header_includes.update( idl_type.impl_includes_for_type(interfaces_info)) setter_value = 'value' if idl_type.is_array_buffer_view_or_typed_array: setter_value += '.View()' non_null_type = idl_type.inner_type if idl_type.is_nullable else idl_type setter_inline = 'inline ' if (non_null_type.is_basic_type or non_null_type.is_enum or non_null_type.is_wrapper_type) else '' return { 'cpp_default_value': cpp_default_value, 'cpp_name': cpp_name, 'has_explicit_presence': has_explicit_presence, 'getter_expression': cpp_name + '_', 'getter_name': getter_name_for_dictionary_member(member), 'has_method_expression': has_method_expression(), 'has_method_name': has_method_name_for_dictionary_member(member), 'is_nullable': idl_type.is_nullable, 'is_traceable': idl_type.is_traceable, 'member_cpp_type': idl_type.cpp_type_args(used_in_cpp_sequence=True), 'null_setter_name': null_setter_name_for_dictionary_member(member), 'nullable_indicator_name': nullable_indicator_name, 'rvalue_cpp_type': idl_type.cpp_type_args(used_as_rvalue_type=True), 'setter_inline': setter_inline, 'setter_name': setter_name_for_dictionary_member(member), 'setter_value': setter_value, }
def member_context(dictionary, member): extended_attributes = member.extended_attributes idl_type = member.idl_type idl_type.add_includes_for_type(extended_attributes) unwrapped_idl_type = unwrap_nullable_if_needed(idl_type) if member.is_required and member.default_value: raise Exception('Required member %s must not have a default value.' % member.name) # In most cases, we don't have to distinguish `null` and `not present`, # and use null-states (e.g. nullptr, foo.IsUndefinedOrNull()) to show such # states for some types for memory usage and performance. # For types whose |has_explicit_presence| is True, we provide explicit # states of presence. has_explicit_presence = (idl_type.is_nullable and idl_type.inner_type.is_interface_type) def default_values(): if not member.default_value: return None, None if member.default_value.is_null: return None, 'v8::Null(isolate)' cpp_default_value = unwrapped_idl_type.literal_cpp_value( member.default_value) v8_default_value = unwrapped_idl_type.cpp_value_to_v8_value( cpp_value=cpp_default_value, isolate='isolate', creation_context='creationContext') return cpp_default_value, v8_default_value cpp_default_value, v8_default_value = default_values() snake_case_name = to_snake_case(member.name) cpp_value = snake_case_name + "_cpp_value" v8_value = snake_case_name + "_value" has_value_or_default = snake_case_name + "_has_value_or_default" getter_name = getter_name_for_dictionary_member(member) is_deprecated_dictionary = unwrapped_idl_type.name == 'Dictionary' return { 'cpp_default_value': cpp_default_value, 'cpp_type': unwrapped_idl_type.cpp_type, 'cpp_value': cpp_value, 'cpp_value_to_v8_value': unwrapped_idl_type.cpp_value_to_v8_value( cpp_value='impl.%s()' % getter_name, isolate='isolate', creation_context='creationContext', extended_attributes=extended_attributes), 'deprecate_as': v8_utilities.deprecate_as(member), 'enum_type': idl_type.enum_type, 'enum_values': idl_type.enum_values, 'getter_name': getter_name, 'has_explicit_presence': has_explicit_presence, 'has_method_name': has_method_name_for_dictionary_member(member), 'idl_type': idl_type.base_type, 'is_interface_type': idl_type.is_interface_type and not is_deprecated_dictionary, 'is_nullable': idl_type.is_nullable, 'is_object': unwrapped_idl_type.name == 'Object' or is_deprecated_dictionary, 'is_string_type': idl_type.preprocessed_type.is_string_type, 'is_required': member.is_required, 'name': member.name, 'origin_trial_feature_name': v8_utilities.origin_trial_feature_name(member), # [OriginTrialEnabled] 'runtime_enabled_feature_name': v8_utilities.runtime_enabled_feature_name(member), # [RuntimeEnabled] 'setter_name': setter_name_for_dictionary_member(member), 'has_value_or_default': has_value_or_default, 'null_setter_name': null_setter_name_for_dictionary_member(member), 'v8_default_value': v8_default_value, 'v8_value': v8_value, 'v8_value_to_local_cpp_value': idl_type.v8_value_to_local_cpp_value(extended_attributes, v8_value, cpp_value, isolate='isolate', use_exception_state=True), }
def bindings_tests(output_directory, verbose, suppress_diff): executive = Executive() def list_files(directory): files = [] for component in os.listdir(directory): if component not in COMPONENT_DIRECTORY: continue directory_with_component = os.path.join(directory, component) for filename in os.listdir(directory_with_component): files.append(os.path.join(directory_with_component, filename)) return files def diff(filename1, filename2): # Python's difflib module is too slow, especially on long output, so # run external diff(1) command cmd = [ 'diff', '-u', # unified format '-N', # treat absent files as empty filename1, filename2 ] # Return output and don't raise exception, even though diff(1) has # non-zero exit if files differ. return executive.run_command(cmd, error_handler=lambda x: None) def is_cache_file(filename): return filename.endswith('.cache') def delete_cache_files(): # FIXME: Instead of deleting cache files, don't generate them. cache_files = [ path for path in list_files(output_directory) if is_cache_file(os.path.basename(path)) ] for cache_file in cache_files: os.remove(cache_file) def identical_file(reference_filename, output_filename): reference_basename = os.path.basename(reference_filename) if not os.path.isfile(reference_filename): print 'Missing reference file!' print '(if adding new test, update reference files)' print reference_basename print return False if not filecmp.cmp(reference_filename, output_filename): # cmp is much faster than diff, and usual case is "no difference", # so only run diff if cmp detects a difference print 'FAIL: %s' % reference_basename if not suppress_diff: print diff(reference_filename, output_filename) return False if verbose: print 'PASS: %s' % reference_basename return True def identical_output_files(output_files): reference_files = [ os.path.join(REFERENCE_DIRECTORY, os.path.relpath(path, output_directory)) for path in output_files ] return all([ identical_file(reference_filename, output_filename) for (reference_filename, output_filename) in zip(reference_files, output_files) ]) def no_excess_files(output_files): generated_files = set( [os.path.relpath(path, output_directory) for path in output_files]) excess_files = [] for path in list_files(REFERENCE_DIRECTORY): relpath = os.path.relpath(path, REFERENCE_DIRECTORY) # Ignore backup files made by a VCS. if os.path.splitext(relpath)[1] == '.orig': continue if relpath not in generated_files: excess_files.append(relpath) if excess_files: print( 'Excess reference files! ' '(probably cruft from renaming or deleting):\n' + '\n'.join(excess_files)) return False return True try: generate_interface_dependencies() for component in COMPONENT_DIRECTORY: output_dir = os.path.join(output_directory, component) if not os.path.exists(output_dir): os.makedirs(output_dir) options = IdlCompilerOptions(output_directory=output_dir, impl_output_directory=output_dir, cache_directory=None, target_component=component) if component == 'core': partial_interface_output_dir = os.path.join( output_directory, 'modules') if not os.path.exists(partial_interface_output_dir): os.makedirs(partial_interface_output_dir) partial_interface_options = IdlCompilerOptions( output_directory=partial_interface_output_dir, impl_output_directory=None, cache_directory=None, target_component='modules') idl_filenames = [] dictionary_impl_filenames = [] partial_interface_filenames = [] input_directory = os.path.join(TEST_INPUT_DIRECTORY, component) for filename in os.listdir(input_directory): if (filename.endswith('.idl') and # Dependencies aren't built # (they are used by the dependent) filename not in DEPENDENCY_IDL_FILES): idl_path = os.path.realpath( os.path.join(input_directory, filename)) idl_filenames.append(idl_path) idl_basename = os.path.basename(idl_path) name_from_basename, _ = os.path.splitext(idl_basename) definition_name = get_first_interface_name_from_idl( get_file_contents(idl_path)) is_partial_interface_idl = to_snake_case( definition_name) != name_from_basename if not is_partial_interface_idl: interface_info = interfaces_info[definition_name] if interface_info['is_dictionary']: dictionary_impl_filenames.append(idl_path) if component == 'core' and interface_info[ 'dependencies_other_component_full_paths']: partial_interface_filenames.append(idl_path) info_provider = component_info_providers[component] partial_interface_info_provider = component_info_providers[ 'modules'] generate_union_type_containers(CodeGeneratorUnionType, info_provider, options) generate_callback_function_impl(CodeGeneratorCallbackFunction, info_provider, options) generate_bindings(CodeGeneratorV8, info_provider, options, idl_filenames) generate_bindings(CodeGeneratorWebAgentAPI, info_provider, options, idl_filenames) generate_bindings(CodeGeneratorV8, partial_interface_info_provider, partial_interface_options, partial_interface_filenames) generate_dictionary_impl(CodeGeneratorDictionaryImpl, info_provider, options, dictionary_impl_filenames) generate_origin_trial_features(info_provider, options, [ filename for filename in idl_filenames if filename not in dictionary_impl_filenames ]) finally: delete_cache_files() # Detect all changes output_files = list_files(output_directory) passed = identical_output_files(output_files) passed &= no_excess_files(output_files) if passed: if verbose: print print PASS_MESSAGE return 0 print print FAIL_MESSAGE return 1