Refactor performance-server to make it work in IDE, and bump npm deps
diff --git a/kotlin-native/tools/performance-server/build.gradle.kts b/kotlin-native/tools/performance-server/build.gradle.kts
index 8171676..5b62254 100644
--- a/kotlin-native/tools/performance-server/build.gradle.kts
+++ b/kotlin-native/tools/performance-server/build.gradle.kts
@@ -34,26 +34,19 @@
kotlin {
sourceSets {
- val commonMain by getting {
+ commonMain {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
}
- kotlin.srcDir("../benchmarks/shared/src")
}
- val jsMain by creating {
+ jsMain {
dependencies {
- implementation(npm("body-parser", "~1.20.0"))
+ implementation(npm("body-parser", "~1.20.3"))
implementation(npm("debug", "~4.3.4"))
implementation(npm("ejs", "~3.1.7"))
- implementation(npm("express", "~4.19.2"))
- implementation(npm("kotlin", "~1.6.20"))
+ implementation(npm("express", "~4.20.0"))
implementation(npm("node-fetch", "~2.6.6"))
}
- kotlin {
- srcDir("src/main/kotlin")
- srcDir("src/main/kotlin-js")
- srcDir("shared/src/main/kotlin")
- }
}
}
@@ -61,12 +54,6 @@
js {
moduleName = "app"
nodejs()
- compilations.all {
- compilerOptions.configure {
- moduleKind.set(MODULE_COMMONJS)
- sourceMap.set(true)
- }
- }
binaries.executable()
}
}
diff --git a/kotlin-native/tools/performance-server/gradle/wrapper/gradle-wrapper.properties b/kotlin-native/tools/performance-server/gradle/wrapper/gradle-wrapper.properties
index f78e028..c8a852e 100644
--- a/kotlin-native/tools/performance-server/gradle/wrapper/gradle-wrapper.properties
+++ b/kotlin-native/tools/performance-server/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
-distributionSha256Sum=7ba68c54029790ab444b39d7e293d3236b2632631fb5f2e012bb28b4ff669e4b
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/kotlin-native/tools/performance-server/kotlin-js-store/yarn.lock b/kotlin-native/tools/performance-server/kotlin-js-store/yarn.lock
index 7eff2fe..d67a645 100644
--- a/kotlin-native/tools/performance-server/kotlin-js-store/yarn.lock
+++ b/kotlin-native/tools/performance-server/kotlin-js-store/yarn.lock
@@ -10,10 +10,10 @@
mime-types "~2.1.34"
negotiator "0.6.3"
-ansi-colors@4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
- integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+ansi-colors@^4.1.3:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+ integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
ansi-regex@^5.0.1:
version "5.0.1"
@@ -46,9 +46,9 @@
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
async@^3.2.3:
- version "3.2.5"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
- integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
+ version "3.2.6"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
+ integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
balanced-match@^1.0.0:
version "1.0.2"
@@ -60,10 +60,10 @@
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
-body-parser@1.20.2, body-parser@~1.20.0:
- version "1.20.2"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
- integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
+body-parser@1.20.3, body-parser@~1.20.3:
+ version "1.20.3"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
+ integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
@@ -73,7 +73,7 @@
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
- qs "6.11.0"
+ qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -100,7 +100,7 @@
dependencies:
fill-range "^7.1.1"
-browser-stdout@1.3.1:
+browser-stdout@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
@@ -139,10 +139,10 @@
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chokidar@3.5.3:
- version "3.5.3"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
- integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+chokidar@^3.5.3:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
+ integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
@@ -209,19 +209,12 @@
dependencies:
ms "2.0.0"
-debug@4.3.4:
- version "4.3.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
- integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+debug@^4.3.5, debug@~4.3.4:
+ version "4.3.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+ integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
dependencies:
- ms "2.1.2"
-
-debug@~4.3.4:
- version "4.3.5"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
- integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
- dependencies:
- ms "2.1.2"
+ ms "^2.1.3"
decamelize@^4.0.0:
version "4.0.0"
@@ -247,10 +240,10 @@
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
-diff@5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
- integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
+diff@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
+ integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
ee-first@1.1.1:
version "1.1.1"
@@ -274,6 +267,11 @@
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+encodeurl@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
+ integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
+
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
@@ -287,16 +285,16 @@
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
escalade@^3.1.1:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
- integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
-escape-string-regexp@4.0.0:
+escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
@@ -306,37 +304,37 @@
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
-express@~4.19.2:
- version "4.19.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
- integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
+express@~4.20.0:
+ version "4.20.0"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
+ integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.20.2"
+ body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
- encodeurl "~1.0.2"
+ encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
- merge-descriptors "1.0.1"
+ merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
- path-to-regexp "0.1.7"
+ path-to-regexp "0.1.10"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
- send "0.18.0"
- serve-static "1.15.0"
+ send "0.19.0"
+ serve-static "1.16.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
@@ -370,7 +368,7 @@
statuses "2.0.1"
unpipe "~1.0.0"
-find-up@5.0.0:
+find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
@@ -436,7 +434,7 @@
dependencies:
is-glob "^4.0.1"
-glob@8.1.0:
+glob@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
@@ -483,7 +481,7 @@
dependencies:
function-bind "^1.1.2"
-he@1.2.0:
+he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -564,26 +562,28 @@
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
jake@^10.8.5:
- version "10.9.1"
- resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.1.tgz#8dc96b7fcc41cb19aa502af506da4e1d56f5e62b"
- integrity sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==
+ version "10.9.2"
+ resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
+ integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==
dependencies:
async "^3.2.3"
chalk "^4.0.2"
filelist "^1.0.4"
minimatch "^3.1.2"
-js-yaml@4.1.0:
+js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
-kotlin@~1.6.20:
- version "1.6.21"
- resolved "https://registry.yarnpkg.com/kotlin/-/kotlin-1.6.21.tgz#b7f1be888ba04c3b59807bb197909cba4d0e9e47"
- integrity sha512-ulTTjmeZZfvlxxMFD/EjaGMlZ3pNypwOkQLK6WZjbsS7kTYqufekoiJD+mksmJQZTmc1MNW4M6bmvM5WbO1vKQ==
+kotlin-web-helpers@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/kotlin-web-helpers/-/kotlin-web-helpers-2.0.0.tgz#b112096b273c1e733e0b86560998235c09a19286"
+ integrity sha512-xkVGl60Ygn/zuLkDPx+oHj7jeLR7hCvoNF99nhwXMn8a3ApB4lLiC9pk4ol4NHPjyoCbvQctBqvzUcp8pkqyWw==
+ dependencies:
+ format-util "^1.0.5"
locate-path@^6.0.0:
version "6.0.0"
@@ -592,7 +592,7 @@
dependencies:
p-locate "^5.0.0"
-log-symbols@4.1.0:
+log-symbols@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
@@ -605,10 +605,10 @@
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
-merge-descriptors@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
- integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+merge-descriptors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+ integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2:
version "1.1.2"
@@ -632,13 +632,6 @@
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
-minimatch@5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
- integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
- dependencies:
- brace-expansion "^2.0.1"
-
minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -646,50 +639,45 @@
dependencies:
brace-expansion "^1.1.7"
-minimatch@^5.0.1:
+minimatch@^5.0.1, minimatch@^5.1.6:
version "5.1.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
dependencies:
brace-expansion "^2.0.1"
-mocha@10.3.0:
- version "10.3.0"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.3.0.tgz#0e185c49e6dccf582035c05fa91084a4ff6e3fe9"
- integrity sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==
+mocha@10.7.3:
+ version "10.7.3"
+ resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752"
+ integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==
dependencies:
- ansi-colors "4.1.1"
- browser-stdout "1.3.1"
- chokidar "3.5.3"
- debug "4.3.4"
- diff "5.0.0"
- escape-string-regexp "4.0.0"
- find-up "5.0.0"
- glob "8.1.0"
- he "1.2.0"
- js-yaml "4.1.0"
- log-symbols "4.1.0"
- minimatch "5.0.1"
- ms "2.1.3"
- serialize-javascript "6.0.0"
- strip-json-comments "3.1.1"
- supports-color "8.1.1"
- workerpool "6.2.1"
- yargs "16.2.0"
- yargs-parser "20.2.4"
- yargs-unparser "2.0.0"
+ ansi-colors "^4.1.3"
+ browser-stdout "^1.3.1"
+ chokidar "^3.5.3"
+ debug "^4.3.5"
+ diff "^5.2.0"
+ escape-string-regexp "^4.0.0"
+ find-up "^5.0.0"
+ glob "^8.1.0"
+ he "^1.2.0"
+ js-yaml "^4.1.0"
+ log-symbols "^4.1.0"
+ minimatch "^5.1.6"
+ ms "^2.1.3"
+ serialize-javascript "^6.0.2"
+ strip-json-comments "^3.1.1"
+ supports-color "^8.1.1"
+ workerpool "^6.5.1"
+ yargs "^16.2.0"
+ yargs-parser "^20.2.9"
+ yargs-unparser "^2.0.0"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-ms@2.1.3:
+ms@2.1.3, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -754,10 +742,10 @@
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-path-to-regexp@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
- integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+path-to-regexp@0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
+ integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1"
@@ -779,6 +767,13 @@
dependencies:
side-channel "^1.0.4"
+qs@6.13.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+ integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
+ dependencies:
+ side-channel "^1.0.6"
+
randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -842,17 +837,36 @@
range-parser "~1.2.1"
statuses "2.0.1"
-serialize-javascript@6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
- integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+send@0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
+ integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+serialize-javascript@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
+ integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
dependencies:
randombytes "^2.1.0"
-serve-static@1.15.0:
- version "1.15.0"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
- integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+serve-static@1.16.0:
+ version "1.16.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
+ integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
@@ -876,7 +890,7 @@
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
-side-channel@^1.0.4:
+side-channel@^1.0.4, side-channel@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
@@ -920,18 +934,11 @@
dependencies:
ansi-regex "^5.0.1"
-strip-json-comments@3.1.1:
+strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-supports-color@8.1.1:
- version "8.1.1"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
- integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
- dependencies:
- has-flag "^4.0.0"
-
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@@ -939,6 +946,13 @@
dependencies:
has-flag "^4.0.0"
+supports-color@^8.1.1:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -964,10 +978,10 @@
media-typer "0.3.0"
mime-types "~2.1.24"
-typescript@5.4.3:
- version "5.4.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff"
- integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==
+typescript@5.5.4:
+ version "5.5.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
+ integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
@@ -997,10 +1011,10 @@
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
-workerpool@6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
- integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
+workerpool@^6.5.1:
+ version "6.5.1"
+ resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
+ integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
wrap-ansi@^7.0.0:
version "7.0.0"
@@ -1021,17 +1035,12 @@
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
-yargs-parser@20.2.4:
- version "20.2.4"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
- integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
-
-yargs-parser@^20.2.2:
+yargs-parser@^20.2.2, yargs-parser@^20.2.9:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
-yargs-unparser@2.0.0:
+yargs-unparser@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
@@ -1041,7 +1050,7 @@
flat "^5.0.2"
is-plain-obj "^2.1.0"
-yargs@16.2.0:
+yargs@^16.2.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
diff --git a/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/buildInfo/BuildInfo.kt b/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/buildInfo/BuildInfo.kt
deleted file mode 100644
index 1ffc600..0000000
--- a/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/buildInfo/BuildInfo.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
- * that can be found in the LICENSE file.
- */
-
-package org.jetbrains.buildInfo
-
-import org.jetbrains.report.*
-import org.jetbrains.report.json.*
-
-@JsExport
-data class Build(val buildNumber: String, val startTime: String, val finishTime: String, val branch: String,
- val commits: String, val failuresNumber: Int) {
-
- companion object : EntityFromJsonFactory<Build> {
- override fun create(data: JsonElement): Build {
- if (data is JsonObject) {
- val buildNumber = elementToString(data.getRequiredField("buildNumber"), "buildNumber").replace("\"", "")
- val startTime = elementToString(data.getRequiredField("startTime"), "startTime").replace("\"", "")
- val finishTime = elementToString(data.getRequiredField("finishTime"), "finishTime").replace("\"", "")
- val branch = elementToString(data.getRequiredField("branch"), "branch").replace("\"", "")
- val commits = elementToString(data.getRequiredField("commits"), "commits")
- val failuresNumber = elementToInt(data.getRequiredField("failuresNumber"), "failuresNumber")
- return Build(buildNumber, startTime, finishTime, branch, commits, failuresNumber)
- } else {
- error("Top level entity is expected to be an object. Please, check origin files.")
- }
- }
- }
-
- private fun formatTime(time: String, targetZone: Int = 3): String {
- val matchResult = "^\\d{8}T(\\d{2})(\\d{2})\\d{2}((\\+|-)\\d{2})".toRegex().find(time)?.groupValues
- matchResult?.let {
- val timeZone = matchResult[3].toInt()
- val timeDifference = targetZone - timeZone
- var hours = (matchResult[1].toInt() + timeDifference)
- if (hours > 23) {
- hours -= 24
- }
- return "${if (hours < 10) "0$hours" else "$hours"}:${matchResult[2]}"
- } ?: error { "Wrong format of time $startTime" }
- }
-
- val date: String by lazy {
- val matchResult = "^(\\d{4})(\\d{2})(\\d{2})".toRegex().find(startTime)?.groupValues
- matchResult?.let { "${matchResult[3]}/${matchResult[2]}/${matchResult[1]}" }
- ?: error { "Wrong format of time $startTime" }
- }
- val formattedStartTime: String by lazy {
- formatTime(startTime)
- }
- val formattedFinishTime: String by lazy {
- formatTime(finishTime)
- }
-}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/analyzer/Statistics.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/analyzer/Statistics.kt
new file mode 100644
index 0000000..00e8079
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/analyzer/Statistics.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the LICENSE file.
+ */
+
+package org.jetbrains.analyzer
+
+import org.jetbrains.report.BenchmarkResult
+import org.jetbrains.report.MeanVariance
+import org.jetbrains.report.MeanVarianceBenchmark
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.pow
+import kotlin.math.sqrt
+
+// Calculate difference in percentage compare to another.
+fun MeanVarianceBenchmark.calcPercentageDiff(other: MeanVarianceBenchmark): MeanVariance {
+ if (score == 0.0 && variance == 0.0 && other.score == 0.0 && other.variance == 0.0)
+ return MeanVariance(score, variance)
+ require(other.score >= 0 &&
+ other.variance >= 0 &&
+ (other.score - other.variance != 0.0 || other.score == 0.0),
+ { "Mean and variance should be positive and not equal!" })
+
+ // Analyze intervals. Calculate difference between border points.
+ val (bigValue, smallValue) = if (score > other.score) Pair(this, other) else Pair(other, this)
+ val bigValueIntervalStart = bigValue.score - bigValue.variance
+ val bigValueIntervalEnd = bigValue.score + bigValue.variance
+ val smallValueIntervalStart = smallValue.score - smallValue.variance
+ val smallValueIntervalEnd = smallValue.score + smallValue.variance
+ if (smallValueIntervalEnd > bigValueIntervalStart) {
+ // Interval intersect.
+ return MeanVariance(0.0, 0.0)
+ }
+ val mean = ((smallValueIntervalEnd - bigValueIntervalStart) / bigValueIntervalStart) *
+ (if (score > other.score) -1 else 1)
+
+ val maxValueChange = ((bigValueIntervalEnd - smallValueIntervalEnd) / bigValueIntervalEnd)
+ val minValueChange = ((bigValueIntervalStart - smallValueIntervalStart) / bigValueIntervalStart)
+ val variance = abs(abs(mean) - max(minValueChange, maxValueChange))
+ return MeanVariance(mean * 100, variance * 100)
+}
+
+// Calculate ratio value compare to another.
+fun MeanVarianceBenchmark.calcRatio(other: MeanVarianceBenchmark): MeanVariance {
+ if (other.score == 0.0 && other.variance == 0.0)
+ return MeanVariance(1.0, 0.0)
+ require(other.score >= 0 &&
+ other.variance >= 0 &&
+ (other.score - other.variance != 0.0 || other.score == 0.0),
+ { "Mean and variance should be positive and not equal!" })
+ val mean = if (other.score != 0.0) (score / other.score) else 0.0
+ val minRatio = (score - variance) / (other.score + other.variance)
+ val maxRatio = (score + variance) / (other.score - other.variance)
+ val ratioConfInt = min(abs(minRatio - mean), abs(maxRatio - mean))
+ return MeanVariance(mean, ratioConfInt)
+}
+
+fun geometricMean(values: Collection<Double>, totalNumber: Int = values.size) =
+ with(values.asSequence().filter { it != 0.0 }) {
+ if (count() == 0 || totalNumber == 0) {
+ 0.0
+ } else {
+ map { it.pow(1.0 / totalNumber) }.reduce { a, b -> a * b }
+ }
+ }
+
+fun computeMeanVariance(samples: List<Double>): MeanVariance {
+ val removedBroadSamples = 0.2
+ val zStar = 1.67 // Critical point for 90% confidence of normal distribution.
+ // Skip several minimal and maximum values.
+ val filteredSamples = if (samples.size >= 1/removedBroadSamples) {
+ samples.sorted().subList((samples.size * removedBroadSamples).toInt(),
+ samples.size - (samples.size * removedBroadSamples).toInt())
+ } else {
+ samples
+ }
+
+ val mean = filteredSamples.sum() / filteredSamples.size
+ val variance = samples.indices.sumOf {
+ (samples[it] - mean) * (samples[it] - mean)
+ } / samples.size
+ val confidenceInterval = sqrt(variance / samples.size) * zStar
+ return MeanVariance(mean, confidenceInterval)
+}
+
+// Calculate average results for benchmarks (each benchmark can be run several times).
+fun collectMeanResults(benchmarks: Map<String, List<BenchmarkResult>>): BenchmarksTable {
+ return benchmarks.map { (name, resultsSet) ->
+ val repeatedSequence = IntArray(resultsSet.size)
+ var metric = BenchmarkResult.Metric.EXECUTION_TIME
+ var currentStatus = BenchmarkResult.Status.PASSED
+ var currentWarmup = -1
+
+ // Results can be already processed.
+ if (resultsSet[0] is MeanVarianceBenchmark) {
+ require(resultsSet.size == 1) { "Several MeanVarianceBenchmark instances." }
+ name to resultsSet[0] as MeanVarianceBenchmark
+ } else {
+ // Collect common benchmark values and check them.
+ resultsSet.forEachIndexed { index, result ->
+ // If there was at least one failure, summary is marked as failure.
+ if (result.status == BenchmarkResult.Status.FAILED) {
+ currentStatus = result.status
+ }
+ repeatedSequence[index] = result.repeat
+ if (currentWarmup != -1)
+ if (result.warmup != currentWarmup)
+ println("Check data consistency. Warmup value for benchmark '${result.name}' differs.")
+ currentWarmup = result.warmup
+ metric = result.metric
+ }
+
+ repeatedSequence.sort()
+ // Check if there are missed loop during running benchmarks.
+ repeatedSequence.forEachIndexed { index, element ->
+ if (index != 0)
+ if ((element - repeatedSequence[index - 1]) != 1)
+ println("Check data consistency. For benchmark '$name' there is no run" +
+ " between ${repeatedSequence[index - 1]} and $element.")
+ }
+
+ // Create mean and variance benchmarks result.
+ val scoreMeanVariance = computeMeanVariance(resultsSet.map { it.score })
+ val runtimeInUsMeanVariance = computeMeanVariance(resultsSet.map { it.runtimeInUs })
+ val meanBenchmark = MeanVarianceBenchmark(name, currentStatus, scoreMeanVariance.mean, metric,
+ runtimeInUsMeanVariance.mean, repeatedSequence[resultsSet.size - 1],
+ currentWarmup, scoreMeanVariance.variance)
+ name to meanBenchmark
+ }
+ }.toMap()
+}
+
+fun collectBenchmarksDurations(benchmarks: Map<String, List<BenchmarkResult>>): Map<String, Double> =
+ benchmarks.map { (name, resultsSet) ->
+ name to resultsSet.sumOf { it.runtimeInUs }
+ }.toMap()
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/analyzer/SummaryBenchmarksReport.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/analyzer/SummaryBenchmarksReport.kt
new file mode 100644
index 0000000..c0ceb01
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/analyzer/SummaryBenchmarksReport.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the LICENSE file.
+ */
+
+package org.jetbrains.analyzer
+
+import org.jetbrains.report.BenchmarkResult
+import org.jetbrains.report.BenchmarksReport
+import org.jetbrains.report.Compiler
+import org.jetbrains.report.Environment
+import org.jetbrains.report.MeanVariance
+import org.jetbrains.report.MeanVarianceBenchmark
+import kotlin.math.abs
+
+typealias SummaryBenchmark = Pair<MeanVarianceBenchmark?, MeanVarianceBenchmark?>
+typealias BenchmarksTable = Map<String, MeanVarianceBenchmark>
+typealias ScoreChange = Pair<MeanVariance, MeanVariance>
+
+class DetailedBenchmarksReport(currentBenchmarks: Map<String, List<BenchmarkResult>>,
+ previousBenchmarks: Map<String, List<BenchmarkResult>>? = null,
+ val meaningfulChangesValue: Double = 0.5) {
+ // Report created by joining comparing reports.
+ val mergedReport: Map<String, SummaryBenchmark>
+
+ // Maps with changes of performance.
+ var regressions = mapOf<String, ScoreChange>()
+ private set
+ var improvements = mapOf<String, ScoreChange>()
+ private set
+
+ // Summary value of report - geometric mean.
+ val geoMeanBenchmark: SummaryBenchmark
+ var geoMeanScoreChange: ScoreChange? = null
+ private set
+
+ init {
+ // Count avarage values for each benchmark.
+ val currentBenchmarksTable = collectMeanResults(currentBenchmarks)
+ val previousBenchmarksTable = previousBenchmarks?.let {
+ collectMeanResults(previousBenchmarks)
+ }
+ mergedReport = createMergedReport(currentBenchmarksTable, previousBenchmarksTable)
+ geoMeanBenchmark = calculateGeoMeanBenchmark(currentBenchmarksTable, previousBenchmarksTable)
+
+ if (previousBenchmarks != null) {
+ // Check changes in environment and tools.
+ analyzePerformanceChanges()
+ }
+ }
+ // Analyze and collect changes in performance between same becnhmarks.
+ private fun analyzePerformanceChanges() {
+ val performanceChanges = mergedReport.asSequence().map { (name, element) ->
+ getBenchmarkPerfomanceChange(name, element)
+ }.filterNotNull().groupBy {
+ if (it.second.first.mean > 0) "regressions" else "improvements"
+ }
+
+ // Sort regressions and improvements.
+ regressions = performanceChanges["regressions"]
+ ?.sortedByDescending { it.second.first.mean }?.map { it.first to it.second }
+ ?.toMap() ?: mapOf<String, ScoreChange>()
+ improvements = performanceChanges["improvements"]
+ ?.sortedBy { it.second.first.mean }?.map { it.first to it.second }
+ ?.toMap() ?: mapOf<String, ScoreChange>()
+
+ // Calculate change for geometric mean.
+ val (current, previous) = geoMeanBenchmark
+ geoMeanScoreChange = current?.let {
+ previous?.let {
+ Pair(current.calcPercentageDiff(previous), current.calcRatio(previous))
+ }
+ }
+ }
+
+ // Merge current and compare to report.
+ private fun createMergedReport(currentBenchmarks: BenchmarksTable, previousBenchmarks: BenchmarksTable?):
+ Map<String, SummaryBenchmark> {
+ val mergedTable = mutableMapOf<String, SummaryBenchmark>()
+ mergedTable.apply {
+ currentBenchmarks.forEach { (name, current) ->
+ // Check existance of benchmark in previous results.
+ if (previousBenchmarks == null || name !in previousBenchmarks) {
+ getOrPut(name) { SummaryBenchmark(current, null) }
+ } else {
+ getOrPut(name) { SummaryBenchmark(current, previousBenchmarks[name]) }
+ }
+ }
+ }
+
+ // Add removed benchmarks to merged report.
+ mergedTable.apply {
+ previousBenchmarks?.filter { (key, _) -> key !in currentBenchmarks }?.forEach { (key, value) ->
+ getOrPut(key) { SummaryBenchmark(null, value) }
+ }
+ }
+
+ return mergedTable
+ }
+
+ // Calculate geometric mean.
+ private fun calculateGeoMeanBenchmark(currentBenchmarks: BenchmarksTable, previousBenchmarks: BenchmarksTable?):
+ SummaryBenchmark {
+ // Calculate geometric mean.
+ val currentGeoMean = createGeoMeanBenchmark(currentBenchmarks)
+ val previousGeoMean = previousBenchmarks?.let { createGeoMeanBenchmark(previousBenchmarks) }
+ return SummaryBenchmark(currentGeoMean, previousGeoMean)
+ }
+
+ private fun getBenchmarkPerfomanceChange(name: String, benchmark: SummaryBenchmark): Pair<String, ScoreChange>? {
+ val (current, previous) = benchmark
+ current?.let {
+ previous?.let {
+ // Calculate metrics for showing difference.
+ val percent = current.calcPercentageDiff(previous)
+ val ratio = current.calcRatio(previous)
+ if (abs(percent.mean) - percent.variance >= meaningfulChangesValue) {
+ return Pair(name, Pair(percent, ratio))
+ }
+ }
+ }
+ return null
+ }
+
+ // Create geometric mean.
+ private fun createGeoMeanBenchmark(benchTable: BenchmarksTable): MeanVarianceBenchmark {
+ val geoMeanBenchmarkName = "Geometric mean"
+ val geoMean = geometricMean(benchTable.toList().map { (_, value) -> value.score })
+ val varianceGeoMean = geometricMean(benchTable.toList().map { (_, value) -> value.variance })
+ return MeanVarianceBenchmark(geoMeanBenchmarkName, geoMean, varianceGeoMean)
+ }
+}
+
+// Summary report with comparasion of separate benchmarks results.
+class SummaryBenchmarksReport(private val currentReport: BenchmarksReport,
+ private val previousReport: BenchmarksReport? = null,
+ private val meaningfulChangesValue: Double = 0.5) {
+
+ // Count avarage values for each benchmark.
+ private val detailedMetricReports: Map<BenchmarkResult.Metric, DetailedBenchmarksReport> = BenchmarkResult.Metric.entries.associateWith { metric ->
+ val currentBenchmarks = currentReport.benchmarks.map { (name, benchmarks) ->
+ name to benchmarks.filter { it.metric == metric }
+ }.filter { it.second.isNotEmpty() }.toMap()
+ val previousBenchmarks = previousReport?.benchmarks?.map { (name, benchmarks) ->
+ name to benchmarks.filter { it.metric == metric }
+ }?.filter { it.second.isNotEmpty() }?.toMap()
+ DetailedBenchmarksReport(
+ currentBenchmarks,
+ previousBenchmarks,
+ meaningfulChangesValue
+ )
+ }
+
+ private val benchmarksDurations: Map<String, Pair<Double?, Double?>>
+
+ // Environment and tools.
+ private val environments: Pair<Environment, Environment?>
+ private val compilers: Pair<Compiler, Compiler?>
+
+ private fun <T> getReducedResult(convertor: (DetailedBenchmarksReport) -> List<T>): List<T> {
+ return detailedMetricReports.values.map {
+ convertor(it)
+ }.flatten()
+ }
+
+ init {
+ benchmarksDurations = calculateBenchmarksDuration(currentReport, previousReport)
+ environments = Pair(currentReport.env, previousReport?.env)
+ compilers = Pair(currentReport.compiler, previousReport?.compiler)
+ }
+
+ // Get benchmark report.
+ fun getBenchmarksReport(takeMainReport: Boolean = true) =
+ if (takeMainReport)
+ BenchmarksReport(environments.first, getReducedResult { report ->
+ report.mergedReport.map { (_, value) -> value.first!! }
+ }, compilers.first)
+ else
+ BenchmarksReport(environments.second!!, getReducedResult { report ->
+ report.mergedReport.map { (_, value) -> value.second!! }
+ }, compilers.second!!)
+
+ // Generate map with summary durations of each benchmark.
+ private fun calculateBenchmarksDuration(currentReport: BenchmarksReport, previousReport: BenchmarksReport?):
+ Map<String, Pair<Double?, Double?>> {
+ val currentDurations = collectBenchmarksDurations(currentReport.benchmarks)
+ val previousDurations = previousReport?.let {
+ collectBenchmarksDurations(previousReport.benchmarks)
+ } ?: mapOf()
+ return currentDurations.keys.union(previousDurations.keys).associateWith { Pair(currentDurations[it], previousDurations[it]) }
+ }
+
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/BenchmarksReport.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/BenchmarksReport.kt
new file mode 100644
index 0000000..b60e001
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/BenchmarksReport.kt
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the LICENSE file.
+ */
+
+package org.jetbrains.report
+
+import org.jetbrains.report.json.*
+
+interface JsonSerializable {
+ fun serializeFields(): String
+
+ fun toJson(): String {
+ return """
+ {
+ ${serializeFields()}
+ }
+ """
+ }
+
+ // Convert iterable objects arrays, lists to json.
+ fun <T> arrayToJson(data: Iterable<T>): String {
+ return data.joinToString(prefix = "[", postfix = "]") {
+ if (it is JsonSerializable) it.toJson() else it.toString()
+ }
+ }
+}
+
+interface EntityFromJsonFactory<T> : ConvertedFromJson {
+ fun create(data: JsonElement): T
+}
+
+// Parse array with benchmarks to list
+fun parseBenchmarksArray(data: JsonElement): List<BenchmarkResult> {
+ if (data is JsonArray) {
+ return data.jsonArray.map {
+ if (MeanVarianceBenchmark.isMeanVarianceBenchmark(it))
+ MeanVarianceBenchmark.create(it as JsonObject)
+ else BenchmarkResult.create(it as JsonObject)
+ }
+ } else {
+ error("Benchmarks field is expected to be an array. Please, check origin files.")
+ }
+}
+
+// Class for benchmarks report with all information of run.
+open class BenchmarksReport(val env: Environment, benchmarksList: List<BenchmarkResult>, val compiler: Compiler) :
+ JsonSerializable {
+
+ companion object : EntityFromJsonFactory<BenchmarksReport> {
+ override fun create(data: JsonElement): BenchmarksReport {
+ if (data is JsonObject) {
+ val env = Environment.create(data.getRequiredField("env"))
+ val benchmarksObj = data.getRequiredField("benchmarks")
+ val compiler = Compiler.create(data.getRequiredField("kotlin"))
+ val buildNumberField = data.getOptionalField("buildNumber")
+ val benchmarksList = parseBenchmarksArray(benchmarksObj)
+ val report = BenchmarksReport(env, benchmarksList, compiler)
+ buildNumberField?.let { report.buildNumber = (it as JsonLiteral).unquoted() }
+ return report
+ } else {
+ error("Top level entity is expected to be an object. Please, check origin files.")
+ }
+ }
+
+ // Made a map of becnhmarks with name as key from list.
+ private fun structBenchmarks(benchmarksList: List<BenchmarkResult>) =
+ benchmarksList.groupBy { it.name }
+ }
+
+ val benchmarks = structBenchmarks(benchmarksList)
+
+ var buildNumber: String? = null
+
+ override fun serializeFields(): String {
+ val buildNumberField = buildNumber?.let {
+ """,
+ "buildNumber": "$buildNumber"
+ """
+ } ?: ""
+ return """
+ "env": ${env.toJson()},
+ "kotlin": ${compiler.toJson()},
+ "benchmarks": ${arrayToJson(benchmarks.flatMap { it.value })}$buildNumberField
+ """.trimIndent()
+ }
+
+ fun merge(other: BenchmarksReport): BenchmarksReport {
+ val mergedBenchmarks = HashMap(benchmarks)
+ other.benchmarks.forEach {
+ if (it.key in mergedBenchmarks) {
+ error("${it.key} already exists in report!")
+ }
+ }
+ mergedBenchmarks.putAll(other.benchmarks)
+ return BenchmarksReport(env, mergedBenchmarks.flatMap { it.value }, compiler)
+ }
+
+ // Concatenate benchmarks report if they have same environment and compiler.
+ operator fun plus(other: BenchmarksReport): BenchmarksReport {
+ if (compiler != other.compiler || env.machine != other.env.machine) {
+ error("It's impossible to concat reports from different machines!")
+ }
+ return merge(other)
+ }
+}
+
+// Class for kotlin compiler
+data class Compiler(val backend: Backend, val kotlinVersion: String) : JsonSerializable {
+
+ enum class BackendType(val type: String) {
+ JVM("jvm"),
+ NATIVE("native")
+ }
+
+ companion object : EntityFromJsonFactory<Compiler> {
+ override fun create(data: JsonElement): Compiler {
+ if (data is JsonObject) {
+ val backend = Backend.create(data.getRequiredField("backend"))
+ val kotlinVersion = elementToString(data.getRequiredField("kotlinVersion"), "kotlinVersion")
+
+ return Compiler(backend, kotlinVersion)
+ } else {
+ error("Kotlin entity is expected to be an object. Please, check origin files.")
+ }
+ }
+
+ fun backendTypeFromString(s: String): BackendType? = BackendType.values().find { it.type == s }
+ }
+
+ // Class for compiler backend
+ data class Backend(val type: BackendType, val version: String, val flags: List<String>) : JsonSerializable {
+ companion object : EntityFromJsonFactory<Backend> {
+ override fun create(data: JsonElement): Backend {
+ if (data is JsonObject) {
+ val typeElement = data.getRequiredField("type")
+ if (typeElement is JsonLiteral) {
+ val type = backendTypeFromString(typeElement.unquoted())
+ ?: error("Backend type should be 'jvm' or 'native'")
+ val version = elementToString(data.getRequiredField("version"), "version")
+ val flagsArray = data.getOptionalField("flags")
+ var flags: List<String> = emptyList()
+ if (flagsArray != null && flagsArray is JsonArray) {
+ flags = flagsArray.jsonArray.map { it.toString() }
+ }
+ return Backend(type, version, flags)
+ } else {
+ error("Backend type should be string literal.")
+ }
+ } else {
+ error("Backend entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+
+ override fun serializeFields(): String {
+ val result = """
+ "type": "${type.type}",
+ "version": "${version}""""
+ // Don't print flags field if there is no one.
+ if (flags.isEmpty()) {
+ return """$result
+ """
+ } else {
+ return """
+ $result,
+ "flags": ${arrayToJson(flags.map { if (it.startsWith("\"")) it else "\"$it\"" })}
+ """
+ }
+ }
+ }
+
+ override fun serializeFields(): String {
+ return """
+ "backend": ${backend.toJson()},
+ "kotlinVersion": "${kotlinVersion}"
+ """
+ }
+}
+
+// Class for description of environment of benchmarks run
+data class Environment(val machine: Machine, val jdk: JDKInstance) : JsonSerializable {
+
+ companion object : EntityFromJsonFactory<Environment> {
+ override fun create(data: JsonElement): Environment {
+ if (data is JsonObject) {
+ val machine = Machine.create(data.getRequiredField("machine"))
+ val jdk = JDKInstance.create(data.getRequiredField("jdk"))
+
+ return Environment(machine, jdk)
+ } else {
+ error("Environment entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+
+ // Class for description of machine used for benchmarks run.
+ data class Machine(val cpu: String, val os: String) : JsonSerializable {
+ companion object : EntityFromJsonFactory<Machine> {
+ override fun create(data: JsonElement): Machine {
+ if (data is JsonObject) {
+ val cpu = elementToString(data.getRequiredField("cpu"), "cpu")
+ val os = elementToString(data.getRequiredField("os"), "os")
+
+ return Machine(cpu, os)
+ } else {
+ error("Machine entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+
+ override fun serializeFields(): String {
+ return """
+ "cpu": "$cpu",
+ "os": "$os"
+ """
+ }
+ }
+
+ // Class for description of jdk used for benchmarks run.
+ data class JDKInstance(val version: String, val vendor: String) : JsonSerializable {
+ companion object : EntityFromJsonFactory<JDKInstance> {
+ override fun create(data: JsonElement): JDKInstance {
+ if (data is JsonObject) {
+ val version = elementToString(data.getRequiredField("version"), "version")
+ val vendor = elementToString(data.getRequiredField("vendor"), "vendor")
+
+ return JDKInstance(version, vendor)
+ } else {
+ error("JDK entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+
+ override fun serializeFields(): String {
+ return """
+ "version": "$version",
+ "vendor": "$vendor"
+ """
+ }
+ }
+
+ override fun serializeFields(): String {
+ return """
+ "machine": ${machine.toJson()},
+ "jdk": ${jdk.toJson()}
+ """
+ }
+}
+
+open class BenchmarkResult(val name: String, val status: Status,
+ val score: Double, val metric: Metric, val runtimeInUs: Double,
+ val repeat: Int, val warmup: Int) : JsonSerializable {
+
+ enum class Metric(val suffix: String, val value: String) {
+ EXECUTION_TIME("", "EXECUTION_TIME"),
+ CODE_SIZE(".codeSize", "CODE_SIZE"),
+ COMPILE_TIME(".compileTime", "COMPILE_TIME"),
+ BUNDLE_SIZE(".bundleSize", "BUNDLE_SIZE")
+ }
+
+ constructor(name: String, score: Double) : this(name, Status.PASSED, score, Metric.EXECUTION_TIME, 0.0, 0, 0)
+
+ companion object : EntityFromJsonFactory<BenchmarkResult> {
+
+ override fun create(data: JsonElement): BenchmarkResult {
+ if (data is JsonObject) {
+ var name = elementToString(data.getRequiredField("name"), "name")
+ val metricElement = data.getOptionalField("metric")
+ val metric = if (metricElement != null && metricElement is JsonLiteral)
+ metricFromString(metricElement.unquoted()) ?: Metric.EXECUTION_TIME
+ else Metric.EXECUTION_TIME
+ val statusElement = data.getRequiredField("status")
+ if (statusElement is JsonLiteral) {
+ val status = statusFromString(statusElement.unquoted())
+ ?: error("Status should be PASSED or FAILED")
+
+ val score = elementToDouble(data.getRequiredField("score"), "score")
+ val runtimeInUs = elementToDouble(data.getRequiredField("runtimeInUs"), "runtimeInUs")
+ val repeat = elementToInt(data.getRequiredField("repeat"), "repeat")
+ val warmup = elementToInt(data.getRequiredField("warmup"), "warmup")
+
+ return BenchmarkResult(name, status, score, metric, runtimeInUs, repeat, warmup)
+ } else {
+ error("Status should be string literal.")
+ }
+ } else {
+ error("Benchmark entity is expected to be an object. Please, check origin files.")
+ }
+ }
+
+ fun statusFromString(s: String): Status? = Status.values().find { it.value == s }
+ fun metricFromString(s: String): Metric? = Metric.values().find { it.value == s }
+ }
+
+ enum class Status(val value: String) {
+ PASSED("PASSED"),
+ FAILED("FAILED")
+ }
+
+ override fun serializeFields(): String {
+ return """
+ "name": "${name.removeSuffix(metric.suffix)}",
+ "status": "${status.value}",
+ "score": ${score},
+ "metric": "${metric.value}",
+ "runtimeInUs": ${runtimeInUs},
+ "repeat": ${repeat},
+ "warmup": ${warmup}
+ """
+ }
+
+ val shortName: String
+ get() = name.removeSuffix(metric.suffix)
+}
+
+// Entity to describe avarage values which conssists of mean and variance values.
+data class MeanVariance(val mean: Double, val variance: Double)
+
+// Processed benchmark result with calculated mean and variance value.
+open class MeanVarianceBenchmark(name: String, status: BenchmarkResult.Status, score: Double, metric: BenchmarkResult.Metric,
+ runtimeInUs: Double, repeat: Int, warmup: Int, val variance: Double) :
+ BenchmarkResult(name, status, score, metric, runtimeInUs, repeat, warmup) {
+
+ constructor(name: String, score: Double, variance: Double) : this(name, BenchmarkResult.Status.PASSED, score,
+ BenchmarkResult.Metric.EXECUTION_TIME, 0.0, 0, 0, variance)
+
+ companion object : EntityFromJsonFactory<MeanVarianceBenchmark> {
+
+ fun isMeanVarianceBenchmark(data: JsonElement) = data is JsonObject && data.getOptionalField("variance") != null
+
+ override fun create(data: JsonElement): MeanVarianceBenchmark {
+ if (data is JsonObject) {
+ val baseBenchmark = BenchmarkResult.create(data)
+ val variance = elementToDouble(data.getRequiredField("variance"), "variance")
+ return MeanVarianceBenchmark(baseBenchmark.name, baseBenchmark.status, baseBenchmark.score, baseBenchmark.metric,
+ baseBenchmark.runtimeInUs, baseBenchmark.repeat, baseBenchmark.warmup, variance)
+ } else {
+ error("Benchmark entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+
+ override fun serializeFields(): String {
+ return """
+ ${super.serializeFields()},
+ "variance": $variance
+ """
+ }
+}
+
+// Benchmark with set results stability state.
+open class BenchmarkWithStabilityState(name: String, status: BenchmarkResult.Status, score: Double, metric: BenchmarkResult.Metric,
+ runtimeInUs: Double, repeat: Int, warmup: Int, val unstable: Boolean) :
+ BenchmarkResult(name, status, score, metric, runtimeInUs, repeat, warmup) {
+
+ constructor(benchmarkResult: BenchmarkResult, unstable: Boolean) : this(benchmarkResult.name,
+ benchmarkResult.status, benchmarkResult.score, benchmarkResult.metric,
+ benchmarkResult.runtimeInUs, benchmarkResult.repeat, benchmarkResult.warmup, unstable)
+
+ override fun serializeFields(): String {
+ return """
+ ${super.serializeFields()},
+ "unstable": $unstable
+ """
+ }
+
+ companion object : EntityFromJsonFactory<BenchmarkResult> {
+ override fun create(data: JsonElement): BenchmarkWithStabilityState {
+ val parsedObject = BenchmarkResult.create(data)
+ if (data is JsonObject) {
+ val unstableElement = data.getOptionalField("unstable")
+ val unstableFlag = if (unstableElement != null && unstableElement is JsonPrimitive)
+ unstableElement.boolean else false
+ return BenchmarkWithStabilityState(parsedObject, unstableFlag)
+ } else {
+ error("Benchmark entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/ConvertedFromJson.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/ConvertedFromJson.kt
new file mode 100644
index 0000000..7f16418
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/ConvertedFromJson.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.report.json
+
+// Entity can be created from json description.
+interface ConvertedFromJson {
+ // Methods for conversion to expected type with checks of possibility of such conversions.
+ fun elementToDouble(element: JsonElement, name: String): Double =
+ if (element is JsonPrimitive)
+ element.double
+ else
+ error("Field '$name' in '$element' is expected to be a double number. Please, check origin files.")
+
+ fun elementToInt(element: JsonElement, name: String): Int =
+ if (element is JsonPrimitive)
+ element.int
+ else
+ error("Field '$name' in '$element' is expected to be an integer number. Please, check origin files.")
+
+ fun elementToString(element: JsonElement, name:String): String =
+ if (element is JsonLiteral)
+ element.unquoted()
+ else
+ error("Field '$name' in '$element' is expected to be a string. Please, check origin files.")
+
+ fun elementToStringOrNull(element: JsonElement, name:String): String? =
+ when (element) {
+ is JsonLiteral -> element.unquoted()
+ is JsonNull -> null
+ else -> error("Field '$name' in '$element' is expected to be a string. Please, check origin files.")
+ }
+}
+
+fun JsonObject.getRequiredField(fieldName: String): JsonElement {
+ return getOrNull(fieldName) ?: error("Field '$fieldName' doesn't exist in '$this'. Please, check origin files.")
+}
+
+fun JsonObject.getOptionalField(fieldName: String): JsonElement? {
+ return getOrNull(fieldName)
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonElement.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonElement.kt
new file mode 100644
index 0000000..6ca19c9
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonElement.kt
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.report.json
+
+/**
+ * Class representing single JSON element.
+ * Can be [JsonPrimitive], [JsonArray] or [JsonObject].
+ *
+ * [JsonElement.toString] properly prints JSON tree as valid JSON, taking into
+ * account quoted values and primitives
+ */
+sealed class JsonElement {
+
+ /**
+ * Convenience method to get current element as [JsonPrimitive]
+ * @throws JsonElementTypeMismatchException is current element is not a [JsonPrimitive]
+ */
+ open val primitive: JsonPrimitive
+ get() = error("JsonLiteral")
+
+ /**
+ * Convenience method to get current element as [JsonObject]
+ * @throws JsonElementTypeMismatchException is current element is not a [JsonObject]
+ */
+ open val jsonObject: JsonObject
+ get() = error("JsonObject")
+
+ /**
+ * Convenience method to get current element as [JsonArray]
+ * @throws JsonElementTypeMismatchException is current element is not a [JsonArray]
+ */
+ open val jsonArray: JsonArray
+ get() = error("JsonArray")
+
+ /**
+ * Convenience method to get current element as [JsonNull]
+ * @throws JsonElementTypeMismatchException is current element is not a [JsonNull]
+ */
+ open val jsonNull: JsonNull
+ get() = error("JsonPrimitive")
+
+ /**
+ * Checks whether current element is [JsonNull]
+ */
+ val isNull: Boolean
+ get() = this === JsonNull
+
+ private fun error(element: String): Nothing =
+ throw JsonElementTypeMismatchException(this::class.toString(), element)
+}
+
+/**
+ * Class representing JSON primitive value. Can be either [JsonLiteral] or [JsonNull].
+ */
+sealed class JsonPrimitive : JsonElement() {
+
+ /**
+ * Content of given element without quotes. For [JsonNull] this methods returns `"null"`
+ */
+ abstract val content: String
+
+ /**
+ * Content of the given element without quotes or `null` if current element is [JsonNull]
+ */
+ abstract val contentOrNull: String?
+
+ @Suppress("LeakingThis")
+ final override val primitive: JsonPrimitive = this
+
+ /**
+ * Returns content of current element as int
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+ val int: Int get() = content.toInt()
+
+ /**
+ * Returns content of current element as int or `null` if current element is not a valid representation of number
+ **/
+ val intOrNull: Int? get() = content.toIntOrNull()
+
+ /**
+ * Returns content of current element as long
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+ val long: Long get() = content.toLong()
+
+ /**
+ * Returns content of current element as long or `null` if current element is not a valid representation of number
+ */
+ val longOrNull: Long? get() = content.toLongOrNull()
+
+ /**
+ * Returns content of current element as double
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+ val double: Double get() = content.toDouble()
+
+ /**
+ * Returns content of current element as double or `null` if current element is not a valid representation of number
+ */
+ val doubleOrNull: Double? get() = content.toDoubleOrNull()
+
+ /**
+ * Returns content of current element as float
+ * @throws NumberFormatException if current element is not a valid representation of number
+ */
+ val float: Float get() = content.toFloat()
+
+ /**
+ * Returns content of current element as float or `null` if current element is not a valid representation of number
+ */
+ val floatOrNull: Float? get() = content.toFloatOrNull()
+
+ /**
+ * Returns content of current element as boolean
+ * @throws IllegalStateException if current element doesn't represent boolean
+ */
+ val boolean: Boolean get() = content.toBooleanStrict()
+
+ /**
+ * Returns content of current element as boolean or `null` if current element is not a valid representation of boolean
+ */
+ val booleanOrNull: Boolean? get() = content.toBooleanStrictOrNull()
+
+ override fun toString() = content
+}
+
+/**
+ * Class representing JSON literals: numbers, booleans and string.
+ * Strings are always quoted.
+ */
+data class JsonLiteral(
+ private val body: Any,
+ private val isString: Boolean
+) : JsonPrimitive() {
+
+ override val content = body.toString()
+ override val contentOrNull: String = content
+
+ /**
+ * Creates number literal
+ */
+ constructor(number: Number) : this(number, false)
+
+ /**
+ * Creates boolean literal
+ */
+ constructor(boolean: Boolean) : this(boolean, false)
+
+ /**
+ * Creates quoted string literal
+ */
+ constructor(string: String) : this(string, true)
+
+ override fun toString() =
+ if (isString) buildString { printQuoted(content) }
+ else content
+
+ fun unquoted() = content
+}
+
+/**
+ * Class representing JSON `null` value
+ */
+object JsonNull : JsonPrimitive() {
+ override val jsonNull: JsonNull = this
+ override val content: String = "null"
+ override val contentOrNull: String? = null
+}
+
+/**
+ * Class representing JSON object, consisting of name-value pairs, where value is arbitrary [JsonElement]
+ */
+data class JsonObject(val content: Map<String, JsonElement>) : JsonElement(), Map<String, JsonElement> by content {
+
+ override val jsonObject: JsonObject = this
+
+ /**
+ * Returns [JsonElement] associated with given [key]
+ * @throws NoSuchElementException if element is not present
+ */
+ override fun get(key: String): JsonElement = content[key] ?: throw NoSuchElementException("Element $key is missing")
+
+ /**
+ * Returns [JsonElement] associated with given [key] or `null` if element is not present
+ */
+ fun getOrNull(key: String): JsonElement? = content[key]
+
+ /**
+ * Returns [JsonPrimitive] associated with given [key]
+ *
+ * @throws NoSuchElementException if element is not present
+ * @throws JsonElementTypeMismatchException if element is present, but has invalid type
+ */
+ fun getPrimitive(key: String): JsonPrimitive = get(key) as? JsonPrimitive
+ ?: unexpectedJson(key, "JsonPrimitive")
+
+ /**
+ * Returns [JsonObject] associated with given [key]
+ *
+ * @throws NoSuchElementException if element is not present
+ * @throws JsonElementTypeMismatchException if element is present, but has invalid type
+ */
+ fun getObject(key: String): JsonObject = get(key) as? JsonObject
+ ?: unexpectedJson(key, "JsonObject")
+
+ /**
+ * Returns [JsonArray] associated with given [key]
+ *
+ * @throws NoSuchElementException if element is not present
+ * @throws JsonElementTypeMismatchException if element is present, but has invalid type
+ */
+ fun getArray(key: String): JsonArray = get(key) as? JsonArray
+ ?: unexpectedJson(key, "JsonArray")
+
+ /**
+ * Returns [JsonPrimitive] associated with given [key] or `null` if element
+ * is not present or has different type
+ */
+ fun getPrimitiveOrNull(key: String): JsonPrimitive? = content[key] as? JsonPrimitive
+
+ /**
+ * Returns [JsonObject] associated with given [key] or `null` if element
+ * is not present or has different type
+ */
+ fun getObjectOrNull(key: String): JsonObject? = content[key] as? JsonObject
+
+ /**
+ * Returns [JsonArray] associated with given [key] or `null` if element
+ * is not present or has different type
+ */
+ fun getArrayOrNull(key: String): JsonArray? = content[key] as? JsonArray
+
+ /**
+ * Returns [J] associated with given [key]
+ *
+ * @throws NoSuchElementException if element is not present
+ * @throws JsonElementTypeMismatchException if element is present, but has invalid type
+ */
+ inline fun <reified J : JsonElement> getAs(key: String): J = get(key) as? J
+ ?: unexpectedJson(key, J::class.toString())
+
+ /**
+ * Returns [J] associated with given [key] or `null` if element
+ * is not present or has different type
+ */
+ inline fun <reified J : JsonElement> lookup(key: String): J? = content[key] as? J
+
+ override fun toString(): String {
+ return content.entries.joinToString(
+ prefix = "{",
+ postfix = "}",
+ transform = {(k, v) -> """"$k": $v"""}
+ )
+ }
+}
+
+data class JsonArray(val content: List<JsonElement>) : JsonElement(), List<JsonElement> by content {
+
+ override val jsonArray: JsonArray = this
+
+ /**
+ * Returns [index]-th element of an array as [JsonPrimitive]
+ * @throws JsonElementTypeMismatchException if element has invalid type
+ */
+ fun getPrimitive(index: Int) = content[index] as? JsonPrimitive
+ ?: unexpectedJson("at $index", "JsonPrimitive")
+
+ /**
+ * Returns [index]-th element of an array as [JsonObject]
+ * @throws JsonElementTypeMismatchException if element has invalid type
+ */
+ fun getObject(index: Int) = content[index] as? JsonObject
+ ?: unexpectedJson("at $index", "JsonObject")
+
+ /**
+ * Returns [index]-th element of an array as [JsonArray]
+ * @throws JsonElementTypeMismatchException if element has invalid type
+ */
+ fun getArray(index: Int) = content[index] as? JsonArray
+ ?: unexpectedJson("at $index", "JsonArray")
+
+ /**
+ * Returns [index]-th element of an array as [JsonPrimitive] or `null` if element is missing or has different type
+ */
+ fun getPrimitiveOrNull(index: Int) = content.getOrNull(index) as? JsonPrimitive
+
+ /**
+ * Returns [index]-th element of an array as [JsonObject] or `null` if element is missing or has different type
+ */
+ fun getObjectOrNull(index: Int) = content.getOrNull(index) as? JsonObject
+
+ /**
+ * Returns [index]-th element of an array as [JsonArray] or `null` if element is missing or has different type
+ */
+ fun getArrayOrNull(index: Int) = content.getOrNull(index) as? JsonArray
+
+ /**
+ * Returns [index]-th element of an array as [J]
+ * @throws JsonElementTypeMismatchException if element has invalid type
+ */
+ inline fun <reified J : JsonElement> getAs(index: Int): J = content[index] as? J
+ ?: unexpectedJson("at $index", J::class.toString())
+
+ /**
+ * Returns [index]-th element of an array as [J] or `null` if element is missing or has different type
+ */
+ inline fun <reified J : JsonElement> getAsOrNull(index: Int): J? = content.getOrNull(index) as? J
+
+ override fun toString() = content.joinToString(prefix = "[", postfix = "]")
+}
+
+fun unexpectedJson(key: String, expected: String): Nothing =
+ throw JsonElementTypeMismatchException(key, expected)
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonExceptions.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonExceptions.kt
new file mode 100644
index 0000000..4b7acaa
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonExceptions.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.report.json
+
+class JsonInvalidValueInStrictModeException(value: Any, valueDescription: String) : Exception(
+ "$value is not a valid $valueDescription as per JSON spec.\n" +
+ "You can disable strict mode to serialize such values"
+) {
+ constructor(floatValue: Float) : this(floatValue, "float")
+ constructor(doubleValue: Double) : this(doubleValue, "double")
+}
+
+class JsonUnknownKeyException(key: String) : Exception(
+ "Strict JSON encountered unknown key: $key\n" +
+ "You can disable strict mode to skip unknown keys"
+)
+
+class JsonParsingException(position: Int, message: String) : Exception("Invalid JSON at $position: $message")
+
+class JsonElementTypeMismatchException(key: String, expected: String) : Exception("Element $key is not a $expected")
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonParser.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonParser.kt
new file mode 100644
index 0000000..bef8c80
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonParser.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.report.json
+
+import org.jetbrains.report.json.EscapeCharMappings.ESC2C
+// special strings
+internal const val NULL = "null"
+
+// special chars
+internal const val COMMA = ','
+internal const val COLON = ':'
+internal const val BEGIN_OBJ = '{'
+internal const val END_OBJ = '}'
+internal const val BEGIN_LIST = '['
+internal const val END_LIST = ']'
+internal const val STRING = '"'
+internal const val STRING_ESC = '\\'
+internal const val INVALID = 0.toChar()
+internal const val UNICODE_ESC = 'u'
+// token classes
+internal const val TC_OTHER: Byte = 0
+internal const val TC_STRING: Byte = 1
+internal const val TC_STRING_ESC: Byte = 2
+internal const val TC_WS: Byte = 3
+internal const val TC_COMMA: Byte = 4
+internal const val TC_COLON: Byte = 5
+internal const val TC_BEGIN_OBJ: Byte = 6
+internal const val TC_END_OBJ: Byte = 7
+internal const val TC_BEGIN_LIST: Byte = 8
+internal const val TC_END_LIST: Byte = 9
+internal const val TC_NULL: Byte = 10
+internal const val TC_INVALID: Byte = 11
+internal const val TC_EOF: Byte = 12
+// mapping from chars to token classes
+private const val CTC_MAX = 0x7e
+// mapping from escape chars real chars
+private const val C2ESC_MAX = 0x5d
+private const val ESC2C_MAX = 0x75
+
+internal val C2TC = ByteArray(CTC_MAX).apply {
+ for (i in 0..0x20)
+ initC2TC(i, TC_INVALID)
+ initC2TC(0x09, TC_WS)
+ initC2TC(0x0a, TC_WS)
+ initC2TC(0x0d, TC_WS)
+ initC2TC(0x20, TC_WS)
+ initC2TC(COMMA, TC_COMMA)
+ initC2TC(COLON, TC_COLON)
+ initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
+ initC2TC(END_OBJ, TC_END_OBJ)
+ initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
+ initC2TC(END_LIST, TC_END_LIST)
+ initC2TC(STRING, TC_STRING)
+ initC2TC(STRING_ESC, TC_STRING_ESC)
+}
+// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC]
+internal object EscapeCharMappings {
+ internal val ESC2C = CharArray(ESC2C_MAX)
+ internal val C2ESC = CharArray(C2ESC_MAX).apply {
+ for (i in 0x00..0x1f)
+ initC2ESC(i, UNICODE_ESC)
+ initC2ESC(0x08, 'b')
+ initC2ESC(0x09, 't')
+ initC2ESC(0x0a, 'n')
+ initC2ESC(0x0c, 'f')
+ initC2ESC(0x0d, 'r')
+ initC2ESC('/', '/')
+ initC2ESC(STRING, STRING)
+ initC2ESC(STRING_ESC, STRING_ESC)
+ }
+ private fun CharArray.initC2ESC(c: Int, esc: Char) {
+ this[c] = esc
+ if (esc != UNICODE_ESC) ESC2C[esc.code] = c.toChar()
+ }
+ private fun CharArray.initC2ESC(c: Char, esc: Char) = initC2ESC(c.code, esc)
+}
+private fun ByteArray.initC2TC(c: Int, cl: Byte) {
+ this[c] = cl
+}
+private fun ByteArray.initC2TC(c: Char, cl: Byte) {
+ initC2TC(c.code, cl)
+}
+internal fun charToTokenClass(c: Char) = if (c.code < CTC_MAX) C2TC[c.code] else TC_OTHER
+internal fun escapeToChar(c: Int): Char = if (c < ESC2C_MAX) ESC2C[c] else INVALID
+// JSON low level parser
+internal class Parser(val source: String) {
+ var curPos: Int = 0 // position in source
+ private set
+ // updated by nextToken
+ var tokenPos: Int = 0
+ private set
+ var tc: Byte = TC_EOF
+ private set
+ // update by nextString/nextLiteral
+ private var offset = -1 // when offset >= 0 string is in source, otherwise in buf
+ private var length = 0 // length of string
+ private var buf = CharArray(16) // only used for strings with escapes
+ init {
+ nextToken()
+ }
+ internal inline fun requireTc(expected: Byte, lazyErrorMsg: () -> String) {
+ if (tc != expected)
+ fail(tokenPos, lazyErrorMsg())
+ }
+ val canBeginValue: Boolean
+ get() = when (tc) {
+ TC_BEGIN_LIST, TC_BEGIN_OBJ, TC_OTHER, TC_STRING, TC_NULL -> true
+ else -> false
+ }
+
+ @OptIn(ExperimentalStdlibApi::class)
+ fun takeStr(): String {
+ if (tc != TC_OTHER && tc != TC_STRING) fail(tokenPos, "Expected string or non-null literal")
+ val prevStr = if (offset < 0)
+ buf.concatToString(0, length) else
+ source.substring(offset, offset + length)
+ nextToken()
+ return prevStr
+ }
+ private fun append(ch: Char) {
+ if (length >= buf.size) buf = buf.copyOf(2 * buf.size)
+ buf[length++] = ch
+ }
+ // initializes buf usage upon the first encountered escaped char
+ private fun appendRange(source: String, fromIndex: Int, toIndex: Int) {
+ val addLen = toIndex - fromIndex
+ val oldLen = length
+ val newLen = oldLen + addLen
+ if (newLen > buf.size) buf = buf.copyOf(newLen.coerceAtLeast(2 * buf.size))
+ for (i in 0 until addLen) buf[oldLen + i] = source[fromIndex + i]
+ length += addLen
+ }
+ fun nextToken() {
+ val source = source
+ var curPos = curPos
+ val maxLen = source.length
+ while (true) {
+ if (curPos >= maxLen) {
+ tokenPos = curPos
+ tc = TC_EOF
+ return
+ }
+ val ch = source[curPos]
+ val tc = charToTokenClass(ch)
+ when (tc) {
+ TC_WS -> curPos++ // skip whitespace
+ TC_OTHER -> {
+ nextLiteral(source, curPos)
+ return
+ }
+ TC_STRING -> {
+ nextString(source, curPos)
+ return
+ }
+ else -> {
+ this.tokenPos = curPos
+ this.tc = tc
+ this.curPos = curPos + 1
+ return
+ }
+ }
+ }
+ }
+ private fun nextLiteral(source: String, startPos: Int) {
+ tokenPos = startPos
+ offset = startPos
+ var curPos = startPos
+ val maxLen = source.length
+ while (true) {
+ curPos++
+ if (curPos >= maxLen || charToTokenClass(source[curPos]) != TC_OTHER) break
+ }
+ this.curPos = curPos
+ length = curPos - offset
+ tc = if (rangeEquals(source, offset, length, NULL)) TC_NULL else TC_OTHER
+ }
+ private fun nextString(source: String, startPos: Int) {
+ tokenPos = startPos
+ length = 0 // in buffer
+ var curPos = startPos + 1
+ var lastPos = curPos
+ val maxLen = source.length
+ parse@ while (true) {
+ if (curPos >= maxLen) fail(curPos, "Unexpected end in string")
+ if (source[curPos] == STRING) {
+ break@parse
+ } else if (source[curPos] == STRING_ESC) {
+ appendRange(source, lastPos, curPos)
+ val newPos = appendEsc(source, curPos + 1)
+ curPos = newPos
+ lastPos = newPos
+ } else {
+ curPos++
+ }
+ }
+ if (lastPos == startPos + 1) {
+ // there was no escaped chars
+ this.offset = lastPos
+ this.length = curPos - lastPos
+ } else {
+ // some escaped chars were there
+ appendRange(source, lastPos, curPos)
+ this.offset = -1
+ }
+ this.curPos = curPos + 1
+ tc = TC_STRING
+ }
+ private fun appendEsc(source: String, startPos: Int): Int {
+ var curPos = startPos
+ require(curPos < source.length, curPos) { "Unexpected end after escape char" }
+ val curChar = source[curPos++]
+ if (curChar == UNICODE_ESC) {
+ curPos = appendHex(source, curPos)
+ } else {
+ val c = escapeToChar(curChar.code)
+ require(c != INVALID, curPos) { "Invalid escaped char '$curChar'" }
+ append(c)
+ }
+ return curPos
+ }
+ private fun appendHex(source: String, startPos: Int): Int {
+ var curPos = startPos
+ append(
+ ((fromHexChar(source, curPos++) shl 12) +
+ (fromHexChar(source, curPos++) shl 8) +
+ (fromHexChar(source, curPos++) shl 4) +
+ fromHexChar(source, curPos++)).toChar()
+ )
+ return curPos
+ }
+ fun skipElement() {
+ if (tc != TC_BEGIN_OBJ && tc != TC_BEGIN_LIST) {
+ nextToken()
+ return
+ }
+ val tokenStack = mutableListOf<Byte>()
+ do {
+ when (tc) {
+ TC_BEGIN_LIST, TC_BEGIN_OBJ -> tokenStack.add(tc)
+ TC_END_LIST -> {
+ if (tokenStack.last() != TC_BEGIN_LIST) throw JsonParsingException(curPos, "found ] instead of }")
+ tokenStack.removeAt(tokenStack.size - 1)
+ }
+ TC_END_OBJ -> {
+ if (tokenStack.last() != TC_BEGIN_OBJ) throw JsonParsingException(curPos, "found } instead of ]")
+ tokenStack.removeAt(tokenStack.size - 1)
+ }
+ }
+ nextToken()
+ } while (tokenStack.isNotEmpty())
+ }
+}
+// Utility functions
+private fun fromHexChar(source: String, curPos: Int): Int {
+ require(curPos < source.length, curPos) { "Unexpected end in unicode escape" }
+ val curChar = source[curPos]
+ return when (curChar) {
+ in '0'..'9' -> curChar.code - '0'.code
+ in 'a'..'f' -> curChar.code - 'a'.code + 10
+ in 'A'..'F' -> curChar.code - 'A'.code + 10
+ else -> fail(curPos, "Invalid toHexChar char '$curChar' in unicode escape")
+ }
+}
+private fun rangeEquals(source: String, start: Int, length: Int, str: String): Boolean {
+ val n = str.length
+ if (length != n) return false
+ for (i in 0 until n) if (source[start + i] != str[i]) return false
+ return true
+}
+internal inline fun require(condition: Boolean, pos: Int, msg: () -> String) {
+ if (!condition)
+ fail(pos, msg())
+}
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun fail(pos: Int, msg: String): Nothing {
+ throw JsonParsingException(pos, msg)
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonTreeParser.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonTreeParser.kt
new file mode 100644
index 0000000..23f3ccb
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/JsonTreeParser.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// A bit changed part of kotlinx.serialization plugin
+package org.jetbrains.report.json
+
+class JsonTreeParser internal constructor(private val p: Parser) {
+
+ companion object {
+ fun parse(input: String): JsonElement = JsonTreeParser(input).readFully()
+ }
+
+ constructor(input: String) : this(Parser(input))
+
+ private fun readObject(): JsonElement {
+ p.requireTc(TC_BEGIN_OBJ) { "Expected start of object" }
+ p.nextToken()
+ val result: MutableMap<String, JsonElement> = hashMapOf()
+ while (true) {
+ if (p.tc == TC_COMMA) p.nextToken()
+ if (!p.canBeginValue) break
+ val key = p.takeStr()
+ p.requireTc(TC_COLON) { "Expected ':'" }
+ p.nextToken()
+ val elem = read()
+ result[key] = elem
+ }
+ p.requireTc(TC_END_OBJ) { "Expected end of object" }
+ p.nextToken()
+ return JsonObject(result)
+ }
+
+ private fun readValue(isString: Boolean): JsonElement {
+ val str = p.takeStr()
+ return JsonLiteral(str, isString)
+ }
+
+ private fun readArray(): JsonElement {
+ p.requireTc(TC_BEGIN_LIST) { "Expected start of array" }
+ p.nextToken()
+ val result: MutableList<JsonElement> = arrayListOf()
+ while (true) {
+ if (p.tc == TC_COMMA) p.nextToken()
+ if (!p.canBeginValue) break
+ val elem = read()
+ result.add(elem)
+ }
+ p.requireTc(TC_END_LIST) { "Expected end of array" }
+ p.nextToken()
+ return JsonArray(result)
+ }
+
+ fun read(): JsonElement {
+ if (!p.canBeginValue) fail(p.curPos, "Can't begin reading value from here")
+ val tc = p.tc
+ return when (tc) {
+ TC_NULL -> JsonNull.also { p.nextToken() }
+ TC_STRING -> readValue(isString = true)
+ TC_OTHER -> readValue(isString = false)
+ TC_BEGIN_OBJ -> readObject()
+ TC_BEGIN_LIST -> readArray()
+ else -> fail(p.curPos, "Can't begin reading element")
+ }
+ }
+
+ fun readFully(): JsonElement {
+ val r = read()
+ p.requireTc(TC_EOF) { "Input wasn't consumed fully" }
+ return r
+ }
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/StringOps.kt b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/StringOps.kt
new file mode 100644
index 0000000..1c22c66
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/commonMain/kotlin/org/jetbrains/report/json/StringOps.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.report.json
+
+private fun toHexChar(i: Int) : Char {
+ val d = i and 0xf
+ return if (d < 10) (d + '0'.code).toChar()
+ else (d - 10 + 'a'.code).toChar()
+}
+
+private val ESCAPE_CHARS: Array<String?> = arrayOfNulls<String>(128).apply {
+ for (c in 0..0x1f) {
+ val c1 = toHexChar(c shr 12)
+ val c2 = toHexChar(c shr 8)
+ val c3 = toHexChar(c shr 4)
+ val c4 = toHexChar(c)
+ this[c] = "\\u$c1$c2$c3$c4"
+ }
+ this['"'.code] = "\\\""
+ this['\\'.code] = "\\\\"
+ this['\t'.code] = "\\t"
+ this['\b'.code] = "\\b"
+ this['\n'.code] = "\\n"
+ this['\r'.code] = "\\r"
+ this[0x0c] = "\\f"
+}
+
+internal fun StringBuilder.printQuoted(value: String) {
+ append(STRING)
+ var lastPos = 0
+ val length = value.length
+ for (i in 0 until length) {
+ val c = value[i].code
+ // Do not replace this constant with C2ESC_MAX (which is smaller than ESCAPE_CHARS size),
+ // otherwise JIT won't eliminate range check and won't vectorize this loop
+ if (c >= ESCAPE_CHARS.size) continue // no need to escape
+ val esc = ESCAPE_CHARS[c] ?: continue
+ append(value, lastPos, i) // flush prev
+ append(esc)
+ lastPos = i + 1
+ }
+ append(value, lastPos, length)
+ append(STRING)
+}
+
+/**
+ * Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
+ * and throws [IllegalStateException] otherwise.
+ */
+fun String.toBooleanStrict(): Boolean = toBooleanStrictOrNull() ?: throw IllegalStateException("$this does not represent a Boolean")
+
+/**
+ * Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
+ * and returns `null` otherwise.
+ */
+fun String.toBooleanStrictOrNull(): Boolean? = when {
+ this.equals("true", ignoreCase = true) -> true
+ this.equals("false", ignoreCase = true) -> false
+ else -> null
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin/database/BenchmarksIndexesDispatcher.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/database/BenchmarksIndexesDispatcher.kt
similarity index 99%
rename from kotlin-native/tools/performance-server/src/main/kotlin/database/BenchmarksIndexesDispatcher.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/database/BenchmarksIndexesDispatcher.kt
index d600a7b..f8e26f9 100644
--- a/kotlin-native/tools/performance-server/src/main/kotlin/database/BenchmarksIndexesDispatcher.kt
+++ b/kotlin-native/tools/performance-server/src/jsMain/kotlin/database/BenchmarksIndexesDispatcher.kt
@@ -7,7 +7,6 @@
import kotlin.js.Promise
import org.jetbrains.elastic.*
-import org.jetbrains.utils.*
import org.jetbrains.report.json.*
import org.jetbrains.report.*
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin/database/DatabaseRequests.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/database/DatabaseRequests.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/src/main/kotlin/database/DatabaseRequests.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/database/DatabaseRequests.kt
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin/main.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/main.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/src/main/kotlin/main.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/main.kt
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin/network/CachableResponseDispatcher.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/network/CachableResponseDispatcher.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/src/main/kotlin/network/CachableResponseDispatcher.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/network/CachableResponseDispatcher.kt
diff --git a/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/buildInfo/BuildInfo.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/buildInfo/BuildInfo.kt
new file mode 100644
index 0000000..8056755
--- /dev/null
+++ b/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/buildInfo/BuildInfo.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the LICENSE file.
+ */
+
+package org.jetbrains.buildInfo
+
+import org.jetbrains.report.*
+import org.jetbrains.report.json.*
+
+@JsExport
+data class Build(val buildNumber: String, val startTime: String, val finishTime: String, val branch: String,
+ val commits: String, val failuresNumber: Int) {
+
+ companion object : EntityFromJsonFactory<Build> {
+ override fun create(data: JsonElement): Build {
+ if (data is JsonObject) {
+ val buildNumber = elementToString(data.getRequiredField("buildNumber"), "buildNumber").replace("\"", "")
+ val startTime = elementToString(data.getRequiredField("startTime"), "startTime").replace("\"", "")
+ val finishTime = elementToString(data.getRequiredField("finishTime"), "finishTime").replace("\"", "")
+ val branch = elementToString(data.getRequiredField("branch"), "branch").replace("\"", "")
+ val commits = elementToString(data.getRequiredField("commits"), "commits")
+ val failuresNumber = elementToInt(data.getRequiredField("failuresNumber"), "failuresNumber")
+ return Build(buildNumber, startTime, finishTime, branch, commits, failuresNumber)
+ } else {
+ error("Top level entity is expected to be an object. Please, check origin files.")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/elastic/ElasticSearchConnector.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/elastic/ElasticSearchConnector.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/elastic/ElasticSearchConnector.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/elastic/ElasticSearchConnector.kt
diff --git a/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/elastic/ElasticSearchIndex.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/elastic/ElasticSearchIndex.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/elastic/ElasticSearchIndex.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/elastic/ElasticSearchIndex.kt
diff --git a/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/network/NetworkConnector.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/network/NetworkConnector.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/network/NetworkConnector.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/network/NetworkConnector.kt
diff --git a/kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/network/UrlNetworkConnector.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/network/UrlNetworkConnector.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/shared/src/main/kotlin/org/jetbrains/network/UrlNetworkConnector.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/org/jetbrains/network/UrlNetworkConnector.kt
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin/routes/route.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/routes/route.kt
similarity index 99%
rename from kotlin-native/tools/performance-server/src/main/kotlin/routes/route.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/routes/route.kt
index 6987362..fa79ede 100644
--- a/kotlin-native/tools/performance-server/src/main/kotlin/routes/route.kt
+++ b/kotlin-native/tools/performance-server/src/jsMain/kotlin/routes/route.kt
@@ -3,8 +3,6 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
-import org.w3c.xhr.*
-import kotlin.js.json
import kotlin.js.Date
import kotlin.js.Promise
import org.jetbrains.database.*
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin/utils/AsyncUtils.kt b/kotlin-native/tools/performance-server/src/jsMain/kotlin/utils/AsyncUtils.kt
similarity index 100%
rename from kotlin-native/tools/performance-server/src/main/kotlin/utils/AsyncUtils.kt
rename to kotlin-native/tools/performance-server/src/jsMain/kotlin/utils/AsyncUtils.kt
diff --git a/kotlin-native/tools/performance-server/src/main/kotlin-js/org/jetbrains/analyzer/Utils.kt b/kotlin-native/tools/performance-server/src/main/kotlin-js/org/jetbrains/analyzer/Utils.kt
deleted file mode 100644
index 06cda7f..0000000
--- a/kotlin-native/tools/performance-server/src/main/kotlin-js/org/jetbrains/analyzer/Utils.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
- * that can be found in the LICENSE file.
- */
-
-package org.jetbrains.analyzer
-
-import org.w3c.xhr.*
-import kotlinx.browser.*
-import kotlin.js.*
-
-actual fun readFile(fileName: String): String {
- error("Reading from local file for JS isn't supported")
-}
-
-actual fun writeToFile(fileName: String, text: String) {
- error("Writing to local file for JS isn't supported")
-}
-
-actual fun Double.format(decimalNumber: Int): String =
- this.asDynamic().toFixed(decimalNumber)
-
-actual fun assert(value: Boolean, lazyMessage: () -> Any) {
- if (!value) error(lazyMessage)
-}
-
-actual fun sendGetRequest(url: String, user: String?, password: String?, followLocation: Boolean) : String {
- error("Unsupported")
-}
-
-actual fun getDefaultPerformanceServerUrl() : String? = null
\ No newline at end of file