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

Side by Side Diff: nacl_bindings_generator/generate_nacl_bindings.py

Issue 1398213003: Refactored Non-SFI and SFI NaCl into separate directories. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 2 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
« no previous file with comments | « nacl_bindings/monacl_sel_main.cc ('k') | nacl_bindings_generator/interface.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 # pylint: disable=W0104,W0106,F0401,R0201
7
8 import errno
9 import os.path
10 import sys
11
12 import interface
13
14
15 def ScriptDir():
16 return os.path.dirname(os.path.abspath(__file__))
17
18
19 def RepoRoot():
20 return os.path.dirname(ScriptDir())
21
22
23 def _AddThirdPartyImportPath():
24 sys.path.append(os.path.join(RepoRoot(), 'third_party'))
25
26
27 _AddThirdPartyImportPath()
28 import jinja2
29
30 loader = jinja2.FileSystemLoader(ScriptDir())
31 jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True)
32
33
34 # Accumulate lines of code with varying levels of indentation.
35 class CodeWriter(object):
36 def __init__(self):
37 self._lines = []
38 self._margin = ''
39 self._margin_stack = []
40
41 def __lshift__(self, line):
42 self._lines.append((self._margin + line).rstrip())
43
44 def PushMargin(self):
45 self._margin_stack.append(self._margin)
46 self._margin += ' '
47
48 def PopMargin(self):
49 self._margin = self._margin_stack.pop()
50
51 def GetValue(self):
52 return '\n'.join(self._lines).rstrip() + '\n'
53
54 def Indent(self):
55 return Indent(self)
56
57
58 # Context handler that automatically indents and dedents a CodeWriter
59 class Indent(object):
60 def __init__(self, writer):
61 self._writer = writer
62
63 def __enter__(self):
64 self._writer.PushMargin()
65
66 def __exit__(self, type_, value, traceback):
67 self._writer.PopMargin()
68
69
70 def TemplateFile(name):
71 return os.path.join(os.path.dirname(__file__), name)
72
73
74 # Wraps comma separated lists as needed.
75 # TODO(teravest): Eliminate Wrap() and use "git cl format" when code is checked
76 # in.
77 def Wrap(pre, items, post):
78 complete = pre + ', '.join(items) + post
79 if len(complete) <= 80:
80 return [complete]
81 lines = [pre]
82 indent = ' '
83 for i, item in enumerate(items):
84 if i < len(items) - 1:
85 lines.append(indent + item + ',')
86 else:
87 lines.append(indent + item + post)
88 return lines
89
90
91 def GeneratorWarning():
92 return ('// WARNING this file was generated by %s\n// Do not edit by hand.' %
93 os.path.basename(__file__))
94
95
96 # Untrusted library which thunks from the public Mojo API to the IRT interface
97 # implementing the public Mojo API.
98 def GenerateLibMojo(functions, common_vars, out):
99 template = jinja_env.get_template('libmojo.cc.tmpl')
100
101 code = CodeWriter()
102
103 for f in functions:
104 for line in Wrap('%s %s(' % (f.return_type, f.name), f.ParamList(), ') {'):
105 code << line
106
107 with code.Indent():
108 code << 'struct nacl_irt_mojo* irt_mojo = get_irt_mojo();'
109 code << 'if (irt_mojo == NULL)'
110 with code.Indent():
111 code << 'return MOJO_RESULT_INTERNAL;'
112 code << 'return irt_mojo->%s(%s);' % (
113 f.name, ', '.join([p.name for p in f.params]))
114
115 code << '}'
116 code << ''
117
118 body = code.GetValue()
119 text = template.render(
120 body=body,
121 **common_vars)
122 out.write(text)
123
124
125 # Parameters passed into trusted code are handled differently depending on
126 # details of the parameter. ParamImpl instances encapsulate these differences
127 # and are used to generate the code that transfers parameters across the
128 # untrusted/trusted boundary.
129 class ParamImpl(object):
130 def __init__(self, param):
131 self.param = param
132
133 # Declare whatever variables are needed to handle this particular parameter.
134 def DeclareVars(self, code):
135 raise NotImplementedError(type(self))
136
137 # Convert the untrusted representation of the parameter into a trusted
138 # representation, such as a scalar value or a trusted pointer into the
139 # untrusted address space.
140 def ConvertParam(self):
141 raise NotImplementedError(type(self))
142
143 # For this particular parameter, what expression should be passed when
144 # invoking the trusted Mojo API function?
145 def CallParam(self):
146 raise NotImplementedError(type(self))
147
148 # After invoking the trusted Mojo API function, transfer data back into
149 # untrusted memory. Overriden for Out and InOut parameters.
150 def CopyOut(self, code):
151 pass
152
153 # Converting array parameters needs to be defered until after the scalar
154 # parameter containing the size of the array has itself been converted.
155 def IsArray(self):
156 return False
157
158
159 class ScalarInputImpl(ParamImpl):
160 def DeclareVars(self, code):
161 code << '%s %s_value;' % (self.param.base_type, self.param.name)
162
163 def ConvertParam(self):
164 p = self.param
165 return ('ConvertScalarInput(nap, params[%d], &%s_value)' %
166 (p.uid + 1, p.name))
167
168 def CallParam(self):
169 return '%s_value' % self.param.name
170
171
172 class ScalarOutputImpl(ParamImpl):
173 def DeclareVars(self, code):
174 code << '%s volatile* %s_ptr;' % (self.param.base_type, self.param.name)
175 code << '%s %s_value;' % (self.param.base_type, self.param.name)
176
177 def ConvertParam(self):
178 p = self.param
179 return ('ConvertScalarOutput(nap, params[%d], %s, &%s_ptr)' %
180 (p.uid + 1, CBool(p.is_optional), p.name))
181
182 def CallParam(self):
183 name = self.param.name
184 expr = '&%s_value' % name
185 if self.param.is_optional:
186 expr = '%s_ptr ? %s : NULL' % (name, expr)
187 return expr
188
189 def CopyOut(self, code):
190 name = self.param.name
191 if self.param.is_struct:
192 # C++ errors when you try to copy a volatile struct pointer.
193 # (There are no default copy constructors for this case.)
194 # memcpy instead.
195 copy_stmt = ('memcpy_volatile_out(%s_ptr, &%s_value, sizeof(%s));' %
196 (name, name, self.param.base_type))
197 else:
198 copy_stmt = '*%s_ptr = %s_value;' % (name, name)
199
200 if self.param.is_optional:
201 code << 'if (%s_ptr != NULL) {' % (name)
202 with code.Indent():
203 code << copy_stmt
204 code << '}'
205 else:
206 code << copy_stmt
207
208
209 class ScalarInOutImpl(ParamImpl):
210 def DeclareVars(self, code):
211 code << '%s volatile* %s_ptr;' % (self.param.base_type, self.param.name)
212 code << '%s %s_value;' % (self.param.base_type, self.param.name)
213
214 def ConvertParam(self):
215 p = self.param
216 return ('ConvertScalarInOut(nap, params[%d], %s, &%s_value, &%s_ptr)' %
217 (p.uid + 1, CBool(p.is_optional), p.name, p.name))
218
219 def CallParam(self):
220 name = self.param.name
221 expr = '&%s_value' % name
222 if self.param.is_optional:
223 expr = '%s_ptr ? %s : NULL' % (name, expr)
224 return expr
225
226 def CopyOut(self, code):
227 name = self.param.name
228 if self.param.is_optional:
229 code << 'if (%s_ptr != NULL) {' % (name)
230 with code.Indent():
231 code << '*%s_ptr = %s_value;' % (name, name)
232 code << '}'
233 else:
234 code << '*%s_ptr = %s_value;' % (name, name)
235
236
237 class PointerInputImpl(ParamImpl):
238 def DeclareVars(self, code):
239 code << '%s %s_value;' % (self.param.base_type, self.param.name)
240
241 def ConvertParam(self):
242 p = self.param
243 return ('ConvertPointerInput(nap, params[%d], &%s_value)' %
244 (p.uid + 1, p.name))
245
246 def CallParam(self):
247 return '%s_value' % self.param.name
248
249
250 class PointerInOutImpl(ParamImpl):
251 def DeclareVars(self, code):
252 code << 'uint32_t volatile* %s_ptr;' % (self.param.name,)
253 code << '%s %s_value;' % (self.param.base_type, self.param.name)
254
255 def ConvertParam(self):
256 p = self.param
257 return ('ConvertPointerInOut(nap, params[%d], %s, &%s_value, &%s_ptr)' %
258 (p.uid + 1, CBool(p.is_optional), p.name, p.name))
259
260 def CallParam(self):
261 name = self.param.name
262 expr = '&%s_value' % name
263 if self.param.is_optional:
264 expr = '%s_ptr ? %s : NULL' % (name, expr)
265 return expr
266
267 def CopyOut(self, code):
268 assert not self.param.is_optional
269 name = self.param.name
270 if self.param.is_optional:
271 code << 'if (%s_ptr != NULL) {' % (name)
272 with code.Indent():
273 code << 'CopyOutPointer(nap, %s_value, %s_ptr);' % (name, name)
274 code << '}'
275 else:
276 code << 'CopyOutPointer(nap, %s_value, %s_ptr);' % (name, name)
277
278 class ArrayImpl(ParamImpl):
279 def DeclareVars(self, code):
280 code << '%s %s;' % (self.param.param_type, self.param.name)
281
282 def ConvertParam(self):
283 p = self.param
284 if p.base_type == 'void':
285 element_size = '1'
286 else:
287 element_size = 'sizeof(*%s)' % p.name
288
289 return ('ConvertArray(nap, params[%d], %s, %s, %s, &%s)' %
290 (p.uid + 1, p.size + '_value', element_size, CBool(p.is_optional),
291 p.name))
292
293 def CallParam(self):
294 return self.param.name
295
296 def IsArray(self):
297 return True
298
299
300 class ExtensibleStructInputImpl(ParamImpl):
301 def DeclareVars(self, code):
302 code << '%s %s;' % (self.param.param_type, self.param.name)
303
304 def ConvertParam(self):
305 p = self.param
306 return ('ConvertExtensibleStructInput(nap, params[%d], %s, &%s)' %
307 (p.uid + 1, CBool(p.is_optional), p.name))
308
309 def CallParam(self):
310 return self.param.name
311
312
313 def ImplForParam(p):
314 if p.IsScalar():
315 if p.is_output:
316 if p.is_pointer:
317 return PointerInOutImpl(p)
318 elif p.is_input:
319 return ScalarInOutImpl(p)
320 else:
321 if p.is_always_written:
322 return ScalarOutputImpl(p)
323 else:
324 # Mojo defines that some of its outputs will not be set in specific
325 # cases. To avoid the complexity of determining if the output was set
326 # by Mojo, copy the output's current value (possibly junk) and copy it
327 # back to untrusted memory afterwards.
328 return ScalarInOutImpl(p)
329 else:
330 if p.is_pointer:
331 return PointerInputImpl(p)
332 else:
333 return ScalarInputImpl(p)
334 elif p.is_array:
335 return ArrayImpl(p)
336 elif p.is_struct:
337 if p.is_input and not p.is_output and p.is_extensible:
338 return ExtensibleStructInputImpl(p)
339 if not p.is_input and p.is_output and not p.is_extensible:
340 return ScalarOutputImpl(p)
341 assert False, p.name
342
343
344 def CBool(value):
345 return 'true' if value else 'false'
346
347
348 # A trusted wrapper that validates the arguments passed from untrusted code
349 # before passing them to the underlying public Mojo API.
350 def GenerateMojoSyscall(functions, common_vars, out):
351 template = jinja_env.get_template('mojo_syscall.cc.tmpl')
352
353 code = CodeWriter()
354 code.PushMargin()
355
356 for f in functions:
357 is_implemented = not f.broken_in_nacl
358
359 impls = [ImplForParam(p) for p in f.params]
360 impls.append(ImplForParam(f.result_param))
361
362 code << 'case %d:' % f.uid
363
364 if not is_implemented:
365 with code.Indent():
366 code << 'fprintf(stderr, "%s not implemented\\n");' % f.name
367 code << 'return -1;'
368 continue
369
370 code.PushMargin()
371
372 code << '{'
373
374 with code.Indent():
375 num_params = len(f.params) + 2
376 code << 'if (num_params != %d) {' % num_params
377 with code.Indent():
378 code << 'return -1;'
379 code << '}'
380
381 # Declare temporaries.
382 for impl in impls:
383 impl.DeclareVars(code)
384
385 def ConvertParam(code, impl):
386 code << 'if (!%s) {' % impl.ConvertParam()
387 with code.Indent():
388 code << 'return -1;'
389 code << '}'
390
391 code << '{'
392 with code.Indent():
393 code << 'ScopedCopyLock copy_lock(nap);'
394 # Convert and validate pointers in two passes.
395 # Arrays cannot be validated until the size parameter has been
396 # converted.
397 for impl in impls:
398 if not impl.IsArray():
399 ConvertParam(code, impl)
400 for impl in impls:
401 if impl.IsArray():
402 ConvertParam(code, impl)
403 code << '}'
404 code << ''
405
406 # Call
407 getParams = [impl.CallParam() for impl in impls[:-1]]
408 callTarget = f.name
409 # Redirect to namespaced functions.
410 if callTarget.startswith("Mojo"):
411 callTarget = callTarget[:4] + 'SystemImpl' + callTarget[4:]
412 getParams = ['g_mojo_system'] + getParams
413 code << 'result_value = %s(%s);' % (callTarget, ', '.join(getParams))
414 code << ''
415
416 # Write outputs
417 code << '{'
418 with code.Indent():
419 code << 'ScopedCopyLock copy_lock(nap);'
420 for impl in impls:
421 impl.CopyOut(code)
422 code << '}'
423 code << ''
424
425 code << 'return 0;'
426 code << '}'
427
428 code.PopMargin()
429
430 body = code.GetValue()
431 text = template.render(
432 body=body,
433 **common_vars)
434 out.write(text)
435
436
437 # A header declaring the IRT interface for accessing Mojo functions.
438 def GenerateMojoIrtHeader(functions, common_vars, out):
439 template = jinja_env.get_template('mojo_irt.h.tmpl')
440 code = CodeWriter()
441
442 code << 'struct nacl_irt_mojo {'
443 with code.Indent():
444 for f in functions:
445 for line in Wrap('%s (*%s)(' % (f.return_type, f.name),
446 f.ParamList(),
447 ');'):
448 code << line
449
450 code << '};'
451
452 body = code.GetValue()
453
454 text = template.render(
455 body=body,
456 **common_vars)
457 out.write(text)
458
459 # IRT interface which implements the Mojo public API.
460 def GenerateMojoIrtImplementation(functions, common_vars, out):
461 template = jinja_env.get_template('mojo_irt.c.tmpl')
462 code = CodeWriter()
463
464 for f in functions:
465 for line in Wrap('static %s irt_%s(' % (f.return_type, f.name),
466 f.ParamList(),
467 ') {'):
468 code << line
469
470 # 2 extra parameters: message ID and return value.
471 num_params = len(f.params) + 2
472
473 with code.Indent():
474 code << 'uint32_t params[%d];' % num_params
475 return_type = f.result_param.base_type
476 if return_type == 'MojoResult':
477 default = 'MOJO_RESULT_INVALID_ARGUMENT'
478 elif return_type == 'MojoTimeTicks':
479 default = '0'
480 else:
481 raise Exception('Unhandled return type: ' + return_type)
482 code << '%s %s = %s;' % (return_type, f.result_param.name, default)
483
484 # Message ID
485 code << 'params[0] = %d;' % f.uid
486 # Parameter pointers
487 cast_template = 'params[%d] = (uint32_t)(%s);'
488 for p in f.params:
489 ptr = p.name
490 if p.IsPassedByValue():
491 ptr = '&' + ptr
492 code << cast_template % (p.uid + 1, ptr)
493 # Return value pointer
494 code << cast_template % (num_params - 1, '&' + f.result_param.name)
495
496 code << 'DoMojoCall(params, sizeof(params));'
497 code << 'return %s;' % f.result_param.name
498
499 # Add body here.
500 code << "};"
501 code << "\n"
502
503 # Now we've emitted all the functions, but we still need the struct
504 # definition.
505 code << 'struct nacl_irt_mojo kIrtMojo = {'
506 for f in functions:
507 with code.Indent():
508 code << '&irt_%s,' % f.name
509 code << '};'
510
511 body = code.GetValue()
512
513 text = template.render(
514 body=body,
515 **common_vars)
516 out.write(text)
517
518
519 def OutFile(dir_path, name):
520 if not os.path.exists(dir_path):
521 try:
522 os.makedirs(dir_path)
523 except OSError as e:
524 # There may have been a race to create this directory.
525 if e.errno != errno.EEXIST:
526 raise
527 full_path = os.path.join(dir_path, name)
528 print full_path
529 return open(full_path, 'w')
530
531
532 def main():
533 root_dir = RepoRoot()
534
535 platform_dir = 'mojo/public/platform/nacl'
536 bindings_dir = 'nacl_bindings'
537
538 full_platform_dir = os.path.join(root_dir, platform_dir)
539 full_bindings_dir = os.path.join(root_dir, bindings_dir)
540
541 common_vars = dict(
542 generator_warning=GeneratorWarning(),
543 platform_dir=platform_dir,
544 platform_dir_header_path=platform_dir.replace("/", "_").upper(),
545 bindings_dir=bindings_dir,
546 )
547
548 mojo = interface.MakeInterface()
549
550 out = OutFile(full_platform_dir, 'libmojo.cc')
551 GenerateLibMojo(mojo.functions, common_vars, out)
552
553 out = OutFile(full_bindings_dir, 'mojo_syscall.cc')
554 GenerateMojoSyscall(mojo.functions, common_vars, out)
555
556 out = OutFile(full_platform_dir, 'mojo_irt.h')
557 GenerateMojoIrtHeader(mojo.functions, common_vars, out)
558
559 out = OutFile(full_bindings_dir, 'mojo_irt.c')
560 GenerateMojoIrtImplementation(mojo.functions, common_vars, out)
561
562 if __name__ == '__main__':
563 main()
OLDNEW
« no previous file with comments | « nacl_bindings/monacl_sel_main.cc ('k') | nacl_bindings_generator/interface.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698