mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
function base64DecToArr (sBase64) {
|
||||
/*\
|
||||
|*|
|
||||
|*| Base64 / binary data / UTF-8 strings utilities
|
||||
|*|
|
||||
|*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
|
||||
|*|
|
||||
\*/
|
||||
|
||||
/* Array of bytes to Base64 string decoding */
|
||||
|
||||
function b64ToUint6 (nChr) {
|
||||
|
||||
return nChr > 64 && nChr < 91 ?
|
||||
nChr - 65
|
||||
: nChr > 96 && nChr < 123 ?
|
||||
nChr - 71
|
||||
: nChr > 47 && nChr < 58 ?
|
||||
nChr + 4
|
||||
: nChr === 43 ?
|
||||
62
|
||||
: nChr === 47 ?
|
||||
63
|
||||
:
|
||||
0;
|
||||
|
||||
}
|
||||
|
||||
var
|
||||
nInLen = sBase64.length,
|
||||
nOutLen = nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
|
||||
|
||||
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
|
||||
nMod4 = nInIdx & 3;
|
||||
nUint24 |= b64ToUint6(sBase64.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
|
||||
if (nMod4 === 3 || nInLen - nInIdx === 1) {
|
||||
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
|
||||
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
|
||||
}
|
||||
nUint24 = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return taBytes;
|
||||
}
|
||||
@@ -40,6 +40,8 @@ else
|
||||
BUILD_DIR="$1"
|
||||
fi
|
||||
|
||||
apt-get update && apt-get install lz4
|
||||
|
||||
WORKSPACE=/root/project
|
||||
|
||||
cd $WORKSPACE
|
||||
@@ -71,8 +73,8 @@ make soljson
|
||||
|
||||
cd ..
|
||||
mkdir -p upload
|
||||
cp "$BUILD_DIR/libsolc/soljson.js" upload/
|
||||
cp "$BUILD_DIR/libsolc/soljson.js" ./
|
||||
scripts/ci/pack_soljson.sh "$BUILD_DIR/libsolc/soljson.js" "$BUILD_DIR/libsolc/soljson.wasm" upload/soljson.js
|
||||
cp upload/soljson.js ./
|
||||
|
||||
OUTPUT_SIZE=$(ls -la soljson.js)
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
function uncompress(source, uncompressedSize) {
|
||||
/*
|
||||
based off https://github.com/emscripten-core/emscripten/blob/main/third_party/mini-lz4.js
|
||||
The license only applies to the body of this function (``uncompress``).
|
||||
====
|
||||
MiniLZ4: Minimal LZ4 block decoding and encoding.
|
||||
|
||||
based off of node-lz4, https://github.com/pierrec/node-lz4
|
||||
|
||||
====
|
||||
Copyright (c) 2012 Pierre Curto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
====
|
||||
|
||||
changes have the same license
|
||||
*/
|
||||
/**
|
||||
* Decode a block. Assumptions: input contains all sequences of a
|
||||
* chunk, output is large enough to receive the decoded data.
|
||||
* If the output buffer is too small, an error will be thrown.
|
||||
* If the returned value is negative, an error occurred at the returned offset.
|
||||
*
|
||||
* @param {ArrayBufferView} input input data
|
||||
* @param {ArrayBufferView} output output data
|
||||
* @param {number=} sIdx
|
||||
* @param {number=} eIdx
|
||||
* @return {number} number of decoded bytes
|
||||
* @private
|
||||
*/
|
||||
function uncompressBlock (input, output, sIdx, eIdx) {
|
||||
sIdx = sIdx || 0
|
||||
eIdx = eIdx || (input.length - sIdx)
|
||||
// Process each sequence in the incoming data
|
||||
for (var i = sIdx, n = eIdx, j = 0; i < n;) {
|
||||
var token = input[i++]
|
||||
|
||||
// Literals
|
||||
var literals_length = (token >> 4)
|
||||
if (literals_length > 0) {
|
||||
// length of literals
|
||||
var l = literals_length + 240
|
||||
while (l === 255) {
|
||||
l = input[i++]
|
||||
literals_length += l
|
||||
}
|
||||
|
||||
// Copy the literals
|
||||
var end = i + literals_length
|
||||
while (i < end) output[j++] = input[i++]
|
||||
|
||||
// End of buffer?
|
||||
if (i === n) return j
|
||||
}
|
||||
|
||||
// Match copy
|
||||
// 2 bytes offset (little endian)
|
||||
var offset = input[i++] | (input[i++] << 8)
|
||||
|
||||
// XXX 0 is an invalid offset value
|
||||
if (offset === 0) return j
|
||||
if (offset > j) return -(i-2)
|
||||
|
||||
// length of match copy
|
||||
var match_length = (token & 0xf)
|
||||
var l = match_length + 240
|
||||
while (l === 255) {
|
||||
l = input[i++]
|
||||
match_length += l
|
||||
}
|
||||
// Copy the match
|
||||
var pos = j - offset // position of the match copy in the current output
|
||||
var end = j + match_length + 4 // minmatch = 4
|
||||
while (j < end) output[j++] = output[pos++]
|
||||
}
|
||||
|
||||
return j
|
||||
}
|
||||
var result = new ArrayBuffer(uncompressedSize);
|
||||
var sourceIndex = 0;
|
||||
var destIndex = 0;
|
||||
var blockSize;
|
||||
while((blockSize = (source[sourceIndex] | (source[sourceIndex + 1] << 8) | (source[sourceIndex + 2] << 16) | (source[sourceIndex + 3] << 24))) > 0)
|
||||
{
|
||||
sourceIndex += 4;
|
||||
if (blockSize & 0x80000000)
|
||||
{
|
||||
blockSize &= 0x7FFFFFFFF;
|
||||
for (var i = 0; i < blockSize; i++) {
|
||||
result[destIndex++] = source[sourceIndex++];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
destIndex += uncompressBlock(source, new Uint8Array(result, destIndex, uncompressedSize - destIndex), sourceIndex, sourceIndex + blockSize);
|
||||
sourceIndex += blockSize;
|
||||
}
|
||||
}
|
||||
return new Uint8Array(result, 0, uncompressedSize);
|
||||
}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(realpath "$(dirname "$0")")"
|
||||
soljson_js="$1"
|
||||
soljson_wasm="$2"
|
||||
soljson_wasm_size=$(wc -c "${soljson_wasm}" | cut -d ' ' -f 1)
|
||||
output="$3"
|
||||
|
||||
(( $# == 3 )) || { >&2 echo "Usage: $0 soljson.js soljson.wasm packed_soljson.js"; exit 1; }
|
||||
|
||||
# If this changes in an emscripten update, it's probably nothing to worry about,
|
||||
# but we should double-check when it happens and adjust the tail command below.
|
||||
[[ $(head -c 5 "${soljson_js}") == "null;" ]] || { >&2 echo 'Expected soljson.js to start with "null;"'; exit 1; }
|
||||
|
||||
echo "Packing $soljson_js and $soljson_wasm to $output."
|
||||
(
|
||||
echo -n 'var Module = Module || {}; Module["wasmBinary"] = '
|
||||
echo -n '(function(source, uncompressedSize) {'
|
||||
# Note that base64DecToArr assumes no trailing equals signs.
|
||||
cpp "${script_dir}/base64DecToArr.js" | grep -v "^#.*"
|
||||
# Note that mini-lz4.js assumes no file header and no frame crc checksums.
|
||||
cpp "${script_dir}/mini-lz4.js" | grep -v "^#.*"
|
||||
echo 'return uncompress(base64DecToArr(source), uncompressedSize);})('
|
||||
echo -n '"'
|
||||
# We fix lz4 format settings, remove the 8 bytes file header and remove the trailing equals signs of the base64 encoding.
|
||||
lz4c --no-frame-crc --best --favor-decSpeed "${soljson_wasm}" - | tail -c +8 | base64 -w 0 | sed 's/[^A-Za-z0-9\+\/]//g'
|
||||
echo '",'
|
||||
echo -n "${soljson_wasm_size});"
|
||||
# Remove "null;" from the js wrapper.
|
||||
tail -c +6 "${soljson_js}"
|
||||
) > "$output"
|
||||
|
||||
echo "Testing $output."
|
||||
echo "process.stdout.write(require('$(realpath "${output}")').wasmBinary)" | node | cmp "${soljson_wasm}" && echo "Binaries match."
|
||||
# Allow the wasm binary to be garbage collected after compilation.
|
||||
echo 'Module["wasmBinary"] = undefined;' >> "${output}"
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Reads multiple individual benchmark reports produced by scripts from
|
||||
# test/externalTests/ from standard input and creates a combined report.
|
||||
#
|
||||
# Usage:
|
||||
# <script name>.sh < <CONCATENATED_REPORTS>
|
||||
#
|
||||
# CONCATENATED_REPORTS: JSON report files concatenated into a single stream (e.g. using cat).
|
||||
#
|
||||
# Example:
|
||||
# cat reports/externalTests/benchmark-*.json | <script name>.sh
|
||||
# ------------------------------------------------------------------------------
|
||||
# This file is part of solidity.
|
||||
#
|
||||
# solidity is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# solidity is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
# (c) 2021 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# We expect a series of dicts of the form {"<project>": {"<preset>": {...}}}.
|
||||
# Unfortunately jq's built-in `add` filter can't handle nested dicts and
|
||||
# would just overwrite values sharing a project name instead of merging them.
|
||||
|
||||
# This is done by first grouping the dicts into an array of the form
|
||||
# [
|
||||
# [{"key": "<project1>", "value": {"<preset1>": {...}}}, {"key": "<project1>", "value": {"<preset2>": {...}}, ...],
|
||||
# [{"key": "<project2>", "value": {"<preset1>": {...}}}, {"key": "<project2>", "value": {"<preset2>": {...}}, ...],
|
||||
# ...
|
||||
# ]
|
||||
# and then using reduce() on each group sharing the same project name to convert it into a
|
||||
# dict having preset names as keys.
|
||||
jq --slurp --indent 4 --sort-keys '
|
||||
map(to_entries[]) |
|
||||
group_by(.key) |
|
||||
map({
|
||||
(.[0].key): (
|
||||
reduce (.[].value | to_entries[]) as {$key, $value} (
|
||||
{}; . + {
|
||||
($key): $value
|
||||
}
|
||||
)
|
||||
)
|
||||
}) |
|
||||
add
|
||||
'
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from typing import Dict, Optional, Tuple
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
REPORT_HEADER_REGEX = re.compile(r'''
|
||||
^[|\s]+ Solc[ ]version:\s*(?P<solc_version>[\w\d.]+)
|
||||
[|\s]+ Optimizer[ ]enabled:\s*(?P<optimize>[\w]+)
|
||||
[|\s]+ Runs:\s*(?P<runs>[\d]+)
|
||||
[|\s]+ Block[ ]limit:\s*(?P<block_limit>[\d]+)\s*gas
|
||||
[|\s]+$
|
||||
''', re.VERBOSE)
|
||||
METHOD_HEADER_REGEX = re.compile(r'^[|\s]+Methods[|\s]+$')
|
||||
METHOD_COLUMN_HEADERS_REGEX = re.compile(r'''
|
||||
^[|\s]+ Contract
|
||||
[|\s]+ Method
|
||||
[|\s]+ Min
|
||||
[|\s]+ Max
|
||||
[|\s]+ Avg
|
||||
[|\s]+ \#[ ]calls
|
||||
[|\s]+ \w+[ ]\(avg\)
|
||||
[|\s]+$
|
||||
''', re.VERBOSE)
|
||||
METHOD_ROW_REGEX = re.compile(r'''
|
||||
^[|\s]+ (?P<contract>[^|]+)
|
||||
[|\s]+ (?P<method>[^|]+)
|
||||
[|\s]+ (?P<min>[^|]+)
|
||||
[|\s]+ (?P<max>[^|]+)
|
||||
[|\s]+ (?P<avg>[^|]+)
|
||||
[|\s]+ (?P<call_count>[^|]+)
|
||||
[|\s]+ (?P<eur_avg>[^|]+)
|
||||
[|\s]+$
|
||||
''', re.VERBOSE)
|
||||
FRAME_REGEX = re.compile(r'^[-|\s]+$')
|
||||
DEPLOYMENT_HEADER_REGEX = re.compile(r'^[|\s]+Deployments[|\s]+% of limit[|\s]+$')
|
||||
DEPLOYMENT_ROW_REGEX = re.compile(r'''
|
||||
^[|\s]+ (?P<contract>[^|]+)
|
||||
[|\s]+ (?P<min>[^|]+)
|
||||
[|\s]+ (?P<max>[^|]+)
|
||||
[|\s]+ (?P<avg>[^|]+)
|
||||
[|\s]+ (?P<percent_of_limit>[^|]+)\s*%
|
||||
[|\s]+ (?P<eur_avg>[^|]+)
|
||||
[|\s]+$
|
||||
''', re.VERBOSE)
|
||||
|
||||
|
||||
class ReportError(Exception):
|
||||
pass
|
||||
|
||||
class ReportValidationError(ReportError):
|
||||
pass
|
||||
|
||||
class ReportParsingError(Exception):
|
||||
def __init__(self, message: str, line: str, line_number: int):
|
||||
# pylint: disable=useless-super-delegation # It's not useless, it adds type annotations.
|
||||
super().__init__(message, line, line_number)
|
||||
|
||||
def __str__(self):
|
||||
return f"Parsing error on line {self.args[2] + 1}: {self.args[0]}\n{self.args[1]}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MethodGasReport:
|
||||
min_gas: int
|
||||
max_gas: int
|
||||
avg_gas: int
|
||||
call_count: int
|
||||
total_gas: int = field(init=False)
|
||||
|
||||
def __post_init__(self):
|
||||
object.__setattr__(self, 'total_gas', self.avg_gas * self.call_count)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ContractGasReport:
|
||||
min_deployment_gas: Optional[int]
|
||||
max_deployment_gas: Optional[int]
|
||||
avg_deployment_gas: Optional[int]
|
||||
methods: Optional[Dict[str, MethodGasReport]]
|
||||
total_method_gas: int = field(init=False, default=0)
|
||||
|
||||
def __post_init__(self):
|
||||
if self.methods is not None:
|
||||
object.__setattr__(self, 'total_method_gas', sum(method.total_gas for method in self.methods.values()))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GasReport:
|
||||
solc_version: str
|
||||
optimize: bool
|
||||
runs: int
|
||||
block_limit: int
|
||||
contracts: Dict[str, ContractGasReport]
|
||||
total_method_gas: int = field(init=False)
|
||||
total_deployment_gas: int = field(init=False)
|
||||
|
||||
def __post_init__(self):
|
||||
object.__setattr__(self, 'total_method_gas', sum(
|
||||
total_method_gas
|
||||
for total_method_gas in (contract.total_method_gas for contract in self.contracts.values())
|
||||
if total_method_gas is not None
|
||||
))
|
||||
object.__setattr__(self, 'total_deployment_gas', sum(
|
||||
contract.avg_deployment_gas
|
||||
for contract in self.contracts.values()
|
||||
if contract.avg_deployment_gas is not None
|
||||
))
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(asdict(self), indent=4, sort_keys=True)
|
||||
|
||||
|
||||
def parse_bool(input_string: str) -> bool:
|
||||
if input_string == 'true':
|
||||
return True
|
||||
elif input_string == 'false':
|
||||
return True
|
||||
else:
|
||||
raise ValueError(f"Invalid boolean value: '{input_string}'")
|
||||
|
||||
|
||||
def parse_optional_int(input_string: str, default: Optional[int] = None) -> Optional[int]:
|
||||
if input_string.strip() == '-':
|
||||
return default
|
||||
|
||||
return int(input_string)
|
||||
|
||||
|
||||
def parse_report_header(line: str) -> Optional[dict]:
|
||||
match = REPORT_HEADER_REGEX.match(line)
|
||||
if match is None:
|
||||
return None
|
||||
|
||||
return {
|
||||
'solc_version': match.group('solc_version'),
|
||||
'optimize': parse_bool(match.group('optimize')),
|
||||
'runs': int(match.group('runs')),
|
||||
'block_limit': int(match.group('block_limit')),
|
||||
}
|
||||
|
||||
|
||||
def parse_method_row(line: str, line_number: int) -> Optional[Tuple[str, str, MethodGasReport]]:
|
||||
match = METHOD_ROW_REGEX.match(line)
|
||||
if match is None:
|
||||
raise ReportParsingError("Expected a table row with method details.", line, line_number)
|
||||
|
||||
avg_gas = parse_optional_int(match['avg'])
|
||||
call_count = int(match['call_count'])
|
||||
|
||||
if avg_gas is None and call_count == 0:
|
||||
# No calls, no gas values. Uninteresting. Skip the row.
|
||||
return None
|
||||
|
||||
return (
|
||||
match['contract'].strip(),
|
||||
match['method'].strip(),
|
||||
MethodGasReport(
|
||||
min_gas=parse_optional_int(match['min'], avg_gas),
|
||||
max_gas=parse_optional_int(match['max'], avg_gas),
|
||||
avg_gas=avg_gas,
|
||||
call_count=call_count,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def parse_deployment_row(line: str, line_number: int) -> Tuple[str, int, int, int]:
|
||||
match = DEPLOYMENT_ROW_REGEX.match(line)
|
||||
if match is None:
|
||||
raise ReportParsingError("Expected a table row with deployment details.", line, line_number)
|
||||
|
||||
return (
|
||||
match['contract'].strip(),
|
||||
parse_optional_int(match['min'].strip()),
|
||||
parse_optional_int(match['max'].strip()),
|
||||
int(match['avg'].strip()),
|
||||
)
|
||||
|
||||
|
||||
def preprocess_unicode_frames(input_string: str) -> str:
|
||||
# The report has a mix of normal pipe chars and its unicode variant.
|
||||
# Let's just replace all frame chars with normal pipes for easier parsing.
|
||||
return input_string.replace('\u2502', '|').replace('·', '|')
|
||||
|
||||
|
||||
def parse_report(rst_report: str) -> GasReport:
|
||||
report_params = None
|
||||
methods_by_contract = {}
|
||||
deployment_costs = {}
|
||||
expected_row_type = None
|
||||
|
||||
for line_number, line in enumerate(preprocess_unicode_frames(rst_report).splitlines()):
|
||||
try:
|
||||
if (
|
||||
line.strip() == "" or
|
||||
FRAME_REGEX.match(line) is not None or
|
||||
METHOD_COLUMN_HEADERS_REGEX.match(line) is not None
|
||||
):
|
||||
continue
|
||||
if METHOD_HEADER_REGEX.match(line) is not None:
|
||||
expected_row_type = 'method'
|
||||
continue
|
||||
if DEPLOYMENT_HEADER_REGEX.match(line) is not None:
|
||||
expected_row_type = 'deployment'
|
||||
continue
|
||||
|
||||
new_report_params = parse_report_header(line)
|
||||
if new_report_params is not None:
|
||||
if report_params is not None:
|
||||
raise ReportParsingError("Duplicate report header.", line, line_number)
|
||||
|
||||
report_params = new_report_params
|
||||
continue
|
||||
|
||||
if expected_row_type == 'method':
|
||||
parsed_row = parse_method_row(line, line_number)
|
||||
if parsed_row is None:
|
||||
continue
|
||||
|
||||
(contract, method, method_report) = parsed_row
|
||||
|
||||
if contract not in methods_by_contract:
|
||||
methods_by_contract[contract] = {}
|
||||
|
||||
if method in methods_by_contract[contract]:
|
||||
# Report must be generated with full signatures for method names to be unambiguous.
|
||||
raise ReportParsingError(f"Duplicate method row for '{contract}.{method}'.", line, line_number)
|
||||
|
||||
methods_by_contract[contract][method] = method_report
|
||||
elif expected_row_type == 'deployment':
|
||||
(contract, min_gas, max_gas, avg_gas) = parse_deployment_row(line, line_number)
|
||||
|
||||
if contract in deployment_costs:
|
||||
raise ReportParsingError(f"Duplicate contract deployment row for '{contract}'.", line, line_number)
|
||||
|
||||
deployment_costs[contract] = (min_gas, max_gas, avg_gas)
|
||||
else:
|
||||
assert expected_row_type is None
|
||||
raise ReportParsingError("Found data row without a section header.", line, line_number)
|
||||
|
||||
except ValueError as error:
|
||||
raise ReportParsingError(error.args[0], line, line_number) from error
|
||||
|
||||
if report_params is None:
|
||||
raise ReportValidationError("Report header not found.")
|
||||
|
||||
report_params['contracts'] = {
|
||||
contract: ContractGasReport(
|
||||
min_deployment_gas=deployment_costs.get(contract, (None, None, None))[0],
|
||||
max_deployment_gas=deployment_costs.get(contract, (None, None, None))[1],
|
||||
avg_deployment_gas=deployment_costs.get(contract, (None, None, None))[2],
|
||||
methods=methods_by_contract.get(contract),
|
||||
)
|
||||
for contract in methods_by_contract.keys() | deployment_costs.keys()
|
||||
}
|
||||
|
||||
return GasReport(**report_params)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
report = parse_report(sys.stdin.read())
|
||||
print(report.to_json())
|
||||
except ReportError as exception:
|
||||
print(f"{exception}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Reads a combined benchmark report from standard input and outputs an abbreviated
|
||||
# report containing only totals. Can handle individual reports coming directly
|
||||
# from scripts in test/externalTests/ as well as combined report from merge_benchmarks.sh.
|
||||
#
|
||||
# Usage:
|
||||
# <script name>.sh < <CONCATENATED_REPORTS>
|
||||
#
|
||||
# CONCATENATED_REPORTS: JSON report files concatenated into a single stream (e.g. using cat).
|
||||
#
|
||||
# Example:
|
||||
# cat reports/externalTests/benchmark-*.json | <script name>.sh
|
||||
# ------------------------------------------------------------------------------
|
||||
# This file is part of solidity.
|
||||
#
|
||||
# solidity is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# solidity is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
# (c) 2021 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
|
||||
|
||||
# Iterates over presets in a dict of the form {"<project>": {"<preset>": {...}}} and for each
|
||||
# one preserves only the few keys with totals that we want to see in the summary.
|
||||
exec "${REPO_ROOT}/scripts/externalTests/merge_benchmarks.sh" | jq --indent 4 --sort-keys '
|
||||
with_entries({
|
||||
key: .key,
|
||||
value: .value | with_entries({
|
||||
key: .key,
|
||||
value: {
|
||||
bytecode_size: .value.total_bytecode_size,
|
||||
method_gas: .value.gas.total_method_gas,
|
||||
deployment_gas: .value.gas.total_deployment_gas,
|
||||
version: .value.project.version
|
||||
}
|
||||
})
|
||||
})
|
||||
'
|
||||
Reference in New Issue
Block a user