1# Copyright 2017 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5# pylint: disable=relative-import 6 7import argparse 8import os 9import posixpath 10 11from code_generator import initialize_jinja_env 12from idl_reader import IdlReader 13from utilities import create_component_info_provider, write_file 14import utilities 15import v8_attributes 16import v8_interface 17import v8_types 18import v8_utilities 19 20INCLUDES = frozenset([ 21 'third_party/blink/renderer/bindings/core/v8/generated_code_helper.h', 22 'third_party/blink/renderer/bindings/core/v8/v8_html_document.h', 23 'third_party/blink/renderer/bindings/core/v8/v8_initializer.h', 24 'third_party/blink/renderer/bindings/core/v8/v8_window.h', 25 'third_party/blink/renderer/platform/bindings/v8_object_constructor.h', 26 'v8/include/v8.h' 27]) 28 29TEMPLATE_FILE = 'external_reference_table.cc.tmpl' 30 31SNAPSHOTTED_INTERFACES = frozenset([ 32 'Window', 33 'EventTarget', 34 'HTMLDocument', 35 'Document', 36 'Node', 37]) 38 39 40def parse_args(): 41 parser = argparse.ArgumentParser() 42 parser.add_argument( 43 '--idl-files-list', 44 type=str, 45 required=True, 46 help='file listing IDL files') 47 parser.add_argument( 48 '--output', type=str, required=True, help='output file path') 49 parser.add_argument( 50 '--info-dir', 51 type=str, 52 required=True, 53 help='directory contains component info') 54 parser.add_argument( 55 '--cache-dir', type=str, required=True, help='cache directory') 56 parser.add_argument( 57 '--target-component', type=str, required=True, help='target component') 58 return parser.parse_known_args() 59 60 61# This class creates a Jinja template context about an interface. 62class InterfaceTemplateContextBuilder(object): 63 def __init__(self, opts, info_provider): 64 self._opts = opts 65 self._info_provider = info_provider 66 67 def create_interface_context(self, interface, component, interfaces): 68 '''Creates a Jinja context which is based on an interface.''' 69 70 assert component in ['core', 'modules'] 71 72 name = '%s%s' % (v8_utilities.cpp_name(interface), 73 'Partial' if interface.is_partial else '') 74 75 # Constructors 76 has_constructor_callback = False 77 if not interface.is_partial: 78 constructors = any(constructor.name == 'Constructor' 79 for constructor in interface.constructors) 80 custom_constructors = interface.custom_constructors 81 html_constructor = 'HTMLConstructor' in interface.extended_attributes 82 has_constructor_callback = constructors or custom_constructors or html_constructor 83 84 attributes = [] 85 methods = [] 86 has_cross_origin_indexed_getter = False 87 has_cross_origin_named_enum = False 88 has_cross_origin_named_getter = False 89 has_cross_origin_named_setter = False 90 has_security_check = False 91 indexed_property_getter = None 92 is_global = False 93 named_property_getter = None 94 component_info = self._info_provider.component_info 95 if interface.name in SNAPSHOTTED_INTERFACES: 96 attributes = [ 97 v8_attributes.attribute_context(interface, attribute, 98 interfaces, component_info) 99 for attribute in interface.attributes 100 ] 101 methods = v8_interface.methods_context(interface, 102 component_info)['methods'] 103 is_global = 'Global' in interface.extended_attributes 104 105 named_property_getter = v8_interface.property_getter( 106 interface.named_property_getter, ['name']) 107 indexed_property_getter = v8_interface.property_getter( 108 interface.indexed_property_getter, ['index']) 109 110 if not interface.is_partial: 111 has_security_check = ( 112 'CheckSecurity' in interface.extended_attributes 113 and interface.name != 'EventTarget') 114 has_cross_origin_named_getter = ( 115 any(method['is_cross_origin'] for method in methods) 116 or any(attribute['has_cross_origin_getter'] 117 for attribute in attributes)) 118 has_cross_origin_named_setter = any( 119 attribute['has_cross_origin_setter'] 120 for attribute in attributes) 121 has_cross_origin_indexed_getter = ( 122 indexed_property_getter 123 and indexed_property_getter['is_cross_origin']) 124 has_cross_origin_named_enum = has_cross_origin_named_getter \ 125 or has_cross_origin_named_setter 126 if (named_property_getter 127 and named_property_getter['is_cross_origin']): 128 has_cross_origin_named_getter = True 129 130 return { 131 'attributes': 132 attributes, 133 'component': 134 component, 135 'has_constructor_callback': 136 has_constructor_callback, 137 'has_cross_origin_named_getter': 138 has_cross_origin_named_getter, 139 'has_cross_origin_named_setter': 140 has_cross_origin_named_setter, 141 'has_cross_origin_named_enumerator': 142 has_cross_origin_named_enum, 143 'has_cross_origin_indexed_getter': 144 has_cross_origin_indexed_getter, 145 'has_security_check': 146 has_security_check, 147 'indexed_property_getter': 148 indexed_property_getter, 149 'indexed_property_setter': 150 v8_interface.property_setter(interface.indexed_property_setter, 151 interface), 152 'indexed_property_deleter': 153 v8_interface.property_deleter(interface.indexed_property_deleter), 154 'internal_namespace': 155 v8_interface.internal_namespace(interface), 156 'is_partial': 157 interface.is_partial, 158 'methods': 159 methods, 160 'name': 161 name, 162 'named_constructor': 163 v8_interface.named_constructor_context(interface), 164 'named_property_getter': 165 named_property_getter, 166 'named_property_setter': 167 v8_interface.property_setter(interface.named_property_setter, 168 interface), 169 'named_property_deleter': 170 v8_interface.property_deleter(interface.named_property_deleter), 171 'v8_class': 172 v8_utilities.v8_class_name_or_partial(interface), 173 } 174 175 176# This class applies a Jinja template and creates a .cpp file for the external reference table. 177class ExternalReferenceTableGenerator(object): 178 def __init__(self, opts, info_provider): 179 self._opts = opts 180 self._info_provider = info_provider 181 self._reader = IdlReader(info_provider.interfaces_info, opts.cache_dir) 182 self._interface_contexts = {} 183 self._include_files = set(INCLUDES) 184 v8_types.set_component_dirs( 185 info_provider.interfaces_info['component_dirs']) 186 187 # Creates a Jinja context from an IDL file. 188 def process_idl_file(self, idl_filename): 189 definitions = self._reader.read_idl_definitions(idl_filename) 190 for component in definitions: 191 target_definitions = definitions[component] 192 interfaces = target_definitions.interfaces 193 first_name = target_definitions.first_name 194 if first_name in interfaces.keys(): 195 interface = interfaces[first_name] 196 self._process_interface(interface, component, interfaces) 197 198 # Creates a Jinja context from an interface. Some interfaces are not used 199 # in V8 context snapshot, so we can skip them. 200 def _process_interface(self, interface, component, interfaces): 201 def has_impl(interface): 202 component_info = self._info_provider.component_info 203 runtime_features = component_info['runtime_enabled_features'] 204 # Non legacy callback interface does not provide V8 callbacks. 205 if interface.is_callback: 206 return len(interface.constants) > 0 207 if v8_utilities.runtime_enabled_feature_name( 208 interface, runtime_features): 209 return False 210 if 'Exposed' not in interface.extended_attributes: 211 return True 212 return any( 213 exposure.exposed == 'Window' 214 and exposure.runtime_enabled is None 215 for exposure in interface.extended_attributes['Exposed']) 216 217 if not has_impl(interface): 218 return 219 220 context_builder = InterfaceTemplateContextBuilder( 221 self._opts, self._info_provider) 222 context = context_builder.create_interface_context( 223 interface, component, interfaces) 224 name = '%s%s' % (interface.name, 225 'Partial' if interface.is_partial else '') 226 self._interface_contexts[name] = context 227 228 # Do not include unnecessary header files. 229 if not context['attributes'] and not context['named_property_setter']: 230 return 231 232 include_file = 'third_party/blink/renderer/bindings/%s/v8/%s.h' % ( 233 component, utilities.to_snake_case(context['v8_class'])) 234 self._include_files.add(include_file) 235 236 # Gathers all interface-dependent information and returns as a Jinja template context. 237 def _create_template_context(self): 238 interfaces = [] 239 for name in sorted(self._interface_contexts): 240 interfaces.append(self._interface_contexts[name]) 241 header_path = 'third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_external_references.h' 242 include_files = list(self._include_files) 243 return { 244 'class': 'V8ContextSnapshotExternalReferences', 245 'interfaces': interfaces, 246 'include_files': sorted(include_files), 247 'this_include_header_path': header_path, 248 'code_generator': os.path.basename(__file__), 249 'jinja_template_filename': TEMPLATE_FILE 250 } 251 252 # Applies a Jinja template on a context and generates a C++ code. 253 def generate(self): 254 jinja_env = initialize_jinja_env(self._opts.cache_dir) 255 context = self._create_template_context() 256 cpp_template = jinja_env.get_template(TEMPLATE_FILE) 257 cpp_text = cpp_template.render(context) 258 return cpp_text 259 260 261def main(): 262 opts, _ = parse_args() 263 # TODO(peria): get rid of |info_provider| 264 info_provider = create_component_info_provider(opts.info_dir, 265 opts.target_component) 266 generator = ExternalReferenceTableGenerator(opts, info_provider) 267 268 idl_files = utilities.read_idl_files_list_from_file(opts.idl_files_list) 269 for idl_file in idl_files: 270 generator.process_idl_file(idl_file) 271 output_code = generator.generate() 272 output_path = opts.output 273 write_file(output_code, output_path) 274 275 276if __name__ == '__main__': 277 main() 278