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 | |
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 = [] | |
Mark Seaborn
2014/09/09 19:13:11
Can these be made private?
Nick Bray (chromium)
2014/09/09 23:12:32
Done.
| |
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): | |
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. | |
Mark Seaborn
2014/09/09 19:13:11
"separated"
Nick Bray (chromium)
2014/09/09 23:12:32
Done.
| |
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 def GeneratorWarning(): | |
76 return ("// WARNING this file was generated by %s\n// Do not edit by hand." % | |
77 os.path.basename(__file__)) | |
78 | |
79 # Untrusted library implementing the public Mojo API. | |
80 def GenerateLibMojo(functions, out): | |
81 template = LoadTemplate("libmojo.cc.template") | |
82 | |
83 code = CodeWriter() | |
84 | |
85 for f in functions: | |
86 for line in Wrap("%s %s(" % (f.returnType, f.name), f.paramList(), "){"): | |
87 code << line | |
88 numParams = len(f.params) + 2 | |
89 | |
90 with code.indent(): | |
91 code << "uint32_t params[%d];" % numParams | |
92 returnType = f.resultParam.baseType | |
93 if returnType == "MojoResult": | |
94 default = "MOJO_RESULT_INVALID_ARGUMENT" | |
95 elif returnType == "MojoTimeTicks": | |
96 default = "0" | |
97 else: | |
98 raise Exception("Unhandled return type: " + returnType) | |
99 code << "%s %s = %s;" % (returnType, f.resultParam.name, default) | |
100 | |
101 # Message ID | |
102 code << "params[0] = %d;" % f.uid | |
103 # Parameter pointers | |
104 castTemplate = "params[%d] = reinterpret_cast<uint32_t>(%s);" | |
105 for p in f.params: | |
106 ptr = p.name | |
107 if p.isPassedByValue(): | |
108 ptr = "&" + ptr | |
109 code << castTemplate % (p.uid + 1, ptr) | |
110 # Return value pointer | |
111 code << castTemplate % (numParams - 1, "&" + f.resultParam.name) | |
112 | |
113 code << "DoMojoCall(params, sizeof(params));" | |
114 code << "return %s;" % f.resultParam.name | |
115 | |
116 code << "}" | |
117 code << "" | |
118 | |
119 body = code.getValue() | |
120 text = template.substitute( | |
121 generator_warning=GeneratorWarning(), | |
122 body=body) | |
123 out.write(text) | |
124 | |
125 | |
126 # Parameters passed into trusted code are handled differently depending on | |
127 # details of the parameter. Encapsulate these differences in a polymorphic type. | |
128 class ParamImpl(object): | |
129 def __init__(self, param): | |
130 self.param = param | |
131 | |
132 def copyOut(self, code): | |
133 pass | |
134 | |
135 def isArray(self): | |
136 return False | |
137 | |
138 | |
139 class ScalarInputImpl(ParamImpl): | |
140 def declareVars(self, code): | |
141 code << "%s %s_value;" % (self.param.baseType, self.param.name) | |
142 | |
143 def convertParam(self): | |
144 p = self.param | |
145 return ("ConvertScalarInput(nap, params[%d], &%s_value)" % | |
146 (p.uid + 1, p.name)) | |
147 | |
148 def callParam(self): | |
149 return "%s_value" % self.param.name | |
150 | |
151 | |
152 class ScalarOutputImpl(ParamImpl): | |
153 def declareVars(self, code): | |
154 code << "%s volatile* %s_ptr;" % (self.param.baseType, self.param.name) | |
155 code << "%s %s_value;" % (self.param.baseType, self.param.name) | |
156 | |
157 def convertParam(self): | |
158 p = self.param | |
159 return "ConvertScalarOutput(nap, params[%d], &%s_ptr)" % (p.uid + 1, p.name) | |
160 | |
161 def callParam(self): | |
162 return "&%s_value" % self.param.name | |
163 | |
164 def copyOut(self, code): | |
165 name = self.param.name | |
166 code << "*%s_ptr = %s_value;" % (name, name) | |
167 | |
168 | |
169 class ScalarInOutImpl(ParamImpl): | |
170 def declareVars(self, code): | |
171 code << "%s volatile* %s_ptr;" % (self.param.baseType, self.param.name) | |
172 code << "%s %s_value;" % (self.param.baseType, self.param.name) | |
173 | |
174 def convertParam(self): | |
175 p = self.param | |
176 return ("ConvertScalarInOut(nap, params[%d], %s, &%s_value, &%s_ptr)" % | |
177 (p.uid + 1, CBool(p.isOptional), p.name, p.name)) | |
178 | |
179 def callParam(self): | |
180 name = self.param.name | |
181 expr = "&%s_value" % name | |
182 if self.param.isOptional: | |
183 expr = "%s_ptr ? %s : NULL" % (name, expr) | |
184 return expr | |
185 | |
186 def copyOut(self, code): | |
187 name = self.param.name | |
188 if self.param.isOptional: | |
189 code << "if (%s_ptr != NULL) {" % (name) | |
190 with code.indent(): | |
191 code << "*%s_ptr = %s_value;" % (name, name) | |
192 code << "}" | |
193 else: | |
194 code << "*%s_ptr = %s_value;" % (name, name) | |
195 | |
196 | |
197 class ArrayImpl(ParamImpl): | |
198 def declareVars(self, code): | |
199 code << "%s %s;" % (self.param.paramType, self.param.name) | |
200 | |
201 def convertParam(self): | |
202 p = self.param | |
203 if p.baseType == "void": | |
204 return ("ConvertBytes(nap, params[%d], %s, %s, &%s)" % | |
205 (p.uid + 1, p.size + "_value", CBool(p.isOptional), p.name)) | |
206 else: | |
207 return ("ConvertArray(nap, params[%d], %s, %s, &%s)" % | |
208 (p.uid + 1, p.size + "_value", CBool(p.isOptional), p.name)) | |
209 | |
210 def callParam(self): | |
211 return self.param.name | |
212 | |
213 def isArray(self): | |
214 return True | |
215 | |
216 | |
217 class StructInputImpl(ParamImpl): | |
218 def declareVars(self, code): | |
219 code << "%s %s;" % (self.param.paramType, self.param.name) | |
220 | |
221 def convertParam(self): | |
222 p = self.param | |
223 return ("ConvertStruct(nap, params[%d], %s, &%s)" % | |
224 (p.uid + 1, CBool(p.isOptional), p.name)) | |
225 | |
226 def callParam(self): | |
227 return self.param.name | |
228 | |
229 | |
230 def ImplForParam(p): | |
231 if p.isScalar(): | |
232 if p.isOutput: | |
233 if p.isInput: | |
234 return ScalarInOutImpl(p) | |
235 else: | |
236 return ScalarOutputImpl(p) | |
237 else: | |
238 return ScalarInputImpl(p) | |
239 elif p.isArray: | |
240 return ArrayImpl(p) | |
241 elif p.isStruct: | |
242 return StructInputImpl(p) | |
243 else: | |
244 assert False, p | |
245 | |
246 | |
247 def CBool(value): | |
248 return "true" if value else "false" | |
249 | |
250 # A trusted wrapper that validates the arguments passed from untrusted code | |
251 # before passing them to the underlying public Mojo API. | |
252 def GenerateMojoSyscall(functions, out): | |
253 template = LoadTemplate("mojo_syscall.cc.template") | |
254 | |
255 code = CodeWriter() | |
256 code.pushMargin() | |
257 | |
258 for f in functions: | |
259 impls = [ImplForParam(p) for p in f.params] | |
260 impls.append(ImplForParam(f.resultParam)) | |
261 | |
262 code << "case %d:" % f.uid | |
263 | |
264 code.pushMargin() | |
265 | |
266 code << "{" | |
267 | |
268 with code.indent(): | |
269 numParams = len(f.params) + 2 | |
270 code << "if (numParams != %d) {" % numParams | |
271 with code.indent(): | |
272 code << "return -1;" | |
273 code << "}" | |
274 | |
275 # Declare temporaries. | |
276 for impl in impls: | |
277 impl.declareVars(code) | |
278 | |
279 def convertParam(code, impl): | |
280 code << "if (!%s) {" % impl.convertParam() | |
281 with code.indent(): | |
282 code << "return -1;" | |
283 code << "}" | |
284 | |
285 code << "{" | |
286 with code.indent(): | |
287 code << "ScopedCopyLock copy_lock(nap);" | |
288 # Convert and validate pointers in two passes. | |
289 # Arrays cannot be validated util the size parameter has been converted. | |
290 for impl in impls: | |
291 if not impl.isArray(): | |
292 convertParam(code, impl) | |
293 for impl in impls: | |
294 if impl.isArray(): | |
295 convertParam(code, impl) | |
296 code << "}" | |
297 code << "" | |
298 | |
299 # Call | |
300 getParams = [impl.callParam() for impl in impls[:-1]] | |
301 code << "result_value = %s(%s);" % (f.name, ", ".join(getParams)) | |
302 code << "" | |
303 | |
304 # Write outputs | |
305 code << "{" | |
306 with code.indent(): | |
307 code << "ScopedCopyLock copy_lock(nap);" | |
308 # TODO copy out on failure? | |
309 for impl in impls: | |
310 impl.copyOut(code) | |
311 code << "}" | |
312 code << "" | |
313 | |
314 code << "return 0;" | |
315 code << "}" | |
316 | |
317 code.popMargin() | |
318 | |
319 body = code.getValue() | |
320 text = template.substitute( | |
321 generator_warning=GeneratorWarning(), | |
322 body=body) | |
323 out.write(text) | |
324 | |
325 def OutFile(dir_path, name): | |
326 if not os.path.exists(dir_path): | |
327 os.makedirs(dir_path) | |
328 return open(os.path.join(dir_path, name), "w") | |
329 | |
330 def main(args): | |
331 usage = "usage: %prog [options]" | |
332 parser = optparse.OptionParser(usage=usage) | |
333 parser.add_option( | |
334 "-d", | |
335 dest="out_dir", | |
336 metavar="DIR", | |
337 help="output generated code into directory DIR") | |
338 options, args = parser.parse_args(args=args) | |
339 if not options.out_dir: | |
340 parser.error("-d is required") | |
341 if args: | |
342 parser.error("unexpected positional arguments: %s" % " ".join(args)) | |
343 | |
344 mojo = interface.MakeInterface() | |
345 | |
346 out = OutFile(options.out_dir, "libmojo.cc") | |
347 GenerateLibMojo(mojo.functions, out) | |
348 | |
349 out = OutFile(options.out_dir, "mojo_syscall.cc") | |
350 GenerateMojoSyscall(mojo.functions, out) | |
351 | |
352 | |
353 if __name__ == "__main__": | |
354 main(sys.argv[1:]) | |
OLD | NEW |