Научная статья на тему 'Тестирование производительности компилятора с помощью генетического программирования'

Тестирование производительности компилятора с помощью генетического программирования Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
198
30
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
COMPILERS / MACHINE LEARNING / GENETIC PROGRAMMING / CODE GENERATION / COMPILERS TESTING / PERFORMANCE TESTING / КОМПИЛЯТОРЫ / МАШИННОЕ ОБУЧЕНИЕ / ГЕНЕТИЧЕСКОЕ ПРОГРАММИРОВАНИЕ / ГЕНЕРАЦИЯ КОДА / ТЕСТИРОВАНИЕ КОМПИЛЯТОРОВ / ТЕСТИРОВАНИЕ ПРОИЗВОДИТЕЛЬНОСТИ

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Петухов В.А.

В данной статье рассматривается проблема тестирования компиляторов в целом, делается обзор существующих подходов в этой области с указанием ограничения области применения, достоинств и недостатков, включая подходы, основанные на применении алгоритмов машинного обучения. Затем предлагается новый подход для решения проблемы тестирования компилятора в контексте тестирования производительности генерация кода с помощью генетического программирования и запуск компилятора на нём с замерами некоторых характеристик производительности. В статье также приводятся некоторые предварительные результаты применения данного подхода на компиляторе языка программирования Kotlin.The article considers the problem of testing compilers in general, existing approaches in this area and their restrictions of the scope, advantages and disadvantages, including approaches based on the use of machine learning algorithms. Then a new approach is proposed for solving the problem of testing the compiler in the context of performance testing generating code using genetic programming and running the compiler on it with measurements of some performance metrics. The article also provides some preliminary results of applying this approach to the Kotlin programming language compiler.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Тестирование производительности компилятора с помощью генетического программирования»

ТЕСТИРОВАНИЕ ПРОИЗВОДИТЕЛЬНОСТИ КОМПИЛЯТОРА

С ПОМОЩЬЮ ГЕНЕТИЧЕСКОГО ПРОГРАММИРОВАНИЯ

COMPILER PERFORMANCE TESTING THROUGH GENETIC

PROGRAMMING

УДК 004.4'242 DOI: 10.24411/2658-4964-2020-1106

Петухов В.А., аспирант, 1 курс

факультет «Информационных технологий и программирования» Университет ИТМО Россия, г. Санкт-Петербург

Petukhov V.A., i@victor.am

АННОТАЦИЯ

В данной статье рассматривается проблема тестирования компиляторов в целом, делается обзор существующих подходов в этой области с указанием ограничения области применения, достоинств и недостатков, включая подходы, основанные на применении алгоритмов машинного обучения. Затем предлагается новый подход для решения проблемы тестирования компилятора в контексте тестирования производительности - генерация кода с помощью генетического программирования и запуск компилятора на нём с замерами некоторых характеристик производительности. В статье также приводятся некоторые предварительные результаты применения данного подхода на компиляторе языка программирования Kotlin.

ANNOTATION

The article considers the problem of testing compilers in general, existing approaches in this area and their restrictions of the scope, advantages and disadvantages, including approaches based on the use of machine learning algorithms. Then a new approach is proposed for solving the problem of testing the compiler in the context of performance testing - generating code using genetic programming and running the compiler on it with measurements of some performance metrics. The article also provides some preliminary results of applying this approach to the Kotlin programming language compiler.

Ключевые слова: компиляторы, машинное обучение, генетическое программирование, генерация кода, тестирование компиляторов, тестирование производительности.

Keywords: compilers, machine learning, genetic programming, code generation, compilers testing, performance testing.

Введение

В последние несколько десятилетий область разработки программного обеспечения (ПО) получила большое развитие. Помимо изначально появившихся в компьютерной среде сфер, таких как компьютерные игры, социальные сети и так далее; всё больше аспектов окружающего мира становятся так или иначе связанными с информационными технологиями: дистанционное оказание различных услуг, доставка товаров, системы записи, автоматизация процессов на производствах и многое другое. За всеми такими сферами всегда стоит какое-либо ПО: для служб доставки, как правило, это мобильные приложения, веб-сайты и их серверные части; для роботов на производстве - встроенное ПО для управления движениями робота; и так далее.

