# -*- coding: utf-8 -*- import os from . import ast_parser from . import utils # out-dir is in-dir if not specified def create_header(path, out_dir=None): header = {"path": "", "dir": "", "filename": "", "out_dir": "", "sourcecode": ""} header["path"] = path header["dir"] = os.path.dirname(path) header["filename"] = os.path.basename(path) header["out_dir"] = header["dir"] if out_dir: header["out_dir"] = out_dir header["sourcecode"] = utils.read_file(path) return header def extract_forward_declarations(data): def filter(item): if (item["is_definition"] == False and item["kind"] == "CursorKind.STRUCT_DECL" ): return True def xform(item): return item return utils.recursive_query(data, filter, xform) def extract_vars_named(data, var_names): def filter(item): if (item["kind"] == "CursorKind.VAR_DECL" and item["name"] in var_names ): return True def xform(item): return item found = utils.recursive_query(data, filter, xform) found_names = [] for i in found: found_names.append(i["name"]) notfound_names = [] for name in var_names: if not name in found_names: notfound_names.append(name) return (found, notfound_names) def extract_functions_named(data, function_names): def filter(item): if (item["kind"] == "CursorKind.FUNCTION_DECL" and item["name"] in function_names ): return True def xform(item): return item found = utils.recursive_query(data, filter, xform) found_names = [] for i in found: found_names.append(i["name"]) notfound_names = [] for name in function_names: if not name in found_names: notfound_names.append(name) return (found, notfound_names) # just the typenames of all typerefs no dups def collect_all_typerefs(data): def filter(item): if (item["kind"] == "CursorKind.TYPE_REF" ): return True def xform(item): return item["type"] results = utils.recursive_query(data, filter, xform) no_dups = list(set(results)) return no_dups def extract_type_names(data): def filter(item): if (item["kind"] == "CursorKind.TYPEDEF_DECL" or item["kind"] == "CursorKind.ENUM_DECL" or item["kind"] == "CursorKind.STRUCT_DECL" ): return True def xform(item): ret = item["type"] return ret return utils.recursive_query(data, filter, xform) def resolve_typerefs(ast, typeref_names): types = [] typeref_resolved = [] typeref_notfound = [] for typeref_name in typeref_names: res = resolve_typeref(ast, typeref_name) if not res: typeref_notfound.append(typeref_name) else: types.append(res) typeref_resolved.append(typeref_name) return (types, typeref_resolved, typeref_notfound) def resolve_typeref(ast, typeref_name): ret = None ret = extract_enum_decl(ast, typeref_name) if not ret: ret = extract_struct_decl(ast, typeref_name) if not ret: ret = extract_typedef_decl(ast, typeref_name) return ret def extract_enum_decl(ast, name): ret = None def filter(data): if (data["kind"] == "CursorKind.ENUM_DECL" and data["is_definition"] == True # no forward decl and data["type"] == name ): return True res = utils.recursive_query(ast, filter) if len(res) > 1: assert False, "duplicate definition" if len(res) == 1: ret = res[0] return ret def extract_struct_decl(ast, name): ret = None def filter(data): if (data["kind"] == "CursorKind.STRUCT_DECL" and data["is_definition"] == True # no forward decl and data["type"] == name ): return True res = utils.recursive_query(ast, filter) if len(res) > 1: assert False, "duplicate definition" if len(res) == 1: ret = res[0] return ret def extract_typedef_decl(ast, name): ret = None def filter(data): if (data["kind"] == "CursorKind.TYPEDEF_DECL" and data["type"] == name ): return True res = utils.recursive_query(ast, filter) if res: ret = res.pop() if ret["utypekind"] == "Typedef": ret = extract_typedef_decl(ast, ret["utype"]) elif ret["utypekind"] == "Elaborated": enum_decl = extract_enum_decl(ast, ret["utype"]) struct_decl = extract_struct_decl(ast, ret["utype"]) if enum_decl and struct_decl: assert False, "duplicate types" else: ret = enum_decl or struct_decl return ret def find_dup_types(data): def filter(item): if (item["kind"] == "CursorKind.STRUCT_DECL" or item["kind"] == "CursorKind.ENUM_DECL"): return True def xform(item): return item["type"] all_types = utils.recursive_query(data, filter, xform) dups = set() for type in all_types: if all_types.count(type) > 1: dups.add(type) return (all_types, list(dups)) # TODO: Check for primitive type def remove_typedefs_of_primitive(types): types_struct_enum = [] for type in types: if (type["kind"] == "CursorKind.TYPEDEF_DECL"): pass else: types_struct_enum.append(type) return types_struct_enum def parse(libclang_path, header_filename, function_names, var_names, debug_ast=False): astp = ast_parser.ASTParser(libclang_path) # header = create_header("/Users/heck/local-default/include/pEp/pEpEngine.h", out_dir="./") header = create_header(header_filename) header["ast"] = astp.parse(header["path"], follow_includes=True) if debug_ast: utils.write_json(header["ast"], header["out_dir"] + "/" + header["filename"] + ".ast.json") # CDL cid = {"functions": "", "vars": "", "types": ""} # stage 1: extract functions and vars (cid["functions"], cid["functions_notfound"]) = extract_functions_named(header["ast"], function_names) (cid["vars"], cid["vars_notfound"]) = extract_vars_named(header["ast"], var_names) # stage 2: collect type refs cid["types"] = [] typerefs_unresolved = [] cid["typerefs_resolved"] = [] cid["typerefs_notfound"] = [] while True: typerefs_unresolved = collect_all_typerefs(cid) # only list of typenames typerefs_unresolved = list(set(typerefs_unresolved) - (set(cid["typerefs_resolved"]).union(set(cid["typerefs_notfound"])))) if (len(typerefs_unresolved) <= 0): break (types, typerefs_resolved, notfound) = resolve_typerefs(header["ast"], typerefs_unresolved) cid["types"] += types cid["typerefs_resolved"] += typerefs_resolved cid["typerefs_notfound"] += notfound cid["types"] = remove_typedefs_of_primitive(cid["types"]) cid["type_names"] = extract_type_names(cid["types"]) header["cid"] = cid return header # generates simple-ast for each header specified in spec out dir. # def main_old(): # parser = ASTParser("/opt/local/libexec/llvm-9.0/lib/libclang.dylib") # # in_dir = r"/Users/heck/local-default/include/pEp/" # filenames = ["pEpEngine.h", # "keymanagement.h", # "message_api.h", # "message.h", # "sync_api.h", # "key_reset.h", # "Identity.h", # "Rating.h"] # # out_dir = "data/output" # # paths = join_dir_and_filenames(in_dir, filenames) # # headers = [] # for path in paths: # headers.append(create_header(path, out_dir)) # # for header in headers: # print("processing path: " + header["path"] + "...") # header["ast"] = parser.parse(header["path"], header["sourcecode"]) # write_json(header["ast"], header["out_dir"] + "/" + header["filename"] + ".ast.json") # # simpleAst = SimpleAST() # header["simple_ast"] = simpleAst.create_simple_ast(header["ast"]) # write_json(header["simple_ast"], header["out_dir"] + "/" + header["filename"] + ".simple_ast.json") # #