UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

376 lines (374 loc) 17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Builtins = void 0; const tslib_1 = require("tslib"); const ts_utils_1 = require("@neo-one/ts-utils"); const utils_1 = require("@neo-one/utils"); const lodash_1 = tslib_1.__importDefault(require("lodash")); const typescript_1 = tslib_1.__importDefault(require("typescript")); const utils_2 = require("../../utils"); const types_1 = require("./types"); const getMember = (sym, name) => ts_utils_1.tsUtils.symbol.getMemberOrThrow(sym, name); const getExportOrMember = (sym, name) => { const member = ts_utils_1.tsUtils.symbol.getMember(sym, name); return member === undefined ? ts_utils_1.tsUtils.symbol.getExportOrThrow(sym, name) : member; }; const findNonNull = (value) => value.find((val) => val !== undefined); const throwIfNull = (value) => { if (value == undefined) { throw new Error('Something went wrong.'); } return value; }; class Builtins { constructor(context) { this.context = context; this.builtinMembers = new Map(); this.allBuiltinMembers = new Map(); this.builtinInterfaces = new Map(); this.builtinValues = new Map(); this.builtinOverrides = new Map(); this.memoized = utils_2.createMemoized(); } isBuiltinSymbol(symbol) { return symbol !== undefined && (this.builtinValues.has(symbol) || this.builtinInterfaces.has(symbol)); } isBuiltinIdentifier(value) { return (this.getAnyInterfaceSymbolMaybe(value) !== undefined || this.getAnyTypeSymbolMaybe(value) !== undefined || this.getAnyValueSymbolMaybe(value) !== undefined); } isBuiltinFile(file) { return this.getContract() === file; } getMember(value, prop) { return this.memoized('get-member', `${utils_2.nodeKey(value)}:${utils_2.nodeKey(prop)}`, () => { const propSymbol = this.context.analysis.getSymbol(prop); if (propSymbol === undefined) { return undefined; } const valueSymbol = this.context.analysis.getTypeSymbol(value); if (valueSymbol === undefined) { return this.allBuiltinMembers.get(propSymbol); } if (!ts_utils_1.tsUtils.guards.isSuperExpression(value)) { const overridenMember = this.walkOverridesForMember(valueSymbol, propSymbol); if (overridenMember !== undefined) { return overridenMember; } } const members = this.getAllMembers(valueSymbol); return members.get(propSymbol); }); } getOnlyMember(value, name) { return this.getOnlyMemberBase(value, name, (result) => result[1]); } getOnlyMemberSymbol(value, name) { return throwIfNull(this.getOnlyMemberBase(value, name, (result) => result[0])); } getMembers(name, isMember, isEligible, symbolMembers = false) { const filterPseudoSymbol = (symbol, key) => { const symbolSymbol = this.getInterfaceSymbolBase('SymbolConstructor', this.getGlobals()); return ts_utils_1.tsUtils.symbol.getMember(symbolSymbol, key) !== symbol; }; const isSymbolKey = (key) => key.startsWith('__@'); let testKey = (key) => !isSymbolKey(key); let modifyKey = (key) => key; if (symbolMembers) { testKey = isSymbolKey; modifyKey = (key) => key.slice(3); } const members = this.getAllMembers(this.getAnyInterfaceSymbol(name)); const mutableMembers = []; members.forEach((builtin, memberSymbol) => { const memberName = ts_utils_1.tsUtils.symbol.getName(memberSymbol); if (isMember(builtin) && filterPseudoSymbol(memberSymbol, memberName) && testKey(memberName) && isEligible(builtin)) { mutableMembers.push([modifyKey(memberName), builtin]); } }); return mutableMembers; } getInterface(value) { const valueSymbol = this.context.analysis.getSymbol(value); if (valueSymbol === undefined) { return undefined; } return this.builtinInterfaces.get(valueSymbol); } getInterfaceSymbol(value) { return this.getAnyInterfaceSymbol(value); } getValue(value) { const valueSymbol = this.context.analysis.getSymbol(value); if (valueSymbol === undefined) { return undefined; } return this.builtinValues.get(valueSymbol); } getValueInterface(value) { const builtinValue = this.getValue(value); return builtinValue === undefined || !types_1.isBuiltinValueObject(builtinValue) ? undefined : builtinValue.type; } getValueSymbol(value) { return this.getAnyValueSymbol(value); } getTypeSymbol(name) { return this.getAnyTypeSymbol(name); } isInterface(node, testType, name) { return this.memoized('is-interface', `${utils_2.typeKey(testType)}:${name}`, () => { const symbol = this.context.analysis.getSymbolForType(node, testType); if (symbol === undefined) { return false; } const interfaceSymbol = this.getAnyInterfaceSymbol(name); return symbol === interfaceSymbol; }); } isType(node, testType, name) { return this.memoized('is-type', `${utils_2.typeKey(testType)}:${name}`, () => { const symbol = this.context.analysis.getSymbolForType(node, testType); if (symbol === undefined) { return false; } if (name === 'Fixed') { const fixedTagSymbol = this.getAnyInterfaceSymbol('FixedTag'); if (symbol === fixedTagSymbol) { return true; } } if (name === 'ForwardedValue') { const forwardedValueTagSymbol = this.getAnyInterfaceSymbol('ForwardedValueTag'); if (symbol === forwardedValueTagSymbol) { return true; } } const typeSymbol = this.getAnyTypeSymbol(name); return symbol === typeSymbol; }); } isValue(node, name) { return this.memoized('is-value', `${utils_2.nodeKey(node)}:${name}`, () => { const symbol = this.context.analysis.getSymbol(node); if (symbol === undefined) { return false; } const valueSymbol = this.getAnyValueSymbol(name); return symbol === valueSymbol; }); } addMember(valueSymbol, memberSymbol, builtin) { let members = this.builtinMembers.get(valueSymbol); if (members === undefined) { members = new Map(); this.builtinMembers.set(valueSymbol, members); } members.set(memberSymbol, builtin); this.allBuiltinMembers.set(memberSymbol, builtin); const memberSymbolName = ts_utils_1.tsUtils.symbol.getName(memberSymbol); if (memberSymbolName.startsWith('__@')) { const symbolSymbol = this.getInterfaceSymbolBase('SymbolConstructor', this.getGlobals()); const memberSymbolSymbol = getMember(symbolSymbol, memberSymbolName.slice(3)); members.set(memberSymbolSymbol, builtin); this.allBuiltinMembers.set(memberSymbolSymbol, builtin); } } addOverride(superSymbol, overrideSymbol) { this.builtinOverrides.set(superSymbol, overrideSymbol); } addGlobalMember(value, member, builtin) { this.addMemberBase(value, member, builtin, this.getGlobals()); } addContractMember(value, member, builtin) { this.addMemberBase(value, member, builtin, this.getContract()); } addInterface(value, builtin) { this.addInterfaceBase(value, builtin, this.getGlobals()); } addContractInterface(value, builtin) { this.addInterfaceBase(value, builtin, this.getContract()); } addValue(value, builtin) { this.addValueBase(value, builtin, this.getGlobals()); } addTestValue(value, builtin) { const file = this.getTestGlobals(); if (file === undefined) { return; } this.addValueBase(value, builtin, file); } addContractValue(value, builtin) { this.addValueBase(value, builtin, this.getContract()); } walkOverridesForMember(valueSymbol, propSymbol) { const overrideValueSymbol = this.builtinOverrides.get(valueSymbol); if (overrideValueSymbol === undefined) { return undefined; } let overridePropSymbol = this.builtinOverrides.get(propSymbol); if (overridePropSymbol === undefined) { overridePropSymbol = propSymbol; } const member = this.walkOverridesForMember(overrideValueSymbol, overridePropSymbol); if (member !== undefined) { return member; } const overridenMembers = this.getAllMembers(overrideValueSymbol); return overridenMembers.get(overridePropSymbol); } getOnlyMemberBase(value, name, getValue) { return this.memoized('only-member-base', `${value}$${name}`, () => { const symbol = this.getAnyInterfaceOrValueSymbol(value); const members = this.getAllMembers(symbol); const result = [...members.entries()].find(([memberSymbol]) => ts_utils_1.tsUtils.symbol.getName(memberSymbol) === name); return result === undefined ? undefined : getValue(result); }); } getAllMembers(symbol) { return this.memoized('get-all-members', ts_utils_1.symbolKey(symbol), () => { const interfaceMembers = this.builtinMembers.get(symbol); const memberEntries = [...this.getInheritedSymbols(symbol)].reduce((acc, parentInterfaceSymbol) => { const parentInterfaceMembers = this.builtinMembers.get(parentInterfaceSymbol); if (parentInterfaceMembers === undefined) { return acc; } return [...parentInterfaceMembers.entries()].concat(acc); }, interfaceMembers === undefined ? [] : [...interfaceMembers.entries()]); return new Map(memberEntries); }); } addMemberBase(value, member, builtin, file) { let valueSymbol = this.getInterfaceSymbolMaybe(value, file); let memberSymbol; if (valueSymbol === undefined) { valueSymbol = this.getValueSymbolBase(value, file); memberSymbol = getExportOrMember(valueSymbol, member); } else { memberSymbol = getMember(valueSymbol, member); } this.addMember(valueSymbol, memberSymbol, builtin); } getAnyInterfaceOrValueSymbol(value) { const valueSymbol = this.getAnyInterfaceSymbolMaybe(value); return valueSymbol === undefined ? this.getAnyValueSymbol(value) : valueSymbol; } addInterfaceBase(value, builtin, file) { this.builtinInterfaces.set(this.getInterfaceSymbolBase(value, file), builtin); } addValueBase(value, builtin, file) { this.builtinValues.set(this.getValueSymbolBase(value, file), builtin); } getAnyValueSymbol(name) { return this.memoized('any-value-symbol', name, () => throwIfNull(this.getAnyValueSymbolMaybe(name))); } getAnyValueSymbolMaybe(name) { return this.memoized('get-any-value-symbol-maybe', name, () => findNonNull(this.getFiles().map((file) => this.getValueSymbolMaybe(name, file)))); } getValueSymbolBase(name, file) { return throwIfNull(this.getValueSymbolMaybe(name, file)); } getValueSymbolMaybe(name, file) { let decl = ts_utils_1.tsUtils.statement.getVariableDeclaration(file, name); if (decl === undefined) { decl = ts_utils_1.tsUtils.statement.getFunction(file, name); } if (decl === undefined) { decl = ts_utils_1.tsUtils.statement.getEnum(file, name); } if (decl === undefined) { decl = ts_utils_1.tsUtils.statement.getClass(file, name); } if (decl === undefined) { return undefined; } return ts_utils_1.tsUtils.node.getSymbol(this.context.typeChecker, decl); } getAnyInterfaceSymbol(name) { return this.memoized('any-interface-symbol', name, () => throwIfNull(this.getAnyInterfaceSymbolMaybe(name))); } getAnyInterfaceSymbolMaybe(name) { return this.memoized('get-any-interface-symbol-maybe', name, () => findNonNull(this.getFiles().map((file) => this.getInterfaceSymbolMaybe(name, file)))); } getInterfaceSymbolBase(name, file) { return throwIfNull(this.getInterfaceSymbolMaybe(name, file)); } getInterfaceSymbolMaybe(name, file) { return this.getInterfaceSymbols(file)[name]; } getInterfaceSymbols(file) { return this.memoized('interface-symbols', ts_utils_1.tsUtils.file.getFilePath(file), () => { const interfaceDecls = ts_utils_1.tsUtils.statement.getInterfaces(file); const decls = interfaceDecls.concat(ts_utils_1.tsUtils.statement.getEnums(file)); return lodash_1.default.fromPairs(decls.map((decl) => { const type = ts_utils_1.tsUtils.type_.getType(this.context.typeChecker, decl); const symbol = ts_utils_1.tsUtils.type_.getSymbol(type); return [ts_utils_1.tsUtils.node.getName(decl), symbol]; })); }); } getInheritedSymbols(symbol, baseTypes = []) { return this.memoized('get-inherited-symbols', ts_utils_1.symbolKey(symbol), () => { const symbols = new Set(); for (const decl of ts_utils_1.tsUtils.symbol.getDeclarations(symbol)) { if (typescript_1.default.isInterfaceDeclaration(decl) || typescript_1.default.isClassDeclaration(decl) || typescript_1.default.isClassExpression(decl)) { let baseType = baseTypes[0]; let nextBaseTypes = baseTypes.slice(1); if (baseTypes.length === 0) { const currentBaseTypes = ts_utils_1.tsUtils.class_.getBaseTypesFlattened(this.context.typeChecker, decl); baseType = currentBaseTypes[0]; nextBaseTypes = currentBaseTypes.slice(1); } if (baseType !== undefined) { const baseSymbol = this.context.analysis.getSymbolForType(decl, baseType); if (baseSymbol !== undefined) { symbols.add(baseSymbol); this.getInheritedSymbols(baseSymbol, nextBaseTypes).forEach((inheritedSymbol) => { symbols.add(inheritedSymbol); }); } } } } return symbols; }); } getAnyTypeSymbol(name) { return this.memoized('get-any-type-symbol', name, () => throwIfNull(this.getAnyTypeSymbolMaybe(name))); } getAnyTypeSymbolMaybe(name) { return this.memoized('get-any-type-symbol-maybe', name, () => findNonNull(this.getFiles().map((file) => this.getTypeSymbolMaybe(name, file)))); } getTypeSymbolMaybe(name, file) { return this.getTypeSymbols(file)[name]; } getTypeSymbols(file) { return this.memoized('type-symbols', ts_utils_1.tsUtils.file.getFilePath(file), () => { const decls = ts_utils_1.tsUtils.statement.getTypeAliases(file); return lodash_1.default.fromPairs(decls.map((decl) => { const type = ts_utils_1.tsUtils.type_.getType(this.context.typeChecker, decl); const symbol = ts_utils_1.tsUtils.type_.getAliasSymbol(type); return [ts_utils_1.tsUtils.node.getName(decl), symbol]; })); }); } getFiles() { return this.memoized('file-cache', 'files', () => [this.getGlobals(), this.getContract(), this.getTestGlobals()].filter(utils_1.utils.notNull)); } getGlobals() { return this.memoized('file-cache', 'globals', () => ts_utils_1.tsUtils.file.getSourceFileOrThrow(this.context.program, this.context.host.getSmartContractPath('global.d.ts'))); } getContract() { return this.memoized('file-cache', 'contract', () => ts_utils_1.tsUtils.file.getSourceFileOrThrow(this.context.program, this.context.host.getSmartContractPath('index.d.ts'))); } getTestGlobals() { return this.memoized('file-cache', 'test', () => ts_utils_1.tsUtils.file.getSourceFile(this.context.program, this.context.host.getSmartContractPath('harness.d.ts'))); } } exports.Builtins = Builtins; //# sourceMappingURL=Builtins.js.map