@neo-one/smart-contract-compiler-esnext-esm
Version:
NEO•ONE TypeScript smart contract compiler.
227 lines (225 loc) • 11.5 kB
JavaScript
import { common } from '@neo-one/client-common-esnext-esm';
import { tsUtils } from '@neo-one/ts-utils-esnext-esm';
import _ from 'lodash';
import { Helper } from '../Helper';
import { isAddress, isHash256, isPublicKey } from './buffer';
import { isOnlyMap } from './map';
export class WrapValRecursiveHelper extends Helper {
constructor(options) {
super();
this.checkValue = options.checkValue === undefined ? false : options.checkValue;
this.type = options.type;
this.optional = options.optional;
}
emit(sb, node, options) {
if (this.type === undefined) {
return;
}
const createHandleValue = (hasValue, body) => (innerOptions) => {
if (!innerOptions.pushValue) {
if (hasValue) {
sb.emitOp(node, 'DROP');
}
return;
}
body(innerOptions);
};
const handleUndefined = createHandleValue(false, (innerOptions) => {
sb.emitOp(node, 'DROP');
sb.emitHelper(node, innerOptions, sb.helpers.wrapUndefined);
});
const handleBoolean = createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapBoolean);
});
const type = tsUtils.type_.getNonNullableType(this.type);
sb.emitHelper(node, options, sb.helpers.forBuiltinType({
type: this.type,
single: true,
singleUndefined: handleUndefined,
singleFalse: handleBoolean,
optional: this.optional,
array: createHandleValue(true, (innerOptions) => {
const elements = tsUtils.type_.getTupleElements(type);
if (elements === undefined) {
sb.emitHelper(node, innerOptions, sb.helpers.arrMap({
map: (innerInnerOptions) => {
sb.emitHelper(node, innerInnerOptions, sb.helpers.wrapValRecursive({
checkValue: this.checkValue,
type: sb.context.analysis.getNotAnyType(node, tsUtils.type_.getArrayType(type)),
}));
},
}));
}
else {
const tupleElements = elements.map((element) => {
const constraintType = tsUtils.type_.getConstraint(element);
return sb.context.analysis.getNotAnyType(node, constraintType === undefined ? element : constraintType);
});
_.reverse([...tupleElements]).forEach((element, idx) => {
sb.emitOp(node, 'DUP');
sb.emitPushInt(node, elements.length - idx - 1);
sb.emitOp(node, 'PICKITEM');
sb.emitHelper(node, innerOptions, sb.helpers.wrapValRecursive({ checkValue: this.checkValue, type: element }));
sb.emitOp(node, 'SWAP');
});
sb.emitOp(node, 'DROP');
sb.emitPushInt(node, elements.length);
sb.emitOp(node, 'PACK');
}
sb.emitHelper(node, innerOptions, sb.helpers.wrapArray);
}),
arrayStorage: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
boolean: handleBoolean,
buffer: createHandleValue(true, (innerOptions) => {
if (this.checkValue &&
(isAddress(sb.context, node, type) ||
isHash256(sb.context, node, type) ||
isPublicKey(sb.context, node, type))) {
sb.emitOp(node, 'DUP');
sb.emitOp(node, 'SIZE');
sb.emitHelper(node, innerOptions, sb.helpers.if({
condition: () => {
const expectedSize = isAddress(sb.context, node, type)
? common.UINT160_BUFFER_BYTES
: isHash256(sb.context, node, type)
? common.UINT256_BUFFER_BYTES
: common.ECPOINT_BUFFER_BYTES;
sb.emitPushInt(node, expectedSize);
sb.emitOp(node, 'NUMEQUAL');
},
whenFalse: () => {
sb.emitOp(node, 'DROP');
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
},
}));
}
sb.emitHelper(node, innerOptions, sb.helpers.wrapBuffer);
}),
null: createHandleValue(false, (innerOptions) => {
sb.emitOp(node, 'DROP');
sb.emitHelper(node, innerOptions, sb.helpers.wrapNull);
}),
number: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapNumber);
}),
object: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, options, sb.helpers.createObject);
tsUtils.type_.getProperties(type).forEach((prop) => {
const propType = sb.context.analysis.getTypeOfSymbol(prop, node);
sb.emitOp(node, 'TUCK');
sb.emitOp(node, 'OVER');
sb.emitPushString(node, tsUtils.symbol.getName(prop));
sb.emitOp(node, 'OVER');
sb.emitOp(node, 'OVER');
sb.emitHelper(node, innerOptions, sb.helpers.if({
condition: () => {
sb.emitOp(node, 'HASKEY');
},
whenTrue: () => {
sb.emitOp(node, 'PICKITEM');
sb.emitHelper(node, innerOptions, sb.helpers.wrapValRecursive({ type: propType }));
},
whenFalse: () => {
sb.emitOp(node, 'DROP');
sb.emitOp(node, 'DROP');
sb.emitHelper(node, innerOptions, sb.helpers.wrapUndefined);
},
}));
sb.emitPushString(node, tsUtils.symbol.getName(prop));
sb.emitOp(node, 'SWAP');
sb.emitHelper(node, innerOptions, sb.helpers.setDataPropertyObjectProperty);
sb.emitOp(node, 'SWAP');
});
sb.emitOp(node, 'NIP');
}),
string: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapString);
}),
symbol: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapSymbol);
}),
undefined: handleUndefined,
map: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.mapMap({
map: (innerInnerOptions) => {
let keyType;
let valueType;
if (isOnlyMap(sb.context, node, type)) {
const localKeyType = tsUtils.type_.getTypeArgumentsArray(type)[0];
keyType =
localKeyType === undefined ? undefined : sb.context.analysis.getNotAnyType(node, localKeyType);
const localValueType = tsUtils.type_.getTypeArgumentsArray(type)[1];
valueType =
localValueType === undefined ? undefined : sb.context.analysis.getNotAnyType(node, localValueType);
}
sb.emitHelper(node, innerInnerOptions, sb.helpers.wrapValRecursive({
checkValue: this.checkValue,
type: keyType,
}));
sb.emitOp(node, 'SWAP');
sb.emitHelper(node, innerInnerOptions, sb.helpers.wrapValRecursive({
checkValue: this.checkValue,
type: valueType,
}));
sb.emitOp(node, 'SWAP');
},
}));
sb.emitHelper(node, innerOptions, sb.helpers.wrapMap);
}),
mapStorage: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
set: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
setStorage: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
error: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
forwardValue: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapForwardValue);
}),
iteratorResult: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
iterable: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
iterableIterator: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.throwTypeError);
}),
transaction: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapTransaction);
}),
output: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapOutput);
}),
attribute: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapAttribute);
}),
input: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapInput);
}),
account: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapAccount);
}),
asset: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapAsset);
}),
contract: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapContract);
}),
header: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapHeader);
}),
block: createHandleValue(true, (innerOptions) => {
sb.emitHelper(node, innerOptions, sb.helpers.wrapBlock);
}),
}));
}
}
//# sourceMappingURL=WrapValRecursiveHelper.js.map