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.
6 # pylint: disable=W0104,W0106,F0401,R0201
17 return os
.path
.dirname(os
.path
.abspath(__file__
))
20 def _GetDirAbove(dirname
):
21 """Returns the directory "above" this file containing |dirname| (which must
22 also be "above" this file)."""
25 path
, tail
= os
.path
.split(path
)
31 def _AddThirdPartyImportPath():
32 sys
.path
.insert(0, os
.path
.join(_GetDirAbove('mojo'), 'third_party'))
35 _AddThirdPartyImportPath()
38 loader
= jinja2
.FileSystemLoader(_ScriptDir())
39 jinja_env
= jinja2
.Environment(loader
=loader
, keep_trailing_newline
=True)
42 # Accumulate lines of code with varying levels of indentation.
43 class CodeWriter(object):
47 self
._margin
_stack
= []
49 def __lshift__(self
, line
):
50 self
._lines
.append((self
._margin
+ line
).rstrip())
53 self
._margin
_stack
.append(self
._margin
)
57 self
._margin
= self
._margin
_stack
.pop()
60 return '\n'.join(self
._lines
).rstrip() + '\n'
66 # Context handler that automatically indents and dedents a CodeWriter
68 def __init__(self
, writer
):
72 self
._writer
.PushMargin()
74 def __exit__(self
, type_
, value
, traceback
):
75 self
._writer
.PopMargin()
78 def TemplateFile(name
):
79 return os
.path
.join(os
.path
.dirname(__file__
), name
)
82 # Wraps comma separated lists as needed.
83 # TODO(teravest): Eliminate Wrap() and use "git cl format" when code is checked
85 def Wrap(pre
, items
, post
):
86 complete
= pre
+ ', '.join(items
) + post
87 if len(complete
) <= 80:
91 for i
, item
in enumerate(items
):
92 if i
< len(items
) - 1:
93 lines
.append(indent
+ item
+ ',')
95 lines
.append(indent
+ item
+ post
)
99 def GeneratorWarning():
100 return ('// WARNING this file was generated by %s\n// Do not edit by hand.' %
101 os
.path
.basename(__file__
))
104 # Untrusted library which thunks from the public Mojo API to the IRT interface
105 # implementing the public Mojo API.
106 def GenerateLibMojo(functions
, out
):
107 template
= jinja_env
.get_template('libmojo.cc.tmpl')
112 for line
in Wrap('%s %s(' % (f
.return_type
, f
.name
), f
.ParamList(), ') {'):
116 code
<< 'struct nacl_irt_mojo* irt_mojo = get_irt_mojo();'
117 code
<< 'if (irt_mojo == NULL)'
119 code
<< 'return MOJO_RESULT_INTERNAL;'
120 code
<< 'return irt_mojo->%s(%s);' % (
121 f
.name
, ', '.join([p
.name
for p
in f
.params
]))
126 body
= code
.GetValue()
127 text
= template
.render(
128 generator_warning
=GeneratorWarning(),
133 # Parameters passed into trusted code are handled differently depending on
134 # details of the parameter. ParamImpl instances encapsulate these differences
135 # and are used to generate the code that transfers parameters across the
136 # untrusted/trusted boundary.
137 class ParamImpl(object):
138 def __init__(self
, param
):
141 # Declare whatever variables are needed to handle this particular parameter.
142 def DeclareVars(self
, code
):
143 raise NotImplementedError(type(self
))
145 # Convert the untrusted representation of the parameter into a trusted
146 # representation, such as a scalar value or a trusted pointer into the
147 # untrusted address space.
148 def ConvertParam(self
):
149 raise NotImplementedError(type(self
))
151 # For this particular parameter, what expression should be passed when
152 # invoking the trusted Mojo API function?
154 raise NotImplementedError(type(self
))
156 # After invoking the trusted Mojo API function, transfer data back into
157 # untrusted memory. Overriden for Out and InOut parameters.
158 def CopyOut(self
, code
):
161 # Converting array parameters needs to be defered until after the scalar
162 # parameter containing the size of the array has itself been converted.
167 class ScalarInputImpl(ParamImpl
):
168 def DeclareVars(self
, code
):
169 code
<< '%s %s_value;' % (self
.param
.base_type
, self
.param
.name
)
171 def ConvertParam(self
):
173 return ('ConvertScalarInput(nap, params[%d], &%s_value)' %
177 return '%s_value' % self
.param
.name
180 class ScalarOutputImpl(ParamImpl
):
181 def DeclareVars(self
, code
):
182 code
<< '%s volatile* %s_ptr;' % (self
.param
.base_type
, self
.param
.name
)
183 code
<< '%s %s_value;' % (self
.param
.base_type
, self
.param
.name
)
185 def ConvertParam(self
):
187 return ('ConvertScalarOutput(nap, params[%d], %s, &%s_ptr)' %
188 (p
.uid
+ 1, CBool(p
.is_optional
), p
.name
))
191 name
= self
.param
.name
192 expr
= '&%s_value' % name
193 if self
.param
.is_optional
:
194 expr
= '%s_ptr ? %s : NULL' % (name
, expr
)
197 def CopyOut(self
, code
):
198 name
= self
.param
.name
199 if self
.param
.is_struct
:
200 # C++ errors when you try to copy a volatile struct pointer.
201 # (There are no default copy constructors for this case.)
203 copy_stmt
= ('memcpy_volatile_out(%s_ptr, &%s_value, sizeof(%s));' %
204 (name
, name
, self
.param
.base_type
))
206 copy_stmt
= '*%s_ptr = %s_value;' % (name
, name
)
208 if self
.param
.is_optional
:
209 code
<< 'if (%s_ptr != NULL) {' % (name
)
217 class ScalarInOutImpl(ParamImpl
):
218 def DeclareVars(self
, code
):
219 code
<< '%s volatile* %s_ptr;' % (self
.param
.base_type
, self
.param
.name
)
220 code
<< '%s %s_value;' % (self
.param
.base_type
, self
.param
.name
)
222 def ConvertParam(self
):
224 return ('ConvertScalarInOut(nap, params[%d], %s, &%s_value, &%s_ptr)' %
225 (p
.uid
+ 1, CBool(p
.is_optional
), p
.name
, p
.name
))
228 name
= self
.param
.name
229 expr
= '&%s_value' % name
230 if self
.param
.is_optional
:
231 expr
= '%s_ptr ? %s : NULL' % (name
, expr
)
234 def CopyOut(self
, code
):
235 name
= self
.param
.name
236 if self
.param
.is_optional
:
237 code
<< 'if (%s_ptr != NULL) {' % (name
)
239 code
<< '*%s_ptr = %s_value;' % (name
, name
)
242 code
<< '*%s_ptr = %s_value;' % (name
, name
)
245 class ArrayImpl(ParamImpl
):
246 def DeclareVars(self
, code
):
247 code
<< '%s %s;' % (self
.param
.param_type
, self
.param
.name
)
249 def ConvertParam(self
):
251 if p
.base_type
== 'void':
254 element_size
= 'sizeof(*%s)' % p
.name
256 return ('ConvertArray(nap, params[%d], %s, %s, %s, &%s)' %
257 (p
.uid
+ 1, p
.size
+ '_value', element_size
, CBool(p
.is_optional
),
261 return self
.param
.name
267 class ExtensibleStructInputImpl(ParamImpl
):
268 def DeclareVars(self
, code
):
269 code
<< '%s %s;' % (self
.param
.param_type
, self
.param
.name
)
271 def ConvertParam(self
):
273 return ('ConvertExtensibleStructInput(nap, params[%d], %s, &%s)' %
274 (p
.uid
+ 1, CBool(p
.is_optional
), p
.name
))
277 return self
.param
.name
283 return ScalarInOutImpl(p
)
285 if p
.is_always_written
:
286 return ScalarOutputImpl(p
)
288 # Mojo defines that some of its outputs will not be set in specific
289 # cases. To avoid the complexity of determining if the output was set
290 # by Mojo, copy the output's current value (possibly junk) and copy it
291 # back to untrusted memory afterwards.
292 return ScalarInOutImpl(p
)
294 return ScalarInputImpl(p
)
298 if p
.is_input
and not p
.is_output
and p
.is_extensible
:
299 return ExtensibleStructInputImpl(p
)
300 if not p
.is_input
and p
.is_output
and not p
.is_extensible
:
301 return ScalarOutputImpl(p
)
306 return 'true' if value
else 'false'
309 # A trusted wrapper that validates the arguments passed from untrusted code
310 # before passing them to the underlying public Mojo API.
311 def GenerateMojoSyscall(functions
, out
):
312 template
= jinja_env
.get_template('mojo_syscall.cc.tmpl')
318 impls
= [ImplForParam(p
) for p
in f
.params
]
319 impls
.append(ImplForParam(f
.result_param
))
321 code
<< 'case %d:' % f
.uid
328 num_params
= len(f
.params
) + 2
329 code
<< 'if (num_params != %d) {' % num_params
334 # Declare temporaries.
336 impl
.DeclareVars(code
)
338 def ConvertParam(code
, impl
):
339 code
<< 'if (!%s) {' % impl
.ConvertParam()
346 code
<< 'ScopedCopyLock copy_lock(nap);'
347 # Convert and validate pointers in two passes.
348 # Arrays cannot be validated until the size parameter has been
351 if not impl
.IsArray():
352 ConvertParam(code
, impl
)
355 ConvertParam(code
, impl
)
360 getParams
= [impl
.CallParam() for impl
in impls
[:-1]]
361 code
<< 'result_value = %s(%s);' % (f
.name
, ', '.join(getParams
))
367 code
<< 'ScopedCopyLock copy_lock(nap);'
378 body
= code
.GetValue()
379 text
= template
.render(
380 generator_warning
=GeneratorWarning(),
385 # A header declaring the IRT interface for accessing Mojo functions.
386 def GenerateMojoIrtHeader(functions
, out
):
387 template
= jinja_env
.get_template('mojo_irt.h.tmpl')
390 code
<< 'struct nacl_irt_mojo {'
393 for line
in Wrap('%s (*%s)(' % (f
.return_type
, f
.name
),
400 body
= code
.GetValue()
402 text
= template
.render(
403 generator_warning
=GeneratorWarning(),
407 # IRT interface which implements the Mojo public API.
408 def GenerateMojoIrtImplementation(functions
, out
):
409 template
= jinja_env
.get_template('mojo_irt.c.tmpl')
413 for line
in Wrap('static %s irt_%s(' % (f
.return_type
, f
.name
),
418 # 2 extra parameters: message ID and return value.
419 num_params
= len(f
.params
) + 2
422 code
<< 'uint32_t params[%d];' % num_params
423 return_type
= f
.result_param
.base_type
424 if return_type
== 'MojoResult':
425 default
= 'MOJO_RESULT_INVALID_ARGUMENT'
426 elif return_type
== 'MojoTimeTicks':
429 raise Exception('Unhandled return type: ' + return_type
)
430 code
<< '%s %s = %s;' % (return_type
, f
.result_param
.name
, default
)
433 code
<< 'params[0] = %d;' % f
.uid
435 cast_template
= 'params[%d] = (uint32_t)(%s);'
438 if p
.IsPassedByValue():
440 code
<< cast_template
% (p
.uid
+ 1, ptr
)
441 # Return value pointer
442 code
<< cast_template
% (num_params
- 1, '&' + f
.result_param
.name
)
444 code
<< 'DoMojoCall(params, sizeof(params));'
445 code
<< 'return %s;' % f
.result_param
.name
451 # Now we've emitted all the functions, but we still need the struct
453 code
<< 'struct nacl_irt_mojo kIrtMojo = {'
456 code
<< '&irt_%s,' % f
.name
459 body
= code
.GetValue()
461 text
= template
.render(
462 generator_warning
=GeneratorWarning(),
467 def OutFile(dir_path
, name
):
468 if not os
.path
.exists(dir_path
):
470 os
.makedirs(dir_path
)
472 # There may have been a race to create this directory.
473 if e
.errno
!= errno
.EEXIST
:
475 return open(os
.path
.join(dir_path
, name
), 'w')
479 usage
= 'usage: %prog [options]'
480 parser
= optparse
.OptionParser(usage
=usage
)
485 help='output generated code into directory DIR')
486 options
, args
= parser
.parse_args(args
=args
)
487 if not options
.out_dir
:
488 parser
.error('-d is required')
490 parser
.error('unexpected positional arguments: %s' % ' '.join(args
))
492 mojo
= interface
.MakeInterface()
494 out
= OutFile(options
.out_dir
, 'libmojo.cc')
495 GenerateLibMojo(mojo
.functions
, out
)
497 out
= OutFile(options
.out_dir
, 'mojo_syscall.cc')
498 GenerateMojoSyscall(mojo
.functions
, out
)
500 out
= OutFile(options
.out_dir
, 'mojo_irt.h')
501 GenerateMojoIrtHeader(mojo
.functions
, out
)
503 out
= OutFile(options
.out_dir
, 'mojo_irt.c')
504 GenerateMojoIrtImplementation(mojo
.functions
, out
)
506 if __name__
== '__main__':