From 7dcefcbf71ef337103639bbe3f4ad2625565651a Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Wed, 15 Apr 2020 16:49:03 -0400 Subject: [PATCH] Better error reporting for Fragments. --- packages/abi/src.ts/fragments.ts | 87 +++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/packages/abi/src.ts/fragments.ts b/packages/abi/src.ts/fragments.ts index a3fa858f6c..1c7006c19d 100644 --- a/packages/abi/src.ts/fragments.ts +++ b/packages/abi/src.ts/fragments.ts @@ -70,7 +70,7 @@ function parseParamType(param: string, allowIndexed: boolean): ParseNode { let originalParam = param; function throwError(i: number) { - throw new Error("unexpected character '" + originalParam[i] + "' at position " + i + " in '" + originalParam + "'"); + logger.throwArgumentError(`unexpected character at position ${ i }`, "param", param); } param = param.replace(/\s/g, " "); @@ -207,7 +207,7 @@ function parseParamType(param: string, allowIndexed: boolean): ParseNode { } } - if (node.parent) { throw new Error("unexpected eof"); } + if (node.parent) { logger.throwArgumentError("unexpected eof", "param", param); } delete parent.state; @@ -272,7 +272,9 @@ export class ParamType { readonly _isParamType: boolean; constructor(constructorGuard: any, params: any) { - if (constructorGuard !== _constructorGuard) { throw new Error("use fromString"); } + if (constructorGuard !== _constructorGuard) { logger.throwError("use fromString", Logger.errors.UNSUPPORTED_OPERATION, { + operation: "new ParamType()" + }); } populate(this, params); let match = this.type.match(paramTypeArray); @@ -398,7 +400,11 @@ export abstract class Fragment { readonly _isFragment: boolean; constructor(constructorGuard: any, params: any) { - if (constructorGuard !== _constructorGuard) { throw new Error("use a static from method"); } + if (constructorGuard !== _constructorGuard) { + logger.throwError("use a static from method", Logger.errors.UNSUPPORTED_OPERATION, { + operation: "new Fragment()" + }); + } populate(this, params); this._isFragment = true; @@ -451,7 +457,7 @@ export abstract class Fragment { return ConstructorFragment.fromString(value.trim()); } - throw new Error("unknown fragment"); + return logger.throwArgumentError("unsupported fragment", "value", value); } static isFragment(value: any): value is Fragment { @@ -506,7 +512,9 @@ export class EventFragment extends Fragment { static fromObject(value: JsonFragment | EventFragment): EventFragment { if (EventFragment.isEventFragment(value)) { return value; } - if (value.type !== "event") { throw new Error("invalid event object - " + value.type); } + if (value.type !== "event") { + logger.throwArgumentError("invalid event object", "value", value); + } return new EventFragment(_constructorGuard, { name: verifyIdentifier(value.name), @@ -519,7 +527,9 @@ export class EventFragment extends Fragment { static fromString(value: string): EventFragment { let match = value.match(regexParen); - if (!match) { throw new Error("invalid event: " + value); } + if (!match) { + logger.throwArgumentError("invalid event string", "value", value); + } let anonymous = false; match[3].split(" ").forEach((modifier) => { @@ -553,10 +563,10 @@ function parseGas(value: string, params: any): string { let comps = value.split("@"); if (comps.length !== 1) { if (comps.length > 2) { - throw new Error("invalid signature"); + logger.throwArgumentError("invalid human-readable ABI signature", "value", value); } if (!comps[1].match(/^[0-9]+$/)) { - throw new Error("invalid signature gas"); + logger.throwArgumentError("invalid human-readable aBI signature gas", "value", value); } params.gas = BigNumber.from(comps[1]); return comps[0]; @@ -597,7 +607,20 @@ function parseModifiers(value: string, params: any): void { }); } -function verifyState(value: any): { constant: boolean, payable: boolean, stateMutability: string } { +type StateInputValue = { + constant?: boolean; + payable?: boolean; + stateMutability?: string; + type?: string; +}; + +type StateOutputValue = { + constant: boolean; + payable: boolean; + stateMutability: string; +}; + +function verifyState(value: StateInputValue): StateOutputValue { let result: any = { constant: false, payable: true, @@ -611,7 +634,7 @@ function verifyState(value: any): { constant: boolean, payable: boolean, stateMu result.constant = (result.stateMutability === "view" || result.stateMutability === "pure"); if (value.constant != null) { if ((!!value.constant) !== result.constant) { - throw new Error("cannot have constant function with mutability " + result.stateMutability); + logger.throwArgumentError("cannot have constant function with mutability " + result.stateMutability, "value", value); } } @@ -619,7 +642,7 @@ function verifyState(value: any): { constant: boolean, payable: boolean, stateMu result.payable = (result.stateMutability === "payable"); if (value.payable != null) { if ((!!value.payable) !== result.payable) { - throw new Error("cannot have payable function with mutability " + result.stateMutability); + logger.throwArgumentError("cannot have payable function with mutability " + result.stateMutability, "value", value); } } @@ -628,7 +651,7 @@ function verifyState(value: any): { constant: boolean, payable: boolean, stateMu // If payable we can assume non-constant; otherwise we can't assume if (value.constant == null && !result.payable && value.type !== "constructor") { - throw new Error("unable to determine stateMutability"); + logger.throwArgumentError("unable to determine stateMutability", "value", value); } result.constant = !!value.constant; @@ -640,7 +663,7 @@ function verifyState(value: any): { constant: boolean, payable: boolean, stateMu } if (result.payable && result.constant) { - throw new Error("cannot have constant payable function"); + logger.throwArgumentError("cannot have constant payable function", "value", value); } } else if (value.constant != null) { @@ -649,7 +672,7 @@ function verifyState(value: any): { constant: boolean, payable: boolean, stateMu result.stateMutability = (result.constant ? "view": "payable"); } else if (value.type !== "constructor") { - throw new Error("unable to determine stateMutability"); + logger.throwArgumentError("unable to determine stateMutability", "value", value); } return result; @@ -703,11 +726,13 @@ export class ConstructorFragment extends Fragment { static fromObject(value: ConstructorFragment | JsonFragment): ConstructorFragment { if (ConstructorFragment.isConstructorFragment(value)) { return value; } - if (value.type !== "constructor") { throw new Error("invalid constructor object - " + value.type); } + if (value.type !== "constructor") { + logger.throwArgumentError("invalid constructor object", "value", value); + } let state = verifyState(value); if (state.constant) { - throw new Error("constructor cannot be constant"); + logger.throwArgumentError("constructor cannot be constant", "value", value); } return new ConstructorFragment(_constructorGuard, { @@ -725,9 +750,9 @@ export class ConstructorFragment extends Fragment { value = parseGas(value, params); let parens = value.match(regexParen); - if (!parens) { throw new Error("invalid constructor: " + value); } - - if (parens[1].trim() !== "constructor") { throw new Error("invalid constructor"); } + if (!parens || parens[1].trim() !== "constructor") { + logger.throwArgumentError("invalid constructor string", "value", value); + } params.inputs = parseParams(parens[2].trim(), false); @@ -807,7 +832,9 @@ export class FunctionFragment extends ConstructorFragment { static fromObject(value: FunctionFragment | JsonFragment): FunctionFragment { if (FunctionFragment.isFunctionFragment(value)) { return value; } - if (value.type !== "function") { throw new Error("invalid function object - " + value.type); } + if (value.type !== "function") { + logger.throwArgumentError("invalid function object", "value", value); + } let state = verifyState(value); @@ -828,15 +855,17 @@ export class FunctionFragment extends ConstructorFragment { value = parseGas(value, params); let comps = value.split(" returns "); - if (comps.length > 2) { throw new Error("invalid function"); } + if (comps.length > 2) { + logger.throwArgumentError("invalid function string", "value", value); + } let parens = comps[0].match(regexParen); - if (!parens) { throw new Error("invalid signature"); } + if (!parens) { + logger.throwArgumentError("invalid function signature", "value", value); + } params.name = parens[1].trim(); - if (!params.name.match(regexIdentifier)) { - throw new Error("invalid identifier: '" + params.name + "'"); - } + if (params.name) { verifyIdentifier(params.name); } params.inputs = parseParams(parens[2], false); @@ -846,7 +875,7 @@ export class FunctionFragment extends ConstructorFragment { if (comps.length > 1) { let returns = comps[1].match(regexParen); if (returns[1].trim() != "" || returns[3].trim() != "") { - throw new Error("unexpected tokens"); + logger.throwArgumentError("unexpected tokens", "value", value); } params.outputs = parseParams(returns[2], false); } else { @@ -884,7 +913,7 @@ function verifyType(type: string): string { const regexIdentifier = new RegExp("^[A-Za-z_][A-Za-z0-9_]*$"); function verifyIdentifier(value: string): string { if (!value || !value.match(regexIdentifier)) { - throw new Error("invalid identifier: '" + value + "'"); + logger.throwArgumentError(`invalid identifier "${ value }"`, "value", value); } return value; } @@ -909,7 +938,7 @@ function splitNesting(value: string): Array { } else if (c === ")") { depth--; if (depth === -1) { - throw new Error("unbalanced parenthsis"); + logger.throwArgumentError("unbalanced parenthsis", "value", value); } } }