SOAdvancedDissector

SOAdvancedDissector Git Source Tree

Root/cppprototypeparser.py

1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright Grégory Soutadé
5
6# This file is part of SOAdvancedDissector
7
8# SOAdvancedDissector is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# SOAdvancedDissector is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with SOAdvancedDissector. If not, see <http://www.gnu.org/licenses/>.
20#
21
22class CPPPrototypeParser:
23 """Class that extract all parts of a cpp method prototype"""
24
25 def __init__(self, fullname=''):
26 if fullname:
27 self.parse(fullname)
28 else:
29 self._reset()
30
31 def _reset(self):
32 self.fullclassname = '' # Class name with its namespaces
33 self.names = [] # All decoded names : namespaces, class, function
34 self.namespaces = [] # Namespaces part
35 self.parameters = [] # Parameters part
36 self.fullparameters = '' # Full parameters string
37 self.funcname = '' # Function name
38 self.classname = '' # Class name
39 self.fullname = '' # namespaces + class + function + parameters
40 self.has_parameters = False
41 self.is_function = False # Is single function or class method
42
43 # Should parse something like :
44 # uft::ClassDescriptor<layout::MasterConditionalReference>::queryFunc(uft::StructDescriptor const*, void*, uft::Value const&, void*)
45 def parse(self, fullname):
46 """Parse CPP method prototype and fill class members
47 with right values
48
49 Parameters
50 ----------
51 fullname : str
52 Prototype to parse
53 """
54 self._reset()
55 self.fullname = fullname
56 if '(' in fullname:
57 self.has_parameters = True
58 splitname = fullname.split('(')
59 if 'operator()' in fullname:
60 namepart = splitname[0] + '()'
61 self.fullparameters = '(' + ''.join(splitname[2:])
62 else:
63 namepart = splitname[0]
64 self.fullparameters = '(' + ''.join(splitname[1:])
65 self._parseParameters()
66 else:
67 namepart = fullname
68
69 self.names = self._parseName(namepart)
70
71 # Function or class method
72 if self.has_parameters:
73 self.funcname = self.names[-1]
74 # Class method with at least class name + method name
75 if len(self.names) >= 2:
76 self.classname = self.names[-2]
77 self.fullclassname = '::'.join(self.names[:-1])
78 # Contains namespaces
79 if len(self.names) > 2:
80 self.namespaces = self.names[:-2]
81 # Simple function
82 else:
83 self.is_function = True
84 self.funcname = self.names[0]
85 else:
86 # Class name with or without namespaces
87 self.classname = self.names[-1]
88 if len(self.names) > 1:
89 self.namespaces = self.names[:-1]
90
91 if self.funcname.endswith(' const'):
92 self.funcname = self.funcname[:-len(' const')]
93
94 if self.classname.endswith(' const'):
95 self.classname = self.classname[:-len(' const')]
96
97 def _parseName(self, name):
98 """Parse CPP method name and split in different parts
99 using '::' as delimiter.
100 It supports templates names and function prototypes
101
102 Parameters
103 ----------
104 name : str
105 Name to parse
106
107 Returns
108 -------
109 list
110 Splitted name parts
111 """
112 nb_parenthesis = 0
113 nb_chevrons = 0
114 nb_parenthesis = 0
115 cur_buf = ''
116 names = []
117 nb_db_points = 0
118 in_template = False
119 in_parenthesis = False
120 operator = False
121
122 for c in name.strip():
123 if operator:
124 cur_buf += c
125 continue
126
127 if c == '(':
128 cur_buf += c
129 nb_parenthesis += 1
130 in_parenthesis = True
131 elif c == ')':
132 cur_buf += c
133 nb_parenthesis -= 1
134 if nb_parenthesis == 0:
135 in_parenthesis = False
136 elif c == ':':
137 if in_template or in_parenthesis:
138 cur_buf += c
139 continue
140 nb_db_points += 1
141 if nb_db_points == 2:
142 names.append(cur_buf)
143 cur_buf = ''
144 nb_db_points = 0
145 elif c == '<':
146 cur_buf += c
147 in_template = True
148 nb_chevrons += 1
149 elif c == '>':
150 cur_buf += c
151 nb_chevrons -= 1
152 if nb_chevrons == 0:
153 in_template = False
154 else:
155 cur_buf += c
156
157 # Next characters are part of operator name
158 if cur_buf == 'operator':
159 operator = True
160
161 if cur_buf:
162 names.append(cur_buf)
163
164 return names
165
166 def _parseParameters(self):
167 """Parse each parameters and do split on them
168 using '::' as delimiter.
169 Update self.parameters member
170 """
171 nb_chevrons = 0
172 in_template = False
173 cur_buf = ''
174
175 # Parse fullparameters except parenthesis
176 for c in self.fullparameters[1:-1]:
177 if c == ',':
178 if in_template: continue
179 self.parameters.append(self._parseName(cur_buf))
180 cur_buf = ''
181 elif c == '<':
182 cur_buf += c
183 if in_template: continue
184 in_template = True
185 nb_chevrons += 1
186 elif c == '>':
187 cur_buf += c
188 nb_chevrons -= 1
189 if nb_chevrons == 0:
190 in_template = False
191 else:
192 cur_buf += c
193
194 if cur_buf:
195 self.parameters.append(self._parseName(cur_buf))
196
197# Some tests
198if __name__ == "__main__":
199 strings = ['uft::ClassDescriptor<layout::MasterConditionalReference>::queryFunc(uft::StructDescriptor const*, void*, uft::Value const&, void*)',
200 'adept::LicenseImpl::getVoucherID()',
201 'adept::DRMProcessorImpl::addSignIn()',
202 'layout::InlineLayoutEngine::AnnotationGlyphRunCounter::operator()(uft::sref<layout::RunListItem>&)',
203 'dp::PointerVector<void>::~PointerVector()',
204 'adept::IoCallbackWrapper<adept::DRMProcessorImpl>::IoCallbackWrapper(adept::DRMProcessorImpl*, void (adept::DRMProcessorImpl::*)(dp::Unknown*, bool), void (adept::DRMProcessorImpl::*)(double), void (adept::DRMProcessorImpl::*)(dp::String const&))',
205 'tetraphilia::ThreadImpl<T3AppTraits, tetraphilia::PFiber<T3AppTraits>, tetraphilia::NoClientYieldHook<T3AppTraits> >',
206 'debugOutputOpen']
207 for s in strings:
208 p = CPPPrototypeParser(s)
209 print('Original {}'.format(s))
210 print('Full parameters {}'.format(p.fullparameters))
211 print('Names {}'.format(p.names))
212 print('Namespaces {}'.format(p.namespaces))
213 print('Parameters {}'.format(p.parameters))
214 print('Class name {}'.format(p.classname))
215 print('Func name {}'.format(p.funcname))
216 print('Full class name {}'.format(p.fullclassname))
217 print('Full parameters {}'.format(p.fullparameters))
218 print('Is function ? {}'.format(p.is_function))
219 print('Has parameters ? {}'.format(p.has_parameters))
220 print("")

Archive Download this file

Branches