美文网首页
使用antlr4, 用ts/js还原protobuf生成的jav

使用antlr4, 用ts/js还原protobuf生成的jav

作者: ThirstyZebra | 来源:发表于2017-03-23 17:06 被阅读0次

    目录:

    test004.ts

    import {ANTLRInputStream, CommonTokenStream} from "antlr4ts";
    import {JavaLexer} from "../antlr/Java/JavaLexer";
    import {CompilationUnitContext, JavaParser} from "../antlr/Java/JavaParser";
    import {fs} from "mz";
    import {CustomJavaListener} from "./CustomJavaListener";
    import {ParseTreeWalker} from "antlr4ts/tree";
    import * as nodefs from "fs";
    import {CustomClassBodyDeclarationVisitor} from "./CustomClassBodyDeclarationVisitor";
    import {PbStructure} from "./PbStructure";
    
    let data = nodefs.readFileSync('./../../src/ref/msg_svc$PbGetDiscussMsgResp.java', "utf-8");
    
    beginParse(data);
    
    
    function beginParse(data) {
    
        let inputStream = new ANTLRInputStream(data);
        let lexer = new JavaLexer(inputStream);
        let tokenStream = new CommonTokenStream(lexer);
        let parser = new JavaParser(tokenStream);
    
        // Parse the input, where `compilationUnit` is whatever entry point you defined
        let tree: CompilationUnitContext = parser.compilationUnit();
    
        // let walker: ParseTreeWalker = new ParseTreeWalker(); // create standard walker
        // let javaListener: CustomJavaListener = new CustomJavaListener();
        // walker.walk(javaListener, tree); // initiate walk of tree with listener
    
        let pbStructure = new PbStructure();
        let javaVisitor = new CustomClassBodyDeclarationVisitor(pbStructure);
        javaVisitor.visit(tree);
    
        console.log(pbStructure);
    
        // console.log(tree);
    
        // console.log(value);
    }
    
    

    好了, 看下使用visitor写的代码, 这是解析fieldMap字段的

    import {JavaVisitor} from "../antlr/Java/JavaVisitor";
    import {AbstractParseTreeVisitor} from "antlr4ts/tree";
    import {
        ArrayInitializerContext,
        ClassDeclarationContext,
        CreatorContext,
        ExpressionListContext,
        FieldDeclarationContext,
        VariableDeclaratorContext,
        VariableInitializerContext
    } from "../antlr/Java/JavaParser";
    import {PbStructure} from "./PbStructure";
    export class CustomClassBodyDeclarationVisitor<Result> extends AbstractParseTreeVisitor<Result> implements JavaVisitor<Result> {
    
    
        public constructor(public pbStructure: PbStructure) {
            super();
        }
    
        public visitClassDeclaration(ctx: ClassDeclarationContext): Result {
            this.pbStructure.messageName = ctx.Identifier().text;
            this.pbStructure.extend = ctx.typeType().classOrInterfaceType().Identifier(0).text;
            this.visit(ctx.classBody());
            return;
        }
    
        public visitFieldDeclaration(ctx: FieldDeclarationContext): Result {
            if (
                ctx.typeType().text == 'MessageMicro.FieldMap'
            ) {
                this.visit(ctx.variableDeclarators());
            }
            return;
        }
    
        public visitVariableDeclarator(ctx: VariableDeclaratorContext): Result {
            if (ctx.variableDeclaratorId().text == '__fieldMap__') {
                if (ctx.variableInitializer()) {
                    this.visitFieldMapVariableInitializer(ctx.variableInitializer());
                }
            }
            return;
        }
    
        protected defaultResult(): Result {
            return undefined;
        }
    
        /**
         * // MessageMicro.initFieldMap(
         * //      new int[] { 8, 16, 24, 32 },
         * //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
         * //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
         * //      ReqBody.class
         * // );
         * @param ctx
         */
        private visitFieldMapVariableInitializer(ctx: VariableInitializerContext) {
    
            let initFunc = ctx.expression().expression(0);
            if (initFunc.text != 'MessageMicro.initFieldMap') {
                throw new Error('fieldMap function name must be "MessageMicro.initFieldMap"');
            }
            let expressList = ctx.expression().expressionList();
            this.visitFieldMapExpressList(expressList);
        }
    
        /**
         *                // MessageMicro.initFieldMap(
         * expression 0-> //      new int[] { 8, 16, 24, 32 },
         * expression 1-> //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
         * expression 2-> //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
         *                //      ReqBody.class
         *                // );
         *  @param ctx
         */
        private visitFieldMapExpressList(ctx: ExpressionListContext) {
    
            this.pbStructure.flags = this.visit(ctx.expression(0));
            this.pbStructure.fields = this.visit(ctx.expression(1));
            this.pbStructure.defaultValues = this.visit(ctx.expression(2));
        }
    
        public visitCreator(ctx: CreatorContext): Result | any {
            let creatorName = ctx.createdName().text;
            let arrayInitializr = ctx.arrayCreatorRest().arrayInitializer();
            switch (creatorName) {
                case 'int':
                    return this.visitIntArrayInitializer(arrayInitializr);
                case 'String':
                    return this.visitStringArrayInitializer(arrayInitializr);
                case 'Object':
                    return this.visitObjectArrayInitializer(arrayInitializr);
                default:
                    return this.visitDefaultArrayInitializer(arrayInitializr);
            }
        }
    
        public visitDefaultArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => variable);
        }
    
        public visitIntArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => parseInt(variable.text));
        }
    
        public visitObjectArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => {
                //类型初始化
                let expr = variable.expression();
                if (expr &&
                    expr.getChild(1).text == '(' &&
                    expr.getChild(3).text == ')'
                ) {
                    switch (expr.expression(0).text) {
                        case 'Integer.valueOf':
                        case 'Long.valueOf':
                            return parseInt(expr.expressionList().text);
                        default:
                            return variable.text;
                    }
    
                } else {
                    return variable.text;
                }
            });
        }
    
        public visitStringArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => variable.text.replace(/"(.+?)"|'(.+?)'/, "$1$2"));
        }
    
    }
    
    

    运行一下查看一下PbStructure类

    PbStructure {
      messageName: 'Oidb_0x5d0$ReqBody',
      extend: 'MessageMicro',
      flags: [ 8, 16, 24, 32 ],
      fields: 
       [ 'uint32_start_time',
         'rpt_uint32_fieldlist',
         'rpt_uint64_uinlist',
         'uint64_bind_uin' ],
      defaultValues: [ 0, 0, 0, 0 ] }
    

    完善其他字段

    再次添加解析所有字段声明的代码


    PbStructure类

    export class PbStructure {
        public messageName;
    
        //another info
        public extend;
    
        public flags;
        public fields;
        public defaultValues;
        fieldMap: Map<string, { type, name, defaultValue, repeated }>;
    
        constructor() {
            this.fieldMap = new Map();
        }
    }
    

    CustomClassBodyDeclarationVisitor

    import {JavaVisitor} from "../antlr/Java/JavaVisitor";
    import {AbstractParseTreeVisitor} from "antlr4ts/tree";
    import {
        ArrayInitializerContext,
        ClassDeclarationContext,
        CreatorContext,
        ExpressionListContext,
        FieldDeclarationContext,
        VariableDeclaratorContext,
        VariableInitializerContext
    } from "../antlr/Java/JavaParser";
    import {PbStructure} from "./PbStructure";
    export class CustomClassBodyDeclarationVisitor<Result> extends AbstractParseTreeVisitor<Result> implements JavaVisitor<Result> {
    
        private currentHandleField;
    
        private resetCurrentHandleField() {
            this.currentHandleField = {
                type: null,
                name: null,
                defaultValue: null,
                repeated: null
            };
        }
    
        public constructor(public pbStructure: PbStructure) {
            super();
        }
    
        public visitClassDeclaration(ctx: ClassDeclarationContext): Result {
            this.pbStructure.messageName = ctx.Identifier().text;
            this.pbStructure.extend = ctx.typeType().classOrInterfaceType().Identifier(0).text;
            this.visit(ctx.classBody());
            return;
        }
    
        public visitFieldDeclaration(ctx: FieldDeclarationContext): Result {
            switch (ctx.typeType().text) {
                case 'MessageMicro.FieldMap':
                    this.visit(ctx.variableDeclarators());
                    break;
                case 'PBBoolField':
                case 'PBBytesField':
                case 'PBDoubleField':
                case 'PBEnumField':
                case 'PBField':
                case 'PBFixed32Field':
                case 'PBFixed64Field':
                case 'PBFloatField':
                case 'PBInt32Field':
                case 'PBInt64Field':
                case 'PBPrimitiveField':
                case 'PBRepeatField':
                case 'PBRepeatMessageField':
                case 'PBSFixed32Field':
                case 'PBSFixed64Field':
                case 'PBSInt32Field':
                case 'PBSInt64Field':
                case 'PBStringField':
                case 'PBUInt32Field':
                case 'PBUInt64Field':
                    this.resetCurrentHandleField();
                    this.currentHandleField.type = ctx.typeType().text;
                    this.visit(ctx.variableDeclarators());
                    this.pbStructure.fieldMap.set(this.currentHandleField.name, this.currentHandleField);
                    this.resetCurrentHandleField();
                    break;
            }
            return;
        }
    
        public visitVariableDeclarator(ctx: VariableDeclaratorContext): Result {
            if (ctx.variableDeclaratorId().text == '__fieldMap__') {
                if (ctx.variableInitializer()) {
                    this.visitFieldMapVariableInitializer(ctx.variableInitializer());
                }
            } else {
                if (ctx.variableInitializer()) {
                    this.currentHandleField.name = ctx.variableDeclaratorId().text;
                    this.visitPBFieldVariableInitializer(ctx.variableInitializer());
    
                }
            }
            return;
        }
    
        public visitPBFieldVariableInitializer(ctx: VariableInitializerContext) {
            let expr = ctx.expression();
            if (expr &&
                expr.getChild(1).text == '(' &&
                expr.getChild(3).text == ')'
            ) {
                switch (expr.expression(0).text) {
                    case 'PBField.initBool':
                    case 'PBField.initFixed32':
                    case 'PBField.initInt32':
                    case 'PBField.initInt64':
                    case 'PBField.initSFixed32':
                    case 'PBField.initSFixed64':
                    case 'PBField.initSInt32':
                    case 'PBField.initSInt64':
                    case 'PBField.initUInt32':
                    case 'PBField.initUInt64':
                        this.currentHandleField.defaultValue = parseInt(expr.expressionList().text);
                        break;
                    case 'PBField.initBytes'://ByteStringMicro/byte[]
                        this.currentHandleField.defaultValue = expr.expressionList().text;
                        break;
                    case 'PBField.initEnum':
                        break;
                    case 'PBField.initFloat':
                    case 'PBField.initDouble':
                        this.currentHandleField.defaultValue = parseFloat(expr.expressionList().text);
                        break;
                    case 'PBField.initRepeat':
                        const index = expr.expressionList().text.indexOf('__repeatHelper__');
                        if (index > 0) {
                            this.currentHandleField.repeated = expr.expressionList().text.substring(0, index - 1);
                        }
                        break;
                    case 'PBField.initString':
                        this.currentHandleField.defaultValue = expr.expressionList().text.replace(/"(.+?)"|'(.+?)'/, "$1$2")
                }
    
            } else {
            }
        }
    
        protected defaultResult(): Result {
            return undefined;
        }
    
        /**
         * // MessageMicro.initFieldMap(
         * //      new int[] { 8, 16, 24, 32 },
         * //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
         * //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
         * //      ReqBody.class
         * // );
         * @param ctx
         */
        private visitFieldMapVariableInitializer(ctx: VariableInitializerContext) {
    
            let initFunc = ctx.expression().expression(0);
            if (initFunc.text != 'MessageMicro.initFieldMap') {
                throw new Error('fieldMap function name must be "MessageMicro.initFieldMap"');
            }
            let expressList = ctx.expression().expressionList();
            this.visitFieldMapExpressList(expressList);
        }
    
        /**
         *                // MessageMicro.initFieldMap(
         * expression 0-> //      new int[] { 8, 16, 24, 32 },
         * expression 1-> //      new String[] { "uint32_start_time", "rpt_uint32_fieldlist", "rpt_uint64_uinlist", "uint64_bind_uin" },
         * expression 2-> //      new Object[] { Integer.valueOf(0), Integer.valueOf(0), Long.valueOf(0L), Long.valueOf(0L) },
         *                //      ReqBody.class
         *                // );
         *  @param ctx
         */
        private visitFieldMapExpressList(ctx: ExpressionListContext) {
    
            this.pbStructure.flags = this.visit(ctx.expression(0));
            this.pbStructure.fields = this.visit(ctx.expression(1));
            this.pbStructure.defaultValues = this.visit(ctx.expression(2));
        }
    
        public visitCreator(ctx: CreatorContext): Result | any {
            let creatorName = ctx.createdName().text;
            let arrayInitializr = ctx.arrayCreatorRest().arrayInitializer();
            switch (creatorName) {
                case 'int':
                    return this.visitIntArrayInitializer(arrayInitializr);
                case 'String':
                    return this.visitStringArrayInitializer(arrayInitializr);
                case 'Object':
                    return this.visitObjectArrayInitializer(arrayInitializr);
                default:
                    return this.visitDefaultArrayInitializer(arrayInitializr);
            }
        }
    
        public visitDefaultArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => variable);
        }
    
        public visitIntArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => parseInt(variable.text));
        }
    
        public visitStringArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => variable.text.replace(/"(.+?)"|'(.+?)'/, "$1$2"));
        }
    
        public visitObjectArrayInitializer(ctx: ArrayInitializerContext) {
            return ctx.variableInitializer().map(variable => {
                //类型初始化
                let expr = variable.expression();
                if (expr &&
                    expr.getChild(1).text == '(' &&
                    expr.getChild(3).text == ')'
                ) {
                    switch (expr.expression(0).text) {
                        case 'Integer.valueOf':
                        case 'Long.valueOf':
                            return parseInt(expr.expressionList().text);
                        default:
                            return variable.text;
                    }
    
                } else {
                    return variable.text;
                }
            });
        }
    
    }
    
    

    查看打印出来的PbStructure类

    PbStructure {
      fieldMap: 
       Map {
         'rpt_uint32_fieldlist' => { type: 'PBRepeatField',
         name: 'rpt_uint32_fieldlist',
         defaultValue: null,
         repeated: 'PBUInt32Field' },
         'rpt_uint64_uinlist' => { type: 'PBRepeatField',
         name: 'rpt_uint64_uinlist',
         defaultValue: null,
         repeated: 'PBUInt64Field' },
         'uint32_start_time' => { type: 'PBUInt32Field',
         name: 'uint32_start_time',
         defaultValue: 0,
         repeated: null },
         'uint64_bind_uin' => { type: 'PBUInt64Field',
         name: 'uint64_bind_uin',
         defaultValue: 0,
         repeated: null } },
      messageName: 'Oidb_0x5d0$ReqBody',
      extend: 'MessageMicro',
      flags: [ 8, 16, 24, 32 ],
      fields: 
       [ 'uint32_start_time',
         'rpt_uint32_fieldlist',
         'rpt_uint64_uinlist',
         'uint64_bind_uin' ],
      defaultValues: [ 0, 0, 0, 0 ] }
    

    最后通过PbStructure类生成proto文件

    
    

    添加GeneProtoFile代码

    import {PbStructure} from "./PbStructure";
    import {Java2PBType, JavaFieldMap, PBType} from "./PBMap";
    export class GeneProtoFile {
        private indent: number = 0;
    
    
        public constructor(private pbStructure: PbStructure) {
    
        }
    
        public indentWrite(string) {
            this.indentInc();
            this.write(string);
            this.indentDec();
        }
    
        public indentWriteLn(string) {
            this.indentInc();
            this.writeLn(string);
            this.indentDec();
        }
    
        public write(string) {
            let indent = this.indent;
            while (indent-- > 0) {
                this.put("    ");
            }
            this.put(string);
        }
    
        public writeLn(string) {
            this.write(string);
            this.put("\n");
        }
    
        public put(string) {
            process.stdout.write(string);
        }
    
        public indentInc() {
            ++this.indent;
        }
    
        public indentDec() {
            --this.indent;
            if (this.indent < 0) {
                this.indent = 0;
            }
        }
    
    
        public emitProto() {
            this.emitMessage();
    
        }
    
        private emitMessage() {
            this.writeLn(`message ${this.pbStructure.messageName} {`)
            this.indentInc();
            this.emitProtoBody();
            this.indentDec();
            this.write(`}`)
        }
    
        private emitProtoBody() {
            this.pbStructure.flags.map((flag, index) => {
                this.emitProtoField(index);
            });
        }
    
        private emitProtoField(index) {
            let name = this.pbStructure.fields[index];
            let fieldData = this.pbStructure.fieldMap.get(name);
    
            switch (fieldData.type) {
                case JavaFieldMap.PBBytesField:
                    this.writeLn(`${PBType.bytes} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBDoubleField:
                    this.writeLn(`${PBType.double} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBEnumField:
                    this.writeLn(`enum ${fieldData.name};`);//fixme
    
                    break;
                case JavaFieldMap.PBField:
                    this.writeLn(`pb ${fieldData.name};`);//fixme
    
                    break;
                case JavaFieldMap.PBFixed32Field:
                    this.writeLn(`${PBType.fixed32} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBFixed64Field:
                    this.writeLn(`${PBType.fixed64} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBFloatField:
                    this.writeLn(`${PBType.float} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBInt32Field:
                    this.writeLn(`${PBType.int32} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBInt64Field:
                    this.writeLn(`${PBType.int64} ${fieldData.name};`);
                    this.put(`;`);
                    break;
                case JavaFieldMap.PBPrimitiveField:
                    this.writeLn(`primitive ${fieldData.name};`);//fixme
    
                    break;
                case JavaFieldMap.PBRepeatField:
                    this.writeLn(`repeated ${Java2PBType[fieldData.repeated]} ${fieldData.name};`);
                    break;
                case JavaFieldMap.PBRepeatMessageField:
                    this.writeLn(`repeated message ${fieldData.type} ${fieldData.name};`);
                    break;
                case JavaFieldMap.PBSFixed32Field:
                    this.writeLn(`${PBType.sfixed32} ${fieldData.name};`);
                    break;
                case JavaFieldMap.PBSFixed64Field:
                    this.writeLn(`${PBType.sfixed64} ${fieldData.name};`);
                    break;
                case JavaFieldMap.PBSInt32Field:
                    this.writeLn(`${PBType.sint32} ${fieldData.name};`);
                    break;
                case JavaFieldMap.PBSInt64Field:
                    this.writeLn(`${PBType.sint64} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBStringField:
                    this.writeLn(`${PBType.string} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBUInt32Field:
                    this.writeLn(`${PBType.uint32} ${fieldData.name};`);
    
                    break;
                case JavaFieldMap.PBUInt64Field:
                    this.writeLn(`${PBType.uint64} ${fieldData.name};`);
    
                    break;
            }
    
    
        }
    }
    

    修改test004.ts添加如下几行

    let gene = new GeneProtoFile(pbStructure);
    
        gene.emitProto();
    

    最后完成批量解析, 以及一些bug修正
    git地址: http://git.oschina.net/gradee/antlr
    https://github.com/linbolen/botmm

    相关文章

      网友评论

          本文标题:使用antlr4, 用ts/js还原protobuf生成的jav

          本文链接:https://www.haomeiwen.com/subject/qbdtottx.html