UNPKG

@matatbread/typia

Version:

Superfast runtime validators with only one line

336 lines (333 loc) 17.6 kB
import ts from 'typescript'; import { ExpressionFactory } from '../../factories/ExpressionFactory.mjs'; import { IdentifierFactory } from '../../factories/IdentifierFactory.mjs'; import { MetadataCollection } from '../../factories/MetadataCollection.mjs'; import { MetadataFactory } from '../../factories/MetadataFactory.mjs'; import { ProtobufFactory } from '../../factories/ProtobufFactory.mjs'; import { StatementFactory } from '../../factories/StatementFactory.mjs'; import { TypeFactory } from '../../factories/TypeFactory.mjs'; import { MetadataObjectType } from '../../schemas/metadata/MetadataObjectType.mjs'; import { MetadataProperty } from '../../schemas/metadata/MetadataProperty.mjs'; import { FeatureProgrammer } from '../FeatureProgrammer.mjs'; import { FunctionProgrammer } from '../helpers/FunctionProgrammer.mjs'; import { ProtobufUtil } from '../helpers/ProtobufUtil.mjs'; var ProtobufDecodeProgrammer; (function (ProtobufDecodeProgrammer) { ProtobufDecodeProgrammer.decompose = (props) => { const collection = new MetadataCollection(); const meta = ProtobufFactory.metadata({ method: props.modulo.getText(), checker: props.context.checker, transformer: props.context.transformer, collection, type: props.type, }); return { functions: Object.fromEntries(collection .objects() .filter((object) => ProtobufUtil.isStaticObject(object)) .map((object) => [ `${PREFIX}o${object.index}`, StatementFactory.constant({ name: props.functor.useLocal(`${PREFIX}o${object.index}`), value: write_object_function({ context: props.context, functor: props.functor, object, }), }), ])), statements: [], arrow: ts.factory.createArrowFunction(undefined, undefined, [ IdentifierFactory.parameter("input", ts.factory.createTypeReferenceNode("Uint8Array")), ], props.context.importer.type({ file: "typia", name: "Resolved", arguments: [ ts.factory.createTypeReferenceNode(props.name ?? TypeFactory.getFullName({ checker: props.context.checker, type: props.type, })), ], }), undefined, ts.factory.createBlock([ StatementFactory.constant({ name: "reader", value: ts.factory.createNewExpression(props.context.importer.internal("ProtobufReader"), undefined, [ts.factory.createIdentifier("input")]), }), ts.factory.createReturnStatement(decode_regular_object({ top: true, object: meta.objects[0].type, })), ], true)), }; }; ProtobufDecodeProgrammer.write = (props) => { const functor = new FunctionProgrammer(props.modulo.getText()); const result = ProtobufDecodeProgrammer.decompose({ ...props, functor, }); return FeatureProgrammer.writeDecomposed({ modulo: props.modulo, functor, result, }); }; const write_object_function = (props) => ts.factory.createArrowFunction(undefined, undefined, [ IdentifierFactory.parameter("reader"), IdentifierFactory.parameter("length", TypeFactory.keyword("number"), ExpressionFactory.number(-1)), ], TypeFactory.keyword("any"), undefined, ts.factory.createBlock([ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createIdentifier("length"), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createConditionalExpression(ts.factory.createLessThan(ts.factory.createIdentifier("length"), ExpressionFactory.number(0)), undefined, callReader("size"), undefined, ts.factory.createAdd(callReader("index"), ts.factory.createIdentifier("length"))))), ...write_object_function_body({ context: props.context, condition: ts.factory.createLessThan(callReader("index"), ts.factory.createIdentifier("length")), tag: "tag", output: "output", object: props.object, }), ts.factory.createReturnStatement(ts.factory.createIdentifier("output")), ], true)); const write_object_function_body = (props) => { if (props.object.properties.some((p) => p.of_protobuf_ === undefined)) ProtobufFactory.emplaceObject(props.object); const clauses = props.object.properties .map((p) => decode_property({ context: props.context, accessor: IdentifierFactory.access(ts.factory.createIdentifier(props.output), p.key.getSoleLiteral()), protobuf: p.of_protobuf_, metadata: p.value, })) .flat(); return [ StatementFactory.constant({ name: props.output, value: ts.factory.createAsExpression(ts.factory.createObjectLiteralExpression(props.object.properties .filter((p) => !(props.context.compilerOptions.exactOptionalPropertyTypes === true && p.value.optional === true)) .map((p) => ts.factory.createPropertyAssignment(IdentifierFactory.identifier(p.key.getSoleLiteral()), write_property_default_value(p.value))), true), TypeFactory.keyword("any")), }), ts.factory.createWhileStatement(props.condition, ts.factory.createBlock([ StatementFactory.constant({ name: props.tag, value: callReader("uint32"), }), ts.factory.createSwitchStatement(ts.factory.createUnsignedRightShift(ts.factory.createIdentifier(props.tag), ExpressionFactory.number(3)), ts.factory.createCaseBlock([ ...clauses, ts.factory.createDefaultClause([ ts.factory.createExpressionStatement(callReader("skipType", [ ts.factory.createBitwiseAnd(ts.factory.createIdentifier(props.tag), ExpressionFactory.number(7)), ])), ts.factory.createBreakStatement(), ]), ])), ])), ]; }; const write_property_default_value = (value) => ts.factory.createAsExpression(value.nullable ? ts.factory.createNull() : value.isRequired() === false ? ts.factory.createIdentifier("undefined") : value.arrays.length ? ts.factory.createArrayLiteralExpression() : value.maps.length ? ts.factory.createNewExpression(ts.factory.createIdentifier("Map"), undefined, []) : value.natives.length ? ts.factory.createNewExpression(ts.factory.createIdentifier("Uint8Array"), undefined, [ts.factory.createArrayLiteralExpression([])]) : value.atomics.some((a) => a.type === "string") || value.constants.some((c) => c.type === "string" && c.values.some((v) => v.value === "")) || value.templates.some((tpl) => tpl.row.length === 1 && tpl.row[0].getName() === "string") ? ts.factory.createStringLiteral("") : value.objects.length && value.objects.some((obj) => !ProtobufUtil.isStaticObject(obj.type)) ? ts.factory.createObjectLiteralExpression() : ts.factory.createIdentifier("undefined"), TypeFactory.keyword("any")); /* ----------------------------------------------------------- DECODERS ----------------------------------------------------------- */ const decode_property = (props) => props.protobuf.union.map((schema) => decode_property_type({ context: props.context, accessor: props.accessor, schema, required: props.metadata.isRequired() && props.metadata.nullable === false, })); const decode_property_type = (props) => { const out = (title, value) => ts.factory.createCaseClause(ExpressionFactory.number(props.schema.index), [ ts.factory.createExpressionStatement(ts.factory.createIdentifier(`// ${title}`)), ...(Array.isArray(value) ? value : [ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(props.accessor, ts.factory.createToken(ts.SyntaxKind.EqualsToken), value)), ]), ts.factory.createBreakStatement(), ]); // ATOMICS if (props.schema.type === "bytes") return out("bytes", callReader("bytes")); else if (props.schema.type === "bool") return out("bool", callReader("bool")); else if (props.schema.type === "bigint") return out(props.schema.name, callReader(props.schema.name)); else if (props.schema.type === "number") return out(props.schema.name, decode_number(props.schema)); else if (props.schema.type === "string") return out("string", callReader("string")); // INSTANCES else if (props.schema.type === "array") return out(`Array<${props.schema.array.value.getName()}>`, decode_array({ accessor: props.accessor, schema: props.schema, required: props.required, })); else if (props.schema.type === "object") return out(props.schema.object.name, decode_regular_object({ top: false, object: props.schema.object, })); else if (props.schema.type === "map") if (props.schema.map instanceof MetadataObjectType) { const key = props.schema.map.properties[0].key; const value = props.schema.map.properties[0].value; return out(`Record<${key.getName()}, ${value.getName()}>`, decode_map({ context: props.context, accessor: props.accessor, schema: props.schema, required: props.required, key, value, initializer: ts.factory.createObjectLiteralExpression([]), setter: () => ts.factory.createBinaryExpression(ts.factory.createElementAccessExpression(props.accessor, ts.factory.createIdentifier("entry.key")), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier("entry.value")), })); } else { const key = props.schema.map.key; const value = props.schema.map.value; return out(`Map<${key.getName()}, ${value.getName()}>`, decode_map({ context: props.context, accessor: props.accessor, schema: props.schema, required: props.required, key, value, initializer: ts.factory.createNewExpression(ts.factory.createIdentifier("Map"), [TypeFactory.keyword("any"), TypeFactory.keyword("any")], []), setter: () => ts.factory.createCallExpression(IdentifierFactory.access(props.accessor, "set"), undefined, [ ts.factory.createIdentifier("entry.key"), ts.factory.createIdentifier("entry.value"), ]), })); } throw new Error("Error on ProtobufDecodeProgrammer.write(): unknown property type"); }; const decode_number = (schema) => { const value = callReader(schema.name); return schema.name === "int64" || schema.name === "uint64" ? ts.factory.createCallExpression(ts.factory.createIdentifier("Number"), undefined, [value]) : value; }; const decode_array = (props) => { const statements = []; if (props.required === false) statements.push(ts.factory.createBinaryExpression(props.accessor, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), ts.factory.createAsExpression(ts.factory.createArrayLiteralExpression(), ts.factory.createTypeReferenceNode("any[]")))); const decoder = decode_array_value(props.schema.value); if (["bool", "bigint", "number"].includes(props.schema.value.type)) { statements.push(ts.factory.createIfStatement(ts.factory.createStrictEquality(ExpressionFactory.number(2), ts.factory.createBitwiseAnd(ts.factory.createIdentifier("tag"), ExpressionFactory.number(7))), ts.factory.createBlock([ StatementFactory.constant({ name: "piece", value: ts.factory.createAdd(callReader("uint32"), callReader("index")), }), ts.factory.createWhileStatement(ts.factory.createLessThan(callReader("index"), ts.factory.createIdentifier("piece")), ts.factory.createExpressionStatement(ts.factory.createCallExpression(IdentifierFactory.access(props.accessor, "push"), undefined, [decoder]))), ], true), ts.factory.createExpressionStatement(ts.factory.createCallExpression(IdentifierFactory.access(props.accessor, "push"), undefined, [decoder])))); } else statements.push(ts.factory.createCallExpression(IdentifierFactory.access(props.accessor, "push"), undefined, [decoder])); return statements.map((stmt) => ts.isExpression(stmt) ? ts.factory.createExpressionStatement(stmt) : stmt); }; const decode_regular_object = (props) => ts.factory.createCallExpression(ts.factory.createIdentifier(`${PREFIX}o${props.object.index}`), undefined, [ ts.factory.createIdentifier("reader"), ...(props.top ? [] : [callReader("uint32")]), ]); const decode_map = (props) => { const statements = [ ...(props.required === true ? [ ts.factory.createBinaryExpression(props.accessor, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), props.initializer), ] : []), StatementFactory.constant({ name: "piece", value: ts.factory.createAdd(callReader("uint32"), callReader("index")), }), ...write_object_function_body({ context: props.context, condition: ts.factory.createLessThan(callReader("index"), ts.factory.createIdentifier("piece")), tag: "kind", output: "entry", object: createObjectType([ { key: "key", value: props.key, }, { key: "value", value: props.value, }, ]), }), ...(props.required === false ? [ ts.factory.createBinaryExpression(props.accessor, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), props.initializer), ] : []), props.setter(), ]; return [ ts.factory.createExpressionStatement(ExpressionFactory.selfCall(ts.factory.createBlock(statements.map((stmt) => ts.isExpression(stmt) ? ts.factory.createExpressionStatement(stmt) : stmt), true))), ]; }; const decode_array_value = (schema) => { if (schema.type === "bytes") return callReader("bytes"); else if (schema.type === "bool") return callReader("bool"); else if (schema.type === "bigint") return callReader(schema.name); else if (schema.type === "number") return decode_number(schema); else if (schema.type === "string") return callReader("string"); else if (schema.type === "object") return decode_regular_object({ top: false, object: schema.object, }); throw new Error("unreachable condition"); }; })(ProtobufDecodeProgrammer || (ProtobufDecodeProgrammer = {})); const PREFIX = "_pd"; const callReader = (method, args) => ts.factory.createCallExpression(IdentifierFactory.access(ts.factory.createIdentifier("reader"), method), undefined, args); const createObjectType = (definitions) => { const properties = definitions.map((def) => MetadataProperty.create({ key: MetadataFactory.soleLiteral(def.key), value: def.value, description: null, jsDocTags: [], })); const obj = MetadataObjectType.create({ name: "object.o" + Math.random().toString().slice(2), properties, description: undefined, jsDocTags: [], index: -1, validated: true, recursive: false, nullables: [false], }); ProtobufFactory.emplaceObject(obj); return obj; }; export { ProtobufDecodeProgrammer }; //# sourceMappingURL=ProtobufDecodeProgrammer.mjs.map