@neo-one/smart-contract-compiler
Version:
NEO•ONE TypeScript smart contract compiler.
165 lines (163 loc) • 6.78 kB
JavaScript
import { tsUtils } from '@neo-one/ts-utils';
import { utils } from '@neo-one/utils';
import _ from 'lodash';
import ts from 'typescript';
import { toABIReturn } from '../utils';
export class DebugInfoProcessor {
constructor(context, contractInfo) {
this.context = context;
this.contractInfo = contractInfo;
this.sourceFile = contractInfo.smartContract.getSourceFile();
this.endLine = this.sourceFile.getLineAndCharacterOfPosition(this.sourceFile.getEnd()).line;
}
process() {
return {
entrypoint: '',
documents: [this.sourceFile.fileName],
methods: this.processMethods(),
events: [],
};
}
processMethods() {
const propInfos = this.contractInfo.propInfos.filter(utils.notNull);
return _.flatten(propInfos.map((propInfo) => {
switch (propInfo.type) {
case 'function':
const funcRange = this.getSourceRange(propInfo.decl);
if (funcRange === undefined) {
return [];
}
return [
{
id: '',
name: propInfo.name,
params: this.getParameters({
callSignature: propInfo.callSignature,
send: propInfo.send,
claim: propInfo.claim,
}),
range: funcRange,
returnType: this.toDebugReturn(propInfo.decl, propInfo.returnType),
},
];
case 'property':
const propRange = this.getSourceRange(propInfo.decl);
if (propRange === undefined) {
return [];
}
return [
{
id: '',
name: propInfo.name,
params: [],
range: propRange,
returnType: this.toDebugReturn(propInfo.decl, propInfo.propertyType),
},
];
case 'accessor':
return [this.getGetterInfo(propInfo), this.getSetterInfo(propInfo)].filter(utils.notNull);
default:
return [];
}
}));
}
getGetterInfo(propInfo) {
if (propInfo.getter === undefined) {
return undefined;
}
const range = this.getSourceRange(propInfo.getter.decl);
if (range === undefined) {
return undefined;
}
return {
id: '',
name: propInfo.getter.name,
params: [],
range,
returnType: this.toDebugReturn(propInfo.getter.decl, propInfo.propertyType),
};
}
getSetterInfo(propInfo) {
if (propInfo.setter === undefined) {
return undefined;
}
const range = this.getSourceRange(propInfo.setter.decl);
if (range === undefined) {
return undefined;
}
return {
id: '',
name: propInfo.setter.name,
params: [
this.toDebugParameter(propInfo.name, propInfo.getter === undefined ? propInfo.setter.decl : propInfo.getter.decl, propInfo.propertyType, false, propInfo.getter === undefined ? { error: true } : undefined),
].filter(utils.notNull),
range,
returnType: 'Void',
};
}
getParameters({ callSignature, claim = false, send = false, }) {
if (callSignature === undefined) {
return [];
}
let parameters = callSignature.getParameters();
if (claim && this.checkLastParam(parameters, 'ClaimTransaction')) {
parameters = parameters.slice(0, -1);
}
if (send && this.checkLastParam(parameters, 'Transfer')) {
parameters = parameters.slice(0, -1);
}
return parameters.map((parameter) => this.paramToABIParameter(parameter)).filter(utils.notNull);
}
paramToABIParameter(param) {
const decls = tsUtils.symbol.getDeclarations(param);
const decl = utils.nullthrows(decls[0]);
const initializer = tsUtils.initializer.getInitializer(decl);
return this.toDebugParameter(tsUtils.symbol.getName(param), decl, this.getParamSymbolType(param), initializer !== undefined);
}
getParamSymbolType(param) {
const decls = tsUtils.symbol.getDeclarations(param);
const decl = utils.nullthrows(decls[0]);
return this.context.analysis.getTypeOfSymbol(param, decl);
}
checkLastParam(parameters, value) {
return this.checkLastParamBase(parameters, (decl, type) => this.context.builtins.isInterface(decl, type, value));
}
checkLastParamBase(parameters, checkParamType) {
if (parameters.length === 0) {
return false;
}
const lastParam = parameters[parameters.length - 1];
const lastParamType = this.getParamSymbolType(lastParam);
return lastParamType !== undefined && checkParamType(tsUtils.symbol.getDeclarations(lastParam)[0], lastParamType);
}
toDebugParameter(nameIn, node, resolvedTypeIn, optional = false, options = { error: false, warning: false }) {
const name = nameIn.startsWith('_') ? nameIn.slice(1) : nameIn;
let resolvedType = resolvedTypeIn;
if (ts.isParameter(node) && tsUtils.parameter.isRestParameter(node) && resolvedType !== undefined) {
resolvedType = tsUtils.type_.getTypeArgumentsArray(resolvedType)[0];
}
const type = toABIReturn(this.context, node, resolvedType, optional, options);
if (type === undefined) {
return undefined;
}
return `${name},${type.type}`;
}
toDebugReturn(node, resolvedType, optional = false, options = { error: false, warning: false }) {
const type = toABIReturn(this.context, node, resolvedType, optional, options);
return type === undefined ? 'Void' : type.type;
}
getSourceRange(node) {
try {
const { line: start } = this.sourceFile.getLineAndCharacterOfPosition(node.getStart());
const { line: end } = this.sourceFile.getLineAndCharacterOfPosition(node.getEnd());
if (start === this.endLine && end === this.endLine) {
return undefined;
}
return [start, end];
}
catch {
return undefined;
}
}
}
//# sourceMappingURL=DebugInfoProcessor.js.map