目录:
- 使用antlr4, 用ts/js还原protobuf生成的java代码为pb (一)
- 使用antlr4, 用ts/js还原protobuf生成的java代码为pb (二)
- 使用antlr4, 用ts/js还原protobuf生成的java代码为pb (三)
- 使用antlr4, 用ts/js还原protobuf生成的java代码为pb (四)
- 使用antlr4, 用ts/js还原protobuf生成的java代码为pb (五)
仔细分析java代码
package msf.msgsvc;
import com.xxx.pb.MessageMicro;
import com.xxx.pb.PBField;
import com.xxx.pb.PBUInt32Field;
import com.xxx.pb.PBUInt64Field;
public final class msg_svc$PbDeleteMsgReq$MsgItem
extends MessageMicro {
static final MessageMicro.FieldMap __fieldMap__ =
MessageMicro.initFieldMap(
(int[])new int[]{8, 16, 24, 32, 40},
(String[])new String[]{"from_uin", "to_uin", "msg_type", "msg_seq", "msg_uid"},
(Object[])new Object[]{0, 0, 0, 0, 0}, msg_svc$PbDeleteMsgReq$MsgItem.class
);
public final PBUInt64Field from_uin = PBField.initUInt64((long)0);
public final PBUInt32Field msg_seq = PBField.initUInt32((int)0);
public final PBUInt32Field msg_type = PBField.initUInt32((int)0);
public final PBUInt64Field msg_uid = PBField.initUInt64((long)0);
public final PBUInt64Field to_uin = PBField.initUInt64((long)0);
}
序号/PB字段类型(tags) | 字段名称(fields) | defaultValues |
---|---|---|
1/0->8 | from_uin | 0 |
2/0->16 | to_uin | 0 |
3/0->24 | msg_type | 0 |
4/0->32 | msg_seq | 0 |
5/0->40 | msg_uid | 0 |
序号/PB字段类型, 这里序号命名为A, 字段类型为B. 序列化公式为 A<<3|B
.
google protobuf类型参考
Type Meaning Used For 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum 1 64-bit fixed64, sfixed64, double 2 Length-delimited string, bytes, embedded messages, packed repeated fields 3 Start group groups (deprecated) 4 End group groups (deprecated) 5 32-bit fixed32, sfixed32, float
好了, 直接贴代码吧, 随便写的草稿. 别跟着"学坏了"
import {JavaListener} from "../antlr/Java/JavaListener";
import {ParseTreeListener} from "antlr4ts/tree";
import {ArrayInitializerContext, ClassBodyDeclarationContext, JavaParser} from "../antlr/Java/JavaParser";
export class CustomJavaListener implements JavaListener {
public state;
public enterClassBodyDeclaration(ctx: ClassBodyDeclarationContext) {
let flags = [], fields = [], defaultValues = [];
let modifer = ctx.modifier().filter(modifier => {
return modifier.text == 'static';
});
if (modifer.length == 0) return;
let memberDeclaration = ctx.memberDeclaration();
if (!memberDeclaration) return;
let fieldClaration = memberDeclaration.fieldDeclaration();
if (!fieldClaration) return;
let type = fieldClaration.typeType().classOrInterfaceType();
if (type.Identifier().pop().text != 'FieldMap')return;
let variable = fieldClaration.variableDeclarators().variableDeclarator(0);
if (variable.variableDeclaratorId().text != '__fieldMap__'
) {
return;
}
let func = variable.variableInitializer().expression();
if (func.expression(0).expression(0).primary().text != 'MessageMicro' &&
func.expression(0).children[2].text != 'initFieldMap') {
throw new Error('should be `MessageMicro.initFieldMap`');
}
let expressList = func.expressionList();
flags = this.getArray(expressList.expression(0), 'int');
fields = this.getArray(expressList.expression(1), 'String');
defaultValues = this.getArray(expressList.expression(2), 'Object');
console.log(flags, fields, defaultValues);
}
getArray(express2, type) {
let rtn = [];
if (express2.typeType().text != `${type}[]`) {
throw new Error(`express 0 should be ${type}[]`)
} else if (express2.expression(0).children[0].text != 'new' &&
express2.expression(0).children[1].getChild(0).text != `${type}`
) {
throw new Error('express 0 should be new string ')
} else {
let arrayCreatorRest = express2.expression(0).children[1].getChild(1);
let arrayInitializer: ArrayInitializerContext = arrayCreatorRest.getChild(arrayCreatorRest.childCount - 1);
arrayInitializer.variableInitializer().map(variable => {
let v;
if (type.toLowerCase() == 'int') {
v = parseInt(variable.text);
} else if (type.toLowerCase() == 'string') {
v = variable.text.replace(/'(.+?)'|"(.+?)"/g, "$1$2");
} else {
v = variable.text;
}
rtn.push(v);
})
}
return rtn;
}
}
最后再写一点校验代码, 以及最终生成protobuf的代码吧, 我相信对树的遍历大家应该都会写吧
上面是对这个特定的树进行的粗略遍历, 有时树会多出一个父节点, 那么用固定的 A->children[0]->children[0]肯定是不行的. 比如getArray方法里用了express2.expression(0).children[0]
有时这个节点根本是不存在的, 不可能为每个文件写个处理, 所以我们还是按标准方式来做吧.
现在我再来用visitor来完善树的遍历. visitor里可以实现针对规则或是打了标签的grammar的接口.( 默认的java.g4 grammar是没有打标签的, 我们修改一下将可以很快完成我们的工作. 在后面章节再做说明)