Связанные с информационными технологиями сферы могут сильно различаться по нефункциональным требованиям [1]: например, требования к надежности работы сайта-визитки значительно ниже, чем требования к системе управления ядерным реактором на атомной электростанции. Тем не менее, за информационным обеспечением самых различных сфер могут стоять одни и те же технологии - например, языки программирования. Требования к надежности таких повсеместно используемых технологий автоматически становятся довольно высокими, поскольку они могут быть использованы в большом числе разнородных программных продуктов [2], и, таким образом, привести к ошибкам в работе этих программных продуктов.

В данной работе предлагается рассмотреть языки программирования, как наиболее фундаментальную технологию и технологию, надежность которой наиболее сложно доказуема [3]. Часть языка программирования, которая влияет на семантику приложения, код которого на нём написан, - это прежде всего компилятор языка. Ошибка в коде компилятора может повлечь компиляцию кода некоторого приложения в неправильный целевой код. Под неправильным целевым кодом подразумевается целевой код, полученный от компилятора, семантика которого не совпадает с семантикой исходного кода. Например, ошибка в коде компилятора из набора GCC гипотетически могла бы оказать огромное влияние на большую часть существующего в мире программного обеспечения [4], в том числе и на компиляторы других языков, код которых также написан, например, на языке C++.

Тестирование компиляторов

Тестирование компилятора можно производить теми же методами, что и любого другого ПО. Однако отличие компилятора для не тривиального языка программирования (такого как, язык математических выражений для калькулятора) от большинства другого ПО заключается в том, что количество возможных тестовых сценариев для него настолько велико, что человеку в составлении тестовых сценариев не под силу даже приблизиться к этому числу

[5].

Отсюда вытекает актуальность задачи автоматизированного тестирования компилятора [6]. Под автоматизированным тестированием подразумевается автоматическое составлений большого количества разнородных тестовых сценариев и запуск компилятора с подачей ему на вход очередного тестового сценария. Однако стоит отметить ограниченность автоматизированного тестирования: кроме проверки падений компилятора, как правило, без дополнительной информации (например, эталонного компилятора) нет возможности проверить сгенерированный целевой код -неизвестно, какой код считать корректным, а какой - нет [7]. На практике часто такая информация имеется: например, у компилятора может существовать несколько генераторов кода (под разные целевые платформы), и есть возможность тестировать уже сгенерированные программы для разных платформ, подавая на вход им различные данные, пытаясь обнаружить таким образом различие в семантике [8]. Другой пример - у языка может существовать формальная модель некоторой подсистемы, например, подсистемы вывода типов - в этом случае мы можем генерировать тестовые сценарии и сравнивать семантику сгенерированного кода с той, которая была бы получена с использованием модели [9].

Помимо проверки корректности сгенерированного кода и неожиданных падений компилятора (проверки соответствия функциональным требованиям), можно тестировать и некоторые из нефункциональных требований - например, производительность. Стоит отменить, что такое нефункциональное требование как производительность является чрезвычайно важным [10]: например, слишком долгий анализ некоторого выражения может повлечь существенную задержку в получении пользователем результата анализа в среде разработки, что создаст сложность в ведении разработки в целом.

Какой бы ни была конкретная цель тестирования компилятора, одной из главных задач автоматизированного тестирования компилятора является генерация кода (генерация тестовых сценариев) [11]. Сложность генерации кода заключается в том, что, как правило, мы не знаем, какой сгенерированный

код является корректным (синтаксически или семантически), а какой - нет. Безусловно, проверять компилятор полезно с любыми входными данными, в том числе и даже с синтаксически некорректным кодом (одна из возможных проблем - на таком коде в компиляторе может произойти утечка памяти, что отразиться на используемой среде разработке - она на время вычислений компилятора может заблокировать интерфейс). Но наиболее интересным кодом для тестирования компилятора является семантически корректный код, поскольку чаще всего ошибки в компиляторе возникают в наиболее сложных его подсистемах: семантическом анализаторе и кодогенераторе [12]. Синтаксические анализаторы, как правило, являются наиболее простыми подсистемами компилятора, и обычно сгенерировать синтаксически корректный код не составляет труда: например, это можно сделать, используя формальную модель этой подсистемы - грамматику языка, которая есть почти у каждого современного языка программирования. Нетривиальной же задачей является сгенерировать семантически корректный код: для этого необходимо учесть все существующие проверки в анализаторе компилятора, что по сложности можно сопоставить с разработкой самого компилятора [13]. На практике эту задачу решают по-разному: кто-то генерирует код по предварительно разработанной модели [14], которая, как правило, соответствует очень ограниченной части подсистемы компилятора; кто-то генерирует код по грамматике языка [9] и отбрасывает подавляющее большинство полученного семантически некорректного кода; кто-то использует для этой задачи алгоритмы машинного обучения [15].

