@neo-one/smart-contract-compiler
Version:
NEO•ONE TypeScript smart contract compiler.
362 lines (360 loc) • 18.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElementAccessExpressionCompiler = void 0;
const tslib_1 = require("tslib");
const ts_utils_1 = require("@neo-one/ts-utils");
const typescript_1 = tslib_1.__importDefault(require("typescript"));
const DiagnosticCode_1 = require("../../DiagnosticCode");
const DiagnosticMessage_1 = require("../../DiagnosticMessage");
const builtins_1 = require("../builtins");
const constants_1 = require("../constants");
const NodeCompiler_1 = require("../NodeCompiler");
class ElementAccessExpressionCompiler extends NodeCompiler_1.NodeCompiler {
constructor() {
super(...arguments);
this.kind = typescript_1.default.SyntaxKind.ElementAccessExpression;
}
visitNode(sb, expr, optionsIn) {
const isOptionalChain = typescript_1.default.isOptionalChain(expr);
const value = ts_utils_1.tsUtils.expression.getExpression(expr);
const valueType = sb.context.analysis.getType(value);
const prop = ts_utils_1.tsUtils.expression.getArgumentExpressionOrThrow(expr);
const propType = sb.context.analysis.getType(prop);
const handleBuiltin = (member, visited) => {
if (builtins_1.isBuiltinInstanceMemberValue(member)) {
member.emitValue(sb, expr, optionsIn, visited);
return;
}
if (builtins_1.isBuiltinMemberValue(member)) {
member.emitValue(sb, expr, optionsIn);
return;
}
if (optionsIn.setValue) {
sb.context.reportError(prop, DiagnosticCode_1.DiagnosticCode.InvalidBuiltinModify, DiagnosticMessage_1.DiagnosticMessage.CannotModifyBuiltin);
}
else {
sb.context.reportError(prop, DiagnosticCode_1.DiagnosticCode.InvalidBuiltinReference, DiagnosticMessage_1.DiagnosticMessage.CannotReferenceBuiltinProperty);
}
};
const builtinProp = sb.context.builtins.getMember(value, prop);
if (builtinProp !== undefined) {
handleBuiltin(builtinProp, false);
return;
}
const getValueCases = (name, useSymbol = false) => sb.context.builtins
.getMembers(name, builtins_1.isBuiltinInstanceMemberValue, () => true, useSymbol)
.map(([propName, builtin]) => ({
condition: () => {
sb.emitOp(prop, 'DUP');
sb.emitPushString(prop, propName);
sb.emitOp(prop, 'EQUAL');
},
whenTrue: () => {
sb.emitOp(expr, 'DROP');
handleBuiltin(builtin, true);
},
}));
const throwTypeError = (innerOptions) => {
sb.emitOp(expr, 'DROP');
sb.emitHelper(expr, innerOptions, sb.helpers.throwTypeError);
};
const throwInnerTypeError = (innerOptions) => {
sb.emitOp(expr, 'DROP');
throwTypeError(innerOptions);
};
const processUndefined = (innerOptions) => {
sb.emitOp(expr, 'DROP');
sb.emitHelper(expr, innerOptions, sb.helpers.wrapUndefined);
};
const throwTypeErrorUnlessOptionalChain = (innerOptions) => {
isOptionalChain ? processUndefined(innerOptions) : throwTypeError(innerOptions);
};
const createHandleProp = (handleString, handleNumber, handleSymbol) => (innerOptions) => {
sb.visit(prop, innerOptions);
sb.emitHelper(prop, innerOptions, sb.helpers.forBuiltinType({
type: propType,
array: throwInnerTypeError,
arrayStorage: throwInnerTypeError,
boolean: throwInnerTypeError,
buffer: throwInnerTypeError,
null: throwInnerTypeError,
number: handleNumber,
object: throwInnerTypeError,
string: handleString,
symbol: handleSymbol,
undefined: throwInnerTypeError,
map: throwInnerTypeError,
mapStorage: throwInnerTypeError,
set: throwInnerTypeError,
setStorage: throwInnerTypeError,
error: throwInnerTypeError,
forwardValue: throwInnerTypeError,
iteratorResult: throwInnerTypeError,
iterable: throwInnerTypeError,
iterableIterator: throwInnerTypeError,
transaction: throwInnerTypeError,
output: throwInnerTypeError,
attribute: throwInnerTypeError,
input: throwInnerTypeError,
account: throwInnerTypeError,
asset: throwInnerTypeError,
contract: throwInnerTypeError,
header: throwInnerTypeError,
block: throwInnerTypeError,
}));
};
const createProcessBuiltin = (name) => {
const handleStringBase = (innerInnerOptions) => {
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases(name, false), () => {
throwInnerTypeError(innerInnerOptions);
}));
};
const handleString = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString);
handleStringBase(innerInnerOptions);
};
const handleNumber = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.toString({ type: propType, knownType: constants_1.Types.Number }));
handleStringBase(innerInnerOptions);
};
const handleSymbol = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases(name, true), () => {
throwInnerTypeError(innerInnerOptions);
}));
};
return createHandleProp(handleString, handleNumber, handleSymbol);
};
const createProcessArray = () => {
const handleNumberBase = (innerInnerOptions) => {
if (optionsIn.pushValue && optionsIn.setValue) {
sb.emitPushInt(expr, 2);
sb.emitOp(expr, 'PICK');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayIndex);
}
else if (optionsIn.pushValue) {
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getArrayIndex);
}
else if (optionsIn.setValue) {
sb.emitOp(expr, 'ROT');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayIndex);
}
else {
sb.emitOp(expr, 'DROP');
sb.emitOp(expr, 'DROP');
}
};
const handleString = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('Array', false), () => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.wrapString);
sb.emitHelper(prop, innerInnerOptions, sb.helpers.toNumber({ type: propType, knownType: constants_1.Types.String }));
handleNumberBase(innerInnerOptions);
}));
};
const handleNumber = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapNumber);
handleNumberBase(innerInnerOptions);
};
const handleSymbol = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('Array', true), () => {
throwInnerTypeError(innerInnerOptions);
}));
};
return createHandleProp(handleString, handleNumber, handleSymbol);
};
const createProcessArrayStorage = () => {
const handleNumberBase = (innerInnerOptions) => {
if (optionsIn.pushValue && optionsIn.setValue) {
sb.emitPushInt(expr, 2);
sb.emitOp(expr, 'PICK');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayStorage);
}
else if (optionsIn.pushValue) {
sb.emitHelper(expr, options, sb.helpers.wrapNumber);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getStructuredStorage({
type: constants_1.Types.ArrayStorage,
keyType: undefined,
knownKeyType: constants_1.Types.Number,
}));
}
else if (optionsIn.setValue) {
sb.emitOp(expr, 'ROT');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayStorage);
}
else {
sb.emitOp(expr, 'DROP');
sb.emitOp(expr, 'DROP');
}
};
const handleString = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('ArrayStorage', false), () => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.wrapString);
sb.emitHelper(prop, innerInnerOptions, sb.helpers.toNumber({ type: propType, knownType: constants_1.Types.String }));
handleNumberBase(innerInnerOptions);
}));
};
const handleNumber = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapNumber);
sb.emitHelper(prop, innerInnerOptions, sb.helpers.coerceToInt);
handleNumberBase(innerInnerOptions);
};
const handleSymbol = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('ArrayStorage', true), () => {
throwInnerTypeError(innerInnerOptions);
}));
};
return createHandleProp(handleString, handleNumber, handleSymbol);
};
const createProcessBuffer = () => {
const handleNumberBase = (innerInnerOptions) => {
if (optionsIn.pushValue) {
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getBufferIndex);
}
else {
sb.emitOp(expr, 'DROP');
sb.emitOp(expr, 'DROP');
}
};
const handleString = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('Buffer', false), () => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.wrapString);
sb.emitHelper(prop, innerInnerOptions, sb.helpers.toNumber({ type: propType, knownType: constants_1.Types.String }));
handleNumberBase(innerInnerOptions);
}));
};
const handleNumber = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapNumber);
handleNumberBase(innerInnerOptions);
};
const handleSymbol = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('Buffer', true), () => {
throwInnerTypeError(innerInnerOptions);
}));
};
return createHandleProp(handleString, handleNumber, handleSymbol);
};
const processObject = (innerOptions) => {
const handleStringBase = (innerInnerOptions) => {
if (optionsIn.pushValue && optionsIn.setValue) {
sb.emitOp(expr, 'OVER');
sb.emitOp(expr, 'OVER');
sb.emitPushInt(expr, 4);
sb.emitOp(expr, 'ROLL');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setPropertyObjectProperty);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getPropertyObjectProperty);
}
else if (optionsIn.pushValue) {
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getPropertyObjectProperty);
}
else if (optionsIn.setValue) {
sb.emitOp(expr, 'ROT');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setPropertyObjectProperty);
}
};
const handleNumber = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.toString({ type: propType, knownType: constants_1.Types.Number }));
handleStringBase(innerInnerOptions);
};
const handleString = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString);
handleStringBase(innerInnerOptions);
};
const handleSymbol = (innerInnerOptions) => {
sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol);
if (optionsIn.pushValue && optionsIn.setValue) {
sb.emitOp(expr, 'OVER');
sb.emitOp(expr, 'OVER');
sb.emitPushInt(expr, 4);
sb.emitOp(expr, 'ROLL');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setSymbolObjectProperty);
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getSymbolObjectProperty);
}
else if (optionsIn.pushValue) {
sb.emitHelper(expr, innerInnerOptions, sb.helpers.getSymbolObjectProperty);
}
else if (optionsIn.setValue) {
sb.emitOp(expr, 'ROT');
sb.emitHelper(expr, innerInnerOptions, sb.helpers.setSymbolObjectProperty);
}
};
sb.visit(prop, innerOptions);
if (!optionsIn.pushValue && !optionsIn.setValue) {
sb.emitOp(expr, 'DROP');
sb.emitOp(expr, 'DROP');
return;
}
sb.emitHelper(prop, innerOptions, sb.helpers.forBuiltinType({
type: propType,
array: throwInnerTypeError,
arrayStorage: throwInnerTypeError,
boolean: throwInnerTypeError,
buffer: throwInnerTypeError,
null: throwInnerTypeError,
number: handleNumber,
object: throwInnerTypeError,
string: handleString,
symbol: handleSymbol,
undefined: throwInnerTypeError,
map: throwInnerTypeError,
mapStorage: throwInnerTypeError,
set: throwInnerTypeError,
setStorage: throwInnerTypeError,
error: throwInnerTypeError,
forwardValue: throwInnerTypeError,
iteratorResult: throwInnerTypeError,
iterable: throwInnerTypeError,
iterableIterator: throwInnerTypeError,
transaction: throwInnerTypeError,
output: throwInnerTypeError,
attribute: throwInnerTypeError,
input: throwInnerTypeError,
account: throwInnerTypeError,
asset: throwInnerTypeError,
contract: throwInnerTypeError,
header: throwInnerTypeError,
block: throwInnerTypeError,
}));
};
const options = sb.pushValueOptions(sb.noSetValueOptions(optionsIn));
sb.visit(value, options);
sb.emitHelper(value, options, sb.helpers.forBuiltinType({
type: valueType,
array: createProcessArray(),
arrayStorage: createProcessArrayStorage(),
boolean: createProcessBuiltin('Boolean'),
buffer: createProcessBuffer(),
null: throwTypeErrorUnlessOptionalChain,
number: createProcessBuiltin('Number'),
object: processObject,
string: createProcessBuiltin('String'),
symbol: createProcessBuiltin('Symbol'),
undefined: throwTypeErrorUnlessOptionalChain,
map: createProcessBuiltin('Map'),
mapStorage: createProcessBuiltin('MapStorage'),
set: createProcessBuiltin('Set'),
setStorage: createProcessBuiltin('SetStorage'),
error: createProcessBuiltin('Error'),
forwardValue: createProcessBuiltin('ForwardValue'),
iteratorResult: createProcessBuiltin('IteratorResult'),
iterable: createProcessBuiltin('Iterable'),
iterableIterator: createProcessBuiltin('IterableIterator'),
transaction: createProcessBuiltin('TransactionBase'),
output: createProcessBuiltin('Output'),
attribute: createProcessBuiltin('AttributeBase'),
input: createProcessBuiltin('Input'),
account: createProcessBuiltin('Account'),
asset: createProcessBuiltin('Asset'),
contract: createProcessBuiltin('Contract'),
header: createProcessBuiltin('Header'),
block: createProcessBuiltin('Block'),
}));
}
}
exports.ElementAccessExpressionCompiler = ElementAccessExpressionCompiler;
//# sourceMappingURL=ElementAccessExpressionCompiler.js.map