OLD | NEW |
---|---|
(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 | |
Mark Seaborn
2014/08/26 16:25:52
Nit: Isn't "gen/" usually for generated code, not
Nick Bray (chromium)
2014/09/03 23:45:02
Done.
| |
6 # pylint: disable=W0104,W0106,F0401,R0201 | |
7 | |
8 import optparse | |
9 import os.path | |
10 import string | |
11 import sys | |
12 | |
13 import interface | |
14 | |
15 # Accumulate lines of code with varying levels of indentation. | |
16 class CodeWriter(object): | |
17 def __init__(self): | |
18 self.lines = [] | |
19 self.margin = "" | |
20 self.margin_stack = [] | |
21 | |
22 def __lshift__(self, line): | |
23 self.lines.append((self.margin + line).rstrip()) | |
24 | |
25 def pushMargin(self): | |
26 self.margin_stack.append(self.margin) | |
27 self.margin += " " | |
28 | |
29 def popMargin(self): | |
30 self.margin = self.margin_stack.pop() | |
31 | |
32 def getValue(self): | |
33 return "\n".join(self.lines).rstrip() + "\n" | |
34 | |
35 def indent(self): | |
36 return Indent(self) | |
37 | |
38 | |
39 # Context handler that automatically indents and dedents a CodeWriter | |
40 class Indent(object): | |
41 def __init__(self, writer): | |
42 self.writer = writer | |
43 | |
44 def __enter__(self): | |
45 self.writer.pushMargin() | |
46 | |
47 def __exit__(self, type_, value, traceback): | |
48 self.writer.popMargin() | |
49 | |
50 | |
51 def templateFile(name): | |
Mark Seaborn
2014/08/26 16:25:52
Style nit: should be TemplateFile (see http://www.
Nick Bray (chromium)
2014/09/03 23:45:02
Done.
| |
52 return os.path.join(os.path.dirname(__file__), name) | |
53 | |
54 | |
55 def loadTemplate(filename): | |
56 data = open(templateFile(filename)).read() | |
57 template = string.Template(data) | |
58 return template | |
59 | |
60 | |
61 # Wraps comma seperated lists as needed. | |
62 def wrap(pre, items, post): | |
63 complete = pre + ", ".join(items) + post | |
64 if len(complete) <= 80: | |
65 return [complete] | |
66 lines = [pre] | |
67 indent = " " | |
68 for i, item in enumerate(items): | |
69 if i < len(items) - 1: | |
70 lines.append(indent + item + ",") | |
71 else: | |
72 lines.append(indent + item + post) | |
73 return lines | |
74 | |
75 | |
76 # Untrusted library implementing the public Mojo API. | |
77 def generateLibMojo(functions, out): | |
78 template = loadTemplate("libmojo.cc.template") | |
79 | |
80 code = CodeWriter() | |
81 | |
82 for f in functions: | |
83 for line in wrap("%s %s(" % (f.returnType, f.name), f.paramList(), "){"): | |
84 code << line | |
85 numParams = len(f.params) + 2 | |
86 | |
87 with code.indent(): | |
88 code << "uint32_t params[%d];" % numParams | |
89 returnType = f.resultParam.baseType | |
90 if returnType == "MojoResult": | |
91 default = "MOJO_RESULT_INVALID_ARGUMENT" | |
92 elif returnType == "MojoTimeTicks": | |
93 default = "0" | |
94 else: | |
95 raise Exception("Unhandled return type: " + returnType) | |
96 code << "%s %s = %s;" % (returnType, f.resultParam.name, default) | |
97 | |
98 # Message ID | |
99 code << "params[0] = %d;" % f.uid | |
100 # Parameter pointers | |
101 castTemplate = "params[%d] = reinterpret_cast<uint32_t>(%s);" | |
102 for p in f.params: | |
103 ptr = p.name | |
104 if p.isPassedByValue(): | |
105 ptr = "&" + ptr | |
106 code << castTemplate % (p.uid + 1, ptr) | |
107 # Return value pointer | |
108 code << castTemplate % (numParams - 1, "&" + f.resultParam.name) | |
109 | |
110 code << "DoMojoCall(params, sizeof(params));" | |
111 code << "return %s;" % f.resultParam.name | |
112 | |
113 code << "}" | |
114 code << "" | |
115 | |
116 body = code.getValue() | |
117 text = template.substitute( | |
118 script_name=os.path.basename(__file__), | |
119 body=body) | |
120 out.write(text) | |
121 | |
122 | |
123 # Parameters passed into trusted code are handled differently depending on | |
124 # details of the parameter. Encapsulate these differences in a polymorphic type. | |
125 class ParamImpl(object): | |
126 def __init__(self, param): | |
127 self.param = param | |
128 | |
129 def copyOut(self, code): | |
130 pass | |
131 | |
132 def isArray(self): | |
133 return False | |
134 | |
135 | |
136 class ScalarInputImpl(ParamImpl): | |
137 def declareVars(self, code): | |
138 code << "%s %s_value;" % (self.param.baseType, self.param.name) | |
139 | |
140 def convertParam(self): | |
141 p = self.param | |
142 return ("ConvertScalarInput(nap, params[%d], &%s_value)" % | |
143 (p.uid + 1, p.name)) | |
144 | |
145 def callParam(self): | |
146 return "%s_value" % self.param.name | |
147 | |
148 | |
149 class ScalarOutputImpl(ParamImpl): | |
150 def declareVars(self, code): | |
151 code << "%s volatile* %s_ptr;" % (self.param.baseType, self.param.name) | |
152 code << "%s %s_value;" % (self.param.baseType, self.param.name) | |
153 | |
154 def convertParam(self): | |
155 p = self.param | |
156 return "ConvertScalarOutput(nap, params[%d], &%s_ptr)" % (p.uid + 1, p.name) | |
157 | |
158 def callParam(self): | |
159 return "&%s_value" % self.param.name | |
160 | |
161 def copyOut(self, code): | |
162 name = self.param.name | |
163 code << "*%s_ptr = %s_value;" % (name, name) | |
164 | |
165 | |
166 class ScalarInOutImpl(ParamImpl): | |
167 def declareVars(self, code): | |
168 code << "%s volatile* %s_ptr;" % (self.param.baseType, self.param.name) | |
169 code << "%s %s_value;" % (self.param.baseType, self.param.name) | |
170 | |
171 def convertParam(self): | |
172 p = self.param | |
173 return ("ConvertScalarInOut(nap, params[%d], %s, &%s_value, &%s_ptr)" % | |
174 (p.uid + 1, cBool(p.isOptional), p.name, p.name)) | |
175 | |
176 def callParam(self): | |
177 name = self.param.name | |
178 expr = "&%s_value" % name | |
179 if self.param.isOptional: | |
180 expr = "%s_ptr ? %s : NULL" % (name, expr) | |
181 return expr | |
182 | |
183 def copyOut(self, code): | |
184 name = self.param.name | |
185 if self.param.isOptional: | |
186 code << "if (%s_ptr != NULL) {" % (name) | |
187 with code.indent(): | |
188 code << "*%s_ptr = %s_value;" % (name, name) | |
189 code << "}" | |
190 else: | |
191 code << "*%s_ptr = %s_value;" % (name, name) | |
192 | |
193 | |
194 class ArrayImpl(ParamImpl): | |
195 def declareVars(self, code): | |
196 code << "%s %s;" % (self.param.paramType, self.param.name) | |
197 | |
198 def convertParam(self): | |
199 p = self.param | |
200 if p.baseType == "void": | |
201 return ("ConvertBytes(nap, params[%d], %s, %s, &%s)" % | |
202 (p.uid + 1, p.size + "_value", cBool(p.isOptional), p.name)) | |
203 else: | |
204 return ("ConvertArray(nap, params[%d], %s, %s, &%s)" % | |
205 (p.uid + 1, p.size + "_value", cBool(p.isOptional), p.name)) | |
206 | |
207 def callParam(self): | |
208 return self.param.name | |
209 | |
210 def isArray(self): | |
211 return True | |
212 | |
213 | |
214 class StructInputImpl(ParamImpl): | |
215 def declareVars(self, code): | |
216 code << "%s %s;" % (self.param.paramType, self.param.name) | |
217 | |
218 def convertParam(self): | |
219 p = self.param | |
220 return ("ConvertStruct(nap, params[%d], %s, &%s)" % | |
221 (p.uid + 1, cBool(p.isOptional), p.name)) | |
222 | |
223 def callParam(self): | |
224 return self.param.name | |
225 | |
226 | |
227 def implForParam(p): | |
228 if p.isScalar(): | |
229 if p.isOutput: | |
230 if p.isInput: | |
231 return ScalarInOutImpl(p) | |
232 else: | |
233 return ScalarOutputImpl(p) | |
234 else: | |
235 return ScalarInputImpl(p) | |
236 elif p.isArray: | |
237 return ArrayImpl(p) | |
238 elif p.isStruct: | |
239 return StructInputImpl(p) | |
240 else: | |
241 assert False, p | |
242 | |
243 | |
244 def cBool(value): | |
245 return "true" if value else "false" | |
246 | |
247 # A trusted wrapper that validates the arguments passed from untrusted code | |
248 # before passing them to the underlying public Mojo API. | |
249 def generateMojoSyscall(functions, out): | |
250 template = loadTemplate("mojo_syscall.cc.template") | |
251 | |
252 code = CodeWriter() | |
253 code.pushMargin() | |
254 | |
255 for f in functions: | |
256 impls = [implForParam(p) for p in f.params] | |
257 impls.append(implForParam(f.resultParam)) | |
258 | |
259 code << "case %d:" % f.uid | |
260 | |
261 code.pushMargin() | |
262 | |
263 code << "{" | |
264 | |
265 with code.indent(): | |
266 numParams = len(f.params) + 2 | |
267 code << "if (numParams != %d) {" % numParams | |
268 with code.indent(): | |
269 code << "return -1;" | |
270 code << "}" | |
271 | |
272 # Declare temporaries. | |
273 for impl in impls: | |
274 impl.declareVars(code) | |
275 | |
276 def convertParam(code, impl): | |
277 code << "if (!%s) {" % impl.convertParam() | |
278 with code.indent(): | |
279 code << "return -1;" | |
280 code << "}" | |
281 | |
282 code << "{" | |
283 with code.indent(): | |
284 code << "ScopedCopyLock copyLock(nap);" | |
Mark Seaborn
2014/08/26 16:25:52
Style nit: "copy_lock"
Nick Bray (chromium)
2014/09/03 23:45:02
Done.
| |
285 # Convert and validate pointers in two passes. | |
286 # Arrays cannot be validated util the size parameter has been converted. | |
287 for impl in impls: | |
288 if not impl.isArray(): | |
289 convertParam(code, impl) | |
290 for impl in impls: | |
291 if impl.isArray(): | |
292 convertParam(code, impl) | |
293 code << "}" | |
294 code << "" | |
295 | |
296 # Call | |
297 getParams = [impl.callParam() for impl in impls[:-1]] | |
298 code << "result_value = %s(%s);" % (f.name, ", ".join(getParams)) | |
299 code << "" | |
300 | |
301 # Write outputs | |
302 code << "{" | |
303 with code.indent(): | |
304 code << "ScopedCopyLock copyLock(nap);" | |
305 # TODO copy out on failure? | |
306 for impl in impls: | |
307 impl.copyOut(code) | |
308 code << "}" | |
309 code << "" | |
310 | |
311 code << "return 0;" | |
312 code << "}" | |
313 | |
314 code.popMargin() | |
315 | |
316 body = code.getValue() | |
317 text = template.substitute( | |
318 script_name=os.path.basename(__file__), | |
319 body=body) | |
320 out.write(text) | |
321 | |
322 def outFile(dir_path, name): | |
323 if not os.path.exists(dir_path): | |
324 os.makedirs(dir_path) | |
325 return open(os.path.join(dir_path, name), "w") | |
326 | |
327 def main(args): | |
328 usage = "usage: %prog [options]" | |
329 parser = optparse.OptionParser(usage=usage) | |
330 parser.add_option( | |
331 "-d", | |
332 dest="out_dir", | |
333 metavar="DIR", | |
334 help="output generated code into directory DIR") | |
335 options, args = parser.parse_args(args=args) | |
336 if not options.out_dir: | |
337 parser.error("-d is required") | |
338 if args: | |
339 parser.error("unexpected positional arguments: %s" % " ".join(args)) | |
340 | |
341 mojo = interface.makeInterface() | |
342 | |
343 out = outFile(options.out_dir, "libmojo.cc") | |
344 generateLibMojo(mojo.functions, out) | |
345 | |
346 out = outFile(options.out_dir, "mojo_syscall.cc") | |
347 generateMojoSyscall(mojo.functions, out) | |
348 | |
349 | |
350 if __name__ == "__main__": | |
351 main(sys.argv[1:]) | |
OLD | NEW |