Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
code_generator_dart.py
Go to the documentation of this file.
1# Copyright (C) 2013 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7# * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9# * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13# * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28"""Generate Blink C++ bindings (.h and .cpp files) for use by Dart:HTML.
29
30If run itself, caches Jinja templates (and creates dummy file for build,
31since cache filenames are unpredictable and opaque).
32
33This module is *not* concurrency-safe without care: bytecode caching creates
34a race condition on cache *write* (crashes if one process tries to read a
35partially-written cache). However, if you pre-cache the templates (by running
36the module itself), then you can parallelize compiling individual files, since
37cache *reading* is safe.
38
39Input: An object of class IdlDefinitions, containing an IDL interface X
40Output: DartX.h and DartX.cpp
41
42Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
43"""
44
45import os
46import pickle
47import re
48import sys
49
50# Path handling for libraries and templates
51# Paths have to be normalized because Jinja uses the exact template path to
52# determine the hash used in the cache filename, and we need a pre-caching step
53# to be concurrency-safe. Use absolute path because __file__ is absolute if
54# module is imported, and relative if executed directly.
55# If paths differ between pre-caching and individual file compilation, the cache
56# is regenerated, which causes a race condition and breaks concurrent build,
57# since some compile processes will try to read the partially written cache.
58module_path, module_filename = os.path.split(os.path.realpath(__file__))
59third_party_dir = os.path.normpath(
60 os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pardir,
61 os.pardir))
62templates_dir = os.path.normpath(os.path.join(module_path, 'templates'))
63
64# Make sure extension is .py, not .pyc or .pyo, so doesn't depend on caching
65module_pyname = os.path.splitext(module_filename)[0] + '.py'
66
67# jinja2 is in chromium's third_party directory.
68# Insert at 1 so at front to override system libraries, and
69# after path[0] == invoking script dir
70sys.path.insert(1, third_party_dir)
71
72# Add the base compiler scripts to the path here as in compiler.py
73dart_script_path = os.path.dirname(os.path.abspath(__file__))
74script_path = os.path.join(
75 os.path.dirname(os.path.dirname(dart_script_path)), 'scripts')
76sys.path.extend([script_path])
77
78import jinja2
79
80import idl_types
81from idl_types import IdlType
82from utilities import write_pickle_file
83from v8_globals import includes
84from dart_utilities import DartUtilities
85
86# TODO(jacobr): remove this hacked together list.
87INTERFACES_WITHOUT_RESOLVERS = frozenset([
88 'TypeConversions', 'GCObservation', 'InternalProfilers',
89 'InternalRuntimeFlags', 'InternalSettings', 'InternalSettingsGenerated',
90 'Internals', 'LayerRect', 'LayerRectList', 'MallocStatistics',
91 'TypeConversions'
92])
93
94
95class CodeGeneratorDart(object):
96
97 def __init__(self, interfaces_info, cache_dir):
98 interfaces_info = interfaces_info or {}
99 self.interfaces_info = interfaces_info
101
102 # Set global type info
103 idl_types.set_ancestors(
104 dict((interface_name, interface_info['ancestors'])
105 for interface_name, interface_info in interfaces_info.items()
106 if interface_info['ancestors']))
107 IdlType.set_callback_interfaces(
108 set(interface_name
109 for interface_name, interface_info in interfaces_info.items()
110 if interface_info['is_callback_interface']))
111 IdlType.set_implemented_as_interfaces(
112 dict((interface_name, interface_info['implemented_as'])
113 for interface_name, interface_info in interfaces_info.items()
114 if interface_info['implemented_as']))
115 IdlType.set_garbage_collected_types(
116 set(interface_name
117 for interface_name, interface_info in interfaces_info.items()
118 if 'GarbageCollected' in
119 interface_info['inherited_extended_attributes']))
120
121 def generate_code(self, definitions, interface_name, idl_pickle_filename,
122 only_if_changed):
123 """Returns .h/.cpp code as (header_text, cpp_text)."""
124 try:
125 interface = definitions.interfaces[interface_name]
126 except KeyError:
127 raise Exception('%s not in IDL definitions' % interface_name)
128
129 # Store other interfaces for introspection
130 interfaces.update(definitions.interfaces)
131
132 # Set local type info
133 IdlType.set_callback_functions(definitions.callback_functions.keys())
134 IdlType.set_enums((enum.name, enum.values)
135 for enum in definitions.enumerations.values())
136
137 # Select appropriate Jinja template and contents function
138 if interface.is_callback:
139 header_template_filename = 'callback_interface_h.template'
140 cpp_template_filename = 'callback_interface_cpp.template'
141 generate_contents = dart_callback_interface.generate_callback_interface
142 else:
143 header_template_filename = 'interface_h.template'
144 cpp_template_filename = 'interface_cpp.template'
145 generate_contents = dart_interface.generate_interface
146 header_template = self.jinja_env.get_template(header_template_filename)
147 cpp_template = self.jinja_env.get_template(cpp_template_filename)
148
149 # Generate contents (input parameters for Jinja)
150 template_contents = generate_contents(interface)
151 template_contents['code_generator'] = module_pyname
152
153 # Add includes for interface itself and any dependencies
154 interface_info = self.interfaces_info[interface_name]
155 template_contents['header_includes'].add(interface_info['include_path'])
156 template_contents['header_includes'] = sorted(
157 template_contents['header_includes'])
158 includes.update(interface_info.get('dependencies_include_paths', []))
159
160 # Remove includes that are not needed for Dart and trigger fatal
161 # compile warnings if included. These IDL files need to be
162 # imported by Dart to generate the list of events but the
163 # associated header files do not contain any code used by Dart.
164 includes.discard('core/dom/GlobalEventHandlers.h')
165 includes.discard('core/frame/DOMWindowEventHandlers.h')
166
167 template_contents['cpp_includes'] = sorted(includes)
168
169 idl_world = {'interface': None, 'callback': None}
170
171 # Load the pickle file for this IDL.
172 if os.path.isfile(idl_pickle_filename):
173 with open(idl_pickle_filename) as idl_pickle_file:
174 idl_global_data = pickle.load(idl_pickle_file)
175 idl_pickle_file.close()
176 idl_world['interface'] = idl_global_data['interface']
177 idl_world['callback'] = idl_global_data['callback']
178
179 if 'interface_name' in template_contents:
180 interface_global = {
181 'name':
182 template_contents['interface_name'],
183 'parent_interface':
184 template_contents['parent_interface'],
185 'is_active_dom_object':
186 template_contents['is_active_dom_object'],
187 'is_event_target':
188 template_contents['is_event_target'],
189 'has_resolver':
190 template_contents['interface_name'] not in
191 INTERFACES_WITHOUT_RESOLVERS,
192 'is_node':
193 template_contents['is_node'],
194 'conditional_string':
195 template_contents['conditional_string'],
196 }
197 idl_world['interface'] = interface_global
198 else:
199 callback_global = {'name': template_contents['cpp_class']}
200 idl_world['callback'] = callback_global
201
202 write_pickle_file(idl_pickle_filename, idl_world, only_if_changed)
203
204 # Render Jinja templates
205 header_text = header_template.render(template_contents)
206 cpp_text = cpp_template.render(template_contents)
207 return header_text, cpp_text
208
209 # Generates global file for all interfaces.
210 def generate_globals(self, output_directory):
211 header_template_filename = 'global_h.template'
212 cpp_template_filename = 'global_cpp.template'
213
214 # Delete the global pickle file we'll rebuild from each pickle generated
215 # for each IDL file '(%s_globals.pickle) % interface_name'.
216 global_pickle_filename = os.path.join(output_directory, 'global.pickle')
217 if os.path.isfile(global_pickle_filename):
218 os.remove(global_pickle_filename)
219
220 # List of all interfaces and callbacks for global code generation.
221 world = {'interfaces': [], 'callbacks': []}
222
223 # Load all pickled data for each interface.
224 listing = os.listdir(output_directory)
225 for filename in listing:
226 if filename.endswith('_globals.pickle'):
227 idl_filename = os.path.join(output_directory, filename)
228 with open(idl_filename) as idl_pickle_file:
229 idl_world = pickle.load(idl_pickle_file)
230 if 'interface' in idl_world:
231 # FIXME: Why are some of these None?
232 if idl_world['interface']:
233 world['interfaces'].append(idl_world['interface'])
234 if 'callbacks' in idl_world:
235 # FIXME: Why are some of these None?
236 if idl_world['callbacks']:
237 world['callbacks'].append(idl_world['callback'])
238 idl_pickle_file.close()
239
240 world['interfaces'] = sorted(world['interfaces'],
241 key=lambda x: x['name'])
242 world['callbacks'] = sorted(world['callbacks'], key=lambda x: x['name'])
243
244 template_contents = world
245 template_contents['code_generator'] = module_pyname
246
247 header_template = self.jinja_env.get_template(header_template_filename)
248 header_text = header_template.render(template_contents)
249
250 cpp_template = self.jinja_env.get_template(cpp_template_filename)
251 cpp_text = cpp_template.render(template_contents)
252 return header_text, cpp_text
253
254
255def initialize_jinja_env(cache_dir):
256 jinja_env = jinja2.Environment(
257 loader=jinja2.FileSystemLoader(templates_dir),
258 # Bytecode cache is not concurrency-safe unless pre-cached:
259 # if pre-cached this is read-only, but writing creates a race condition.
260 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir),
261 keep_trailing_newline=True, # newline-terminate generated files
262 lstrip_blocks=True, # so can indent control flow tags
263 trim_blocks=True)
264 jinja_env.filters.update({
265 'blink_capitalize': DartUtilities.capitalize,
266 'conditional': conditional_if_endif,
267 'runtime_enabled': runtime_enabled_if,
268 })
269 return jinja_env
270
271
272# [Conditional]
273def conditional_if_endif(code, conditional_string):
274 # Jinja2 filter to generate if/endif directive blocks
275 if not conditional_string:
276 return code
277 return ('#if %s\n' % conditional_string + code +
278 '#endif // %s\n' % conditional_string)
279
280
281# [RuntimeEnabled]
282def runtime_enabled_if(code, runtime_enabled_function_name):
283 if not runtime_enabled_function_name:
284 return code
285 # Indent if statement to level of original code
286 indent = re.match(' *', code).group(0)
287 return ('%sif (%s())\n' % (indent, runtime_enabled_function_name) +
288 ' %s' % code)
289
290
291################################################################################
292
293
294def main(argv):
295 # If file itself executed, cache templates
296 try:
297 cache_dir = argv[1]
298 dummy_filename = argv[2]
299 except IndexError as err:
300 print('Usage: %s OUTPUT_DIR DUMMY_FILENAME' % argv[0])
301 return 1
302
303 # Cache templates
304 jinja_env = initialize_jinja_env(cache_dir)
305 template_filenames = [
306 filename for filename in os.listdir(templates_dir)
307 # Skip .svn, directories, etc.
308 if filename.endswith(('.cpp', '.h', '.template'))
309 ]
310 for template_filename in template_filenames:
311 jinja_env.get_template(template_filename)
312
313 # Create a dummy file as output for the build system,
314 # since filenames of individual cache files are unpredictable and opaque
315 # (they are hashes of the template path, which varies based on environment)
316 with open(dummy_filename, 'w') as dummy_file:
317 pass # |open| creates or touches the file
318
319
320if __name__ == '__main__':
321 sys.exit(main(sys.argv))
void print(void *str)
Definition bridge.cpp:126
__init__(self, interfaces_info, cache_dir)
generate_code(self, definitions, interface_name, idl_pickle_filename, only_if_changed)
static void append(char **dst, size_t *count, const char *src, size_t n)
Definition editor.cpp:211
runtime_enabled_if(code, runtime_enabled_function_name)
conditional_if_endif(code, conditional_string)
Definition main.py:1