OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # | |
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 """ Generator for Pnacl Shim functions that bridges the calling conventions | |
8 between GCC and PNaCl. """ | |
9 | |
10 from datetime import datetime | |
11 import difflib | |
12 import glob | |
13 import os | |
14 import sys | |
15 | |
16 from idl_ast import IDLAst | |
17 from idl_c_proto import CGen | |
18 from idl_generator import Generator | |
19 from idl_gen_wrapper import Interface, WrapperGen | |
20 from idl_log import ErrOut, InfoOut, WarnOut | |
21 from idl_option import GetOption, Option, ParseOptions | |
22 from idl_outfile import IDLOutFile | |
23 from idl_parser import ParseFiles | |
24 | |
25 Option('pnaclshim', 'Name of the pnacl shim file.', | |
26 default='temp_pnacl_shim.c') | |
27 | |
28 | |
29 # TODO(jvoung): this doesn't do anything yet (except change some dbg messages). | |
30 Option('disable_pnacl_opt', 'Turn off optimization of pnacl shim.') | |
31 | |
32 | |
33 class PnaclGen(WrapperGen): | |
noelallen1
2011/11/23 18:06:11
Python style is:
"""One line description.
Followe
jvoung - send to chromium...
2011/11/23 22:39:28
Done.
| |
34 """PnaclGen - A subclass of WrapperGenerator which takes the IDL sources and | |
35 generates shim code for bridging the calling conventions between GCC | |
36 and PNaCl (LLVM). | |
37 """ | |
38 | |
39 def __init__(self): | |
40 WrapperGen.__init__(self, | |
41 'Pnacl', | |
42 'Pnacl Shim Gen', | |
43 'pnacl', | |
44 'Generate the PNaCl shim.') | |
45 self.cgen = CGen() | |
46 self._skip_opt = False | |
47 self._pnacl_attribute = '__attribute__((pnaclcall))' | |
48 | |
49 | |
50 ############################################################ | |
51 | |
52 def InterfaceNeedsWrapper(self, iface, releases): | |
53 """ Return true if the interface has ANY methods that need wrapping. """ | |
54 if self._skip_opt: | |
55 return True | |
56 for release in iface.GetUniqueReleases(releases): | |
57 version = iface.GetVersion(release) | |
58 if self.InterfaceVersionNeedsWrapping(iface, version): | |
59 return True | |
60 return False | |
61 | |
62 | |
63 def InterfaceVersionNeedsWrapping(self, iface, version): | |
64 """ Return true if the interface+version has ANY methods that | |
65 need wrapping. """ | |
66 if self._skip_opt: | |
67 return True | |
68 for member in iface.GetListOf('Member'): | |
69 release = member.GetRelease(version) | |
70 if self.MemberNeedsWrapping(member, release): | |
71 return True | |
72 return False | |
73 | |
74 | |
75 def MemberNeedsWrapping(self, member, release): | |
76 """ Return true if a particular member function at a particular | |
77 release needs wrapping. """ | |
78 if self._skip_opt: | |
79 return True | |
80 if not member.InReleases([release]): | |
81 return False | |
82 ret, name, array, args_spec = self.cgen.GetComponents(member, | |
83 release, | |
84 'store') | |
85 return self.TypeNeedsWrapping(ret, []) or self.ArgsNeedWrapping(args_spec) | |
86 | |
87 | |
88 def ArgsNeedWrapping(self, args): | |
89 """ Return true if any parameter in the list needs wrapping. """ | |
90 for arg in args: | |
91 (type_str, name, array_dims, more_args) = arg | |
92 if self.TypeNeedsWrapping(type_str, array_dims): | |
93 return True | |
94 return False | |
95 | |
96 | |
97 def TypeNeedsWrapping(self, type_node, array_dims): | |
98 """ Return true if a parameter type needs wrapping. | |
99 Currently, this is true for byval aggregates. """ | |
100 is_aggregate = type_node.startswith('struct') or \ | |
101 type_node.startswith('union') | |
102 is_reference = (type_node.find('*') != -1 or array_dims != []) | |
103 return is_aggregate and not is_reference | |
104 | |
105 ############################################################ | |
106 | |
107 | |
108 def GenerateWrapperForPPBMethod(self, iface, member): | |
109 result = [] | |
110 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
111 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
112 func_prefix, False) | |
113 result.append('static %s\n%s {\n' % (self._pnacl_attribute, sig)) | |
114 result.append(' const struct %s *iface = %s.real_iface;\n' % | |
115 (iface.struct_name, self.GetWrapperInfoName(iface))) | |
116 ret, name, array, cspec = self.cgen.GetComponents(member, | |
117 iface.release, | |
118 'store') | |
119 ret_str, args_str = self.GetReturnArgs(ret, cspec) | |
120 result.append(' %siface->%s(%s);\n}\n\n' % (ret_str, | |
121 member.GetName(), args_str)) | |
122 return result | |
123 | |
124 | |
125 def GenerateWrapperForPPPMethod(self, iface, member): | |
126 result = [] | |
127 func_prefix = self.WrapperMethodPrefix(iface.node, iface.release) | |
128 sig = self.cgen.GetSignature(member, iface.release, 'store', | |
129 func_prefix, False) | |
130 result.append('static %s {\n' % sig) | |
131 result.append(' const struct %s *iface = %s.real_iface;\n' % | |
132 (iface.struct_name, self.GetWrapperInfoName(iface))) | |
133 temp_fp = self.cgen.GetSignature(member, iface.release, 'return', | |
134 'temp_fp', | |
135 func_as_ptr=True, | |
136 ptr_prefix=self._pnacl_attribute + ' ', | |
137 include_name=False) | |
138 cast = self.cgen.GetSignature(member, iface.release, 'return', | |
139 prefix='', | |
140 func_as_ptr=True, | |
141 ptr_prefix=self._pnacl_attribute + ' ', | |
142 include_name=False) | |
143 result.append(' %s = ((%s)iface->%s);\n' % (temp_fp, | |
144 cast, | |
145 member.GetName())) | |
146 ret, name, array, cspec = self.cgen.GetComponents(member, | |
147 iface.release, | |
148 'store') | |
149 ret_str, args_str = self.GetReturnArgs(ret, cspec) | |
150 result.append(' %stemp_fp(%s);\n}\n\n' % (ret_str, args_str)) | |
151 return result | |
152 | |
153 | |
154 def GenerateRange(self, ast, releases, options): | |
155 """ Generate shim code for a range of releases. """ | |
156 | |
157 self._skip_opt = GetOption('disable_pnacl_opt') | |
158 self.SetOutputFile(GetOption('pnaclshim')) | |
159 return WrapperGen.GenerateRange(self, ast, releases, options) | |
160 | |
161 pnaclgen = PnaclGen() | |
162 | |
163 ###################################################################### | |
164 # Tests. | |
165 | |
166 # Clean a string representing an object definition and return then string | |
167 # as a single space delimited set of tokens. | |
168 def CleanString(instr): | |
169 instr = instr.strip() | |
170 instr = instr.split() | |
171 return ' '.join(instr) | |
172 | |
173 | |
174 def PrintErrorDiff(old, new): | |
175 oldlines = old.split(';') | |
176 newlines = new.split(';') | |
177 d = difflib.Differ() | |
178 diff = d.compare(oldlines, newlines) | |
179 ErrOut.Log('Diff is:\n%s' % '\n'.join(diff)) | |
180 | |
181 | |
182 def GetOldTestOutput(ast): | |
183 # Scan the top-level comments in the IDL file for comparison. | |
184 old = [] | |
185 for filenode in ast.GetListOf('File'): | |
186 for node in filenode.GetChildren(): | |
187 instr = node.GetOneOf('Comment') | |
188 if not instr: continue | |
189 instr.Dump() | |
190 old.append(instr.GetName()) | |
191 return CleanString(''.join(old)) | |
192 | |
193 | |
194 def TestFiles(filenames, test_releases): | |
195 ast = ParseFiles(filenames) | |
196 iface_releases = pnaclgen.DetermineInterfaces(ast, test_releases) | |
197 new_output = CleanString(pnaclgen.GenerateWrapperForMethods( | |
198 iface_releases, comments=False)) | |
199 old_output = GetOldTestOutput(ast) | |
200 if new_output != old_output: | |
201 PrintErrorDiff(old_output, new_output) | |
202 ErrOut.Log('Failed pnacl generator test.') | |
203 return 1 | |
204 else: | |
205 InfoOut.Log('Passed pnacl generator test.') | |
206 return 0 | |
207 | |
208 | |
209 def Main(args): | |
210 filenames = ParseOptions(args) | |
211 test_releases = ['M13', 'M14', 'M15'] | |
212 if not filenames: | |
213 idldir = os.path.split(sys.argv[0])[0] | |
214 idldir = os.path.join(idldir, 'test_gen_pnacl', '*.idl') | |
215 filenames = glob.glob(idldir) | |
216 filenames = sorted(filenames) | |
217 if GetOption('test'): | |
218 # Run the tests. | |
219 return TestFiles(filenames, test_releases) | |
220 | |
221 # Otherwise, generate the output file (for potential use as golden file). | |
222 ast = ParseFiles(filenames) | |
223 return pnaclgen.GenerateRange(ast, test_releases, filenames) | |
224 | |
225 | |
226 if __name__ == '__main__': | |
227 retval = Main(sys.argv[1:]) | |
228 sys.exit(retval) | |
OLD | NEW |