UNPKG

18.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const client_common_1 = require("@neo-one/client-common");
5const ts_utils_1 = require("@neo-one/ts-utils");
6const utils_1 = require("@neo-one/utils");
7const typescript_1 = tslib_1.__importDefault(require("typescript"));
8const types_1 = require("../compile/helper/types");
9const DiagnosticCode_1 = require("../DiagnosticCode");
10const DiagnosticMessage_1 = require("../DiagnosticMessage");
11const utils_2 = require("../utils");
12exports.DEFAULT_DIAGNOSTIC_OPTIONS = {
13 error: true,
14 warning: false,
15};
16class 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}
392exports.AnalysisService = AnalysisService;
393
394//# sourceMappingURL=AnalysisService.js.map