1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const client_common_1 = require("@neo-one/client-common");
|
5 | const ts_utils_1 = require("@neo-one/ts-utils");
|
6 | const utils_1 = require("@neo-one/utils");
|
7 | const typescript_1 = tslib_1.__importDefault(require("typescript"));
|
8 | const types_1 = require("../compile/helper/types");
|
9 | const DiagnosticCode_1 = require("../DiagnosticCode");
|
10 | const DiagnosticMessage_1 = require("../DiagnosticMessage");
|
11 | const utils_2 = require("../utils");
|
12 | exports.DEFAULT_DIAGNOSTIC_OPTIONS = {
|
13 | error: true,
|
14 | warning: false,
|
15 | };
|
16 | class AnalysisService {
|
17 | constructor(context) {
|
18 | this.context = context;
|
19 | this.memoized = utils_2.createMemoized();
|
20 | }
|
21 | getFunctionReturnType(node, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
22 | if (typescript_1.default.isAccessor(node)) {
|
23 | return this.getType(node);
|
24 | }
|
25 | const typeNode = ts_utils_1.tsUtils.type_.getTypeNode(node);
|
26 | if (typeNode !== undefined) {
|
27 | return this.getNotAnyType(typeNode, ts_utils_1.tsUtils.type_.getTypeFromTypeNode(this.context.typeChecker, typeNode));
|
28 | }
|
29 | const signatureTypes = this.extractSignature(node, options);
|
30 | return signatureTypes === undefined ? undefined : signatureTypes.returnType;
|
31 | }
|
32 | extractAllSignatures(node, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
33 | return this.extractAllSignaturesForType(node, this.getType(node), options);
|
34 | }
|
35 | extractSignature(node, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
36 | return this.extractSignatureForType(node, this.getType(node), options);
|
37 | }
|
38 | getSignatures(node) {
|
39 | const signature = this.context.typeChecker.getResolvedSignature(node);
|
40 | if (signature !== undefined && !ts_utils_1.tsUtils.signature.isFailure(signature)) {
|
41 | return [signature];
|
42 | }
|
43 | const expr = ts_utils_1.tsUtils.expression.getExpressionForCall(node);
|
44 | const type = this.getType(expr);
|
45 | if (type === undefined) {
|
46 | return undefined;
|
47 | }
|
48 | return ts_utils_1.tsUtils.type_.getCallSignatures(type);
|
49 | }
|
50 | extractAllSignaturesForType(node, type, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
51 | const signatures = type === undefined ? undefined : ts_utils_1.tsUtils.type_.getCallSignatures(type);
|
52 | if (signatures === undefined) {
|
53 | return [];
|
54 | }
|
55 | return signatures.map((signature) => this.extractSignatureTypes(node, signature, options)).filter(utils_1.utils.notNull);
|
56 | }
|
57 | extractSignatureForType(node, type, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
58 | const signatureTypes = this.extractAllSignaturesForType(node, type, options);
|
59 | if (signatureTypes.length === 0) {
|
60 | return undefined;
|
61 | }
|
62 | if (signatureTypes.length !== 1) {
|
63 | this.report(options, node, DiagnosticCode_1.DiagnosticCode.MultipleSignatures, DiagnosticMessage_1.DiagnosticMessage.MultipleSignatures);
|
64 | return undefined;
|
65 | }
|
66 | return signatureTypes[0];
|
67 | }
|
68 | extractSignaturesForCall(node, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
69 | const signatures = this.getSignatures(node);
|
70 | if (signatures === undefined) {
|
71 | return undefined;
|
72 | }
|
73 | return signatures.map((signature) => this.extractSignatureTypes(node, signature, options)).filter(utils_1.utils.notNull);
|
74 | }
|
75 | extractSignatureTypes(node, signature, options = exports.DEFAULT_DIAGNOSTIC_OPTIONS) {
|
76 | const params = ts_utils_1.tsUtils.signature.getParameters(signature);
|
77 | const paramTypes = params.map((param) => this.getTypeOfSymbol(param, node));
|
78 | const paramDeclsNullable = params.map((param) => ts_utils_1.tsUtils.symbol.getValueDeclaration(param));
|
79 | const nullParamIndex = paramDeclsNullable.indexOf(undefined);
|
80 | if (nullParamIndex !== -1) {
|
81 | const nullParam = params[nullParamIndex];
|
82 | this.report(options, node, DiagnosticCode_1.DiagnosticCode.Invariant, DiagnosticMessage_1.DiagnosticMessage.MissingParameterDeclaration, ts_utils_1.tsUtils.symbol.getName(nullParam));
|
83 | return undefined;
|
84 | }
|
85 | const paramDecls = paramDeclsNullable.filter(utils_1.utils.notNull).filter(typescript_1.default.isParameter);
|
86 | const declToType = new Map();
|
87 | for (const [paramDecl, paramType] of utils_1.utils.zip(paramDecls, paramTypes)) {
|
88 | declToType.set(paramDecl, paramType);
|
89 | }
|
90 | return {
|
91 | paramDecls,
|
92 | paramTypes: declToType,
|
93 | returnType: this.getNotAnyType(node, ts_utils_1.tsUtils.signature.getReturnType(signature)),
|
94 | };
|
95 | }
|
96 | extractLiteralAddress(original) {
|
97 | return this.memoized('extract-literal-address', utils_2.nodeKey(original), () => this.extractLiteral(original, 'AddressConstructor', (value) => {
|
98 | try {
|
99 | return client_common_1.common.stringToUInt160(client_common_1.addressToScriptHash(value));
|
100 | }
|
101 | catch (_a) {
|
102 | return client_common_1.common.stringToUInt160(value);
|
103 | }
|
104 | }, client_common_1.common.bufferToUInt160));
|
105 | }
|
106 | extractLiteralHash256(original) {
|
107 | return this.extractLiteral(original, 'Hash256Constructor', client_common_1.common.stringToUInt256, client_common_1.common.bufferToUInt256);
|
108 | }
|
109 | extractLiteralPublicKey(original) {
|
110 | return this.extractLiteral(original, 'PublicKeyConstructor', client_common_1.common.stringToECPoint, client_common_1.common.bufferToECPoint);
|
111 | }
|
112 | getType(node, options = {}) {
|
113 | return this.memoized('get-type', utils_2.nodeKey(node), () => this.getNotAnyType(node, ts_utils_1.tsUtils.type_.getType(this.context.typeChecker, node), options));
|
114 | }
|
115 | getTypeOfSymbol(symbol, node) {
|
116 | if (symbol === undefined) {
|
117 | return undefined;
|
118 | }
|
119 | return this.memoized('get-type-of-symbol', `${ts_utils_1.symbolKey(symbol)}:${utils_2.nodeKey(node)}`, () => this.getNotAnyType(node, ts_utils_1.tsUtils.type_.getTypeAtLocation(this.context.typeChecker, symbol, node)));
|
120 | }
|
121 | getSymbol(node) {
|
122 | return this.memoized('symbol', utils_2.nodeKey(node), () => {
|
123 | const symbol = ts_utils_1.tsUtils.node.getSymbol(this.context.typeChecker, node);
|
124 | if (symbol === undefined) {
|
125 | return undefined;
|
126 | }
|
127 | const aliased = ts_utils_1.tsUtils.symbol.getAliasedSymbol(this.context.typeChecker, symbol);
|
128 | if (aliased !== undefined) {
|
129 | return aliased;
|
130 | }
|
131 | return symbol;
|
132 | });
|
133 | }
|
134 | getTypeSymbol(node) {
|
135 | return this.memoized('get-type-symbol', utils_2.nodeKey(node), () => {
|
136 | const type = this.getType(node);
|
137 | return this.getSymbolForType(node, type);
|
138 | });
|
139 | }
|
140 | getSymbolForType(_node, type) {
|
141 | if (type === undefined) {
|
142 | return undefined;
|
143 | }
|
144 | return this.memoized('get-symbol-for-type', utils_2.typeKey(type), () => {
|
145 | let symbol = ts_utils_1.tsUtils.type_.getAliasSymbol(type);
|
146 | if (symbol === undefined) {
|
147 | symbol = ts_utils_1.tsUtils.type_.getSymbol(type);
|
148 | }
|
149 | if (symbol === undefined) {
|
150 | return undefined;
|
151 | }
|
152 | const aliased = ts_utils_1.tsUtils.symbol.getAliasedSymbol(this.context.typeChecker, symbol);
|
153 | if (aliased !== undefined) {
|
154 | return aliased;
|
155 | }
|
156 | return symbol;
|
157 | });
|
158 | }
|
159 | getNotAnyType(node, type, { error = true } = {}) {
|
160 | if (type !== undefined && ts_utils_1.tsUtils.type_.isAny(type)) {
|
161 | if (error && !ts_utils_1.tsUtils.type_.isErrorType(type)) {
|
162 | this.context.reportTypeError(node);
|
163 | }
|
164 | return undefined;
|
165 | }
|
166 | if (type !== undefined) {
|
167 | const constraintType = ts_utils_1.tsUtils.type_.getConstraint(type);
|
168 | if (constraintType !== undefined) {
|
169 | return constraintType;
|
170 | }
|
171 | }
|
172 | return type;
|
173 | }
|
174 | extractStorageKey(node) {
|
175 | return this.memoized('extract-storage-key', utils_2.nodeKey(node), () => {
|
176 | const smartContract = ts_utils_1.tsUtils.node.getFirstAncestorByTest(node, typescript_1.default.isClassDeclaration);
|
177 | if (smartContract === undefined || !this.isSmartContract(smartContract)) {
|
178 | return undefined;
|
179 | }
|
180 | const decl = ts_utils_1.tsUtils.node.getFirstAncestorByTest(node, typescript_1.default.isPropertyDeclaration);
|
181 | if (decl === undefined) {
|
182 | return undefined;
|
183 | }
|
184 | return ts_utils_1.tsUtils.node.getName(decl);
|
185 | });
|
186 | }
|
187 | isSmartContract(node) {
|
188 | return this.memoized('is-smart-contract', utils_2.nodeKey(node), () => {
|
189 | const extendsExpr = ts_utils_1.tsUtils.class_.getExtends(node);
|
190 | const isSmartContract = extendsExpr !== undefined &&
|
191 | this.context.builtins.isValue(ts_utils_1.tsUtils.expression.getExpression(extendsExpr), 'SmartContract');
|
192 | if (isSmartContract) {
|
193 | return true;
|
194 | }
|
195 | const baseClasses = ts_utils_1.tsUtils.class_.getBaseClasses(this.context.typeChecker, node);
|
196 | if (baseClasses.some((value) => this.context.builtins.isValue(value, 'SmartContract'))) {
|
197 | return true;
|
198 | }
|
199 | const baseClass = ts_utils_1.tsUtils.class_.getBaseClass(this.context.typeChecker, node);
|
200 | return baseClass !== undefined && this.isSmartContract(baseClass);
|
201 | });
|
202 | }
|
203 | isSmartContractNode(node) {
|
204 | return this.memoized('is-smart-contract-node', utils_2.nodeKey(node), () => {
|
205 | const symbol = this.getSymbol(node);
|
206 | if (symbol === undefined) {
|
207 | return false;
|
208 | }
|
209 | const decls = ts_utils_1.tsUtils.symbol.getDeclarations(symbol);
|
210 | if (decls.length === 0) {
|
211 | return false;
|
212 | }
|
213 | const decl = decls[0];
|
214 | return typescript_1.default.isClassDeclaration(decl) && this.isSmartContract(decl);
|
215 | });
|
216 | }
|
217 | getSymbolAndAllInheritedSymbols(node) {
|
218 | return this.memoized('get-symbol-and-all-inherited-symbols', utils_2.nodeKey(node), () => {
|
219 | const symbol = this.getSymbol(node);
|
220 | const symbols = [symbol].filter(utils_1.utils.notNull);
|
221 | if (typescript_1.default.isClassDeclaration(node) || typescript_1.default.isClassExpression(node) || typescript_1.default.isInterfaceDeclaration(node)) {
|
222 | const baseTypes = ts_utils_1.tsUtils.class_.getBaseTypesFlattened(this.context.typeChecker, node);
|
223 | return symbols.concat(baseTypes.map((baseType) => this.getSymbolForType(node, baseType)).filter(utils_1.utils.notNull));
|
224 | }
|
225 | return symbols;
|
226 | });
|
227 | }
|
228 | isValidStorageType(node, type) {
|
229 | return !ts_utils_1.tsUtils.type_.hasType(type, (tpe) => !ts_utils_1.tsUtils.type_.isOnlyType(tpe, (tp) => types_1.isOnlyUndefined(this.context, node, tp) ||
|
230 | types_1.isOnlyNull(this.context, node, tp) ||
|
231 | types_1.isOnlyBoolean(this.context, node, tp) ||
|
232 | types_1.isOnlyNumber(this.context, node, tp) ||
|
233 | types_1.isOnlyString(this.context, node, tp) ||
|
234 | types_1.isOnlySymbol(this.context, node, tp) ||
|
235 | types_1.isOnlyBuffer(this.context, node, tp) ||
|
236 | this.isValidStorageArray(node, tp) ||
|
237 | this.isValidStorageMap(node, tp) ||
|
238 | this.isValidStorageSet(node, tp)));
|
239 | }
|
240 | findReferencesAsNodes(node) {
|
241 | return this.memoized('find-references-as-nodes', utils_2.nodeKey(node), () => ts_utils_1.tsUtils.reference
|
242 | .findReferencesAsNodes(this.context.program, this.context.languageService, node)
|
243 | .filter((found) => this.context.sourceFiles.has(ts_utils_1.tsUtils.node.getSourceFile(found))));
|
244 | }
|
245 | isSmartContractMixinFunction(node) {
|
246 | const parameters = ts_utils_1.tsUtils.parametered.getParameters(node);
|
247 | if (parameters.length !== 1) {
|
248 | return false;
|
249 | }
|
250 | const signatureTypess = this.extractAllSignatures(node);
|
251 | if (signatureTypess.length !== 1) {
|
252 | return false;
|
253 | }
|
254 | const signatureTypes = signatureTypess[0];
|
255 | const firstParam = signatureTypes.paramDecls[0];
|
256 | const firstParamType = signatureTypes.paramTypes.get(firstParam);
|
257 | if (firstParamType === undefined || ts_utils_1.tsUtils.type_.getConstructSignatures(firstParamType).length !== 1) {
|
258 | return false;
|
259 | }
|
260 | const constructSignatureTypes = this.extractSignatureTypes(firstParam, ts_utils_1.tsUtils.type_.getConstructSignatures(firstParamType)[0]);
|
261 | if (constructSignatureTypes === undefined) {
|
262 | return false;
|
263 | }
|
264 | const returnTypeSymbol = this.getSymbolForType(firstParam, constructSignatureTypes.returnType);
|
265 | return returnTypeSymbol !== undefined && returnTypeSymbol === this.context.builtins.getValueSymbol('SmartContract');
|
266 | }
|
267 | isValidStorageArray(node, type) {
|
268 | if (!types_1.isOnlyArray(this.context, node, type)) {
|
269 | return false;
|
270 | }
|
271 | const typeArguments = ts_utils_1.tsUtils.type_.getTypeArgumentsArray(type);
|
272 | if (typeArguments.length !== 1) {
|
273 | return true;
|
274 | }
|
275 | return this.isValidStorageType(node, typeArguments[0]);
|
276 | }
|
277 | isValidStorageMap(node, type) {
|
278 | if (!types_1.isOnlyMap(this.context, node, type)) {
|
279 | return false;
|
280 | }
|
281 | const typeArguments = ts_utils_1.tsUtils.type_.getTypeArgumentsArray(type);
|
282 | if (typeArguments.length !== 2) {
|
283 | return true;
|
284 | }
|
285 | return this.isValidStorageType(node, typeArguments[0]) && this.isValidStorageType(node, typeArguments[1]);
|
286 | }
|
287 | isValidStorageSet(node, type) {
|
288 | if (!types_1.isOnlySet(this.context, node, type)) {
|
289 | return false;
|
290 | }
|
291 | const typeArguments = ts_utils_1.tsUtils.type_.getTypeArgumentsArray(type);
|
292 | if (typeArguments.length !== 1) {
|
293 | return true;
|
294 | }
|
295 | return this.isValidStorageType(node, typeArguments[0]);
|
296 | }
|
297 | extractLiteral(original, name, processText, processBuffer) {
|
298 | return this.traceIdentifier(original, (node) => {
|
299 | if (!typescript_1.default.isCallExpression(node) && !typescript_1.default.isTaggedTemplateExpression(node)) {
|
300 | return undefined;
|
301 | }
|
302 | const expr = typescript_1.default.isCallExpression(node) ? ts_utils_1.tsUtils.expression.getExpression(node) : ts_utils_1.tsUtils.template.getTag(node);
|
303 | const symbol = this.getSymbol(expr);
|
304 | const hash256From = this.context.builtins.getOnlyMemberSymbol(name, 'from');
|
305 | const bufferFrom = this.context.builtins.getOnlyMemberSymbol('BufferConstructor', 'from');
|
306 | if (symbol === hash256From) {
|
307 | const arg = typescript_1.default.isCallExpression(node)
|
308 | ? ts_utils_1.tsUtils.argumented.getArguments(node)[0]
|
309 | : ts_utils_1.tsUtils.template.getTemplate(node);
|
310 | if (typescript_1.default.isTaggedTemplateExpression(node) &&
|
311 | !typescript_1.default.isNoSubstitutionTemplateLiteral(ts_utils_1.tsUtils.template.getTemplate(node))) {
|
312 | return undefined;
|
313 | }
|
314 | if (arg === undefined) {
|
315 | return undefined;
|
316 | }
|
317 | return this.traceIdentifier(arg, (value) => {
|
318 | if (typescript_1.default.isStringLiteral(value) || typescript_1.default.isNoSubstitutionTemplateLiteral(value)) {
|
319 | try {
|
320 | return processText(ts_utils_1.tsUtils.literal.getLiteralValue(value));
|
321 | }
|
322 | catch (_a) {
|
323 | }
|
324 | }
|
325 | return undefined;
|
326 | });
|
327 | }
|
328 | if (symbol === bufferFrom && typescript_1.default.isCallExpression(node)) {
|
329 | const arg = ts_utils_1.tsUtils.argumented.getArguments(node)[0];
|
330 | if (arg === undefined) {
|
331 | return undefined;
|
332 | }
|
333 | return this.traceIdentifier(arg, (value) => {
|
334 | if (!typescript_1.default.isStringLiteral(value)) {
|
335 | return undefined;
|
336 | }
|
337 | try {
|
338 | return processBuffer(Buffer.from(ts_utils_1.tsUtils.literal.getLiteralValue(value), 'hex'));
|
339 | }
|
340 | catch (_a) {
|
341 | return undefined;
|
342 | }
|
343 | });
|
344 | }
|
345 | return undefined;
|
346 | });
|
347 | }
|
348 | traceIdentifier(nodeIn, extractValue) {
|
349 | const node = this.unwrapExpression(nodeIn);
|
350 | if (typescript_1.default.isIdentifier(node)) {
|
351 | const symbol = this.getSymbol(node);
|
352 | if (symbol === undefined) {
|
353 | return undefined;
|
354 | }
|
355 | const decl = ts_utils_1.tsUtils.symbol.getValueDeclaration(symbol);
|
356 | if (decl === undefined) {
|
357 | return undefined;
|
358 | }
|
359 | if (!typescript_1.default.isVariableDeclaration(decl)) {
|
360 | return undefined;
|
361 | }
|
362 | const parent = ts_utils_1.tsUtils.node.getParent(decl);
|
363 | if (!typescript_1.default.isVariableDeclarationList(parent) || !ts_utils_1.tsUtils.modifier.isConst(parent)) {
|
364 | return undefined;
|
365 | }
|
366 | const initializer = ts_utils_1.tsUtils.initializer.getInitializer(parent);
|
367 | if (initializer === undefined) {
|
368 | return undefined;
|
369 | }
|
370 | return this.traceIdentifier(initializer, extractValue);
|
371 | }
|
372 | return extractValue(node);
|
373 | }
|
374 | unwrapExpression(node) {
|
375 | if (typescript_1.default.isParenthesizedExpression(node)) {
|
376 | return ts_utils_1.tsUtils.expression.getExpression(node);
|
377 | }
|
378 | if (typescript_1.default.isAsExpression(node)) {
|
379 | return ts_utils_1.tsUtils.expression.getExpression(node);
|
380 | }
|
381 | return node;
|
382 | }
|
383 | report(options, node, code, message, ...args) {
|
384 | if (options.error) {
|
385 | this.context.reportError(node, code, message, ...args);
|
386 | }
|
387 | else if (options.warning) {
|
388 | this.context.reportWarning(node, code, message, ...args);
|
389 | }
|
390 | }
|
391 | }
|
392 | exports.AnalysisService = AnalysisService;
|
393 |
|
394 |
|