mirror of
https://github.com/github/codeql-action.git
synced 2025-12-27 01:30:10 +08:00
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com> Co-authored-by: Henry Mercer <henrymercer@github.com>
180 lines
7.7 KiB
JavaScript
180 lines
7.7 KiB
JavaScript
import { UnknownFieldHandler, WireType } from "./binary-format-contract";
|
|
import { LongType, ScalarType } from "./reflection-info";
|
|
import { reflectionLongConvert } from "./reflection-long-convert";
|
|
import { reflectionScalarDefault } from "./reflection-scalar-default";
|
|
/**
|
|
* Reads proto3 messages in binary format using reflection information.
|
|
*
|
|
* https://developers.google.com/protocol-buffers/docs/encoding
|
|
*/
|
|
export class ReflectionBinaryReader {
|
|
constructor(info) {
|
|
this.info = info;
|
|
}
|
|
prepare() {
|
|
var _a;
|
|
if (!this.fieldNoToField) {
|
|
const fieldsInput = (_a = this.info.fields) !== null && _a !== void 0 ? _a : [];
|
|
this.fieldNoToField = new Map(fieldsInput.map(field => [field.no, field]));
|
|
}
|
|
}
|
|
/**
|
|
* Reads a message from binary format into the target message.
|
|
*
|
|
* Repeated fields are appended. Map entries are added, overwriting
|
|
* existing keys.
|
|
*
|
|
* If a message field is already present, it will be merged with the
|
|
* new data.
|
|
*/
|
|
read(reader, message, options, length) {
|
|
this.prepare();
|
|
const end = length === undefined ? reader.len : reader.pos + length;
|
|
while (reader.pos < end) {
|
|
// read the tag and find the field
|
|
const [fieldNo, wireType] = reader.tag(), field = this.fieldNoToField.get(fieldNo);
|
|
if (!field) {
|
|
let u = options.readUnknownField;
|
|
if (u == "throw")
|
|
throw new Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.info.typeName}`);
|
|
let d = reader.skip(wireType);
|
|
if (u !== false)
|
|
(u === true ? UnknownFieldHandler.onRead : u)(this.info.typeName, message, fieldNo, wireType, d);
|
|
continue;
|
|
}
|
|
// target object for the field we are reading
|
|
let target = message, repeated = field.repeat, localName = field.localName;
|
|
// if field is member of oneof ADT, use ADT as target
|
|
if (field.oneof) {
|
|
target = target[field.oneof];
|
|
// if other oneof member selected, set new ADT
|
|
if (target.oneofKind !== localName)
|
|
target = message[field.oneof] = {
|
|
oneofKind: localName
|
|
};
|
|
}
|
|
// we have handled oneof above, we just have read the value into `target[localName]`
|
|
switch (field.kind) {
|
|
case "scalar":
|
|
case "enum":
|
|
let T = field.kind == "enum" ? ScalarType.INT32 : field.T;
|
|
let L = field.kind == "scalar" ? field.L : undefined;
|
|
if (repeated) {
|
|
let arr = target[localName]; // safe to assume presence of array, oneof cannot contain repeated values
|
|
if (wireType == WireType.LengthDelimited && T != ScalarType.STRING && T != ScalarType.BYTES) {
|
|
let e = reader.uint32() + reader.pos;
|
|
while (reader.pos < e)
|
|
arr.push(this.scalar(reader, T, L));
|
|
}
|
|
else
|
|
arr.push(this.scalar(reader, T, L));
|
|
}
|
|
else
|
|
target[localName] = this.scalar(reader, T, L);
|
|
break;
|
|
case "message":
|
|
if (repeated) {
|
|
let arr = target[localName]; // safe to assume presence of array, oneof cannot contain repeated values
|
|
let msg = field.T().internalBinaryRead(reader, reader.uint32(), options);
|
|
arr.push(msg);
|
|
}
|
|
else
|
|
target[localName] = field.T().internalBinaryRead(reader, reader.uint32(), options, target[localName]);
|
|
break;
|
|
case "map":
|
|
let [mapKey, mapVal] = this.mapEntry(field, reader, options);
|
|
// safe to assume presence of map object, oneof cannot contain repeated values
|
|
target[localName][mapKey] = mapVal;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Read a map field, expecting key field = 1, value field = 2
|
|
*/
|
|
mapEntry(field, reader, options) {
|
|
let length = reader.uint32();
|
|
let end = reader.pos + length;
|
|
let key = undefined; // javascript only allows number or string for object properties
|
|
let val = undefined;
|
|
while (reader.pos < end) {
|
|
let [fieldNo, wireType] = reader.tag();
|
|
switch (fieldNo) {
|
|
case 1:
|
|
if (field.K == ScalarType.BOOL)
|
|
key = reader.bool().toString();
|
|
else
|
|
// long types are read as string, number types are okay as number
|
|
key = this.scalar(reader, field.K, LongType.STRING);
|
|
break;
|
|
case 2:
|
|
switch (field.V.kind) {
|
|
case "scalar":
|
|
val = this.scalar(reader, field.V.T, field.V.L);
|
|
break;
|
|
case "enum":
|
|
val = reader.int32();
|
|
break;
|
|
case "message":
|
|
val = field.V.T().internalBinaryRead(reader, reader.uint32(), options);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Unknown field ${fieldNo} (wire type ${wireType}) in map entry for ${this.info.typeName}#${field.name}`);
|
|
}
|
|
}
|
|
if (key === undefined) {
|
|
let keyRaw = reflectionScalarDefault(field.K);
|
|
key = field.K == ScalarType.BOOL ? keyRaw.toString() : keyRaw;
|
|
}
|
|
if (val === undefined)
|
|
switch (field.V.kind) {
|
|
case "scalar":
|
|
val = reflectionScalarDefault(field.V.T, field.V.L);
|
|
break;
|
|
case "enum":
|
|
val = 0;
|
|
break;
|
|
case "message":
|
|
val = field.V.T().create();
|
|
break;
|
|
}
|
|
return [key, val];
|
|
}
|
|
scalar(reader, type, longType) {
|
|
switch (type) {
|
|
case ScalarType.INT32:
|
|
return reader.int32();
|
|
case ScalarType.STRING:
|
|
return reader.string();
|
|
case ScalarType.BOOL:
|
|
return reader.bool();
|
|
case ScalarType.DOUBLE:
|
|
return reader.double();
|
|
case ScalarType.FLOAT:
|
|
return reader.float();
|
|
case ScalarType.INT64:
|
|
return reflectionLongConvert(reader.int64(), longType);
|
|
case ScalarType.UINT64:
|
|
return reflectionLongConvert(reader.uint64(), longType);
|
|
case ScalarType.FIXED64:
|
|
return reflectionLongConvert(reader.fixed64(), longType);
|
|
case ScalarType.FIXED32:
|
|
return reader.fixed32();
|
|
case ScalarType.BYTES:
|
|
return reader.bytes();
|
|
case ScalarType.UINT32:
|
|
return reader.uint32();
|
|
case ScalarType.SFIXED32:
|
|
return reader.sfixed32();
|
|
case ScalarType.SFIXED64:
|
|
return reflectionLongConvert(reader.sfixed64(), longType);
|
|
case ScalarType.SINT32:
|
|
return reader.sint32();
|
|
case ScalarType.SINT64:
|
|
return reflectionLongConvert(reader.sint64(), longType);
|
|
}
|
|
}
|
|
}
|