actions/github/codeql-action-v2/node_modules/twirp-ts/build/protoc-gen-twirp-ts/interpreter.js

436 lines
18 KiB
JavaScript

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RuntimeEnumBuilder = exports.Interpreter = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const rt = __importStar(require("@protobuf-ts/runtime"));
const runtime_1 = require("@protobuf-ts/runtime");
/**
* Code borrowed from @protobuf-js/plugin all the rights of this code goes to the author
*
*
* The protobuf-ts plugin generates code for message types from descriptor
* protos. This class also creates message types from descriptor protos, but
* but instead of generating code, it creates the type in-memory.
*
* This means that it is possible, for example, to read a message from binary
* data without any generated code.
*
* The protobuf-ts plugin uses the interpreter to read custom options at
* compile time and convert them to JSON.
*
* Since the interpreter creates fully functional message types including
* reflection information, the protobuf-ts plugin uses the interpreter as
* single source of truth for generating message interfaces and reflection
* information.
*/
class Interpreter {
constructor(registry) {
this.registry = registry;
this.messageTypes = new Map();
this.enumInfos = new Map();
}
/**
* Returns a map of custom options for the provided descriptor.
* The map is an object indexed by the extension field name.
* The value of the extension field is provided in JSON format.
*
* This works by:
* - searching for option extensions for the given descriptor proto
* in the registry.
* - for example, providing a google.protobuf.FieldDescriptorProto
* searches for all extensions on google.protobuf.FieldOption.
* - extensions are just fields, so we build a synthetic message
* type with all the (extension) fields.
* - the field names are created by DescriptorRegistry.getExtensionName(),
* which produces for example "spec.option_name", where "spec" is
* the package and "option_name" is the field name.
* - then we concatenate all unknown field data of the option and
* read the data with our synthetic message type
* - the read message is then simply converted to JSON
*
* The optional "optionBlacklist" will exclude matching options.
* The blacklist can contain exact extension names, or use the wildcard
* character `*` to match a namespace or even all options.
*
* Note that options on options (google.protobuf.*Options) are not
* supported.
*/
readOptions(descriptor, excludeOptions = []) {
// if options message not present, there cannot be any extension options
if (!descriptor.options) {
return undefined;
}
// if no unknown fields present, can exit early
let unknownFields = rt.UnknownFieldHandler.list(descriptor.options);
if (!unknownFields.length) {
return undefined;
}
let optionsTypeName;
if (plugin_framework_1.FieldDescriptorProto.is(descriptor) &&
plugin_framework_1.DescriptorProto.is(this.registry.parentOf(descriptor))) {
optionsTypeName = "google.protobuf.FieldOptions";
}
else if (plugin_framework_1.MethodDescriptorProto.is(descriptor)) {
optionsTypeName = "google.protobuf.MethodOptions";
}
else if (this.registry.fileOf(descriptor) === descriptor) {
optionsTypeName = "google.protobuf.FileOptions";
}
else if (plugin_framework_1.ServiceDescriptorProto.is(descriptor)) {
optionsTypeName = "google.protobuf.ServiceOptions";
}
else if (plugin_framework_1.DescriptorProto.is(descriptor)) {
optionsTypeName = "google.protobuf.MessageOptions";
}
else {
throw new Error("interpreter expected field or method descriptor");
}
// create a synthetic type that has all extension fields for field options
const typeName = `$synthetic.${optionsTypeName}`;
let type = this.messageTypes.get(typeName);
if (!type) {
type = new rt.MessageType(typeName, this.buildFieldInfos(this.registry.extensionsFor(optionsTypeName), excludeOptions), {});
this.messageTypes.set(typeName, type);
}
// concat all unknown field data
const unknownWriter = new rt.BinaryWriter();
for (let { no, wireType, data } of unknownFields) {
unknownWriter.tag(no, wireType).raw(data);
}
const unknownBytes = unknownWriter.finish();
// read data, to json
const json = type.toJson(type.fromBinary(unknownBytes, { readUnknownField: false }));
runtime_1.assert(rt.isJsonObject(json));
// apply blacklist
if (excludeOptions) {
// we distinguish between literal blacklist (no wildcard)
let literals = excludeOptions.filter((str) => !str.includes("*"));
// and wildcard, which we turn into RE
let wildcards = excludeOptions
.filter((str) => str.includes("*"))
.map((str) => str.replace(/[.+\-?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*"));
// then we delete the blacklisted options
for (let key of Object.keys(json)) {
for (let str of literals)
if (key === str)
delete json[key];
for (let re of wildcards)
if (key.match(re))
delete json[key];
}
}
// were *all* options blacklisted?
if (!Object.keys(json).length) {
return undefined;
}
return json;
}
/**
* Get a runtime type for the given message type name or message descriptor.
* Creates the type if not created previously.
*
* Honors our file option "ts.exclude_options".
*/
getMessageType(descriptorOrTypeName) {
let descriptor = typeof descriptorOrTypeName === "string"
? this.registry.resolveTypeName(descriptorOrTypeName)
: descriptorOrTypeName;
let typeName = this.registry.makeTypeName(descriptor);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(descriptor));
let type = this.messageTypes.get(typeName);
if (!type) {
type = this.buildMessageType(typeName, descriptor.field, []);
this.messageTypes.set(typeName, type);
}
return type;
}
/**
* Get runtime information for an enum.
* Creates the info if not created previously.
*/
getEnumInfo(descriptorOrTypeName) {
var _a;
let descriptor = typeof descriptorOrTypeName === "string"
? this.registry.resolveTypeName(descriptorOrTypeName)
: descriptorOrTypeName;
let typeName = this.registry.makeTypeName(descriptor);
runtime_1.assert(plugin_framework_1.EnumDescriptorProto.is(descriptor));
let enumInfo = (_a = this.enumInfos.get(typeName)) !== null && _a !== void 0 ? _a : this.buildEnumInfo(descriptor);
this.enumInfos.set(typeName, enumInfo);
return enumInfo;
}
/**
* Create a name for a field or a oneof.
* - use lowerCamelCase
* - escape reserved object property names by
* adding '$' at the end
* - don't have to escape reserved keywords
*/
static createTypescriptNameForField(descriptor, additionalReservedWords = "", escapeCharacter = "$") {
const reservedObjectProperties = "__proto__,toString".split(",");
let name = descriptor.name;
runtime_1.assert(name !== undefined);
name = rt.lowerCamelCase(name);
if (reservedObjectProperties.includes(name)) {
name = name + escapeCharacter;
}
if (additionalReservedWords.split(",").includes(name)) {
name = name + escapeCharacter;
}
return name;
}
buildMessageType(typeName, fields, excludeOptions) {
let desc = this.registry.resolveTypeName(typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(desc));
return new rt.MessageType(typeName, this.buildFieldInfos(fields, excludeOptions), this.readOptions(desc, excludeOptions));
}
// skips GROUP field type
buildFieldInfos(fieldDescriptors, excludeOptions) {
const result = [];
for (const fd of fieldDescriptors) {
if (this.registry.isGroupField(fd)) {
// We ignore groups.
// Note that groups are deprecated and not supported in proto3.
continue;
}
const fi = this.buildFieldInfo(fd, excludeOptions);
if (fi) {
result.push(fi);
}
}
return result;
}
// throws on unexpected field types, notably GROUP
buildFieldInfo(fieldDescriptor, excludeOptions) {
var _a, _b;
runtime_1.assert(fieldDescriptor.number);
runtime_1.assert(fieldDescriptor.name);
let info = {};
// no: The field number of the .proto field.
info.no = fieldDescriptor.number;
// name: The original name of the .proto field.
info.name = fieldDescriptor.name;
// kind: discriminator
info.kind = undefined;
// localName: The name of the field in the runtime.
let localName = Interpreter.createTypescriptNameForField(fieldDescriptor);
if (localName !== rt.lowerCamelCase(fieldDescriptor.name)) {
info.localName = localName;
}
// jsonName: The name of the field in JSON.
const jsonName = this.registry.getFieldCustomJsonName(fieldDescriptor);
if (jsonName !== undefined) {
info.jsonName = jsonName;
}
// oneof: The name of the `oneof` group, if this field belongs to one.
if (this.registry.isUserDeclaredOneof(fieldDescriptor)) {
runtime_1.assert(fieldDescriptor.oneofIndex !== undefined);
const parentDescriptor = this.registry.parentOf(fieldDescriptor);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(parentDescriptor));
const ooDecl = parentDescriptor.oneofDecl[fieldDescriptor.oneofIndex];
info.oneof = Interpreter.createTypescriptNameForField(ooDecl);
}
// repeat: Is the field repeated?
if (this.registry.isUserDeclaredRepeated(fieldDescriptor)) {
let packed = this.registry.shouldBePackedRepeated(fieldDescriptor);
info.repeat = packed ? rt.RepeatType.PACKED : rt.RepeatType.UNPACKED;
}
// opt: Is the field optional?
if (this.registry.isScalarField(fieldDescriptor) ||
this.registry.isEnumField(fieldDescriptor)) {
if (this.registry.isUserDeclaredOptional(fieldDescriptor)) {
info.opt = true;
}
}
// jsonName: The name for JSON serialization / deserialization.
if (fieldDescriptor.jsonName) {
info.jsonName = fieldDescriptor.jsonName;
}
if (this.registry.isScalarField(fieldDescriptor)) {
// kind:
info.kind = "scalar";
// T: Scalar field type.
info.T = this.registry.getScalarFieldType(fieldDescriptor);
// L?: JavaScript long type
let L = this.determineNonDefaultLongType(info.T, (_a = fieldDescriptor.options) === null || _a === void 0 ? void 0 : _a.jstype);
if (L !== undefined) {
info.L = L;
}
}
else if (this.registry.isEnumField(fieldDescriptor)) {
// kind:
info.kind = "enum";
// T: Return enum field type info.
info.T = () => this.getEnumInfo(this.registry.getEnumFieldEnum(fieldDescriptor));
}
else if (this.registry.isMessageField(fieldDescriptor)) {
// kind:
info.kind = "message";
// T: Return message field type handler.
info.T = () => this.getMessageType(this.registry.getMessageFieldMessage(fieldDescriptor));
}
else if (this.registry.isMapField(fieldDescriptor)) {
// kind:
info.kind = "map";
// K: Map field key type.
info.K = this.registry.getMapKeyType(fieldDescriptor);
// V: Map field value type.
info.V = {};
let mapV = this.registry.getMapValueType(fieldDescriptor);
if (typeof mapV === "number") {
info.V = {
kind: "scalar",
T: mapV,
};
let L = this.determineNonDefaultLongType(info.V.T, (_b = fieldDescriptor.options) === null || _b === void 0 ? void 0 : _b.jstype);
if (L !== undefined) {
info.V.L = L;
}
}
else if (plugin_framework_1.DescriptorProto.is(mapV)) {
const messageDescriptor = mapV;
info.V = {
kind: "message",
T: () => this.getMessageType(messageDescriptor),
};
}
else {
const enumDescriptor = mapV;
info.V = {
kind: "enum",
T: () => this.getEnumInfo(enumDescriptor),
};
}
}
else {
throw new Error(`Unexpected field type for ${this.registry.formatQualifiedName(fieldDescriptor)}`);
}
// extension fields are treated differently
if (this.registry.isExtension(fieldDescriptor)) {
let extensionName = this.registry.getExtensionName(fieldDescriptor);
// always optional (unless repeated...)
info.opt = info.repeat === undefined || info.repeat === rt.RepeatType.NO;
info.name = extensionName;
info.localName = extensionName;
info.jsonName = extensionName;
info.oneof = undefined;
}
else {
info.options = this.readOptions(fieldDescriptor, excludeOptions);
}
return info;
}
buildEnumInfo(descriptor) {
let sharedPrefix = this.registry.findEnumSharedPrefix(descriptor, `${descriptor.name}`);
let hasZero = descriptor.value.some((v) => v.number === 0);
let builder = new RuntimeEnumBuilder();
if (!hasZero) {
throw new Error("must provide zero value for enum " + descriptor.name);
}
for (let enumValueDescriptor of descriptor.value) {
let name = enumValueDescriptor.name;
runtime_1.assert(name !== undefined);
runtime_1.assert(enumValueDescriptor.number !== undefined);
if (sharedPrefix) {
name = name.substring(sharedPrefix.length);
}
builder.add(name, enumValueDescriptor.number);
}
let enumInfo = [
this.registry.makeTypeName(descriptor),
builder.build(),
];
if (sharedPrefix) {
enumInfo = [enumInfo[0], enumInfo[1], sharedPrefix];
}
return enumInfo;
}
determineNonDefaultLongType(scalarType, jsTypeOption) {
if (!Interpreter.isLongValueType(scalarType)) {
return undefined;
}
if (jsTypeOption !== undefined) {
switch (jsTypeOption) {
case plugin_framework_1.FieldOptions_JSType.JS_STRING:
// omitting L equals to STRING
return undefined;
case plugin_framework_1.FieldOptions_JSType.JS_NORMAL:
return rt.LongType.BIGINT;
case plugin_framework_1.FieldOptions_JSType.JS_NUMBER:
return rt.LongType.NUMBER;
}
}
return undefined;
}
/**
* Is this a 64 bit integral or fixed type?
*/
static isLongValueType(type) {
switch (type) {
case rt.ScalarType.INT64:
case rt.ScalarType.UINT64:
case rt.ScalarType.FIXED64:
case rt.ScalarType.SFIXED64:
case rt.ScalarType.SINT64:
return true;
default:
return false;
}
}
}
exports.Interpreter = Interpreter;
/**
* Builds a typescript enum lookup object,
* compatible with enums generated by @protobuf-ts/plugin.
*/
class RuntimeEnumBuilder {
constructor() {
this.values = [];
}
add(name, number) {
this.values.push({ name, number });
}
isValid() {
try {
this.build();
}
catch (e) {
return false;
}
return true;
}
build() {
if (this.values.map((v) => v.name).some((name, i, a) => a.indexOf(name) !== i)) {
throw new Error("duplicate names");
}
let object = {};
for (let v of this.values) {
object[v.number] = v.name;
object[v.name] = v.number;
}
if (rt.isEnumObject(object)) {
return object;
}
throw new Error("not a typescript enum object");
}
}
exports.RuntimeEnumBuilder = RuntimeEnumBuilder;