Skip to content

Commit

Permalink
Fixes to isFunctionCallExternal() after end-to-end test (#215)
Browse files Browse the repository at this point in the history
* Reintroduce isFunctionCallExternal() as method of InferType. Impoved detection of calls in external context. Added relevant cases to a test sample.

* Update dependencies

* Update test sample

* Fix few more edge cases to address failures of Scribble tests

* Fix review remarks
  • Loading branch information
blitz-1306 authored Jul 13, 2023
1 parent 18444a4 commit c8e68fe
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 142 deletions.
36 changes: 18 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 93 additions & 1 deletion src/types/infer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
encodeFuncSignature,
resolveAny
} from "../ast";
import { DataLocation } from "../ast/constants";
import { DataLocation, ExternalReferenceType } from "../ast/constants";
import { assert, eq, forAll, forAny, pp } from "../misc";
import { ABIEncoderVersion, abiTypeToCanonicalName, abiTypeToLibraryCanonicalName } from "./abi";
import {
Expand Down Expand Up @@ -98,6 +98,7 @@ import { TypeSubstituion, applySubstitution, buildSubstitutions } from "./polymo
import { types } from "./reserved";
import {
BINARY_OPERATOR_GROUPS,
CALL_BUILTINS,
SUBDENOMINATION_MULTIPLIERS,
castable,
decimalToRational,
Expand Down Expand Up @@ -2662,4 +2663,95 @@ export class InferType {

return resolvedCalleeT;
}

private isExternalCallContext(expr: Expression): boolean {
if (
expr instanceof Identifier ||
expr instanceof MemberAccess ||
expr instanceof FunctionCallOptions ||
expr instanceof FunctionCall
) {
const exprT = this.typeOf(expr);

if (exprT instanceof UserDefinedType) {
if (exprT.definition instanceof ContractDefinition) {
return true;
}
}

if (exprT instanceof TypeNameType) {
return (
exprT.type instanceof UserDefinedType &&
exprT.type.definition instanceof ContractDefinition &&
exprT.type.definition.kind === ContractKind.Library
);
}
}

if (
expr instanceof MemberAccess ||
expr instanceof FunctionCallOptions ||
expr instanceof FunctionCall
) {
return this.isExternalCallContext(expr.vExpression);
}

if (expr instanceof Conditional) {
return (
this.isExternalCallContext(expr.vTrueExpression) ||
this.isExternalCallContext(expr.vFalseExpression)
);
}

if (expr instanceof TupleExpression && expr.vComponents.length === 1) {
return this.isExternalCallContext(expr.vComponents[0]);
}

return false;
}

isFunctionCallExternal(call: FunctionCall): boolean {
if (call.kind !== FunctionCallKind.FunctionCall) {
return false;
}

if (
call.vFunctionCallType === ExternalReferenceType.Builtin &&
CALL_BUILTINS.includes(call.vFunctionName)
) {
return true;
}

let exprT = this.typeOf(call.vExpression);

if (exprT instanceof FunctionLikeSetType) {
const calleeT = this.typeOfCallee(call);

if (!(calleeT instanceof FunctionType)) {
return false;
}

exprT = calleeT;
}

if (exprT instanceof FunctionType) {
if (exprT.implicitFirstArg) {
/**
* Calls via using-for are not considered as external.
* Currently "implicitFirstArg" is used only for using-for.
*/
return false;
}

if (exprT.visibility === FunctionVisibility.External) {
return true;
}

if (exprT.visibility === FunctionVisibility.Public) {
return this.isExternalCallContext(call.vExpression);
}
}

return false;
}
}
49 changes: 1 addition & 48 deletions src/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@ import {
ErrorDefinition,
EventDefinition,
Expression,
ExternalReferenceType,
FunctionCall,
FunctionCallKind,
FunctionDefinition,
FunctionKind,
FunctionStateMutability,
FunctionVisibility,
MemberAccess,
ModifierDefinition,
SourceUnit,
StructDefinition,
TupleExpression,
VariableDeclaration
} from "../ast";
import { assert, eq, forAll, forAny } from "../misc";
import { InferType, types } from "../types";
import { types } from "../types";
import { ABIEncoderVersion } from "./abi";
import {
AddressType,
Expand Down Expand Up @@ -317,49 +313,6 @@ export function getABIEncoderVersion(unit: SourceUnit, compilerVersion: string):
return lt(compilerVersion, "0.8.0") ? ABIEncoderVersion.V1 : ABIEncoderVersion.V2;
}

export function isFunctionCallExternal(call: FunctionCall, inference: InferType): boolean {
if (call.kind !== FunctionCallKind.FunctionCall) {
return false;
}

if (
call.vFunctionCallType === ExternalReferenceType.Builtin &&
CALL_BUILTINS.includes(call.vFunctionName)
) {
return true;
}

const exprT = inference.typeOf(call.vExpression);

if (exprT instanceof FunctionType) {
if (exprT.implicitFirstArg) {
/**
* Calls via using-for are not considered as external.
* Currently "implicitFirstArg" is used only for using-for.
*/
return false;
}

if (exprT.visibility === FunctionVisibility.External) {
return true;
}

if (exprT.visibility === FunctionVisibility.Public) {
/**
* We have external calls when call expression have pattern "expr.fun()",
* where "expr" is an identifier, "this" or an expression of ContractType.
*
* Other expressions, that are invoked via an identifier or struct type
* are requiring function type to have non-public visibility,
* so they are handled by other cases.
*/
return call.vExpression.getChildrenByType(MemberAccess, true).length > 0;
}
}

return false;
}

export function getFallbackRecvFuns(contract: ContractDefinition): FunctionDefinition[] {
const res: FunctionDefinition[] = [];

Expand Down
Loading

0 comments on commit c8e68fe

Please sign in to comment.