Отдельно стоит отметить генерацию кода методами машинного обучения. В то время, как для реализации генератора кода c описанием правил генерации для каждой конструкций языка могут потребоваться существенные инженерные усилия; код реализованного генератора кода методами машинного обучения может быть в сотни раз меньше [16], а результаты не будет ограничены воплощенной в коде формальной моделью.

Предлагаемый подход

Основываясь на вышеупомянутых аргументах в пользу перспективности генерации программного кода методами машинного обучения, предлагается попробовать данную группу подходов для решения задачи генерации семантически корректного кода. В результате обзора существующих применений алгоритмов машинного обучения для генерации исходного кода было найдено несколько проведённых исследований в области генерации исходного кода с целью поиска падений компилятора и различий с некоторым референсным компилятором. В тоже время, нам не удалось найти

достаточного освещения вопроса поиска проблем производительности в компиляторе методами машинного обучения. В связи с этим мы решили провести исследование в данной области. В качестве первой итерации предлагается попробовать решить наиболее простую задачу - пытаться сгенерировать фрагмент кода, время компиляции которого будет максимально велико. Как наиболее простой способ решения данной задачи, мы решили попробовать алгоритмы генетического программирования, а именно, совмещение мутаций исходного кода из начального набора данных с оптимизацией величины времени анализа программы. При том для повышения доли семантически корректного сгенерированного кода было принято решение попробовать реализовать специальную модификацию -разбиение кода из начального набора данных на блоки, помеченные входными и выходными типами и разрешение комбинирования таких блоков только с совместимыми входными и выходными типами. В качестве языка программирования, компилятор которого будет тестироваться, был выбран Kotlin, как один из наиболее молодых и перспективных языков программирования с довольно сложным семантическим анализатором. Так, в 2017 году Kotlin стал одним из официальных языков для разработки мобильных приложений на операционной системе Android [17], а в 2019 году - предпочтительным [18].

Таким образом, в качестве цели исследования можно отменить генерацию исходного кода на ЯП Kotlin путём мутации фрагментов кода из начального набора данных таким образом, чтобы либо время компиляции, либо потребляемая память были максимальны.

Формулируя задачу в терминах генетического программирования, удобно использовать прямое древовидное кодирование, поскольку оно соответствует естественному представлению программного кода -синтаксическому дереву. В качестве набора данных для первой итерации исследования можно использовать существующий набор тестов к компилятору - по большей части это самодостаточные маленькие семантически корректные программы, которые удобно мутировать.

В качестве вероятности селекции предлагается взять простейшую формулу с нормализацией (1).

Pi=?rT (1)

Где fi - значение фитнес-функции для i-го индивидуума, N - количество индивидуумов в популяции. В качестве функции-кроссовера предлагается

взять формулу (2), которая предполагает включение вышеописанной модификации.

Acrossover: Xi ^ Xj (2)

Где X1 - множество всех поддеревьев дерева разбора i-й программы; i, j - случайные индексы (нумерация в порядке обхода дерева разбора в глубину) поддеревьев такие, что выполняется (3).

V Cx е InputTypes(Xm) 3 C2 £ OutputTypes(X1n_1): Cx <: C2 (3)

Запись Cx <: C2 означает отношение подтипизации, а именно, что Cx является подтипом C2 с точки зрения системы типов языка Kotlin [19].

В качестве функции-мутации предлагается выбрать (4), но в первой итерации эксперимента для упрощения мы не будем пользоваться мутациями, а ограничимся лишь скрещиванием.

Amutat1on = removeNode() or generateNode()

or changeNodeValueToGenerated()

В следующих итерациях эксперимента предлагается для мутаций, а именно - для создания новых узлов в синтаксическом дереве, использовать грамматику языка Kotlin.

