UNPKG

@lcap/nasl-parser

Version:

Take Nasl text to Nasl AST with the help of generalized parsing.

555 lines (481 loc) 18 kB
import * as nasl from '@lcap/nasl'; import { isLogicDeclaration, isIdentifier, isCallLogic, isLogic, isAnonymousFunction, isParam, isVariable, isCallQueryComponent, isTypeAnnotation, isStructure, isEntity, isMemberExpression } from '@lcap/nasl-concepts/asserts'; import { CstIdentifier, CstIntEnumRef, UsingNamespace } from './decorated-nasl-ast'; import { head, last, isEmpty, projectIdentifierAST, NASL_CORE_NL, wellDefined, SimpleSillyDeepClone, NAMESPACE_SEP_AST, NASL_UTIL_AST, projectIdentifier, APP_LOGICS_AST, removeDup, APP_STRUCTURE_AST, APP_ENUM, prependNamespace, createQIdentifier, splitByPredicate, NAMESPACE_SEP, APP_ENTITY_AST, extractIdent, NASL_LOGGING_AST, NASL_COLLECTION_AST, } from './common-util'; import { builtinFunctionNames } from './builtin-namespace'; import { addConstructor, addEnumCons, addRefTypeSemInfo, addResolvedNode, CompilerEnvInfo, NodeSemanticInfo } from './resolve-global-bindings'; // 本质上可以合并为一个函数,但是 NASL 上的节点类型不同,所以分开处理了 /* 【P0】调用方式 1:直接带 namespace, nasl::util::方法名(...) 内置函数,硬编码 [O] 内置函数方法名(...) 内置函数,硬编码 app::logics::方法名(...) 逻辑调用,硬编码 [O] My::Namespace::方法名(...) 逻辑调用 [O] 方法名(...) 逻辑调用,无 namespace 默认 apis::应用id::interfaces::方法名(...) 接口调用 connector::连接器种类::连接器名(操作, 数据, ...) 连接器 【P1】调用方式 2:打开 namespace 后直接调用 [O] using nasl.util [O] using app.logics using apis.应用id.interfaces using connector.连接器种类 特殊调用 CallQueryComponent(...) //数据查询 OqlQueryComponent(dataSource, code) // Oql SQL 查询 JsonSerialize() JsonDeserialize() */ export function resolveCallee(cei: CompilerEnvInfo, callee: nasl.CallLogic): nasl.CallLogic | nasl.CallFunction | nasl.NewComposite | nasl.NewStructure { if (!isCallLogic(callee)) { return callee; } const _qName = callee.qName; // 优先找 constructor,在下一个 pass 翻译到 new composite { let test = false; if (lookUpResolvedNodes(cei, { namespace: _qName.namespace, name:`${_qName.name}.constructor` }).length === 1) { test = true; } if (!test) { createHardCodedRefTypeOnUse(cei, _qName); test = lookUpResolvedNodes(cei, { namespace: _qName.namespace, name:`${_qName.name}.constructor` }).length === 1; } if (test) { callee.callKind = 'Constructor'; callee.name = _qName.name; return ctorToNewExpr(cei, callee); } } { // builtin functions 内置函数 if (builtinFunctionNames.includes(_qName.name)) { callee.callKind = 'Function'; callee.calleeNamespace = NASL_UTIL_AST; return toNaslCallFunction(callee); } } { if (_qName.name === 'jsonSerialize') { callee.calleeName = 'jsonSerialize'; callee.calleeNamespace = NASL_UTIL_AST; callee.label = 'JSON 序列化'; return callee; } } { if (_qName.name === 'jsonDeserialize') { callee.calleeName = 'jsonDeserialize'; callee.calleeNamespace = NASL_UTIL_AST; callee.label = 'JSON 反序列化'; return callee; } } // { if (['INFO', 'ERROR', 'WARN', 'DEBUG'].includes (_qName.name)) { callee.calleeName = _qName.name; callee.calleeNamespace = NASL_LOGGING_AST; callee.label = '输出日志'; return callee; } } // general cases // @ts-ignore const defs = lookUpResolvedNodes(cei, callee.qName); if (defs.length === 1) { const nd = head(defs); callee.calleeNamespace = APP_LOGICS_AST; // callee.name = nd.name; 好像不需要 // const info = cei.semInfo.get(nd); return callee; } else if (defs.length > 1) { let msg = `Ambiguous name: ${projectIdentifier(_qName)}, it could be:\n` + defs.map(n => (n as nasl.Identifier).name).join('\n'); throw new Error(msg); } else { const msg = `In resolveCallee, Unresolved identifier: ${projectIdentifier(_qName)}`; throw new Error(msg); } } export function resolveQEnumId(cei: CompilerEnvInfo, id: nasl.Identifier | CstIntEnumRef): nasl.Identifier | nasl.MemberExpression | CstIntEnumRef { if (!(isIdentifier(id) && id?.qName) && !(id instanceof CstIntEnumRef)) { // Identifier 变为 MemberExpression 后,子节点有个 Identifier,不需要再设置 qName 了,所以没有 qName return id; } // hard-coded and non-hard-coded int enum app::enums::EnumName::123 if (id instanceof CstIntEnumRef) { return new nasl.MemberExpression({ object: new nasl.Identifier({ namespace: APP_ENUM, name: id.enumTypeName, }), property: new nasl.Identifier({ name: id.value, }), }); } const _qName = id.qName; // hard-coded string enum if (/app.enums./.test(_qName.namespace.join(NAMESPACE_SEP_AST))) { return createEnumIdMemberExpr(); } // non-hard-coded string enum const def = lookUpResolvedNodes(cei, _qName)?.[0] as nasl.EnumItem; if (def?.concept === 'EnumItem') { return createEnumIdMemberExpr(); } // otherwise, do nothing return id; function createEnumIdMemberExpr() { return new nasl.MemberExpression({ object: new nasl.Identifier({ namespace: APP_ENUM, name: last(_qName.namespace), }), property: new nasl.Identifier({ name: _qName.name }), }); } } function resolveBuiltinCollectionType(ty: nasl.TypeAnnotation): nasl.TypeAnnotation { const ngt = new nasl.TypeAnnotation({ typeKind: 'generic', typeName: ty.qName.name, typeNamespace: NASL_COLLECTION_AST, typeArguments: ty.typeArguments, }); return ngt; } export function resolveTypeRef(cei: CompilerEnvInfo, ty: nasl.TypeAnnotation): nasl.TypeAnnotation { if (!ty || !isTypeAnnotation(ty) || !ty.qName) { return ty; } if (ty.typeKind === 'reference') { if (/^(List|Map)$/.test(ty.qName.name) && ((ty.qName.namespace.length === 0) || new RegExp(NASL_COLLECTION_AST).test(ty.qName.namespace.join(NAMESPACE_SEP_AST)))) { return resolveBuiltinCollectionType(ty); } let def = lookUpResolvedNodes(cei, ty.qName)?.[0] ?? resolveHardCodedRefType(cei, ty); if (!def) { throw new Error(`Unresolved type reference: ${projectIdentifier(ty.qName)}`); } const nsi = cei.semInfo.get(def); ty.typeNamespace = nsi.namespace.join(NAMESPACE_SEP_AST); ty.typeName = nsi.name; } return ty; } // 顺便返回一个(可能是虚假的)数据定义(语义需要),一期硬编码 function resolveHardCodedRefType(cei: CompilerEnvInfo, ty: nasl.TypeAnnotation): nasl.Entity | nasl.Structure | nasl.Enum { if (ty.typeKind === 'reference') { const nd = createHardCodedRefTypeOnUse(cei, ty.qName); if (nd) { // set proper type namespace and name ty.typeNamespace = ty.qName.namespace.join(NAMESPACE_SEP_AST); ty.typeName = ty.qName.name; ty.qName = ty.qName // if (!cei.semInfo.has(es)) { // cei.semInfo.set(es, { name: ty.qName.name, namespace: ty.qName.namespace, isConst: false} ); // cei.resolvedNodes.add(projectIdentifierAST(ty.qName), es); // } return nd; } } return null; } // 无论是看到类型,还是构造器又或是其他,都要创建个假的数据定义,以便后续分析 function createHardCodedRefTypeOnUse(cei: CompilerEnvInfo, qName: CstIdentifier) { const nsp = qName.namespace.join(NAMESPACE_SEP_AST); let test = false; let nd: nasl.Entity | nasl.Structure | nasl.Enum; let tempNsp = cei.currNamespace; // app.structures if (new RegExp(APP_STRUCTURE_AST).test(nsp)) { nd = new nasl.Structure({ name: qName.name, }); test = true; tempNsp = APP_STRUCTURE_AST.split(NAMESPACE_SEP_AST); } // app.dataSources.???.entities else if (/app.dataSources/.test(nsp) && /entities/.test(nsp)) { nd = new nasl.Entity({ name: qName.name, }); test = true; tempNsp = APP_ENTITY_AST.split(NAMESPACE_SEP_AST); } // app.enums else if (/app.enums/.test(nsp)) { nd = new nasl.Enum({ name: qName.name, }); test = true; tempNsp = APP_ENUM.split(NAMESPACE_SEP_AST); } if (!test) { return null; } // add to global definitions const savedNsp = cei.currNamespace; cei.currNamespace = tempNsp; if (nd instanceof nasl.Entity || nd instanceof nasl.Structure) { addResolvedNode(cei, nd); addRefTypeSemInfo(cei, nd); addConstructor(cei, nd); } else if (nd instanceof nasl.Enum) { addResolvedNode(cei, nd); addEnumCons(cei, nd); } cei.currNamespace = savedNsp; return nd; } export function lookUpResolvedNodes(cei: CompilerEnvInfo, nd: { namespace: Array<string>, name: string }) { const qName = nd.namespace.concat(nd.name).join(NAMESPACE_SEP_AST); let defs = [cei.resolvedNodes.get(qName)]; // 基于当前作用域,从内到外,找一遍 // ['nasl', 'core', 'utils'] 查找 nasl 下的、nasl::core 下的、nasl::core::utils 下的 expandNamespaces(cei.currNamespace).forEach(nsp => { const n = prependNamespace(nsp, nd); return defs.push(cei.resolvedNodes.get(n)); }); // 所有额外引入的命名空间,从内到外,找一遍 // 与上面的不同,引了 nasl::core::utils,那么只查找 nasl::core::utils 下的;因此没有 expandNamespaces cei.namespaceOpen.forEach(nsp => { const n = prependNamespace(nsp, nd); defs.push(cei.resolvedNodes.get(n)); }); defs = removeDup(defs.filter(wellDefined)); if (defs.length > 1) { let msg = `Ambiguous name: ${projectIdentifierAST(nd)}, it could be:\n` + defs.map(n => (n as nasl.Identifier).name).join('\n'); throw new Error(msg); } return defs; } function expandNamespaces(fragments: Array<string>) { const nss = []; for (let i = 0; i < fragments.length; i++) { nss.push(fragments.slice(0, i + 1)); } return nss; } // export const addLambdaLocalBindings = (cei: CompilerEnvInfo, naf: nasl.AnonymousFunction): nasl.AnonymousFunction => { // if (!isAnonymousFunction(naf)) { // return naf; // } // // env 里添加形参(类似局部变量) // cei.localVars.push([]) // naf?.params?.forEach(addLocalBindings(cei)); // return naf; // } // Add function-level scoped local variables (including parameters) to our symbol table. const addLocalBindings = (cei: CompilerEnvInfo) => <T extends nasl.Param | nasl.Variable>(node: T): T => { const rNodes = cei.resolvedNodes; rNodes.add(node.name, node); const nsc: NodeSemanticInfo = { namespace: [], name: node.name, isConst: false, }; if (node.typeAnnotation) { nsc['type'] = node.typeAnnotation; } if (node.defaultValue) { nsc['value'] = node.defaultValue.expression; } // constant analysis later cei.semInfo.set(node, nsc); last(cei.localVars).push(node.name); return node; } export const addLogicLocalBindings = (cei: CompilerEnvInfo, nl: nasl.Logic | nasl.AnonymousFunction) :nasl.Logic | nasl.AnonymousFunction => { if (!isLogic(nl) && !isAnonymousFunction(nl)) { return nl; } // Add into symbol table a function's return's semantic information. const addReturnBinding = (cei: CompilerEnvInfo, nr: nasl.Return) : nasl.Return => { const retQVarName = nr.name; cei.resolvedNodes.add(retQVarName, nr); const nsc: NodeSemanticInfo = { namespace: [], name: nr.name, isConst: false, }; if (nr.typeAnnotation) { nsc['type'] = nr.typeAnnotation; } if (nr.defaultValue) { nsc['value'] = nr.defaultValue.expression; } // const analysis later cei.semInfo.set(nr, nsc); last(cei.localVars).push(retQVarName); return nr; } cei.localVars.push([]); // env 里添加形参(类似局部变量) nl?.params?.forEach(addLocalBindings(cei)); // AnonymousFunction 里没有 variables 和 returns if (nl instanceof nasl.Logic) { // env 里添加局部变量 nl?.variables?.forEach(addLocalBindings(cei)); // 添加函数返回类型信息等,return 表达式要用 if (nl.returns && !isEmpty(nl.returns)) { addReturnBinding(cei, head(nl.returns)); } } return nl; } export function removeLogicLocalBindings<T extends nasl.Logic | nasl.AnonymousFunction>(cei: CompilerEnvInfo, node: T): T { if (!isAnonymousFunction(node) && !isLogic(node)) { return node; } const nthe = cei.resolvedNodes; let recentList = cei.localVars.pop(); recentList?.forEach(v => { nthe.remove(v); cei.semInfo.delete(nthe.get(v)); }); return node; } // using xxx.yyy.zzz; /* namespaceOpen: [ ['app', 'dataSources', 'defaultDS', 'entities'], ['nasl'], ... ] after using namespace core namespaceOpen: [ ['app', 'dataSources', 'defaultDS', 'entities'], ['nasl'], ['nasl', 'core'], ... ] after closing namespace core namespaceOpen: [ ['app', 'dataSources', 'defaultDS', 'entities'], ['nasl'], ... ] */ // 尚未支持局部打开命名空间 // 尚未支持关闭打开的命名空间 export const usingNamespace = (cei: CompilerEnvInfo, thisNsp: UsingNamespace): UsingNamespace => { if (thisNsp instanceof UsingNamespace) { cei.namespaceOpen.push(thisNsp.namespace); } return thisNsp; } // 构造函数转为 New 连线构造,难伺候 function ctorToNewExpr(cei: CompilerEnvInfo, nd: nasl.CallLogic): nasl.CallLogic | nasl.NewStructure | nasl.NewComposite { if (!isCallLogic(nd)) { return nd; } if (nd.callKind !== 'Constructor') { return nd; } const keywords = nd.arguments.map(arg => ( console.assert(arg.keyword, 'Arguments in New Composite constructors should all be named'), arg.keyword) ); const selMems = nd.arguments.map(arg => new nasl.SelectMembers({ // ? identifier xyz : literal 1 true 'abc' expression: isMemberExpression(arg.expression) ? extractIdent(arg.expression) : arg.expression, members: isMemberExpression(arg.expression) ? [arg.expression] : [] })); // 无能为力,认怂 const lines = nd.arguments.map((arg, idx) => new nasl.AssignmentLine({ leftIndex: [0, idx], rightIndex: isMemberExpression(arg.expression) ? [idx, 1] : [idx], }) ); return new nasl.NewComposite({ // name: nd.name, 不要名字 难伺候 properties: keywords.map(k => new nasl.Identifier({name: k})), rights: selMems, assignmentLines: lines, typeAnnotation: createNaslRefType(cei, nd.name), }); } function createNaslRefType(cei: CompilerEnvInfo, name: string): nasl.TypeAnnotation { const defs = lookUpResolvedNodes(cei, { name, namespace: []}); const def = defs[0] as nasl.Entity | nasl.Structure | nasl.Enum; const ty = new nasl.TypeAnnotation({ typeKind: 'reference', typeNamespace: dispatchTypeNamespace(def), typeName: name, }); return ty; } const dispatchTypeNamespace = (nd: nasl.Entity | nasl.Structure | nasl.Enum) => { if (isStructure(nd)) { return APP_STRUCTURE_AST; } if (isEntity(nd)) { return APP_ENTITY_AST; } // if (isEnum(nd)) { // return null; // } throw new Error(`Unimplemented in dispatchTypeNamespace: ${nd.toString()}`); } function toNaslCallFunction(nc: nasl.CallLogic): nasl.CallFunction { return new nasl.CallFunction({ calleeName: nc.qName.name, calleeNamespace: NASL_UTIL_AST, arguments: nc.arguments, typeArguments: nc.typeArguments ?? [] // 难伺候 }); } // 二期 function toNaslCallInterface(nc: nasl.CallLogic): nasl.CallInterface { // 已经从 cst 的 .name 转成了 nasl ast 的 .keyword const [authArgs, otherArgs] = splitByPredicate(nc.arguments, arg => arg.keyword === 'authArguments'); if (!isEmpty(authArgs)) { return new nasl.CallAuthInterface({ calleeName: nc.qName.name, calleeNamespace: nc.qName.namespace.join(NAMESPACE_SEP), arguments: otherArgs, authArguments: authArgs }); } else { return new nasl.CallInterface({ calleeName: nc.qName.name, calleeNamespace: nc.qName.namespace.join(NAMESPACE_SEP), arguments: nc.arguments }); } } // 二期 function toNaslCallConnector(nc: nasl.CallLogic): nasl.CallConnector { return new nasl.CallConnector({ calleeName: nc.qName.name, calleeNamespace: nc.qName.namespace.join(NAMESPACE_SEP), arguments: nc.arguments }); }