221 lines
7.9 KiB
Python
221 lines
7.9 KiB
Python
|
#!/usr/bin/env python3
|
||
|
# -*- coding: utf-8 -*-
|
||
|
#
|
||
|
# Copyright Grégory Soutadé
|
||
|
|
||
|
# This file is part of SOAdvancedDissector
|
||
|
|
||
|
# SOAdvancedDissector is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation, either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# SOAdvancedDissector is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with SOAdvancedDissector. If not, see <http://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
|
||
|
class CPPPrototypeParser:
|
||
|
"""Class that extract all parts of a cpp method prototype"""
|
||
|
|
||
|
def __init__(self, fullname=''):
|
||
|
if fullname:
|
||
|
self.parse(fullname)
|
||
|
else:
|
||
|
self._reset()
|
||
|
|
||
|
def _reset(self):
|
||
|
self.fullclassname = '' # Class name with its namespaces
|
||
|
self.names = [] # All decoded names : namespaces, class, function
|
||
|
self.namespaces = [] # Namespaces part
|
||
|
self.parameters = [] # Parameters part
|
||
|
self.fullparameters = '' # Full parameters string
|
||
|
self.funcname = '' # Function name
|
||
|
self.classname = '' # Class name
|
||
|
self.fullname = '' # namespaces + class + function + parameters
|
||
|
self.has_parameters = False
|
||
|
self.is_function = False # Is single function or class method
|
||
|
|
||
|
# Should parse something like :
|
||
|
# uft::ClassDescriptor<layout::MasterConditionalReference>::queryFunc(uft::StructDescriptor const*, void*, uft::Value const&, void*)
|
||
|
def parse(self, fullname):
|
||
|
"""Parse CPP method prototype and fill class members
|
||
|
with right values
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
fullname : str
|
||
|
Prototype to parse
|
||
|
"""
|
||
|
self._reset()
|
||
|
self.fullname = fullname
|
||
|
if '(' in fullname:
|
||
|
self.has_parameters = True
|
||
|
splitname = fullname.split('(')
|
||
|
if 'operator()' in fullname:
|
||
|
namepart = splitname[0] + '()'
|
||
|
self.fullparameters = '(' + ''.join(splitname[2:])
|
||
|
else:
|
||
|
namepart = splitname[0]
|
||
|
self.fullparameters = '(' + ''.join(splitname[1:])
|
||
|
self._parseParameters()
|
||
|
else:
|
||
|
namepart = fullname
|
||
|
|
||
|
self.names = self._parseName(namepart)
|
||
|
|
||
|
# Function or class method
|
||
|
if self.has_parameters:
|
||
|
self.funcname = self.names[-1]
|
||
|
# Class method with at least class name + method name
|
||
|
if len(self.names) >= 2:
|
||
|
self.classname = self.names[-2]
|
||
|
self.fullclassname = '::'.join(self.names[:-1])
|
||
|
# Contains namespaces
|
||
|
if len(self.names) > 2:
|
||
|
self.namespaces = self.names[:-2]
|
||
|
# Simple function
|
||
|
else:
|
||
|
self.is_function = True
|
||
|
self.funcname = self.names[0]
|
||
|
else:
|
||
|
# Class name with or without namespaces
|
||
|
self.classname = self.names[-1]
|
||
|
if len(self.names) > 1:
|
||
|
self.namespaces = self.names[:-1]
|
||
|
|
||
|
if self.funcname.endswith(' const'):
|
||
|
self.funcname = self.funcname[:-len(' const')]
|
||
|
|
||
|
if self.classname.endswith(' const'):
|
||
|
self.classname = self.classname[:-len(' const')]
|
||
|
|
||
|
def _parseName(self, name):
|
||
|
"""Parse CPP method name and split in different parts
|
||
|
using '::' as delimiter.
|
||
|
It supports templates names and function prototypes
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
name : str
|
||
|
Name to parse
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list
|
||
|
Splitted name parts
|
||
|
"""
|
||
|
nb_parenthesis = 0
|
||
|
nb_chevrons = 0
|
||
|
nb_parenthesis = 0
|
||
|
cur_buf = ''
|
||
|
names = []
|
||
|
nb_db_points = 0
|
||
|
in_template = False
|
||
|
in_parenthesis = False
|
||
|
operator = False
|
||
|
|
||
|
for c in name.strip():
|
||
|
if operator:
|
||
|
cur_buf += c
|
||
|
continue
|
||
|
|
||
|
if c == '(':
|
||
|
cur_buf += c
|
||
|
nb_parenthesis += 1
|
||
|
in_parenthesis = True
|
||
|
elif c == ')':
|
||
|
cur_buf += c
|
||
|
nb_parenthesis -= 1
|
||
|
if nb_parenthesis == 0:
|
||
|
in_parenthesis = False
|
||
|
elif c == ':':
|
||
|
if in_template or in_parenthesis:
|
||
|
cur_buf += c
|
||
|
continue
|
||
|
nb_db_points += 1
|
||
|
if nb_db_points == 2:
|
||
|
names.append(cur_buf)
|
||
|
cur_buf = ''
|
||
|
nb_db_points = 0
|
||
|
elif c == '<':
|
||
|
cur_buf += c
|
||
|
in_template = True
|
||
|
nb_chevrons += 1
|
||
|
elif c == '>':
|
||
|
cur_buf += c
|
||
|
nb_chevrons -= 1
|
||
|
if nb_chevrons == 0:
|
||
|
in_template = False
|
||
|
else:
|
||
|
cur_buf += c
|
||
|
|
||
|
# Next characters are part of operator name
|
||
|
if cur_buf == 'operator':
|
||
|
operator = True
|
||
|
|
||
|
if cur_buf:
|
||
|
names.append(cur_buf)
|
||
|
|
||
|
return names
|
||
|
|
||
|
def _parseParameters(self):
|
||
|
"""Parse each parameters and do split on them
|
||
|
using '::' as delimiter.
|
||
|
Update self.parameters member
|
||
|
"""
|
||
|
nb_chevrons = 0
|
||
|
in_template = False
|
||
|
cur_buf = ''
|
||
|
|
||
|
# Parse fullparameters except parenthesis
|
||
|
for c in self.fullparameters[1:-1]:
|
||
|
if c == ',':
|
||
|
if in_template: continue
|
||
|
self.parameters.append(self._parseName(cur_buf))
|
||
|
cur_buf = ''
|
||
|
elif c == '<':
|
||
|
cur_buf += c
|
||
|
if in_template: continue
|
||
|
in_template = True
|
||
|
nb_chevrons += 1
|
||
|
elif c == '>':
|
||
|
cur_buf += c
|
||
|
nb_chevrons -= 1
|
||
|
if nb_chevrons == 0:
|
||
|
in_template = False
|
||
|
else:
|
||
|
cur_buf += c
|
||
|
|
||
|
if cur_buf:
|
||
|
self.parameters.append(self._parseName(cur_buf))
|
||
|
|
||
|
# Some tests
|
||
|
if __name__ == "__main__":
|
||
|
strings = ['uft::ClassDescriptor<layout::MasterConditionalReference>::queryFunc(uft::StructDescriptor const*, void*, uft::Value const&, void*)',
|
||
|
'adept::LicenseImpl::getVoucherID()',
|
||
|
'adept::DRMProcessorImpl::addSignIn()',
|
||
|
'layout::InlineLayoutEngine::AnnotationGlyphRunCounter::operator()(uft::sref<layout::RunListItem>&)',
|
||
|
'dp::PointerVector<void>::~PointerVector()',
|
||
|
'adept::IoCallbackWrapper<adept::DRMProcessorImpl>::IoCallbackWrapper(adept::DRMProcessorImpl*, void (adept::DRMProcessorImpl::*)(dp::Unknown*, bool), void (adept::DRMProcessorImpl::*)(double), void (adept::DRMProcessorImpl::*)(dp::String const&))',
|
||
|
'tetraphilia::ThreadImpl<T3AppTraits, tetraphilia::PFiber<T3AppTraits>, tetraphilia::NoClientYieldHook<T3AppTraits> >',
|
||
|
'debugOutputOpen']
|
||
|
for s in strings:
|
||
|
p = CPPPrototypeParser(s)
|
||
|
print('Original {}'.format(s))
|
||
|
print('Full parameters {}'.format(p.fullparameters))
|
||
|
print('Names {}'.format(p.names))
|
||
|
print('Namespaces {}'.format(p.namespaces))
|
||
|
print('Parameters {}'.format(p.parameters))
|
||
|
print('Class name {}'.format(p.classname))
|
||
|
print('Func name {}'.format(p.funcname))
|
||
|
print('Full class name {}'.format(p.fullclassname))
|
||
|
print('Full parameters {}'.format(p.fullparameters))
|
||
|
print('Is function ? {}'.format(p.is_function))
|
||
|
print('Has parameters ? {}'.format(p.has_parameters))
|
||
|
print("")
|