Фитнес-функцию предлагается выбрать на данный момент в виде (5), но в будущем иметь вольность для её модификации и в дальнейшем подбирать её эмпирически в зависимости от получаемых результатов. Данная формула позволяет учитывать возможный увеличивающийся размер исходного кода, поскольку увеличение времени анализа на коде большего размера является естественным, и не вызвано проблемами в производительности компилятора.

AnalysisTime(P2) * IncreasedAnalysisTime( P1; P2)

Ffintess * =--(5)

in ess IncreasedParseTreeSize(Xm, Xn) ( )

Процесс скрещивания проиллюстрирован на Рисунке 1.

fun testjLO {

/* — Block 1 start — */

val x: Arraycout Number> = materializeO

val y: Comparable<String>? = materializeO

var i: Number = materialized)

/* — Block 1 end — */

/* — Block 2 start — */ /*

* Input types:

* typeOf(x) := Arraycout Number>

* typeOf(y) := Comparable<String>

* typeOf(i) := Number */

val result = if (x.size = 1) {

1..x.singled).toByte() + y!!.compareTo(".") } else {

x.first().toLong 0.rangeTo(y!!.compa reTo("")) } = 1

/* — Block 2 end — */

fun testJ2() {

/* — Block 1 start — */

val x: Array<Int> = materializeO

val y: Comparable<Any> = materializeO

val z: CharSequence = materialize0

var i: Int = materialized)

/*

* Output types: ^^^^^^^ typeOf(x) := Array<Int>

* ^^*De0f(y) := Comparable<String>

* typ?W(i) := Number

/* — Block 1 Ad — */

/* — Block 2 start — */ while (x.size != i) { i++

x[i] = у.compa reTo(x[i].inv()).and(z.length)

}

/* — Block 2 end — */

Array<Int> <: Arraycout Number>

Comparable<in Any> <: Comparable<in String>?

Int <: Number

Рисунок 1 - Процесс скрещивания двух индивидуумов (фрагментов

кода)

Предварительные результаты

В результате проведение первых экспериментов с базовыми описанными выше параметрами и набором тестов компилятора Kotlin в качестве начального набора данных нам уже удалось получить по крайней мере один фрагмент кода, который соответствует действительной проблеме в производительности компилятора. Упрощенный пример представлен на Рисунке 2. В данном примере с каждым добавлением верхней границы для типового параметра T, содержащей его самого, время вывода типов параметров функции test в месте её вызова увеличивается экспоненциально [20].

class A< >: B<T>, C< >, D<T>, E< >

interface B<T> interface C< > interface D< > interface E< >

