@@ -2505,6 +2505,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
2505
2505
appendCode () << " mstore(add(" << m_utils.allocateUnboundedFunction () << " () , " << to_string (returnInfo.estimatedReturnSize ) << " ), 0)\n " ;
2506
2506
}
2507
2507
2508
+ // NOTE: When the expected size of returndata is static, we pass that in to the call opcode and it gets copied automatically.
2509
+ // When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy().
2508
2510
Whiskers templ (R"(
2509
2511
<?checkExtcodesize>
2510
2512
if iszero(extcodesize(<address>)) { <revertNoCode>() }
@@ -2514,22 +2516,29 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
2514
2516
mstore(<pos>, <shl28>(<funSel>))
2515
2517
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
2516
2518
2517
- let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize >)
2519
+ let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <staticReturndataSize >)
2518
2520
<?noTryCall>
2519
2521
if iszero(<success>) { <forwardingRevert>() }
2520
2522
</noTryCall>
2521
2523
<?+retVars> let <retVars> </+retVars>
2522
2524
if <success> {
2523
- <?dynamicReturnSize>
2524
- // copy dynamic return data out
2525
- returndatacopy(<pos>, 0, returndatasize())
2526
- </dynamicReturnSize>
2525
+ <?isReturndataSizeDynamic>
2526
+ let <returnDataSizeVar> := returndatasize()
2527
+ returndatacopy(<pos>, 0, <returnDataSizeVar>)
2528
+ <!isReturndataSizeDynamic>
2529
+ let <returnDataSizeVar> := <staticReturndataSize>
2530
+ <?supportsReturnData>
2531
+ if gt(<returnDataSizeVar>, returndatasize()) {
2532
+ <returnDataSizeVar> := returndatasize()
2533
+ }
2534
+ </supportsReturnData>
2535
+ </isReturndataSizeDynamic>
2527
2536
2528
2537
// update freeMemoryPointer according to dynamic return size
2529
- <finalizeAllocation>(<pos>, <returnSize >)
2538
+ <finalizeAllocation>(<pos>, <returnDataSizeVar >)
2530
2539
2531
2540
// decode return parameters from external try-call into retVars
2532
- <?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize >))
2541
+ <?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnDataSizeVar >))
2533
2542
}
2534
2543
)" );
2535
2544
templ (" revertNoCode" , m_utils.revertReasonIfDebugFunction (" Target contract does not contain code" ));
@@ -2558,21 +2567,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
2558
2567
templ (" funSel" , IRVariable (_functionCall.expression ()).part (" functionSelector" ).name ());
2559
2568
templ (" address" , IRVariable (_functionCall.expression ()).part (" address" ).name ());
2560
2569
2561
- // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
2562
- // This ensures it can catch badly formatted input from external calls.
2563
- if (m_context.evmVersion ().supportsReturndata ())
2564
- templ (" returnSize" , " returndatasize()" );
2565
- else
2566
- templ (" returnSize" , to_string (returnInfo.estimatedReturnSize ));
2567
-
2568
- templ (" reservedReturnSize" , returnInfo.dynamicReturnSize ? " 0" : to_string (returnInfo.estimatedReturnSize ));
2570
+ if (returnInfo.dynamicReturnSize )
2571
+ solAssert (m_context.evmVersion ().supportsReturndata ());
2572
+ templ (" returnDataSizeVar" , m_context.newYulVariable ());
2573
+ templ (" staticReturndataSize" , to_string (returnInfo.estimatedReturnSize ));
2574
+ templ (" supportsReturnData" , m_context.evmVersion ().supportsReturndata ());
2569
2575
2570
2576
string const retVars = IRVariable (_functionCall).commaSeparatedList ();
2571
2577
templ (" retVars" , retVars);
2572
2578
solAssert (retVars.empty () == returnInfo.returnTypes .empty ());
2573
2579
2574
2580
templ (" abiDecode" , m_context.abiFunctions ().tupleDecoder (returnInfo.returnTypes , true ));
2575
- templ (" dynamicReturnSize " , returnInfo.dynamicReturnSize );
2581
+ templ (" isReturndataSizeDynamic " , returnInfo.dynamicReturnSize );
2576
2582
2577
2583
templ (" noTryCall" , !_functionCall.annotation ().tryCall );
2578
2584
0 commit comments