fun < > test{a: T, b: ' ) where : B< >, : C< >, : D< > {

/*

* Upper bounds for T: B<T> => 0.05 sec,

* Upper bounds for T: B<T>, C<T> => 0.24 sec

* Upper bounds for T: B<T>, C<T>, D<T> => 2.8 sec

* Upper bounds for T: B<T>, C<T>, D<T>, E<T> => 16.1 sec

*/

fun mainO {

// exponential complexity during inferring a type parameter

// (intersecting upper bound combinations) test(A(), AO)

}

Рисунок 2 - пример с проблемой производительности компилятора, обнаруженный с помощью предложенного подхода

Заключение

По предварительным результатам реализации описанного подхода нам удалось убедиться в целесообразности продолжения исследования и совершенствования предложенного подхода, заменяя базовые параметры и методы более продвинутыми, адаптированными под исследуемый компилятор языка программирования и тип проблем, которые хотелось бы обнаруживать. В частности, в качестве продолжения работы предлагается реализовать оператор мутации, что предположительно позволит внести разнородность в популяцию, поскольку данный оператор будет опираться на существующую формальную модель - грамматику языка, которая не связана со специально подготовленным и отобранным набором данных (фрагментами исходного кода, таких как, набор тестов к компилятору)

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

ЛИТЕРАТУРА

1. Химонин Ю. И. Сбор и анализ требований к программному продукту [Электронный ресурс]. URL: http ://mastefanov. com/wp-content/uploads/2014/09/0-Software Requirements Khimonin.pdf (дата обращения: 31.05.2020)

2. Michael J. Coleman, Tom Manns. Software Quality Assurance // Macmillan International Higher Education, 1996

3. Rodriguez, Leonardo & Pagano, Miguel & Fridlender, Daniel // Proving Correctness of a Compiler Using Step-indexed Logical Relations. Electronic Notes in Theoretical Computer Science, 2016. No 323. P. 197-214.

4. PYPL Popularity of Programming Language [Электронный ресурс]. URL: http://pypl.github.io/PYPL.html (дата обращения: 31.05.2020).

5. Junjie Chen, Jibesh Patra, Michael Pradel, Yingfei Xiong, Hongyu Zhang, Dan Hao, and Lu Zhang. A Survey of Compiler Testing // ACM Comput. Surv. 53, 1, Article 4. 2020. P. 36.

6. Q. Tao, W. Wu, C. Zhao and W. Shen. An Automatic Testing Approach for Compiler Based on Metamorphic Testing Technique // Asia Pacific Software Engineering Conference, Sydney, NSW. 2010. P. 270-279.

7. Bessonov Vyacheslav, Lyadova Lyudmila. One Approach to Automated Compiler Verification // Proceedings of the 8th Spring/Summer Young Researchers' Colloquium on Software Engineering. 2014. P. 143-149.

8. Daniil Stepanov, Marat Akhin, Mikhail Belyaev. ReduKtor: How We Stopped Worrying About Bugs in Kotlin Compiler // ASE '19: Proceedings of the 34th IEEE/ACM International Conference on Automated Software Engineering. 2019. P. 317-326

9. Godefroid, Patrice & Kiezun, Adam & Levin, Michael. Grammar-based Whitebox Fuzzing // Proceedings of the ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). 2008. No 43. P. 206-215.

10. Neelakantan, K., Athithan, G., & Ghosh, P. The role of compilers in computersystem performance // Current Science. No 60(11). 1991. P. 650-652.

11. Yang Chen, Alex Groce, Chaoqiang Zhang, Weng-Keen Wong, Xiaoli Fern, Eric Eide, and John Regehr. Taming compiler fuzzers // SIGPLAN Not. 48, 6. 2013. P. 197-208.

12. Oege de Moor, Michael I. Schwartzbach. Compiler Construction // 18th International Conference, Held as Part of the Joint European Conferences on Theory and Practice of Software, ETAPS 2009, York, UK, Proceedings. 2009.

13. Han, H., Oh, D., & Cha, S.K. CodeAlchemist: Semantics-Aware Code Generation to Find Vulnerabilities in JavaScript Engines // NDSS. 2019.

14. Rohan Padhye, Caroline Lemieux, Koushik Sen, Mike Papadakis, and Yves Le Traon. Semantic fuzzing with zest // Proceedings of the 28th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA 2019). Association for Computing Machinery, New York, NY, USA. 2019. P. 329340.

15. Chris Cummins, Pavlos Petoumenos, Alastair Murray, and Hugh Leather. Compiler fuzzing through deep learning // Proceedings of the 27th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA 2018). Association for Computing Machinery, New York, NY, USA. 2018. P. 95-105.

16. Uri Alon, Roy Sadaka, Omer Levy, Eran Yahav. Structural Language Models for Any-Code Generation, 2019. [Электронный ресурс]. URL: https: //openreview. net/forum? id=HylZIT4Yvr (дата обращения: 31.05.2020).

17. Google is adding Kotlin as an official programming language for Android development [Электронный ресурс]. URL: https://www.theverge.com/2017/5/17/15654988/google-jet-brains-kotlin-programming-language-android-development-io-2017 (дата обращения: 31.05.2020).

18. Kotlin is now Google's preferred language for Android app development. [Электронный ресурс]. URL: https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/ (дата обращения: 31.05.2020).

19. Kotlin language specification. [Электронный ресурс]. URL: https: //kotlin.github. io/kotlin-spec/ (дата обращения: 31.05.2020).

20. Type inference for argument type is very slow if several interfaces with a type parameter is used as an upper bound of a type parameter. [Электронный ресурс]. URL: https://youtrack.jetbrains.com/issue/KT-28650 (дата обращения: 31.05.2020).

LITERATURE

1. Himonin Y. I. Collection and analysis of software product requirements [Online]. URL: http://mastefanov.com/wp-content/uploads/2014/09/0-Software Requirements Khimonin.pdf (access date: 31.05.2020)

2. Michael J. Coleman, Tom Manns. Software Quality Assurance // Macmillan International Higher Education, 1996

3. Rodriguez, Leonardo & Pagano, Miguel & Fridlender, Daniel // Proving Correctness of a Compiler Using Step-indexed Logical Relations. Electronic Notes in Theoretical Computer Science, 2016. No 323. P. 197-214.

4. PYPL PopularitY of Programming Language [Online]. URL: http://pypl.github.io/PYPL.html (access date: 31.05.2020).

5. Junjie Chen, Jibesh Patra, Michael Pradel, Yingfei Xiong, Hongyu Zhang, Dan Hao, and Lu Zhang. A Survey of Compiler Testing // ACM Comput. Surv. 53, 1, Article 4. 2020. P. 36.

6. Q. Tao, W. Wu, C. Zhao and W. Shen. An Automatic Testing Approach for Compiler Based on Metamorphic Testing Technique // Asia Pacific Software Engineering Conference, Sydney, NSW. 2010. P. 270-279.

7. Bessonov Vyacheslav, Lyadova Lyudmila. One Approach to Automated Compiler Verification // Proceedings of the 8th Spring/Summer Young Researchers' Colloquium on Software Engineering. 2014. P. 143-149.

8. Daniil Stepanov, Marat Akhin, Mikhail Belyaev. ReduKtor: How We Stopped Worrying About Bugs in Kotlin Compiler // ASE '19: Proceedings of the 34th IEEE/ACM International Conference on Automated Software Engineering. 2019. P. 317-326

9. Godefroid, Patrice & Kiezun, Adam & Levin, Michael. Grammar-based Whitebox Fuzzing // Proceedings of the ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). 2008. No 43. P. 206-215.

10. Neelakantan, K., Athithan, G., & Ghosh, P. The role of compilers in computersystem performance // Current Science. No 60(11). 1991. P. 650-652.

11. Yang Chen, Alex Groce, Chaoqiang Zhang, Weng-Keen Wong, Xiaoli Fern, Eric Eide, and John Regehr. Taming compiler fuzzers // SIGPLAN Not. 48, 6. 2013. P. 197-208.

12. Oege de Moor, Michael I. Schwartzbach. Compiler Construction // 18th International Conference, Held as Part of the Joint European Conferences on Theory and Practice of Software, ETAPS 2009, York, UK, Proceedings. 2009.

13. Han, H., Oh, D., & Cha, S.K. CodeAlchemist: Semantics-Aware Code Generation to Find Vulnerabilities in JavaScript Engines // NDSS. 2019.

14. Rohan Padhye, Caroline Lemieux, Koushik Sen, Mike Papadakis, and Yves Le Traon. Semantic fuzzing with zest // Proceedings of the 28th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA 2019). Association for Computing Machinery, New York, NY, USA. 2019. P. 329340.

15. Chris Cummins, Pavlos Petoumenos, Alastair Murray, and Hugh Leather. Compiler fuzzing through deep learning // Proceedings of the 27th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA 2018). Association for Computing Machinery, New York, NY, USA. 2018. P. 95-105.

16. Uri Alon, Roy Sadaka, Omer Levy, Eran Yahav. Structural Language Models for Any-Code Generation, 2019. [Online]. URL: https://openreview.net/forum?id=HylZIT4Yvr (access date: 31.05.2020).

17. Google is adding Kotlin as an official programming language for Android development [Online]. URL: https://www.theverge.com/2017/5/17/15654988/google-jet-brains-kotlin-programming-language-android-development-io-2017 (access date: 31.05.2020).

18. Kotlin is now Google's preferred language for Android app development. [Online]. URL: https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/ (access date: 31.05.2020).

19. Kotlin language specification. [Online]. URL: https://kotlin.github.io/kotlin-spec/ (access date: 31.05.2020).

20. Type inference for argument type is very slow if several interfaces with a type parameter is used as an upper bound of a type parameter. [Online]. URL: https://youtrack.jetbrains.com/issue/KT-28650 (access date: 31.05.2020).

i Надоели баннеры? Вы всегда можете отключить рекламу.